evolver-2.30c.dfsg/0000755000175300017530000000000011672223713014433 5ustar hazelscthazelsctevolver-2.30c.dfsg/evolver.10000644000175300017530000000677511410765113016211 0ustar hazelscthazelsct.\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. .\"See Also: .\"man mdoc.samples for a complete listing of options .\"man mdoc for the short list of editing options .\"/usr/share/misc/mdoc.template .Dd Mon May 19 2003 \" DATE .Dt Evolver 1 \" Program name and manual section number .Sh NAME \" Section Header - required - don't modify .Nm evolver .\" The following lines are read in generating the apropos(man -k) database. Use only key .\" words here as the database is built based on the words here and in the .ND line. .Nd The Surface Evolver, minimize energy of a surface .Sh SYNOPSIS \" Section Header - required - don't modify .Nm .Op Fl adehimqwxy \" [-adehimqwxy] .Op Fl f Ar file \" [-f file] .Op Fl pN \" [-pN] .Op Ar datafile \" Underlined argument - use .Ar anywhere to underline .Sh DESCRIPTION \" Section Header - required - don't modify The Surface Evolver is a program that minimizes the energy of a triangulated surface according to designated energies and constraints. This man page only documents command line options and environment variables. The full package and documentation are available at http://www.susqu.edu/brakke/evolver. .Pp \" Inserts a space Command line options: .Bl -tag -width -indent \" Differs from above in tag removed .It Ar datafile Text file defining a surface. If omitted, you will be prompted. .It Fl a Autoconvert to named quantities when needed (default is on); use "-a-" to deactivate autoconversion. .It Fl d Begin with parser debugging on (equivalent to "debug" runtime command). Beware of copious output. .It Fl e Echo stdin to stdout; meant for testing piped input. .It Fl f Ar file After loading datafile, read commands from .Ar file , then command line prompt. .It Fl h Print help for command line options. .It Fl i Preserve datafile numbers for element id's, rather than renumbering. .It Fl m Begin with memory debugging on (equivalent to "memdebug" runtime command). Beware of copious output. .It Fl pN Run with N concurrent processes. .It Fl q Convert to named quantities at start (equivalent to "convert_to_quantities" runtime command). .It Fl Q Suppress echoing of "read" section of datafile, and other files read in. .It Fl w Exit immediately after any warning or error message; meant for batch runs. .It Fl x Exit immediately after any error message; meant for batch runs. .It Fl y Break to user prompt after any warning message. .El \" Ends the list .Pp .\" .Sh ENVIRONMENT \" May not be needed .\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 .\" .It Ev ENV_VAR_1 .\" Description of ENV_VAR_1 .\" .It Ev ENV_VAR_2 .\" Description of ENV_VAR_2 .\" .El .Sh ENVIRONMENT VARIABLES .Bl -tag -width "EVOLVERPATH" -compact .It EVOLVERPATH Colon-separated list of paths automatically searched for datafiles, included files, or help documentation. .Sh FURTHER DOCUMENTATION See manual224.pdf in the Evolver distribution, browse evolver.htm in the doc subdirectory of the distribution, or browse the documentation on-line at http://www.susqu.edu/brakke/evolver/html/evolver.htm .Sh WEBSITE The Surface Evolver home page is http://www.susqu.edu/brakke/evolver. .Sh BUGS Send bug reports to brakke@susqu.edu. Please include the datafile and exact instructions for reproducing the problem. evolver-2.30c.dfsg/README0000644000175300017530000001256711410765113015321 0ustar hazelscthazelsctInstructions for installing the Surface Evolver on unix-style systems. Quick start: 1. Unpack the Evolver archive. 2. In the src subdirectory, edit Makefile to uncomment the lines for your system. 3. Run "make". 4. Test by running "./evolver ../fe/cube.fe" Detailed instructions: 1. The Evolver is distributed in a compressed tar archive evolver-2.30.tar.gz available from http://www.susqu.edu/brakke/evolver. Get this file into a working directory. The packed archive is about 2MB, unpacks to about 5MB. You will probably need another 3 or 4 MB to compile. 2. Uncompress the archive with gunzip evolver-2.30.tar.gz Extract the files with tar xvf evolver.tar This will unpack into three subdirectories: src (source code), doc (the html version of the manual), and fe (sample datafiles). The working directory will also contain a PDF version of the manual, and a man page, evolver.1. 3. Install the man page: copy evolver.1 to some appropriate place on your manpath, such as /usr/local/share/man/man1. You may have to become root to have permission to do this. 4. Set the EVOLVERPATH environment variable: Evolver needs to find the initial datafile and sometimes other files (e.g. command files for the "read" command, the help documentation files). If the named file is not in the current directory, then an environment variable called EVOLVERPATH will be consulted for a directory search list. The datafile directory and the directory with the HTML documentation files should definitely be included. The format is the same as the usual PATH environment variable. Set it up as usual in your system, in .profile or .login or .cshrc or wherever: Unix C shell: setenv EVOLVERPATH /usr/you/evolver/fe:/usr/you/evolver/doc Bourne shell: EVOLVERPATH=/usr/you/evolver/fe:/usr/you/evolver/doc export EVOLVERPATH 5. Change to the src subdirectory of your Evolver directory. 6. Modify Makefile for your system. Makefile begins with sets of macro definitions for various systems. If your system is listed, remove the comment symbols '#' from start of the appropriate lines. If your system is not there, use the GENERIC lines, or set up your own. If you do define your own, be sure to put a corresponding section in include.h. 7. In Makefile, edit the CFLAGS line to have the proper options (optimization, floating point option, etc.). 8. In Makefile, GRAPH should be the name of a screen graphics interface file. Use glutgraph.o if possible; most systems have OpenGL/GLUT graphics now. GLUT graphics uses a separate thread to display graphics, so if you use GLUT, you must put -DPTHREADS in CFLAGS and put -lpthread in GRAPHLIB. If not using GLUT, for primitive X windows graphics you can use xgraph.o. For no built-in screen graphics at all you can use nulgraph.o. GRAPHLIB should be the appropriate graphics library plus any other libraries needed. 9. If you want to use parallel processes on a multiprocessor machine, put -DPTHREADS in CFLAGS and put -lpthread in GRAPHLIB. Currently, the only calculations done in parallel are basic energies and named quantities. The number of processes actually done in parallel can be controlled with the -p n command line option. 10. If you want Evolver to be able to use geomview, include -DOOGL in CFLAGS. 11. If you want Evolver to operate in a higher space dimension than the the default maximum of 4, include -DMAXCOORD=n in CFLAGS, where n is the maximum space dimension. This sets the upper limit of dimensionality, and is used for allocating space in data structures. 12. If your system supports the long double data type, you can compute and print values in higher precision by compiling with -DLONGDOUBLE in CFLAGS. But this slows computations, and should be used only by precision fanatics. 13. If you wish to use the commands based on the METIS partitioning software (metis, kmetis, body_metis, and metis_factor), then you should download the METIS package from, http://www-users.cs.umn.edu/~karypis/metis/ and "make" the library libmetis.a (on some systems, make complains it cannot find ranlib, but the resulting libmetis.a still works). In Evolver's Makefile, add -DMETIS to CFLAGS, and add -lmetis to GRAPHLIB. You will probably also have to add -L metispath to GRAPHLIB to tell the linker where to find libmetis.a. Note that METIS is incorporated in the Windows executable. If you are using hessian commands on very large surfaces, then metis_factor can be much faster than the other sparse matrix factoring schemes in Evolver, and I highly recommend it. 14. From the shell command prompt in the src directory, run "make". This will produce the Evolver executable file named "evolver" in the src directory. If there are errors, hopefully you will only have to change the system-specific parts of Makefile and include.h to get things to work. If significant changes to other files are needed, let me know at brakke@susqu.edu. 15. Copy the evolver executable to someplace on your PATH, such as $HOME/bin or /usr/local/bin, or make a link someplace on your PATH to the evolver executable. 16. Test by opening a new shell and running "evolver cube". Now you should be able to follow the tutorials in the HTML or printed manual. End of README evolver-2.30c.dfsg/fe/0000755000175300017530000000000011410765113015020 5ustar hazelscthazelsctevolver-2.30c.dfsg/fe/unshear.cmd0000755000175300017530000000554611410765113017167 0ustar hazelscthazelsct// unshear.cmd // Evolver command to get torus fundamental region closer to rectangular. // Works in either 2 or 3 dimensions. (eventually, just 2D now) // NOTE: "unshear" needs to change the torus periods, but can't directly // since torus_periods is a read-only variable (because it is remembered // as a formula). So the assumption is that the shear entries of the // torus_periods matrix are variables of particular names, shearij for // torus_periods[i][j]. (remember indexing starts with 1) // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke if not is_defined("shear21") then errprintf "unshear.cmd assumes torus_periods[2][1] defined as shear21.\n" ; // 2D version unshear2 := { local shifts,vshifts,wrap1,wrap2,wraprest; // test for horizontal shear shifts := floor(torus_periods[2][1]/torus_periods[1][1] + .5); shear21 -= shifts*torus_periods[1][1]; // move vertices over foreach vertex vv do { vshifts := -floor(vv.x*inverse_periods[1][1]+vv.y*inverse_periods[1][2]); if vshifts != 0 then wrap_vertex(vv.id,(vshifts imod 32)); }; // change edge wraps foreach edge ee where ((ee.wrap idiv 64) imod 32) == 1 do { wrap1 := ee.wrap imod 64; wraprest := ee.wrap - wrap1; if wrap1 > 15 then wrap1 -= 32; wrap1 += shifts; ee.wrap := wraprest + (wrap1 imod 32); }; foreach edge ee where ((ee.wrap idiv 64) imod 32) == 31 do { wrap1 := ee.wrap imod 64; wraprest := ee.wrap - wrap1; if wrap1 > 15 then wrap1 -= 32; wrap1 += -shifts; ee.wrap := wraprest + (wrap1 imod 32); }; recalc; // get inverse periods set up right for next stage // test for vertical shear shifts := floor(torus_periods[1][2]/torus_periods[2][2] + .5); shear12 -= shifts*torus_periods[2][2]; // move vertices over foreach vertex vv do { vshifts := -floor(vv.x*inverse_periods[2][1]+vv.y*inverse_periods[2][2]); if vshifts != 0 then wrap_vertex(vv.id,(vshifts imod 32)*64); }; // change edge wraps foreach edge ee where (ee.wrap imod 32) == 1 do { wrap2 := (ee.wrap idiv 64) imod 64; wraprest := ee.wrap - wrap2*64; if wrap2 > 15 then wrap2 -= 32; wrap2 += shifts; ee.wrap := wraprest + (wrap2 imod 32)*64; }; foreach edge ee where (ee.wrap imod 32) == 31 do { wrap2 := (ee.wrap idiv 64) imod 64; wraprest := ee.wrap - wrap2*64; if wrap2 > 15 then wrap2 -= 32; wrap2 -= shifts; ee.wrap := wraprest + (wrap2 imod 32)*64; }; } // 3D version unshear3 := { errprintf "Unshear not yet implemented for 3D.\n"; } // Overall command, just a wrapper unshear := { if not torus then { errprintf "Unshear only relevant to torus model.\n"; return; }; if space_dimension == 2 then unshear2 else if space_dimension == 3 then unshear3 else errprintf "Unshear not implemented for space dimension %d\n", space_dimension; } evolver-2.30c.dfsg/fe/quadbbox.cmd0000755000175300017530000001253511410765113017323 0ustar hazelscthazelsct// quadbbox.cmd // Finds bounding box for each facet in quadratic model. // Not suitable for torus model. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // Usage: Run eboxes or fboxes. Results left in the arrays ebox or fbox // attributes. define edge attribute ebox real [6] // xmin,xmax,ymin,ymax,zmin,zmax define facet attribute fbox real [6] eboxes := { local ii,v0,v1,v2,v4,uopt,denom; if ( !quadratic ) then print "eboxes: Must be in quadratic mode.\n" else foreach edge ee do { ii := 1; while ( ii <= 3 ) do { if ( ii == 1 ) then { v0 := ee.vertex[1].x; v2 := ee.vertex[2].x; v1 := ee.vertex[3].x; } else if ( ii == 2 ) then { v0 := ee.vertex[1].y; v2 := ee.vertex[2].y; v1 := ee.vertex[3].y; } else { v0 := ee.vertex[1].z; v2 := ee.vertex[2].z; v1 := ee.vertex[3].z; } ; if ( v0 > v1 ) then { set ee ebox[2*ii-1] v1; set ee ebox[2*ii] v0; } else { set ee ebox[2*ii-1] v0; set ee ebox[2*ii] v1; }; if ( v2 < ee.ebox[2*ii-1] ) then set ee ebox[2*ii-1] v2; if ( v2 > ee.ebox[2*ii] ) then set ee ebox[2*ii] v2; denom := v0 - 2*v1 + v2; if ( denom == 0.0 ) then { ii := ii+1; continue; }; uopt := (-1.5*v0 + 2*v1 - .5*v2)/denom; if ( uopt <= 0.0 or uopt >= 1.0 ) then { ii := ii+1;continue; }; v4 := 0.5*(1-uopt)*(2-uopt)*v0 + uopt*(2-uopt)*v1 + 0.5*uopt*(uopt-1)*v2; if ( v4 < ee.ebox[2*ii-1] ) then set ee ebox[2*ii-1] v4; if ( v4 > ee.ebox[2*ii] ) then set ee ebox[2*ii] v4; ii := ii + 1; } } } fboxes := { local ii,v0,v1,v2,v3,v4,v5,uopt,denom; local a11,a12,b1,a21,a22,b2,vopt,vcrit; eboxes; if ( ! quadratic ) then print "fboxes: Must be in quadratic mode.\n" else foreach facet ff do { ii := 0; /* which coordinate */ while ( ii < 3 ) do { ii := ii + 1; // so can use continue /* first, edge boxes */ set ff fbox[2*ii-1] ff.edge[1].ebox[2*ii-1]; set ff fbox[2*ii] ff.edge[1].ebox[2*ii]; if ( ff.edge[2].ebox[2*ii-1] < ff.fbox[2*ii-1] ) then set ff fbox[2*ii-1] ff.edge[2].ebox[2*ii-1]; if ( ff.edge[2].ebox[2*ii] > ff.fbox[2*ii] ) then set ff fbox[2*ii] ff.edge[2].ebox[2*ii]; if ( ff.edge[3].ebox[2*ii-1] < ff.fbox[2*ii-1] ) then set ff fbox[2*ii-1] ff.edge[3].ebox[2*ii-1]; if ( ff.edge[3].ebox[2*ii] > ff.fbox[2*ii] ) then set ff fbox[2*ii] ff.edge[3].ebox[2*ii]; if ( ii == 1 ) then { v0 := ff.edge[1].vertex[1].x; v1 := ff.edge[1].vertex[3].x; v2 := ff.edge[1].vertex[2].x; v3 := ff.edge[3].vertex[3].x; v4 := ff.edge[2].vertex[3].x; v5 := ff.edge[2].vertex[2].x; } else if ( ii == 2 ) then { v0 := ff.edge[1].vertex[1].y; v1 := ff.edge[1].vertex[3].y; v2 := ff.edge[1].vertex[2].y; v3 := ff.edge[3].vertex[3].y; v4 := ff.edge[2].vertex[3].y; v5 := ff.edge[2].vertex[2].y; } else { v0 := ff.edge[1].vertex[1].z; v1 := ff.edge[1].vertex[3].z; v2 := ff.edge[1].vertex[2].z; v3 := ff.edge[3].vertex[3].z; v4 := ff.edge[2].vertex[3].z; v5 := ff.edge[2].vertex[2].z; }; // x_u coeff of u a11 := v0 - 2*v1 + v2; // x_u coeff of v a12 := v0 - v1 - v3 + v4; // x_u rhs b1 := -(-1.5*v0 + 2*v1 - .5*v2); // x_v coeff of u a21 := v0 - v1 - v3 + v4; // x_v coeff of v a22 := v0 - 2*v3 + v5; // x_v rhs b2 := -(-1.5*v0 + 2*v3 - 0.5*v5); // solve for critical point denom := a11*a22 - a12*a21; if ( denom == 0.0 ) then continue; uopt := (b1*a22 - b2*a12)/denom; vopt := (a11*b2 - a21*b1)/denom; if ( uopt <= 0.0 ) then continue; if ( vopt <= 0.0 ) then continue; if ( uopt+vopt >= 2.0 ) then continue; vcrit := 0.5*(uopt+vopt-1)*(uopt+vopt-2)*v0 + uopt*(2-uopt-vopt)*v1 + 0.5*uopt*(uopt-1)*v2 + vopt*(2-uopt-vopt)*v3 + uopt*vopt*v4 + 0.5*vopt*(vopt-1)*v5; if ( vcrit < ff.fbox[2*ii-1] ) then set ff fbox[2*ii-1] vcrit; if ( vcrit > ff.fbox[2*ii] ) then set ff fbox[2*ii] vcrit; } } } evolver-2.30c.dfsg/fe/ansurf.cmd0000755000175300017530000000443311410765113017012 0ustar hazelscthazelsct// ansurf.cmd // Surface Evolver command to produce file of ANSYS input for // vertices, edges, and faces to produce a surface // for ANSYS meshing. Beware this is a very simple-minded // translation to ANSYS format. // Modified Feb. 2006 to not assume consecutive numbering of elements. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // usage: ansurf >>> "filename" // Attributes to hold ANSYS numbering of elements define vertex attribute ansurf_knumber real define edge attribute ansurf_lnumber real define facet attribute ansurf_alnumber real // vertices as ANSYS keypoints ansurf_nodes := { local knumber; knumber := 0; foreach vertex vv do { printf "k,,%20.15g,%20.15g,%20.15g\n",x,y,z; knumber += 1; vv.ansurf_knumber := knumber; } } ansurf_edges := { local lnumber; lnumber := 0; if (quadratic) then foreach edge ee do { printf "larc,%g,%g,%g\n",ee.vertex[1].ansurf_knumber, ee.vertex[2].ansurf_knumber,ee.vertex[3].ansurf_knumber; lnumber += 1; ee.ansurf_lnumber := lnumber; } else foreach edge ee do { printf "l,%g,%g\n",ee.vertex[1].ansurf_knumber, ee.vertex[2].ansurf_knumber; lnumber += 1; ee.ansurf_lnumber := lnumber; } } ansurf_faces := { local alnumber; alnumber := 0; foreach facet ff do { printf "al,%g,%g,%g\n", ff.edge[1].ansurf_lnumber,ff.edge[2].ansurf_lnumber, ff.edge[3].ansurf_lnumber; alnumber += 1; ff.ansurf_alnumber := alnumber; } } // define volumes, one per body ansurf_bodies := { foreach body bb do { // select areas local flag; flag := 0; foreach bb.facet ff do { if flag then printf "ASEL,A,AREA,,%g,%g\n",ff.ansurf_alnumber, ff.ansurf_alnumber else printf "ASEL,S,AREA,,%g,%g\n",ff.ansurf_alnumber, ff.ansurf_alnumber; flag := 1; }; printf "VA,ALL\n"; } } // define areas ansurf := { printf "/PREP7\n"; printf "/NOPR\n"; ansurf_nodes; ansurf_edges; ansurf_faces; ansurf_bodies; } // Usage: ansurf >>> "ansys_file" evolver-2.30c.dfsg/fe/x3d.cmd0000755000175300017530000000467611410765113016223 0ustar hazelscthazelsct// x3d.cmd // Surface Evolver script to export surface as an X3D file for 3D viewing // in a browser. // Usage: x3d >>> "filename.x3d" // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke define rgb real[16][4]; rgb[1][1] := 0.0; rgb[1][2] := 0.0; rgb[1][3] := 0.0; rgb[2][1] := 0.0; rgb[2][2] := 0.0; rgb[2][3] := 1.; rgb[3][1] := 0.0; rgb[3][2] := 1.; rgb[3][3] := 0.0; rgb[4][1] := 0.0; rgb[4][2] := 1.; rgb[4][3] := 1.; rgb[5][1] := 1.; rgb[5][2] := 0.0; rgb[5][3] := 0.0; rgb[6][1] := 1.; rgb[6][2] := 0.0; rgb[6][3] := 1.; rgb[7][1] := 1.; rgb[7][2] := 0.5; rgb[7][3] := 0.; rgb[8][1] := .6; rgb[8][2] := .6; rgb[8][3] := .6; rgb[9][1] := .3; rgb[9][2] := .3; rgb[9][3] := .3; rgb[10][1] := .3; rgb[10][2] := .8; rgb[10][3] := 1.; rgb[11][1] := .5; rgb[11][2] := 1.; rgb[11][3] := .5; rgb[12][1] := .5; rgb[12][2] := 1.; rgb[12][3] := 1.; rgb[13][1] := 1.; rgb[13][2] := .5; rgb[13][3] := .5; rgb[14][1] := 1.; rgb[14][2] := .5; rgb[14][3] := 1; rgb[15][1] := 1.; rgb[15][2] := 1.; rgb[15][3] := .0; rgb[16][1] := 1; rgb[16][2] := 1; rgb[16][3] := 1; x3d := { printf"\n"; printf"\n"; printf "\n"; printf "
\n"; printf " \n",datafilename; printf " \n"; printf " \n"; printf "
\n"; printf " \n"; printf " \n"; foreach facet ff where color >= 0 do { printf " \n"; printf " \n"; printf " \n", rgb[ff.color+1][1], rgb[ff.color+1][2],rgb[ff.color+1][3]; printf " \n"; printf " \n"; printf " \n", ff.vertex[1].x,ff.vertex[1].y,ff.vertex[1].z, ff.vertex[2].x,ff.vertex[2].y,ff.vertex[2].z, ff.vertex[3].x,ff.vertex[3].y,ff.vertex[3].z; printf " \n"; printf " \n"; }; printf " \n"; printf " \n"; printf "
\n"; } evolver-2.30c.dfsg/fe/octa.fe0000644000175300017530000000211311410765113016257 0ustar hazelscthazelsct// octa.fe // Evolver data file for symmetric nonstable film in octahedral frame. // Pop edges, iterate, then pop vertices. vertices 1 0 0 0 2 2 0 0 fixed 3 0 2 0 fixed 4 -2 0 0 fixed 5 0 -2 0 fixed 6 0 0 2 fixed 7 0 0 -2 fixed edges 1 2 3 fixed 2 3 4 fixed 3 4 5 fixed 4 5 2 fixed 5 2 6 fixed 6 3 6 fixed 7 4 6 fixed 8 5 6 fixed 9 7 2 fixed 10 7 3 fixed 11 7 4 fixed 12 7 5 fixed 13 1 2 14 1 3 15 1 4 16 1 5 17 1 6 18 7 1 faces 1 13 1 -14 2 14 2 -15 3 15 3 -16 4 16 4 -13 5 13 5 -17 6 14 6 -17 7 15 7 -17 8 16 8 -17 9 9 -13 -18 10 10 -14 -18 11 11 -15 -18 12 12 -16 -18 read // Typical evolution, winding up with the central tetrahedral point // version of the octahedral film. This evolution is not real tidy, // taking several more pops before settling down, but is just recorded // from a trial sequence without tidying up. gogo := { o; U; g 20; t .1; g 10; o; g 20; u; V; t .1; o; g 12; o; u; V; u; V; g 10; t .1; o; V; g 10; t .1; u; V; g 10; t .1; g 10; } evolver-2.30c.dfsg/fe/gaussref.cmd0000755000175300017530000000462511410765113017336 0ustar hazelscthazelsct// gaussref.cmd // Refining using Gauss map as criterion. // Refines edges where difference in normal // exceeds user-set amount. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // Usage: set the variable gaussref_tolerance, and do gaussref. // Set by user; difference in normals in radians gaussref_tolerance := 0.3 gaussref := { local ax,ay,az,bx,by,bz,diff,maga,magb,alen,maxdiff,triples,blen; local gaussref_count; gaussref_count := 0; foreach edge eee do { if max ( eee.vertex[1].edge,valence) <= 2 and max ( eee.vertex[2].edge,valence) <= 2 then { ax := eee.vertex[1].vertexnormal[1]; ay := eee.vertex[1].vertexnormal[2]; az := eee.vertex[1].vertexnormal[3]; bx := eee.vertex[2].vertexnormal[1]; by := eee.vertex[2].vertexnormal[2]; bz := eee.vertex[2].vertexnormal[3]; diff := pi/2 - abs(pi/2 - acos(ax*bx+ay*by+az*bz)) ; if ( diff > gaussref_tolerance ) then { refine eee; gaussref_count += 1; } } else if eee.valence == 2 then { ax := eee.facet[1].x[1]; ay := eee.facet[1].x[2]; az := eee.facet[1].x[3]; bx := eee.facet[2].x[1]; by := eee.facet[2].x[2]; bz := eee.facet[2].x[3]; maga := sqrt(ax^2+ay^2+az^2); magb := sqrt(bx^2+by^2+bz^2); diff := pi/2 - abs(pi/2 - acos((ax*bx+ay*by+az*bz)/maga/magb)) ; if ( diff > gaussref_tolerance ) then { refine eee; gaussref_count += 1; } } else if eee.valence >= 3 then { // check bend in continuation triple lines ax := eee.x; ay := eee.y; az := eee.z; alen := eee.length; maxdiff := 0; triples := 0; foreach eee.vertex vvv do { foreach vvv.edge eeee where (eeee.id != eee.id) and (valence >= 3) do { triples += 1; bx := eeee.x; by := eeee.y; bz := eeee.z; blen := eeee.length; diff := pi/2 - abs(pi/2 - acos((ax*bx+ay*by+az*bz)/alen/blen)) ; if ( diff > gaussref_tolerance ) then { if ( diff > maxdiff ) then maxdiff := diff; } } }; if ( (triples <= 1) and (maxdiff > gaussref_tolerance) ) then { refine eee; gaussref_count += 1; } } }; printf "Edges refined by gaussref: %d\n",gaussref_count; } evolver-2.30c.dfsg/fe/mound.fe0000644000175300017530000000403211410765113016455 0ustar hazelscthazelsct// mound.fe // Evolver data for drop of prescribed volume sitting on plane with gravity. // Contact angle with plane can be varied. PARAMETER angle = 90 // interior angle between plane and surface, degrees gravity_constant 0 // start with gravity off #define WALLT (-cos(angle*pi/180)) // virtual tension of facet on plane constraint 1 /* the table top */ formula: x3 = 0 energy: // for contact angle e1: -(WALLT*y) e2: 0 e3: 0 vertices 1 0.0 0.0 0.0 constraint 1 /* 4 vertices on plane */ 2 1.0 0.0 0.0 constraint 1 3 1.0 1.0 0.0 constraint 1 4 0.0 1.0 0.0 constraint 1 5 0.0 0.0 1.0 6 1.0 0.0 1.0 7 1.0 1.0 1.0 8 0.0 1.0 1.0 9 2.0 2.0 0.0 fixed /* for table top */ 10 2.0 -1.0 0.0 fixed 11 -1.0 -1.0 0.0 fixed 12 -1.0 2.0 0.0 fixed edges /* given by endpoints and attribute */ 1 1 2 constraint 1 /* 4 edges on plane */ 2 2 3 constraint 1 3 3 4 constraint 1 4 4 1 constraint 1 5 5 6 6 6 7 7 7 8 8 8 5 9 1 5 10 2 6 11 3 7 12 4 8 13 9 10 no_refine fixed /* for table top */ 14 10 11 no_refine fixed 15 11 12 no_refine fixed 16 12 9 no_refine fixed faces /* given by oriented edge loop */ 1 1 10 -5 -9 2 2 11 -6 -10 3 3 12 -7 -11 4 4 9 -8 -12 5 5 6 7 8 7 13 14 15 16 no_refine density 0 fixed /* table top for display */ bodies /* one body, defined by its oriented faces */ 1 1 2 3 4 5 volume 1 density 1 read re := {refine edges where on_constraint 1 } // Typical evolution gogo := { re; g 5; r; g 5; r; g 5; hessian; hessian; } // Evolution with 45 degree contact angle gogo2 := { angle := 45; re; g 5; V;V; r; g 5; V;V; r; g 5; hessian; hessian; } // Evolution with 90 contact and high gravity gogo3 := { angle := 90; G 5; re; g 5; r; g 5; r; g 5; hessian; hessian; } // Evolution with 90 contact and negative gravity, i.e. pendant drop gogo4 := { angle := 90; G -2; re; g 5; r; g 5; r; g 5; hessian; hessian; } // Pendant drop falling off ceiling gogo5 := { angle := 90; G -5; re; g 10; t .1; unset vertex constraint 1; g 3; } evolver-2.30c.dfsg/fe/iges128.cmd0000755000175300017530000001733511410765113016703 0ustar hazelscthazelsct// iges128.cmd // Surface Evolver script to write IGES file for surface, using IGES // rational B-spline entity (type 128). // Documentation on IGES format: http://www.iges5x.org/taxonomy/ // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // usage: iges >>> "filename.igs" // Set up color translation array define iges_colors integer [16]; //iges_colors[black] := 1 iges_colors[red] := 2 iges_colors[green] := 3 iges_colors[blue] := 4 iges_colors[yellow] := 5 iges_colors[magenta] := 6 iges_colors[cyan] := 7 iges_colors[white] := 8 iges_colors[brown] := -1 // using colors defined at start of directory iges_colors[lightgray] := -3 iges_colors[darkgray] := -5 iges_colors[lightblue] := -7 iges_colors[lightgreen] := -9 iges_colors[lightcyan] := -11 iges_colors[lightred] := -13 iges_colors[lightmagenta] := -15 iges := { // Flag section // Don't need this since not doing binary or compressed format. // Start section start_counter := 0; start_counter += 1; printf "%-72sS%07d\n","IGES version of Surface Evolver surface",start_counter; start_counter += 1; printf " %-69sS%07d\n",datafilename,start_counter; start_counter += 1; printf "%-72sS%07d\n","Created using iges.cmd Evolver script.", start_counter; // Global section global_counter := 0; global_counter += 1; printf "%-72sG%07d\n","1H,,1H;,",global_counter; global_counter += 1; printf "%02dH%-69sG%07d\n",sizeof("Surface Evolver"),"Surface Evolver,", global_counter; global_counter += 1; message := sprintf "%s,",datafilename; printf "%02dH%-69sG%07d\n",sizeof(datafilename),message,global_counter; global_counter += 1; printf "%02dH%-69sG%07d\n",sizeof("Surface Evolver"),"Surface Evolver,", global_counter; global_counter += 1; printf "%02dH%-69sG%07d\n",sizeof("Surface Evolver"),"Surface Evolver,", global_counter; global_counter += 1; printf "%-72sG%07d\n","32,75,6,75,15,,1.0,1,2HIN,32768,0.0394,", global_counter; global_counter += 1; printf "%-72sG%07d\n","15H00000000.000000,", global_counter; // date xmax := max(vertex,abs(x)); ymax := max(vertex,abs(y)); zmax := max(vertex,abs(z)); maxsize := (xmax > ymax) ? xmax : ymax; maxsize := (zmax > maxsize) ? zmax : maxsize; message := sprintf "%g,%g,",maxsize/10000000,maxsize; global_counter += 1; printf "%-72sG%07d\n",message, global_counter; global_counter += 1; printf "%02dH%-69sG%07d\n",sizeof("Name of author"),"Name of author,", global_counter; global_counter += 1; printf "%02dH%-69sG%07d\n",sizeof("Author's organization"), "Author's organization,", global_counter; global_counter += 1; printf "%-72sG%07d\n","15H00000000.000000;", global_counter; // date // Directory entry section. Each entry 20 8-char fields on two lines. // Fields with default values entype := 0; // 1 and 11 paramdata := 1; // 2 structure := 0; // 3 linefont := 0; // 4 level := 0; // 5 view := 0; // 6 transmat := 0; // 7 label := 0; // 8 status := "00000000"; // 9; actually 4 two-digit numbers directory_counter := 0; // 10 and 20 lineweight := 0; // 12 colornum := 0; // 13 paramcount := 0; // 14 form := 0; // 15 reserved := " 0"; // 16 and 17 entlabel := "entity"; // 18 entsubscr := 0; // 19 // Color definitions for those Evolver colors not supplied in IGES entype := 314; paramcount := 1; status := "00000200"; approxcolor := 7; // incompetent systems default to cyan entlabel := "COLOR"; form := 0; for ( cinx := 1 ; cinx <= 8 ; cinx += 1 ) { directory_counter += 1; printf "%8d%8d%8d%8d%8d%8d%8d%8d%8sD%7d\n",entype,paramdata,structure, linefont,level,view,transmat,label,status,directory_counter; directory_counter += 1; printf "%8d%8d%8d%8d%8d%8s%8s%8s%8dD%7d\n",entype,lineweight, approxcolor, paramcount,form,reserved,reserved,entlabel,entsubscr,directory_counter; paramdata += 1; }; // Facets as "rational b-spline" types entype := 128; status := "00000000"; paramcount := 7; // lines of parameters form := 8; entlabel := " FACET"; define facet attribute fpdata integer; define facet attribute fdir integer; foreach facet ff do { ff.fpdata := paramdata; entsubscr := ff.id; directory_counter += 1; ff.fdir := directory_counter; printf "%8d%8d%8d%8d%8d%8d%8d%8d%8sD%7d\n",entype,paramdata,structure, linefont,level,view,transmat,label,status,directory_counter; directory_counter += 1; printf "%8d%8d%8d%8d%8d%8s%8s%8s%8dD%7d\n",entype,lineweight, iges_colors[ff.color], paramcount,form,reserved,reserved,entlabel,entsubscr,directory_counter; paramdata += paramcount; }; // Parameter data section parameter_counter := 1; // Color definitions dirnum := 1; // corresponding directory line printf "%-64s%8dP%7d\n","314,100.,50.,0.;",dirnum,parameter_counter; // brown parameter_counter += 1; dirnum += 2; printf "%-64s%8dP%7d\n","314,60.,60.,60.;",dirnum,parameter_counter; // l. gray parameter_counter += 1; dirnum += 2; printf "%-64s%8dP%7d\n","314,30.,30.,30.;",dirnum,parameter_counter; // d. gray parameter_counter += 1; dirnum += 2; printf "%-64s%8dP%7d\n","314,30,80,100;",dirnum,parameter_counter; // l. blue parameter_counter += 1; dirnum += 2; printf "%-64s%8dP%7d\n","314,50,100,50;",dirnum,parameter_counter; // l. grn parameter_counter += 1; dirnum += 2; printf "%-64s%8dP%7d\n","314,50,100,100;",dirnum,parameter_counter; // l. cyan parameter_counter += 1; dirnum += 2; printf "%-64s%8dP%7d\n","314,100,50,50;",dirnum,parameter_counter; // l. red parameter_counter += 1; dirnum += 2; printf "%-64s%8dP%7d\n","314,100,50,100;",dirnum,parameter_counter; // l. mag. // Facets entype := 128; k_1 := 1; // knots-1 k_2 := 1; // m_1 := 1; // degree m_2 := 1; // prop1 := 0; // not closed in first paramter prop2 := 0; // not closed in second parameter prop3 := 1; // polynomial instead of rational prop4 := 0; // nonperiodic in first parameter prop5 := 0; // nonperiodic in second parameter minu := 0; maxu := 1; minv := 0; maxv := 1; foreach facet ff do { parameter_counter += 1; if ff.fpdata != parameter_counter then errprintf "ERROR: bad facet parameter line number, facet %d. Is %d, should be %d.\n", ff.id,parameter_counter,ff.fpdata; message := sprintf "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,",entype,k_1,k_2,m_1,m_2, prop1,prop2,prop3,prop4,prop5; printf "%-64s%8dP%7d\n",message,ff.fdir,parameter_counter; message := "0.0,0.0,1.0,1.0,0.0,0.0,1.0,1.0,1.0,1.0,1.0,1.0,"; parameter_counter += 1; printf "%-64s%8dP%7d\n",message,ff.fdir,parameter_counter; message := sprintf "%9.7f,%9.7f,%9.7f,", ff.vertex[1].x,ff.vertex[1].y,ff.vertex[1].z; parameter_counter += 1; printf "%-64s%8dP%7d\n",message,ff.fdir,parameter_counter; message := sprintf "%9.7f,%9.7f,%9.7f,", ff.vertex[2].x,ff.vertex[2].y,ff.vertex[2].z; parameter_counter += 1; printf "%-64s%8dP%7d\n",message,ff.fdir,parameter_counter; message := sprintf "%9.7f,%9.7f,%9.7f,", ff.vertex[3].x,ff.vertex[3].y,ff.vertex[3].z; parameter_counter += 1; printf "%-64s%8dP%7d\n",message,ff.fdir,parameter_counter; message := sprintf "%9.7f,%9.7f,%9.7f,", ff.vertex[3].x,ff.vertex[3].y,ff.vertex[3].z; parameter_counter += 1; printf "%-64s%8dP%7d\n",message,ff.fdir,parameter_counter; message := "0.0,1.0,0.0,1.0;"; //minu,maxu,minv,maxv; parameter_counter += 1; printf "%-64s%8dP%7d\n",message,ff.fdir,parameter_counter; }; // Terminate section printf "S%07dG%07dD%07dP%07d%40sT0000001\n",start_counter,global_counter, directory_counter,parameter_counter," "; } evolver-2.30c.dfsg/fe/rewrap.cmd0000755000175300017530000000402411410765113017010 0ustar hazelscthazelsct// rewrap.cmd // Commands to rewrap torus vertices and edges to get them nicely // within unit cell. This version does 3D; for 2D see rewrap2.cmd. // Moves vertices at most one period at a time, so you may have to // repeat if things are very bad to start with. // Uses torus wrap representation and wrap_vertex builtin command. // Usage: rewrap // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke rewrap := { if space_dimension != 3 then { errprintf"rewrap is for space dimension 3; for 2D use rewrap2.cmd.\n"; return; }; define body attribute old_volume real; // so can adjust volconst set body old_volume volume; foreach vertex vv where vv.x*inverse_periods[1][1]+vv.y*inverse_periods[1][2] + vv.z*inverse_periods[1][3] < 0 do wrap_vertex(vv.id,1); foreach vertex vv where vv.x*inverse_periods[1][1]+vv.y*inverse_periods[1][2] + vv.z*inverse_periods[1][3] >= 1 do wrap_vertex(vv.id,31); foreach vertex vv where vv.x*inverse_periods[2][1]+vv.y*inverse_periods[2][2] + vv.z*inverse_periods[2][3] < 0 do wrap_vertex(vv.id,64); foreach vertex vv where vv.x*inverse_periods[2][1]+vv.y*inverse_periods[2][2] + vv.z*inverse_periods[2][3] >= 1 do wrap_vertex(vv.id,1984); foreach vertex vv where vv.x*inverse_periods[3][1]+vv.y*inverse_periods[3][2] + vv.z*inverse_periods[3][3] < 0 do wrap_vertex(vv.id,4096); foreach vertex vv where vv.x*inverse_periods[3][1]+vv.y*inverse_periods[3][2] + vv.z*inverse_periods[3][3] >= 1 do wrap_vertex(vv.id,126976); recalc; // Adjust volconst local torvol; torvol := abs((torus_periods[1][1]*torus_periods[2][2] - torus_periods[1][2]*torus_periods[2][1])*torus_periods[3][3] + (torus_periods[1][2]*torus_periods[2][3] - torus_periods[1][3]*torus_periods[2][2])*torus_periods[3][1] + (torus_periods[1][3]*torus_periods[2][1] - torus_periods[1][1]*torus_periods[2][3])*torus_periods[3][2]); set body volconst floor((old_volume - volume - volconst)/torvol+.5)*torvol; } evolver-2.30c.dfsg/fe/simplex3.fe0000644000175300017530000000076511410765113017110 0ustar hazelscthazelsct// simplex3.fe // Evolver data for bubble in 4-space. Initially simplex. // Evolves into 3-sphere in 4-space. SIMPLEX_REPRESENTATION SPACE_DIMENSION 4 SURFACE_DIMENSION 3 vertices 1 0 0 0 0 2 1 0 0 0 3 0 1 0 0 4 0 0 1 0 5 0 0 0 1 faces /* given as oriented vertex list */ 1 2 3 4 5 2 3 4 5 1 3 4 5 1 2 4 5 1 2 3 5 1 2 3 4 bodies 1 1 2 3 4 5 volume .04 read // Typical evolution gogo := { r; g 5; U; g 10; r; g 10; r; g 20; r; g 20; } evolver-2.30c.dfsg/fe/quadmeet.cmd0000755000175300017530000001475411410765113017330 0ustar hazelscthazelsct// quadmeet.cmd // For detecting intersection of quadratic facets in 3D // Not suitable for torus model. // Needs quadbbox.cmd to be loaded first. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // Usage: quadmeet // Results: Prints id numbers of intersecting facets. tiny := 1e-20 // criterion for dist^2 = 0 quadmeet := { local xa1,xa2,xa3,xa4,xa5,xa6; local ya1,ya2,ya3,ya4,ya5,ya6; local za1,za2,za3,za4,za5,za6; local xb1,xb2,xb3,xb4,xb5,xb6; local yb1,yb2,yb3,yb4,yb5,yb6; local zb1,zb2,zb3,zb4,zb5,zb6; local eps,ua,va,ub,vb,xa,ya,za,xb,yb,zb,dx,dy,dz,dd; local xau,xav,yau,yav,zau,zav; local xbu,xbv,ybu,ybv,zbu,zbv; local uagrad,vagrad,ubgrad,vbgrad,tt; fboxes; // find facet bounding boxes // test all facet pairs foreach facet fa do { foreach facet fb where fb.id > fa.id do { // first, check bounding boxes if ( fb.fbox[1] >= fa.fbox[2] or fb.fbox[2] <= fa.fbox[1] or fb.fbox[3] >= fa.fbox[4] or fb.fbox[4] <= fa.fbox[3] or fb.fbox[5] >= fa.fbox[6] or fb.fbox[6] <= fa.fbox[5] ) then continue; // extract coordinates xa1 := fa.edge[1].vertex[1].x; xa2 := fa.edge[1].vertex[3].x; xa3 := fa.edge[1].vertex[2].x; xa4 := fa.edge[3].vertex[3].x; xa5 := fa.edge[2].vertex[3].x; xa6 := fa.edge[2].vertex[2].x; ya1 := fa.edge[1].vertex[1].y; ya2 := fa.edge[1].vertex[3].y; ya3 := fa.edge[1].vertex[2].y; ya4 := fa.edge[3].vertex[3].y; ya5 := fa.edge[2].vertex[3].y; ya6 := fa.edge[2].vertex[2].y; za1 := fa.edge[1].vertex[1].z; za2 := fa.edge[1].vertex[3].z; za3 := fa.edge[1].vertex[2].z; za4 := fa.edge[3].vertex[3].z; za5 := fa.edge[2].vertex[3].z; za6 := fa.edge[2].vertex[2].z; xb1 := fb.edge[1].vertex[1].x; xb2 := fb.edge[1].vertex[3].x; xb3 := fb.edge[1].vertex[2].x; xb4 := fb.edge[3].vertex[3].x; xb5 := fb.edge[2].vertex[3].x; xb6 := fb.edge[2].vertex[2].x; yb1 := fb.edge[1].vertex[1].y; yb2 := fb.edge[1].vertex[3].y; yb3 := fb.edge[1].vertex[2].y; yb4 := fb.edge[3].vertex[3].y; yb5 := fb.edge[2].vertex[3].y; yb6 := fb.edge[2].vertex[2].y; zb1 := fb.edge[1].vertex[1].z; zb2 := fb.edge[1].vertex[3].z; zb3 := fb.edge[1].vertex[2].z; zb4 := fb.edge[3].vertex[3].z; zb5 := fb.edge[2].vertex[3].z; zb6 := fb.edge[2].vertex[2].z; // Find minimum distance by Newton's Method eps := .2; // edge guard ua := .7; va := .6; ub := .71; vb := .67; while ( eps > 1e-6 ) do { xa := 0.5*(ua+va-1)*(ua+va-2)*xa1 + ua*(2-ua-va)*xa2 + 0.5*ua*(ua-1)*xa3 + va*(2-ua-va)*xa4 + ua*va*xa5 + 0.5*va*(va-1)*xa6; ya := 0.5*(ua+va-1)*(ua+va-2)*ya1 + ua*(2-ua-va)*ya2 + 0.5*ua*(ua-1)*ya3 + va*(2-ua-va)*ya4 + ua*va*ya5 + 0.5*va*(va-1)*ya6; za := 0.5*(ua+va-1)*(ua+va-2)*za1 + ua*(2-ua-va)*za2 + 0.5*ua*(ua-1)*za3 + va*(2-ua-va)*za4 + ua*va*za5 + 0.5*va*(va-1)*za6; xb := 0.5*(ub+vb-1)*(ub+vb-2)*xb1 + ub*(2-ub-vb)*xb2 + 0.5*ub*(ub-1)*xb3 + vb*(2-ub-vb)*xb4 + ub*vb*xb5 + 0.5*vb*(vb-1)*xb6; yb := 0.5*(ub+vb-1)*(ub+vb-2)*yb1 + ub*(2-ub-vb)*yb2 + 0.5*ub*(ub-1)*yb3 + vb*(2-ub-vb)*yb4 + ub*vb*yb5 + 0.5*vb*(vb-1)*yb6; zb := 0.5*(ub+vb-1)*(ub+vb-2)*zb1 + ub*(2-ub-vb)*zb2 + 0.5*ub*(ub-1)*zb3 + vb*(2-ub-vb)*zb4 + ub*vb*zb5 + 0.5*vb*(vb-1)*zb6; dx := xa-xb; dy := ya-yb; dz := za - zb; dd := dx*dx + dy*dy + dz*dz; if ( dd < tiny ) then break; xau := (ua + va - 1.5)*xa1 + (2-2*ua-va)*xa2 + (ua-0.5)*xa3 - va*xa4 + va*xa5; xav := (ua + va - 1.5)*xa1 - ua*xa2 +(2-ua-2*va)*xa4 + ua*xa5 + (va - 0.5)*xa6; yau := (ua + va - 1.5)*ya1 + (2-2*ua-va)*ya2 + (ua-0.5)*ya3 - va*ya4 + va*ya5; yav := (ua + va - 1.5)*ya1 - ua*ya2 +(2-ua-2*va)*ya4 + ua*ya5 + (va - 0.5)*ya6; zau := (ua + va - 1.5)*za1 + (2-2*ua-va)*za2 + (ua-0.5)*za3 - va*za4 + va*za5; zav := (ua + va - 1.5)*za1 - ua*za2 +(2-ua-2*va)*za4 + ua*za5 + (va - 0.5)*za6; xbu := (ub + vb - 1.5)*xb1 + (2-2*ub-vb)*xb2 + (ub-0.5)*xb3 - vb*xb4 + vb*xb5; xbv := (ub + vb - 1.5)*xb1 - ub*xb2 +(2-ub-2*vb)*xb4 + ub*xb5 + (vb - 0.5)*xb6; ybu := (ub + vb - 1.5)*yb1 + (2-2*ub-vb)*yb2 + (ub-0.5)*yb3 - vb*yb4 + vb*yb5; ybv := (ub + vb - 1.5)*yb1 - ub*yb2 +(2-ub-2*vb)*yb4 + ub*yb5 + (vb - 0.5)*yb6; zbu := (ub + vb - 1.5)*zb1 + (2-2*ub-vb)*zb2 + (ub-0.5)*zb3 - vb*zb4 + vb*zb5; zbv := (ub + vb - 1.5)*zb1 - ub*zb2 +(2-ub-2*vb)*zb4 + ub*zb5 + (vb - 0.5)*zb6; uagrad := 2*dx*xau + 2*dy*yau + 2*dz*zau; vagrad := 2*dx*xav + 2*dy*yav + 2*dz*zav; ubgrad := -(2*dx*xbu + 2*dy*ybu + 2*dz*zbu); vbgrad := -(2*dx*xbv + 2*dy*ybv + 2*dz*zbv); // find multiple of gradient; factor of 2 due to dist^2 tt := 2*dd/(uagrad*uagrad+vagrad*vagrad+ubgrad*ubgrad+vbgrad*vbgrad); // clamp to triangle boundaries if necessary // actually, clamp short of bdry so don't have to worry // about being on boundary. if ( ua - tt*uagrad < eps ) then tt := (-eps+ua)/uagrad; if ( va - tt*vagrad < eps ) then tt := (-eps+va)/vagrad; if ( ua - tt*uagrad + va - tt*vagrad > 2 - eps ) then tt := (-2 + eps + ua + va)/(uagrad + vagrad); if ( ub - tt*ubgrad < eps ) then tt := (-eps+ub)/ubgrad; if ( vb - tt*vbgrad < eps ) then tt := (-eps+vb)/vbgrad; if ( ub - tt*ubgrad + vb - tt*vbgrad > 2 - eps ) then tt := -(2 - eps - ub - vb)/(ubgrad + vbgrad); ua := ua - tt*uagrad; va := va - tt*vagrad; ub := ub - tt*ubgrad; vb := vb - tt*vbgrad; eps := eps/5; }; if ( dd < tiny ) then printf "Facets %d and %d intersect at (%g,%g,%g).\n",xa,ya,za; } } } evolver-2.30c.dfsg/fe/dxf.cmd0000755000175300017530000000262311410765113016274 0ustar hazelscthazelsct// dfx.cmd // Evolver command to produce AutoCad DXF files // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // References: // http://en.wikipedia.org/wiki/AutoCAD_DXF // http://images.autodesk.com/adsk/files/acad_dxf.pdf // Usage: // dxf >>> "filename.dxf" define dxf_colors integer[16]; dxf_colors[1] := 5 // blue dxf_colors[2] := 3 // green dxf_colors[3] := 4 // cyan dxf_colors[4] := 10 // red dxf_colors[5] := 6 // magenta dxf_colors[6] := 1 // brown dxf_colors[7] := 9 // lightgray dxf_colors[8] := 8 // darkgray dxf_colors[9] := 8 // lightblue dxf_colors[10] := 3 // lightgreen dxf_colors[11] := 4 // lightcyan dxf_colors[12] := 14 // lightred dxf_colors[13] := 6 // lightmagenta dxf_colors[14] := 2 // yellow dxf_colors[15] := 7 // white dxf := { printf " 0\nSECTION\n 2\nENTITIES\n"; foreach facet ff do { printf " 0\n3DFACE\n 8\n0main\n"; printf " 10\n%8.6f\n 20\n%8.6f\n 30\n%8.6f\n",ff.vertex[1].x, ff.vertex[1].y,ff.vertex[1].z; printf " 11\n%8.6f\n 21\n%8.6f\n 31\n%8.6f\n",ff.vertex[2].x, ff.vertex[2].y,ff.vertex[2].z; printf " 12\n%8.6f\n 22\n%8.6f\n 32\n%8.6f\n",ff.vertex[3].x, ff.vertex[3].y,ff.vertex[3].z; printf " 13\n%8.6f\n 23\n%8.6f\n 33\n%8.6f\n",ff.vertex[3].x, ff.vertex[3].y,ff.vertex[3].z; // try color printf " 62\n%3d\n",dxf_colors[ff.color]; }; printf " 0\nENDSEC\n 0\nEOF\n"; } evolver-2.30c.dfsg/fe/quadm.fe0000644000175300017530000000125611410765113016447 0ustar hazelscthazelsct// quadm.fe // quadrilateral boundary wire // test of metric INTEGRAL_ORDER 7 METRIC /* 1+4*x1^2 4*x1*x2 2*x1 4*x1*x2 1+4*x2^2 2*x2 2*x1 2*x2 1 */ 1 0 0 0 1 0 0 0 1 constraint 1 formula: x1 = 0 constraint 2 formula: x1 = 1 constraint 3 formula: x2 = 0 constraint 4 formula: x2 = 1 constraint 5 formula: x1^2 + x2^2 + x3 = 0 vertices 1 0 0 0 fixed 2 1 0 -1 fixed 3 1 1 -2 fixed 4 0 1 -1 fixed edges 1 1 2 fixed constraints 3,5 2 2 3 fixed constraints 2,5 3 3 4 fixed constraints 4,5 4 4 1 fixed constraints 1,5 5 2 4 faces 1 1 5 4 2 2 3 -5 read // Typical evolution gogo := { r; g 12; U; r; g 12; r; g 12; r; g 20; } evolver-2.30c.dfsg/fe/crystal.fe0000644000175300017530000000170211410765113017015 0ustar hazelscthazelsct// crystal.fe // Evolver data for cube of prescribed volume with // crystalline surface energy. Evolves into an octahedron. Wulff "octa.wlf" // Wulff vectors for octahedral crystal vertices 1 0.0 0.0 0.0 2 1.0 0.0 0.0 3 1.0 1.0 0.0 4 0.0 1.0 0.0 5 0.0 0.0 1.0 6 1.0 0.0 1.0 7 1.0 1.0 1.0 8 0.0 1.0 1.0 9 0.0 0.0 0.5 10 1.0 0.0 0.5 11 1.0 1.0 0.5 12 0.0 1.0 0.5 edges /* given by endpoints and attribute */ 1 1 2 2 2 3 3 3 4 4 4 1 5 5 6 6 6 7 7 7 8 8 8 5 9 1 9 10 2 10 11 3 11 12 4 12 13 9 5 14 10 6 15 11 7 16 12 8 17 9 10 18 10 11 19 11 12 20 12 9 faces /* given by oriented edge loop */ 1 1 10 -17 -9 2 2 11 -18 -10 3 3 12 -19 -11 4 4 9 -20 -12 5 5 6 7 8 6 -4 -3 -2 -1 7 17 14 -5 -13 8 18 15 -6 -14 9 19 16 -7 -15 10 20 13 -8 -16 bodies /* one body, defined by its oriented faces */ 1 1 2 3 4 5 6 7 8 9 10 volume 1 read // Sample evolution gogo := { g 10; } evolver-2.30c.dfsg/fe/rib.cmd0000755000175300017530000000644711410765113016277 0ustar hazelscthazelsct// rib.cmd // Surface Evolver command to write RenderMan RIB file for surface // Usage: rib >>> "filename.rib" // This version does facets only, on a light blue background. // Viewpoint is the same as 's' command. Orthogonal projection. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke rib := { printf"##RenderMan RIB-Structure 1.0\n"; printf "version 3.03\n"; printf "\n"; printf "Option \"searchpath\" \"shader\" [\".:../shaders:&\"]\n"; printf "Display \"%s.tif\" \"file\" \"rgba\"\n",datafilename; printf "Format 512 512 -1\n"; printf "PixelSamples 1 1\n"; printf "Orientation \"rh\"\n"; printf "Clipping 0.1 10.0\n"; printf "\n"; printf "WorldBegin\n"; printf "\n"; printf "Surface \"constant\"\n"; /* blue background */ printf "Color [ 0.3 0.8 1.0 ]\n"; printf "Polygon \"P\" [ 1 1 9 -1 1 9 -1 -1 9 1 -1 9]\n"; printf "\n"; printf "Translate 0 0 5 \n"; printf "Rotate 240 1 1 1 \n"; printf "Scale -1 1 1 \n"; printf "Scale .66667 .66667 .66667 \n"; printf "ConcatTransform [ %f %f %f %f\n", view_matrix[1][1],view_matrix[2][1],view_matrix[3][1],view_matrix[4][1]; printf " %f %f %f %f\n", view_matrix[1][2],view_matrix[2][2],view_matrix[3][2],view_matrix[4][2]; printf " %f %f %f %f\n", view_matrix[1][3],view_matrix[2][3],view_matrix[3][3],view_matrix[4][3]; printf " %f %f %f %f ]\n", view_matrix[1][4],view_matrix[2][4],view_matrix[3][4],view_matrix[4][4]; printf "LightSource \"ambientlight\" 1 \"intensity\" 0.30\n"; printf "LightSource \"distantlight\" 1 \"from\" [1 0 1] \"to\" [0 0 0] \"intensity\" 0.7\n"; printf "\n"; printf "AttributeBegin\n"; printf " Surface \"constant\"\n"; foreach facet ff do { if ( ff.color == CLEAR ) then continue; if ( ff.color == WHITE ) then printf " Color [ 1.0 1.0 1.0 ]\n" else if ( ff.color == WHITE ) then printf " Color [ 1.0 1.0 1.0 ]\n" else if ( ff.color == BLACK ) then printf " Color [ 0.0 0.0 0.0 ]\n" else if ( ff.color == BLUE ) then printf " Color [ 0.0 0.0 1. ]\n" else if ( ff.color == GREEN ) then printf " Color [ 0.0 1. 0.0 ]\n" else if ( ff.color == CYAN ) then printf " Color [ 0.0 1. 1. ]\n" else if ( ff.color == RED ) then printf " Color [ 1. 0.0 0.0 ]\n" else if ( ff.color == MAGENTA ) then printf " Color [ 1. 0.0 1. ]\n" else if ( ff.color == BROWN ) then printf " Color [ 1. 0.5 0. ]\n" else if ( ff.color == LIGHTGRAY ) then printf " Color [ .6 .6 .6 ]\n" else if ( ff.color == DARKGRAY ) then printf " Color [ .3 .3 .3 ]\n" else if ( ff.color == LIGHTBLUE ) then printf " Color [ .5 .5 1. ]\n" else if ( ff.color == LIGHTGREEN ) then printf " Color [ .5 1. .5 ]\n" else if ( ff.color == LIGHTCYAN ) then printf " Color [ .5 1. 1. ]\n" else if ( ff.color == LIGHTRED ) then printf " Color [ 1. .5 .5 ]\n" else if ( ff.color == LIGHTMAGENTA ) then printf " Color [ 1. .5 1. ]\n" else if ( ff.color == YELLOW ) then printf " Color [ 1. 1. .0 ]\n"; printf " Polygon \"P\" [ %f %f %f %f %f %f %f %f %f ]\n", ff.vertex[1].x,ff.vertex[1].y,ff.vertex[1].z, ff.vertex[2].x,ff.vertex[2].y,ff.vertex[2].z, ff.vertex[3].x,ff.vertex[3].y,ff.vertex[3].z; }; printf "AttributeEnd\n"; printf "\n"; printf "WorldEnd\n"; } evolver-2.30c.dfsg/fe/intersec.cmd0000755000175300017530000000413011410765113017322 0ustar hazelscthazelsct// intersec.cmd // Evolver command to detect intersection of linear edges and facets. // For each facet, finds if any edge intersects in interior. // Usage: read "intersec.cmd" // detect // Output: prints ids of facets and edges that intersect. // Notes: In quadratic model, treats edges and facets as simply linear. // Will not work in Lagrange model. eps := 1e-10 // tolerance for being equal to zero detect := { local ax,ay,az,bx,by,bz,cx,cy,cz,acx,bcx,px,py,pz,qx,qy,qz,qpx,qcx; local denom,lambda,mu,sigma; local acy,acz,bcy,bcz,qpy,qpz,qcy,qcz; if lagrange then { printf "intersect.cmd will not work in Lagrange model.\n";return;}; foreach facet ff do { ax := ff.vertex[1].x; ay := ff.vertex[1].y; az := ff.vertex[1].z; bx := ff.vertex[2].x; by := ff.vertex[2].y; bz := ff.vertex[2].z; cx := ff.vertex[3].x; cy := ff.vertex[3].y; cz := ff.vertex[3].z; acx := ax-cx; acy := ay-cy; acz := az- cz; bcx := bx-cx; bcy := by-cy; bcz := bz- cz; foreach edge ee do { px := ee.vertex[1].x; py := ee.vertex[1].y; pz := ee.vertex[1].z; qx := ee.vertex[2].x; qy := ee.vertex[2].y; qz := ee.vertex[2].z; qpx := qx-px; qpy := qy-py; qpz := qz-pz; qcx := qx-cx; qcy := qy-cy; qcz := qz-cz; denom := qpx*(acy*bcz-acz*bcy) - qpy*(acx*bcz-acz*bcx) + qpz*(acx*bcy-acy*bcx); if ( abs(denom) < eps ) then continue; lambda := (qpx*(qcy*bcz-qcz*bcy) - qpy*(qcx*bcz-qcz*bcx) + qpz*(qcx*bcy-qcy*bcx))/denom; if ( lambda <= eps ) then continue; mu := (qpx*(acy*qcz-acz*qcy) - qpy*(acx*qcz-acz*qcx) + qpz*(acx*qcy-acy*qcx))/denom; if ( mu <= eps ) then continue; if ( lambda + mu > 1-eps ) then continue; sigma := (qcx*(acy*bcz-acz*bcy) - qcy*(acx*bcz-acz*bcx) + qcz*(acx*bcy-acy*bcx))/denom; if ( (sigma <= eps) or (sigma > 1-eps) ) then continue; // now have intersection printf "Facet %g and edge %g intersect.\n",ff.id,ee.id; } } } evolver-2.30c.dfsg/fe/povray.cmd0000755000175300017530000001066311410765113017036 0ustar hazelscthazelsct// povray.cmd // Surface Evolver command for producing POV-Ray input file. // Usage: // Use the "show edge where ..." command to declare which // edges are to be depicted as thin cylinders. // Set "edge_radius" to desired radius of edge cylinders. // Run "povray" and redirect to desired file, e.g. // Enter command: povray >>> "something.pov" // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke edge_radius := 0.003; // adjust this for desired radius of edge cylinders povray := { printf "// %s in POV-Ray format.\n\n",datafilename; printf "light_source { <0,0,300> color rgb <1,1,1> }\n"; printf "light_source { <100,0,0> color rgb <1,1,1> }\n"; printf "camera { location <12,0,0> sky <0,0,1> // right handed \n"; printf " up <0,0,1> right <1.3,0,0> look_at <0,0,0> angle 15 }\n"; printf "background { color <0.3,0.8,1.0> } // light blue\n\n"; printf "// Textures corresponding to Evolver colors\n\n"; printf "#declare t_black = texture { pigment { rgb <0.0,0.0,0.0> }}\n"; printf "#declare t_blue = texture { pigment { rgb <0.0,0.0,1.,> }}\n"; printf "#declare t_green = texture { pigment { rgb <0.0,1.,0.0,> }}\n"; printf "#declare t_cyan = texture { pigment { rgb <0.0,1.,1.,> }}\n"; printf "#declare t_red = texture { pigment { rgb <1.,0.0,0.0,> }}\n"; printf "#declare t_magenta = texture { pigment { rgb <1.,0.0,1.,> }}\n"; printf "#declare t_brown = texture { pigment { rgb <1.,0.5,0.,> }}\n"; printf "#declare t_lightgray = texture { pigment { rgb <.6,.6,.6,> }}\n"; printf "#declare t_darkgray = texture { pigment { rgb <.3,.3,.3,> }}\n"; printf "#declare t_lightblue = texture { pigment { rgb <.3,.8,1.,> }}\n"; printf "#declare t_lightgreen = texture { pigment { rgb <.5,1.,.5,> }}\n"; printf "#declare t_lightcyan = texture { pigment { rgb <.5,1.,1.,> }}\n"; printf "#declare t_lightred = texture { pigment { rgb <1.,.5,.5,> }}\n"; printf "#declare t_lightmagenta = texture { pigment { rgb <1.,.5,1.,> }}\n"; printf "#declare t_yellow = texture { pigment { rgb <1.,1.,.0,> }}\n"; printf "#declare t_white = texture { pigment { rgb <1.,1.,1.,> }}\n"; printf "\n//One overall object.\n"; printf "union {\n"; printf "// All facets in one big mesh object for efficiency.\n"; printf " mesh { \n"; foreach facet ff do { printf " triangle { <%f,%f,%f>,<%f,%f,%f>,<%f,%f,%f> texture {", ff.vertex[1].x,ff.vertex[1].y,ff.vertex[1].z, ff.vertex[2].x,ff.vertex[2].y,ff.vertex[2].z, ff.vertex[3].x,ff.vertex[3].y,ff.vertex[3].z; if ( ff.color == white ) then printf " t_white " else if ( ff.color == black ) then printf " t_black " else if ( ff.color == blue) then printf " t_blue " else if ( ff.color == green ) then printf " t_green " else if ( ff.color == cyan ) then printf " t_cyan " else if ( ff.color == red ) then printf " t_red " else if ( ff.color == magenta ) then printf " t_magenta " else if ( ff.color == brown ) then printf " t_brown " else if ( ff.color == lightgray ) then printf " t_lightgray " else if ( ff.color == darkgray ) then printf " t_darkgray " else if ( ff.color == lightblue ) then printf " t_lightblue " else if ( ff.color == lightgreen ) then printf " t_lightgreen " else if ( ff.color == lightcyan ) then printf " t_lightcyan " else if ( ff.color == lightred ) then printf " t_lightred " else if ( ff.color == lightmagenta ) then printf " t_lightmagenta " else if ( ff.color == yellow ) then printf " t_yellow "; printf " } }\n"; }; printf " } // end of mesh object\n"; // Do desired edges printf "#declare edge_radius = %f;\n",edge_radius; foreach edge ee where ee.show do { printf "cylinder { <%f,%f,%f>,<%f,%f,%f> edge_radius texture { t_black } }\n", ee.vertex[1].x,ee.vertex[1].y,ee.vertex[1].z, ee.vertex[2].x,ee.vertex[2].y,ee.vertex[2].z; }; // Windup printf "// overall viewing transformation\n"; printf " matrix < %f,%f,%f,\n", view_matrix[1][1],view_matrix[2][1],view_matrix[3][1]; printf " %f,%f,%f,\n", view_matrix[1][2],view_matrix[2][2],view_matrix[3][2]; printf " %f,%f,%f,\n", view_matrix[1][3],view_matrix[2][3],view_matrix[3][3]; printf " %f,%f,%f>\n", view_matrix[1][4],view_matrix[2][4],view_matrix[3][4]; printf " } // end of all objects\n"; } evolver-2.30c.dfsg/fe/xray.cmd0000755000175300017530000001114411410765113016474 0ustar hazelscthazelsct// xray.cmd // Produces xray image of a 3D wet foam. Calculates liquid content on // grid of probe lines. Plateau borders are detected as bounded by // facets with less than 2/3 of the maximum facet tension. // Outputs a PostScript file to stdout with a grid of grayscale pixels. // Usage: set xgridsize and ygridsize to the desired resolution, // i.e. pixels across and down and give the command // xray >>> "filename.ps" // The xray command will calculate the bounding box of the surface itself. // Works in torus or non-torus mode. In torus mode, everything will // automatically be wrapped back to the unit cell, so you don't have // to worry about that. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke xgridsize := 100 // xgridsize x ygridsize grid ygridsize := 100 xray := { local xx,yy,jitterx,jittery,minx,miny,maxx,maxy,tension_cutoff; local dx,dy,ax,ay,az,bx,by,bz,cx,cy,cz,hix,hiy,lox,loy,maxi,maxj; local mini,minj,fsign,xyarea,ii,jj,area1,area2,area3,maxr,xpts,ypts; define results real[xgridsize][ygridsize]; for ( xx := 1 ; xx <= xgridsize ; xx += 1 ) for ( yy := 1 ; yy <= ygridsize ; yy += 1 ) results[xx][yy] := 0.0; // clean out old results // random offsets to prevent edge effects */ jitterx := 0.5213414312341; jittery := 0.4934129768877; /* box to map results to */ if torus then { // assumes orthogonal fundamental region minx := 0; miny := 0; maxx := torus_periods[1][1]; maxy := torus_periods[2][2]; tension_cutoff := max(facet,tension)*2/3; // do only Plateau borders } else { minx := min(vertex,x); maxx := max(vertex,x); miny := min(vertex,y); maxy := max(vertex,y); tension_cutoff := 100000; // do all facets }; dx := (maxx-minx)/xgridsize; dy := (maxy-miny)/ygridsize; foreach facet ff where tension < tension_cutoff do { ax := ff.vertex[1].x; ay := ff.vertex[1].y; az := ff.vertex[1].z; bx := ax + ff.edge[1].x; // let Evolver take care of unwrapping by := ay + ff.edge[1].y; bz := az + ff.edge[1].z; cx := bx + ff.edge[2].x; // let Evolver take care of unwrapping cy := by + ff.edge[2].y; cz := bz + ff.edge[2].z; // get bounding box to find possible grid points hix := (ax > bx) ? (ax > cx ? ax : cx) : (bx > cx ? bx : cx); hiy := (ay > by) ? (ay > cy ? ay : cy) : (by > cy ? by : cy); lox := (ax < bx) ? (ax < cx ? ax : cx) : (bx < cx ? bx : cx); loy := (ay < by) ? (ay < cy ? ay : cy) : (by < cy ? by : cy); // compact to integers maxi := floor(hix/dx-jitterx); maxj := floor(hiy/dy-jittery); mini := ceil(lox/dx-jitterx); minj := ceil(loy/dy-jittery); // loop among possible values fsign := ff.z > 0 ? 1 : -1; xyarea := 2*abs(ff.z); for ( ii := mini ; ii <= maxi ; ii += 1 ) for ( jj := minj ; jj <= maxj ; jj += 1 ) { // test inclusion xx := ii*dx + dx*jitterx; yy := jj*dy + dy*jittery; area1 := fsign*((ax-xx)*(by-yy) - (bx-xx)*(ay-yy)); area2 := fsign*((bx-xx)*(cy-yy) - (cx-xx)*(by-yy)); area3 := fsign*((cx-xx)*(ay-yy) - (ax-xx)*(cy-yy)); if ( (area1 > 0) and (area2 > 0) and (area3 > 0) ) then { results[(ii imod xgridsize)+1][(jj imod ygridsize)+1] += fsign*(area1*cz + area2*az + area3*bz)/xyarea; } } }; if ( torus ) then { // correct results for wrap in z for ( ii := 1 ; ii <= xgridsize ; ii += 1 ) for ( jj := 1 ; jj <= ygridsize ; jj += 1 ) results[ii][jj] := results[ii][jj] mod torus_periods[3][3]; }; // map to range 0,1 maxr := 0; for ( ii := 1 ; ii <= xgridsize ; ii += 1 ) for ( jj := 1 ; jj <= ygridsize ; jj += 1 ) if results[ii][jj] > maxr then maxr := results[ii][jj]; if maxr > 0 then for ( ii := 1 ; ii <= xgridsize ; ii += 1 ) for ( jj := 1 ; jj <= ygridsize ; jj += 1 ) results[ii][jj] /= maxr; /* output results in postscript format, making low density white */ xpts := 500; ypts := 500; // point size of image // using kludges to get %% stuff to print right on Windows and Unix printf "%!"; printf"PS-Adobe-3.0 EPSF-3.0\n"; printf "%%"; printf"BoundingBox: 0 0 %d %d\n",xpts,ypts; printf "%%"; printf"Creator: Surface Evolver xray.cmd\n"; printf "%%"; printf"EndComments\n\n"; printf "%f %f scale\n",xpts/xgridsize,ypts/ygridsize; printf "/px { newpath setgray moveto 1 0 rlineto 0 1 rlineto -1 0 rlineto closepath fill} def\n"; for ( ii := 0 ; ii < xgridsize ; ii += 1 ) for ( jj := 0 ; jj < ygridsize ; jj += 1 ) { printf "%f %f %f px\n",ii,jj,(1-results[ii+1][jj+1]); }; printf "\nshowpage\n"; printf "\n%%"; printf"EOF\n"; } evolver-2.30c.dfsg/fe/wetfoam2.cmd0000755000175300017530000006274611410765113017253 0ustar hazelscthazelsct// wetfoam2.cmd // Evolver command to convert dry foam to wet foam. // Dry foam assumptions: // In torus domain (so no awkward boundary conditions) // Only one high valence edge per facet (will refine if finds more ) // Facet tension assumed to be uniformly 1. // Usage: Set "spread" to relative size of border (default 0.2), then // run wetfoam with output redirected to desired file, e.g. // Enter command: wetfoam >>> "wetfile.fe" // The resulting file has Plateau border body, whose number is recorded // in the variable border_body. // This file replaces wetfoam.cmd, which is now obsolete. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke /* Parameter for initial size of Plateau borders */ /* Note this is relative to the size of facets, not absolute. */ spread := .2 // Other global variables used internally here ccount := 0 voffset := 0 wrapnum := 0; border_body := 0; init_attributes := { // some useful attributes, all as dimensioned so can be shrunk to zero size // when done with them. define vertex attribute hivalence integer[1]; // number of high-valence edges define facet attribute corners integer[3]; // which "corner" each vertex // belongs to define facet attribute newedge integer[1]; // pulled-out edge define facet attribute corneredge1 integer[3];// new edge forward from corner define facet attribute corneredge2 integer[3];// new edge backward from corner define facet attribute didflag1 integer[3];// whether put vertex face here define facet attribute didflag2 integer[3];// whether put vertex face here define facet attribute diagedge integer[1]; // diagonal across rectangle } shrink_attributes := { // make storage small so to not pollute memory of original or new file. define vertex attribute hivalence integer[0]; define facet attribute corners integer[0]; define facet attribute newedge integer[0]; define facet attribute corneredge1 integer[0]; define facet attribute corneredge2 integer[0]; define facet attribute didflag1 integer[0]; define facet attribute didflag2 integer[0]; define facet attribute diagedge integer[0]; // and a few arrays while we are at it define cornerx real[0]; define cornery real[0]; define cornerz real[0]; define cornern integer[0]; define borderbody integer[0]; } /* tests and preliminaries */ vertest := { foreach vertex vv do vv.hivalence[1] := sum(vv.edge where valence >= 3,1); } facettest := { local triple; foreach facet ff do { triple := sum(ff.edge where valence >= 3, 1); if ( triple >= 2 ) then { refine ff; } } } wettests := { vertest; facettest; } /* enumerate and consolidate facet corners */ /* corner number of 0 means not adjacent to hi-valence vertex */ do_corners := { local inx; /* enumerate */ ccount := 1; foreach facet ff do { inx := 1; foreach ff.vertex vv do { if vv.hivalence[1] > 0 then { ff.corners[inx] := ccount; ccount += 1; }; inx += 1; }; }; /* consolidate */ local changes; do { changes := 0; foreach edge ee do { if ee.valence != 2 then continue; local ffid; local fffid; local knx; ffid := ee.facet[1].id; fffid := ee.facet[2].id; inx := 1; foreach facet[ffid].vertex vv do { if vv.id != ee.vertex[1].id && vv.id != ee.vertex[2].id then { inx += 1; continue; }; knx := 1; foreach facet[fffid].vertex vvv do { if vv.id == vvv.id then { if facet[ffid].corners[inx] == facet[fffid].corners[knx] then break; if facet[ffid].corners[inx] < facet[fffid].corners[knx] then facet[fffid].corners[knx] := facet[ffid].corners[inx] else facet[ffid].corners[inx] := facet[fffid].corners[knx]; changes += 1; break; }; knx += 1; }; inx += 1; } } } while changes; /* array to hold info on corner vertices */ define cornerx real[ccount]; define cornery real[ccount]; define cornerz real[ccount]; define cornern integer[ccount]; // number of facets in corner inx := 1; while inx <= ccount do { cornern[inx] := 0; cornerx[inx] := 0; cornery[inx] := 0; cornerz[inx] := 0; inx += 1; }; foreach facet ff do { inx := 1; local ffid; ffid := ff.id; /* want positive orientation */ while inx <= 3 do { if ff.corners[inx] != 0 then { cornern[ff.corners[inx]] += 1; cornerx[ff.corners[inx]] += spread*facet[ffid].edge[inx].x + 2*facet[ffid].vertex[inx].x - spread*facet[ffid].edge[inx==1 ? 3 : inx-1].x; cornery[ff.corners[inx]] += spread*facet[ffid].edge[inx].y + 2*facet[ffid].vertex[inx].y - spread*facet[ffid].edge[inx==1 ? 3 : inx-1].y; cornerz[ff.corners[inx]] += spread*facet[ffid].edge[inx].z + 2*facet[ffid].vertex[inx].z - spread*facet[ffid].edge[inx==1 ? 3 : inx-1].z; }; inx += 1; }; }; local kk; kk := 1; while kk <= ccount do { if cornern[kk] > 0 then { cornerx[kk] /= 2*cornern[kk]; cornery[kk] /= 2*cornern[kk]; cornerz[kk] /= 2*cornern[kk]; }; kk += 1; }; } /* end do_corners */ /* print vertex list */ wetverts := { voffset := max(vertex,id)+1; /* start of new vertices */ printf "\nvertices\n"; foreach vertex vv do if ( vv.hivalence[1] < 1 ) then printf "%d %g %g %g\n",vv.id, vv.x,vv.y,vv.z; local kk; kk := 1; while kk <= ccount do { if cornern[kk] > 0 then { printf "%d %g %g %g\n",kk+voffset, cornerx[kk],cornery[kk],cornerz[kk]; }; kk += 1; } } /* end wetverts */ /* convert numerical torus wrap to string and print; called by wetedges */ /* incoming argument: wrapnum */ wrapconvert := { local ww; ww := floor(1e-6 + (wrapnum % 64)); if ( ww == 0 ) then printf " *" else if ( ww == 1 ) then printf " +" else printf " -"; ww := floor(1e-6 + (floor(1e-6+wrapnum/64) % 64) ); if ( ww == 0 ) then printf " *" else if ( ww == 1 ) then printf " +" else printf " -"; ww := floor(1e-6 + (floor(1e-6+wrapnum/4096) % 64) ); if ( ww == 0 ) then printf " *" else if ( ww == 1 ) then printf " +" else printf " -"; } /* print edge list */ wetedges := { local ecounter; local eeid; local first_corner; local corner; local prevff; local previnx; local firstff; local firstinx; local prevcorner; local ffid; local inx; local ffid2; local eeid2; ecounter := max(edge,id)+1; /* start of new edges */ printf "\nedges\n"; /* first, old edges that we keep */ foreach edge ee where valence == 2 do { printf "%d ", ee.id; wrapnum := ee.wrap; foreach ee.vertex vv do { if ( vv.hivalence[1] > 0 ) then { /* find proper pull-out vertex */ local ffid; ffid := ee.facet[1].id; inx := 1; while inx <= 3 do { if facet[ffid].vertex[inx].id == vv.id then { printf "%d ",facet[ffid].corners[inx]+voffset; break; }; inx += 1; } } else printf "%d ",vv.id; }; if ( torus ) then wrapconvert; printf "\n"; }; foreach edge ee where valence >= 3 do { local tail1; local tail2; local tail3; local head1; local head2; local head3; local vv1; local inx; local headv; local tailv; local ffid; local vv2; tail1 := 0; tail2 := 0; tail3 := 0; head1 := 0; head2 := 0; head3 := 0; wrapnum := ee.wrap; /* assemble data about new vertices */ vv1 := ee.vertex[1].id; vv2 := ee.vertex[2].id; foreach ee.facet ff do { /* new edge between facet corners */ ffid := ff.id; /* want positive orientation of facet */ inx := 1; foreach facet[ffid].vertex vv do { if vv.id == vv1 then tailv := ff.corners[inx]+voffset; if vv.id == vv2 then headv := ff.corners[inx]+voffset; inx += 1; }; ff.newedge[1] := ecounter; ff.diagedge[1] := ecounter+3; printf "%d %d %d ",ecounter,tailv,headv; if torus then wrapconvert; printf "/* from edge %d */ ",ee.id; printf "\n"; tail1 := tail2; tail2 := tail3; tail3 := tailv; head1 := head2; head2 := head3; head3 := headv; ecounter += 1; }; printf "%d %d %d ",ecounter,tail1,head2; if torus then wrapconvert; printf "\n"; ecounter += 1; printf "%d %d %d ",ecounter,tail2,head3; if torus then wrapconvert; printf "\n"; ecounter += 1; printf "%d %d %d ",ecounter,tail3,head1; if torus then wrapconvert; printf "\n"; ecounter += 1; }; /* interior points of hi-valence edges */ foreach vertex vv where vv.hivalence[1] == 2 do { /* find the high-valence edges first */ local ffid; eeid := 0; foreach vv.edge ee do { if ee.valence > 2 then { eeid2 := eeid; eeid := ee.id; } }; first_corner := 0; corner := 0; prevff:=0; previnx:=0; firstff := 0; firstinx := 0; foreach edge[eeid].facet ff do { /* find proper corner */ prevcorner := corner; ffid := ff.id; inx := 1; while inx <= 3 do { if facet[ffid].vertex[inx].id == vv.id then { corner := facet[ffid].corners[inx]; break; }; inx += 1; }; if ( first_corner == 0 ) then { first_corner := corner; prevff := ffid; firstff := ffid; firstinx := inx; previnx := inx; continue; }; /* edge from previous corner */ if torus then printf "%d %d %d * * *\n",ecounter,prevcorner+voffset, corner+voffset else printf "%d %d %d\n",ecounter,prevcorner+voffset, corner+voffset; facet[prevff].corneredge1[previnx] := ecounter; facet[ffid].corneredge2[inx] := ecounter; ecounter += 1; prevff := ffid; previnx := inx; }; if torus then printf "%d %d %d * * *\n",ecounter,corner+voffset, first_corner+voffset else printf "%d %d %d \n",ecounter,corner+voffset, first_corner+voffset; facet[prevff].corneredge1[previnx] := ecounter; facet[firstff].corneredge2[firstinx] := ecounter; ecounter += 1; /* set corneredges on facets around the other edge */ local signum; local finx; local finx2; local inx2; if (edge[eeid].vertex[1].id == edge[eeid2].vertex[2].id) or (edge[eeid].vertex[2].id == edge[eeid2].vertex[1].id) then signum := 1 // both in same direction else signum := -1; finx2 := 1; foreach edge[eeid2].facet fff do { ffid2 := fff.id; inx2 := 1; while inx2 <= 3 do { if facet[ffid2].vertex[inx2].id == vv.id then { finx := 1; foreach edge[eeid].facet ff do { ffid := ff.id; inx := 1; while inx <= 3 do { if facet[ffid].corners[inx] != facet[ffid2].corners[inx2] then { inx += 1; continue; }; if signum == 1 then { facet[ffid2].corneredge1[inx2] := facet[ffid].corneredge1[inx]; facet[ffid2].corneredge2[inx2] := facet[ffid].corneredge2[inx]; } else // opposite directions { facet[ffid2].corneredge1[inx2] := -facet[ffid].corneredge2[inx]; facet[ffid2].corneredge2[inx2] := -facet[ffid].corneredge1[inx]; }; break 3; }; inx += 1; }; finx += 1; }; inx2 += 1; }; finx2 += 1; }; }; /* end hivalence edge interior vertices */ /* ends of hi-valence edges */ foreach edge ee where valence >= 3 do { foreach ee.vertex vv where vv.hivalence[1] >= 3 do { first_corner := 0; corner := 0; prevff:=0; previnx:=0; firstff := 0; firstinx := 0; foreach ee.facet ff do { /* find proper corner */ prevcorner := corner; ffid := ff.id; inx := 1; while inx <= 3 do { if facet[ffid].vertex[inx].id == vv.id then { corner := facet[ffid].corners[inx]; break; }; inx += 1; }; if ( first_corner == 0 ) then { first_corner := corner; prevff := ffid; firstff := ffid; firstinx := inx; previnx := inx; continue; }; /* edge from previous corner */ if torus then printf "%d %d %d * * * oldedge %d\n",ecounter, prevcorner+voffset, corner+voffset, ee.id else printf "%d %d %d oldedge %d\n",ecounter, prevcorner+voffset, corner+voffset, ee.id; facet[prevff].corneredge1[previnx] := ecounter; facet[ffid].corneredge2[inx] := ecounter; ecounter += 1; prevff := ffid; previnx := inx; }; if torus then printf "%d %d %d * * * oldedge %d\n",ecounter, corner+voffset, first_corner+voffset, ee.id else printf "%d %d %d oldedge %d\n",ecounter, corner+voffset, first_corner+voffset, ee.id; facet[prevff].corneredge1[previnx] := ecounter; facet[firstff].corneredge2[firstinx] := ecounter; ecounter += 1; } }; } /* end wetedges */ /* auxiliary routine to do one pulled-out face around a hi-valence vertex. Input is ffid, signed id of starting facet, vvid, id of vertex in question. */ ffid := 0; // just to declare vvid := 0; // just to declare fcounter := 0; // just to declare define borderbody integer[1] // just to declare do_one_vertex_face := { local startffid; local did_fnum; local inx; local eeid; local cinx; local cedge; local finx; local evalence; startffid := ffid; did_fnum := 0; do { /* find edge with head at vertex */ inx := 1; foreach facet[ffid].edge ee do { if ee.vertex[2].id == vvid then { eeid := ee.oid; break; }; inx += 1; }; if edge[eeid].valence >= 3 then { /* calculate proper corneredge */ if ffid > 0 then cinx := inx == 3 ? 1 : inx+1 else if inx == 1 then cinx := 1 else cinx := 5-inx; if eeid > 0 then { if facet[ffid].didflag1[cinx] then return ; cedge := facet[ffid].corneredge1[cinx]; facet[ffid].didflag1[cinx] := 1; } else { if facet[ffid].didflag2[cinx] then return; cedge := -facet[ffid].corneredge2[cinx]; facet[ffid].didflag2[cinx] := 1; }; if did_fnum == 0 then { printf "%d ",fcounter; borderbody[fcounter] := facet[ffid].backbody; fcounter += 1; }; did_fnum := 1; printf "%d ",cedge; }; /* find next facet around edge */ finx := 1; evalence := edge[eeid].valence; foreach edge[eeid].facet fff do { if fff.oid == ffid then break; finx += 1; }; ffid := -edge[eeid].facet[finx==evalence ? 1 : finx+1].oid; /* add edge between corners, if necessary */ } while ffid != startffid; printf " tension 0.5 ftype vertexfacet // by old vertex %d\n",vvid; } /* end do_one_vertex_face */ /* print face list, less triangles at nodes */ wetfaces := { fstart := max(facet,id)+1; fcounter := fstart; define borderbody integer[fcounter + 40*sum(vertex,hivalence[1]>2) + 2*sum(edge where valence >= 3, valence)]; printf "\nfaces\n"; /* old facets, replacing triple edges with new */ foreach facet ff do { printf "%d ",ff.id; foreach ff.edge ee do { if ( ee.valence == 2 ) then printf "%d ",ee.oid else printf "%d ",(ee.oid<0?-(ff.newedge[1]):ff.newedge[1]) }; printf " oldbodies %d %d ",ff.frontbody,ff.backbody; printf " ftype oldfacet\n"; }; /* tube faces around triple lines */ foreach edge ee where valence >= 3 do { local evalence; local finx; evalence := ee.valence; finx := 1; while finx <= evalence do { local ea; local diag; local inx; local eb; local ed; local ec; ea := ee.facet[finx].newedge[1]; ec := ee.facet[finx==evalence?1:finx+1].newedge[1]; diag := ee.facet[finx].diagedge[1]; inx := 1; ffid := ee.facet[finx].id; while inx <= 3 do { if facet[ffid].vertex[inx].id == ee.vertex[1].id then eb := facet[ffid].corneredge1[inx]; if facet[ffid].vertex[inx].id == ee.vertex[2].id then ed := facet[ffid].corneredge1[inx]; inx += 1; }; printf "%d %d %d %d tension 0.5 ftype tubefacet\n", fcounter,-ea,diag,-ed; borderbody[fcounter] := ee.facet[finx].backbody; fcounter += 1; printf "%d %d %d %d tension 0.5 ftype tubefacet\n", fcounter,eb,ec,-diag; borderbody[fcounter] := ee.facet[finx].backbody; fcounter += 1; finx += 1; } }; /* vertex faces */ printf "//vertex faces:\n"; foreach facet ff do { ff.didflag1[1] := 0; ff.didflag1[2] := 0; ff.didflag1[3] := 0; ff.didflag2[1] := 0; ff.didflag2[2] := 0; ff.didflag2[3] := 0; }; foreach vertex vv where hivalence[1] >= 3 do { /* try starting at each facet */ vvid := vv.id; // for parameter passing foreach vv.facet ff do { ffid := ff.oid; do_one_vertex_face; ffid := -ff.oid; do_one_vertex_face; } /* end foreach facet facet */ } /* end foreach vertex */ } /* end wetfaces */ /* print body list */ /* new facets will be added to bodies at load time */ wetbodies := { printf "\nbodies \n"; local ss,nn,mm,border_volume; ss := spread*avg(edge where valence==2,length); border_volume := sum(edge where valence==3,length)* ss*ss*0.8; foreach body bod do { printf "%d ",bod.id,bod.target; nn := 0; foreach bod.facet ff do { printf "%d ",ff.oid; nn += 1; if ( nn == 10 ) then { printf "\\\n "; nn := 0;} }; for ( mm := 1 ; mm < fcounter ; mm += 1 ) { if borderbody[mm] == bod.id then { printf "%d ",-mm; nn += 1; if ( nn == 10 ) then { printf "\\\n "; nn := 0;} }; }; printf " volume %g \n",bod.target; }; /* border body can be given its facets */ border_body := max(body,id)+1; printf "%d ",border_body; local fnum; for ( fnum := fstart ; fnum < fcounter ; fnum := fnum ) { for ( nn := 1 ; (nn <= 10) and (fnum < fcounter) ; nn += 1 ) { printf "%d ",fnum; fnum += 1; }; printf "\\\n"; }; printf " volume %g",border_volume; printf "\n"; } /* end wetbodies */ /* commands to finish creating facets and bodies at load time */ read_section := { printf "\nread\n\n"; printf "recalc\n"; printf "set body target volume // so volumes don't exceed torus volume\n"; printf "border_body := %d\n",border_body; } wetfoam := { // applicability tests if space_dimension != 3 then { errprintf "wetfoam only works in space dimension 3.\n"; return; }; if surface_dimension != 2 then { errprintf "wetfoam only works for surface dimension 2.\n"; return; }; // if !torus then // { errprintf "wetfoam only works for torus domain.\n"; return; }; if lagrange_order != 1 then { errprintf "wetfoam only works for linear model.\n"; return; }; // minimize arrays to avoid datafile pollution define cornerx real[1]; define cornery real[1]; define cornerz real[1]; define cornern integer[1]; // number of facets in corner shrink_attributes; list topinfo; init_attributes; wettests; printf "\ndefine edge attribute oldedge integer\n\n"; printf "\ndefine facet attribute oldbodies integer[2]\n\n"; printf "\ndefine facet attribute ftype integer\n"; printf "parameter oldfacet = 1 // original facet\n"; printf "parameter tubefacet = 2 // new facet along tube\n"; printf "parameter vertexfacet = 3 // new facet around vertex\n\n"; do_corners; wetverts; wetedges; wetfaces; wetbodies; read_section; shrink_attributes; } // To user: run wetfoam with output redirected to desired file, i.e. // Enter command: wetfoam >>> "wetfile.fe" evolver-2.30c.dfsg/fe/cube.fe0000644000175300017530000000231211410765113016250 0ustar hazelscthazelsct// cube.fe // Evolver data for cube of prescribed volume. vertices 1 0.0 0.0 0.0 2 1.0 0.0 0.0 3 1.0 1.0 0.0 4 0.0 1.0 0.0 5 0.0 0.0 1.0 6 1.0 0.0 1.0 7 1.0 1.0 1.0 8 0.0 1.0 1.0 edges /* given by endpoints and attribute */ 1 1 2 2 2 3 3 3 4 4 4 1 5 5 6 6 6 7 7 7 8 8 8 5 9 1 5 10 2 6 11 3 7 12 4 8 faces /* given by oriented edge loop */ 1 1 10 -5 -9 2 2 11 -6 -10 3 3 12 -7 -11 4 4 9 -8 -12 5 5 6 7 8 6 -4 -3 -2 -1 bodies /* one body, defined by its oriented faces */ 1 1 2 3 4 5 6 volume 1 read // Typical evolution to sphere gogo := { g 5; r; g 5; hessian; r; g 5; hessian; } // Evolution to very high accuracy, using higher-order Lagrange elements. // To be run on original datafile. gogo2 := { g 5; r; g 5; hessian; r; g 5; hessian; lagrange 2; g 5; hessian; lagrange 4; g 5; hessian; lagrange 6; g 5; hessian; ideal_rad := (3*body[1].volume/4/pi)^(1/3); printf "Area error: %g\n",total_area - 4*pi*ideal_rad^2; printf "Vertex radius spread: %g\n", max(vertex,sqrt((x-.5)^2+(y-.5)^2+(z-.5)^2)) - min(vertex,sqrt((x-.5)^2+(y-.5)^2+(z-.5)^2)); } evolver-2.30c.dfsg/fe/sphere.fe0000644000175300017530000000512111410765113016621 0ustar hazelscthazelsct// sphere.fe // Spherical tank partially filled with liquid with contact angle. SYMMETRIC_CONTENT // natural for a sphere parameter rad = 1.00 // sphere radius parameter angle = 45 // contact angle, in degrees // cosine of contact angle #define WALLT cos(angle*pi/180) // density of body #define RHO 1.0 // Various expressions used in constraints #define wstuff (rad^2*z/(x^2 + y^2)/sqrt(x^2+y^2+z^2)) #define gstuff (G*RHO/8*rad^4*z^4/(x^2 + y^2)*(x^2+y^2+z^2)^2) #define gapstuff (G*RHO*rad^4/8*z^2/(x^2+y^2+z^2)^2) constraint 1 convex // the sphere, as wall for liquid formula: sqrt(x^2 + y^2 + z^2) = rad energy: e1: WALLT*wstuff*y - gstuff*y + G*RHO/8*y*z^2 - gapstuff*y e2: -WALLT*wstuff*x + gstuff*x - G*RHO/8*x*z^2 + gapstuff*x e3: 0 content: c1: -rad*wstuff*y/3 c2: rad*wstuff*x/3 c3: 0 constraint 2 // the sphere, for display only formula: sqrt(x^2 + y^2 + z^2) = rad vertices: 1 rad 0 0 constraint 1 2 0 rad 0 constraint 1 3 -rad 0 0 constraint 1 4 0 -rad 0 constraint 1 5 0 0 rad constraint 2 FIXED // to show back hemisphere 6 0 0 -rad constraint 2 FIXED 7 0 rad 0 constraint 2 FIXED 8 -rad 0 0 constraint 2 FIXED 9 0 -rad 0 constraint 2 FIXED edges: 1 1 2 constraint 1 2 2 3 constraint 1 3 3 4 constraint 1 4 4 1 constraint 1 5 5 9 constraint 2 FIXED // to show back hemisphere 6 9 6 constraint 2 FIXED 7 6 7 constraint 2 FIXED 8 7 5 constraint 2 FIXED 9 5 8 constraint 2 FIXED 10 9 8 constraint 2 FIXED 11 6 8 constraint 2 FIXED 12 7 8 constraint 2 FIXED faces: 1 1 2 3 4 2 5 10 -9 constraint 2 density 0 FIXED // to show back hemisphere 3 -10 6 11 constraint 2 density 0 FIXED 4 -11 7 12 constraint 2 density 0 FIXED 5 8 9 -12 constraint 2 density 0 FIXED bodies: // start with sphere half full 1 1 volume 2*pi*rad^3/3 volconst 2*pi*rad^3/3 density RHO read // Typical short evolution gogo := { g 5; r; g 5; r; g 5; r; g 10; conj_grad; g 20; } // Command to print out coordinates of points along cross-section // of the surface. This starts at vertex 3 and follows edges that // point in the positive x direction. section := { thisv := 3; nextv := thisv; do { printf "%9.6f %9.6f %9.6f\n",vertex[thisv].x,vertex[thisv].y, vertex[thisv].z; foreach vertex[thisv].edge ee do { if ee.x > 10*abs(ee.y) then { nextv := ee.vertex[2].id; break; } }; if nextv == thisv then break; // done thisv := nextv; } while 1; } evolver-2.30c.dfsg/fe/compdump.cmd0000755000175300017530000000060511410765113017335 0ustar hazelscthazelsct// compdump.cmd // command for dumping datafile directly to compressed form // dumpcmd := sprintf "compress >%s.dmp.Z",datafilename dodump := { list topinfo; print "\nvertices\n"; list vertices; print "\nedges\n"; list edges; print "\nfaces\n"; list facets; print "\nbodies\n"; list bodies; list bottominfo } compdump := { print dumpcmd; dodump | dumpcmd } evolver-2.30c.dfsg/fe/bonnet_movie.cmd0000755000175300017530000001142411410765113020176 0ustar hazelscthazelsct// bonnet_movie.cmd // Makes in-memory movie of Bonnet rotation of minimal surface, // one frame per degree for 360 degrees. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // Usage: Evolve initial minimal surface, remove all level-set constraints // and boundaries, get nice view in graphics window. // Run "show_movie" to see screen display of Bonnet rotation. // Run "postscript_movie" to create sequence of PostScript files. read "adjoint.cmd" midvertex := 5 // vertex to keep at origin frame_delay := 0.03 // seconds between frames // Store coordinates for complete Bonnet rotation define vertex attribute bonnet real[360][3] make_movie := { local midx,midy,midz; quiet on; for ( bangle := 0 ; bangle < 359.5 ; bangle += 1 ) { adjoint; midx := vertex[midvertex].x; midy := vertex[midvertex].y; midz := vertex[midvertex].z; foreach vertex vv do { vv.bonnet[bangle+1][1] := vv.x-midx; vv.bonnet[bangle+1][2] := vv.y-midy; vv.bonnet[bangle+1][3] := vv.z-midz; }; flip; }; quiet off; } procedure set_bonnet(integer angle) { foreach vertex vv do { vv.x := vv.bonnet[angle+1][1]; vv.y := vv.bonnet[angle+1][2]; vv.z := vv.bonnet[angle+1][3]; }; recalc; } show_movie := { local next_time; next_time := clock; for ( angle := 0; angle < 359.5 ; angle += 1 ) { while clock < next_time do {}; next_time += frame_delay; set_bonnet(angle); } } // write postscript file for each frame postscript_movie := { full_bounding_box on; for ( angle := 0; angle < 359.5 ; angle += 1 ) { set_bonnet(angle); postscript sprintf"%s.%03d",datafilename,angle; } } // continuous loop movie := { for (;;) show_movie; } // Write split-vertex adjoint datafile. To be done after conconj; // uses enewx values to compute vertices. define facet attribute split_bonnet real[3][360][3] write_split_movie := { printf "// split-vertex discrete adjoint of %s, rotation angle %f.\n", datafilename,bangle; printf "\ndefine vertex attribute bonnet real[360][3]\n\n"; printf "\nvertices\n"; foreach facet ff do { printf "%d %15.10f %15.10f %15.10f bonnet ",3*ff.id-2, ff.edge[1].enewx[1]+ff.edge[2].enewx[1]-ff.edge[3].enewx[1], ff.edge[1].enewx[2]+ff.edge[2].enewx[2]-ff.edge[3].enewx[2], ff.edge[1].enewx[3]+ff.edge[2].enewx[3]-ff.edge[3].enewx[3]; print ff.split_bonnet[1]; printf "\n"; printf "%d %15.10f %15.10f %15.10f bonnet ",3*ff.id-1, -ff.edge[1].enewx[1]+ff.edge[2].enewx[1]+ff.edge[3].enewx[1], -ff.edge[1].enewx[2]+ff.edge[2].enewx[2]+ff.edge[3].enewx[2], -ff.edge[1].enewx[3]+ff.edge[2].enewx[3]+ff.edge[3].enewx[3]; print ff.split_bonnet[2]; printf "\n"; printf "%d %15.10f %15.10f %15.10f bonnet ",3*ff.id, ff.edge[1].enewx[1]-ff.edge[2].enewx[1]+ff.edge[3].enewx[1], ff.edge[1].enewx[2]-ff.edge[2].enewx[2]+ff.edge[3].enewx[2], ff.edge[1].enewx[3]-ff.edge[2].enewx[3]+ff.edge[3].enewx[3]; print ff.split_bonnet[3]; printf "\n"; }; printf "\nedges\n"; foreach facet ff do { printf "%d %d %d\n",3*ff.id-2,3*ff.id-2,3*ff.id-1; printf "%d %d %d\n",3*ff.id-1,3*ff.id-1,3*ff.id; printf "%d %d %d\n",3*ff.id,3*ff.id,3*ff.id-2; }; printf "\nfaces\n"; foreach facet ff do printf "%d %d %d %d\n",ff.id,3*ff.id-2,3*ff.id-1,3*ff.id; printf "\nread\n"; printf "read \"bonnet_movie.cmd\"\n"; } make_split_movie := { local midx,midy,midz; quiet on; for ( bangle := 0 ; bangle < 359.5 ; bangle += 1 ) { adjoint; midx := vertex[midvertex].x; midy := vertex[midvertex].y; midz := vertex[midvertex].z; foreach edge ee do { ee.enewx[1] -= midx; ee.enewx[2] -= midy; ee.enewx[3] -= midz; }; foreach facet ff do { ff.split_bonnet[1][bangle+1][1] := ff.edge[1].enewx[1]+ff.edge[2].enewx[1]-ff.edge[3].enewx[1]; ff.split_bonnet[1][bangle+1][2] := ff.edge[1].enewx[2]+ff.edge[2].enewx[2]-ff.edge[3].enewx[2]; ff.split_bonnet[1][bangle+1][3] := ff.edge[1].enewx[3]+ff.edge[2].enewx[3]-ff.edge[3].enewx[3]; ff.split_bonnet[2][bangle+1][1] := -ff.edge[1].enewx[1]+ff.edge[2].enewx[1]+ff.edge[3].enewx[1]; ff.split_bonnet[2][bangle+1][2] := -ff.edge[1].enewx[2]+ff.edge[2].enewx[2]+ff.edge[3].enewx[2]; ff.split_bonnet[2][bangle+1][3] := -ff.edge[1].enewx[3]+ff.edge[2].enewx[3]+ff.edge[3].enewx[3]; ff.split_bonnet[3][bangle+1][1] := ff.edge[1].enewx[1]-ff.edge[2].enewx[1]+ff.edge[3].enewx[1]; ff.split_bonnet[3][bangle+1][2] := ff.edge[1].enewx[2]-ff.edge[2].enewx[2]+ff.edge[3].enewx[2]; ff.split_bonnet[3][bangle+1][3] := ff.edge[1].enewx[3]-ff.edge[2].enewx[3]+ff.edge[3].enewx[3]; }; flip; }; quiet off; } evolver-2.30c.dfsg/fe/stl.cmd0000755000175300017530000000126011410765113016311 0ustar hazelscthazelsct// stl.cmd // Surface Evolver command to produce STL format text file from surface. // Evolver command line usage: // read "stl.cmd" // stl >>> "filename.stl" // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke stl := { local mag,inx; printf "solid\n"; foreach facet ff do { mag := sqrt(ff.x^2+ff.y^2+ff.z^2); printf "facet normal %f %f %f\n",ff.x/mag,ff.y/mag,ff.z/mag; printf " outer loop\n"; for ( inx := 1 ; inx <= 3 ; inx += 1 ) printf " vertex %f %f %f\n",ff.vertex[inx].x,ff.vertex[inx].y, ff.vertex[inx].z; printf " endloop\n"; printf " endfacet\n"; }; printf "endsolid\n"; } evolver-2.30c.dfsg/fe/tankex.fe0000644000175300017530000000523311410765113016631 0ustar hazelscthazelsct// tankex.fe // Equilibrium shape of liquid in flat-ended cylindrical tank. // Tank has axis along y-axis and flat bottom in x-z plane. This // is so gravity acting vertically draws liquid toward wall. // Straight edges cannot conform exactly to curved walls. // We need to give them an area so that area cannot shrink by straght edges // pulling away from the walls. The gaps are also accounted for // in volume and gravitational energy. SYMMETRIC_CONTENT // for volume calculations // Contact angles, initially for 45 degrees. PARAMETER ENDT = 0.707 /* surface tension of uncovered base */ PARAMETER WALLT = 0.707 /* surface tension of uncovered side wall */ // Gravity components PARAMETER GY = 0 PARAMETER GZ = -1 SPRING_CONSTANT 1 // for most accurate gap areas for constraint 2 #define TR 1.00 /* tank radius */ #define RHO 1.00 /* fuel density */ constraint 1 // flat base function: y = 0 energy: // for contact energy line integral e1: -ENDT*z e2: 0 e3: 0 #define wstuff (WALLT*TR*y/(x^2 + z^2)) // common wall area term #define vstuff (TR^2/3*y/(x^2 + z^2)) // common wall volume term #define gstuff (GY*TR^2*y^2/4/(x^2 + z^2) + GZ*TR^3*y*z/3/(x^2+z^2)**1.5) // common gap gravity term constraint 2 CONVEX // cylindrical wall function: x^2 + z^2 = TR^2 energy: // for contact energy and gravity e1: -wstuff*z + RHO*GY*y^2*z/4 + RHO*GZ*y*z^2/3 - RHO*gstuff*z e2: 0 e3: wstuff*x - RHO*GY*y^2*x/4 - RHO*GZ*y*z*x/3 + RHO*gstuff*x content: // so volumes calculated correctly c1: vstuff*z - z*y/6 + vstuff*z/2 c2: 0 c3: -vstuff*x + x*y/6 - vstuff*x/2 // named quantity for arbitrary direction gravity on facets quantity arb_grav energy method facet_vector_integral global vector_integrand: q1: 0 q2: -RHO*GY*y^2/2 - RHO*GZ*y*z q3: 0 // Now the specification of the initial shape vertices 1 0.5 0.0 0.5 constraint 1 2 0.5 0.0 -0.5 constraint 1 3 -0.5 0.0 -0.5 constraint 1 4 -0.5 0.0 0.5 constraint 1 5 1.0 0.5 0.0 constraint 2 6 0.0 0.5 -1.0 constraint 2 7 -1.0 0.5 0.0 constraint 2 8 0.0 0.5 1.0 constraint 2 edges 1 2 1 constraint 1 2 1 4 constraint 1 3 4 3 constraint 1 4 3 2 constraint 1 5 5 6 constraint 2 6 6 7 constraint 2 7 7 8 constraint 2 8 8 5 constraint 2 9 1 8 10 1 5 11 2 5 12 2 6 13 3 6 14 3 7 15 4 7 16 4 8 faces 1 13 6 -14 2 3 14 -15 3 15 7 -16 4 2 16 -9 5 9 8 -10 6 1 10 -11 7 11 5 -12 8 4 12 -13 bodies 1 1 2 3 4 5 6 7 8 volume 0.6 density 0 /* no default gravity */ evolver-2.30c.dfsg/fe/fourier.cmd0000755000175300017530000000323211410765113017163 0ustar hazelscthazelsct// fourier.cmd // Evolver command to print Fourier components for closed curve // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // Usage: f_component(order) procedure f_component (integer f_order) // f_order is the order of Fourier components. { local e_id,first_e,v_id,newe_id; x_sin_coeff := 0.0; y_sin_coeff := 0.0; z_sin_coeff := 0.0; x_cos_coeff := 0.0; y_cos_coeff := 0.0; z_cos_coeff := 0.0; ecount := 0; // get starting edge, since can't assume edge[1] exists foreach edge do e_id := id; first_e := e_id; v_id := edge[e_id].vertex[1].id; // follow connected edges do { local ss,cc,newv_id; ss := sin(f_order*ecount*2*pi/edge_count)*2/edge_count; x_sin_coeff := x_sin_coeff + vertex[v_id].x*ss; y_sin_coeff := y_sin_coeff + vertex[v_id].y*ss; z_sin_coeff := z_sin_coeff + vertex[v_id].z*ss; cc := cos(f_order*ecount*2*pi/edge_count)*2/edge_count; x_cos_coeff := x_cos_coeff + vertex[v_id].x*cc; y_cos_coeff := y_cos_coeff + vertex[v_id].y*cc; z_cos_coeff := z_cos_coeff + vertex[v_id].z*cc; foreach edge[e_id].vertex vv do { if ( vv.id != v_id ) then { newv_id := vv.id; foreach vv.edge ee do if ee.id != e_id then newe_id := ee.id } }; e_id := newe_id; v_id := newv_id; ecount := ecount + 1; } while ( (e_id != first_e) and (ecount <= edge_count) ); printf "Order %g x y z \n",f_order; printf "sin: %20.15f %20.15f %20.15f\n",x_sin_coeff,y_sin_coeff,z_sin_coeff; printf "cos: %20.15f %20.15f %20.15f\n",x_cos_coeff,y_cos_coeff,z_cos_coeff; } evolver-2.30c.dfsg/fe/addload_example.fe0000644000175300017530000000573511410765113020451 0ustar hazelscthazelsct// addload_example.fe // Demonstration of using the addload command to load multiple // copies of the same file. Basic surface is that of mound.fe, // a liquid drop on a plane. // Each separate mound will have its own contact angle, implemented // by having a contact angle attribute for each edge. // The "setup" script below invokes addload multiple times on this same // datafile, moviing each instance to its appropriate location. // Note that addload does not execute the "read" section of a datafile, // so such "setup" scripts are ru only once. // Programmeer: Ken Brakke, brakke@susqu.edu define edge attribute angle real // interior angle between plane and surface, degrees gravity_constant 0 // start with gravity off #define WALLT (-cos(angle*pi/180)) // virtual tension of facet on plane constraint 1 /* the table top */ formula: x3 = 0 energy: // for contact angle e1: -(WALLT*y) e2: 0 e3: 0 vertices 1 0.0 0.0 0.0 constraint 1 /* 4 vertices on plane */ 2 1.0 0.0 0.0 constraint 1 3 1.0 1.0 0.0 constraint 1 4 0.0 1.0 0.0 constraint 1 5 0.0 0.0 1.0 6 1.0 0.0 1.0 7 1.0 1.0 1.0 8 0.0 1.0 1.0 9 2.0 2.0 0.0 fixed /* for table top */ 10 2.0 -1.0 0.0 fixed 11 -1.0 -1.0 0.0 fixed 12 -1.0 2.0 0.0 fixed edges /* given by endpoints and attribute */ 1 1 2 constraint 1 /* 4 edges on plane */ 2 2 3 constraint 1 3 3 4 constraint 1 4 4 1 constraint 1 5 5 6 6 6 7 7 7 8 8 8 5 9 1 5 10 2 6 11 3 7 12 4 8 13 9 10 no_refine fixed /* for table top */ 14 10 11 no_refine fixed 15 11 12 no_refine fixed 16 12 9 no_refine fixed faces /* given by oriented edge loop */ 1 1 10 -5 -9 2 2 11 -6 -10 3 3 12 -7 -11 4 4 9 -8 -12 5 5 6 7 8 7 13 14 15 16 no_refine density 0 fixed /* table top for display */ bodies /* one body, defined by its oriented faces */ 1 1 2 3 4 5 volume 1 density 1 read setup := { // Read in 3 x 3 arrangement of drops. The first has already been // read in, so we need to read in 8 more copies of this datafile. // We use a vertex attribute to distinguish between old and new // elements; for more elaborate massaging of new input you could // use more attributes on more types of elements. define vertex attribute load_marker integer; for ( row := 0 ; row < 3 ; row += 1 ) for ( col := 0 ; col < 3 ; col += 1 ) { if ( row > 0 or col > 0 ) then addload datafilename; // Move and mark vertices foreach vertex vv where load_marker == 0 do { vv.x += 2*row; vv.y += 2*col; vv.load_marker := 3*row+col+1; // different loads get different marks }; }; // Now assign contact angles to contact line edges foreach edge ee where on_constraint 1 do ee.angle := 90 + (ee.vertex[1].load_marker - 5)*10; } // end setup setup // From here on, we have the usual evolution commands re := {refine edges where on_constraint 1 } // Typical evolution gogo := { re; g 5; r; g 5; r; g 5; hessian; hessian; } evolver-2.30c.dfsg/fe/catbody.fe0000644000175300017530000000501111410765113016756 0ustar hazelscthazelsct// catbody.fe // Evolver data for catenoid with fixed inner volume between plane ends. PARAMETER RMAX = 1.0 PARAMETER ZMAX = 0.55 boundary 1 parameters 1 // upper ring x1: RMAX * cos(p1) x2: RMAX * sin(p1) x3: ZMAX boundary 2 parameters 1 // lower ring x1: RMAX * cos(p1) x2: RMAX * sin(p1) x3: -ZMAX vertices // given in terms of boundary parameter 1 0.00 boundary 1 fixed 2 pi/3 boundary 1 fixed 3 2*pi/3 boundary 1 fixed 4 pi boundary 1 fixed 5 4*pi/3 boundary 1 fixed 6 5*pi/3 boundary 1 fixed 7 0.00 boundary 2 fixed 8 pi/3 boundary 2 fixed 9 2*pi/3 boundary 2 fixed 10 pi boundary 2 fixed 11 4*pi/3 boundary 2 fixed 12 5*pi/3 boundary 2 fixed edges 1 1 2 boundary 1 fixed 2 2 3 boundary 1 fixed 3 3 4 boundary 1 fixed 4 4 5 boundary 1 fixed 5 5 6 boundary 1 fixed 6 6 1 boundary 1 fixed 7 7 8 boundary 2 fixed 8 8 9 boundary 2 fixed 9 9 10 boundary 2 fixed 10 10 11 boundary 2 fixed 11 11 12 boundary 2 fixed 12 12 7 boundary 2 fixed 13 1 7 14 2 8 15 3 9 16 4 10 17 5 11 18 6 12 faces 1 13 7 -14 -1 2 14 8 -15 -2 3 15 9 -16 -3 4 16 10 -17 -4 5 17 11 -18 -5 6 18 12 -13 -6 bodies 1 1 2 3 4 5 6 volconst 2*pi*rmax^2*zmax volume 1.1 read // Typical evolution gogo := { u; r; g 5; r; g 5; r; g 5; hessian; hessian } // Adjusting volume to find zero pressure, i.e. the unstable // equilibrium catenoid. Run after refining to taste. // Note that no 'g' steps are used in iteration after changing // the volume. This is because the triangulation is not optimal, // and 'g' lets the vertices slither sideways, but 'hessian' // moves vertices only perpendicular to the surface (in the default // hessian_normal mode). The magnitude of volume changes is limited // since experimentation showed that too large moves can cause // blowups. adjust := { oldpressure := body[1].pressure; oldtarget := body[1].target; dvol := .01; // starting step size body[1].target += dvol; hessian; hessian; hessian; slope := (body[1].pressure - oldpressure)/dvol; for ( inx := 1 ; inx <= 20; inx += 1 ) { dvol := -body[1].pressure/slope; if abs(dvol) > .01 then dvol := (dvol < 0) ? -.01 : .01; oldpressure := body[1].pressure; body[1].target += dvol; hessian; hessian; hessian; printf "Pressure %g dvol %g slope %f\n",body[1].pressure,dvol,slope; if abs(dvol) > .001 then slope := (body[1].pressure - oldpressure)/dvol; }; } evolver-2.30c.dfsg/fe/saveview.cmd0000755000175300017530000000243611410765113017346 0ustar hazelscthazelsct// saveview.cmd // Evolver command for saving current view matrix in proper form // for reading in. // Typical usage: saveview >>> "viewfile.cmd" // Then to restore view: read "viewfile.cmd" // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke saveview := { printf "oldautodisplay := (autodisplay);\n"; printf "autodisplay off\n"; printf "view_matrix[1][1] := %f\n",view_matrix[1][1]; printf "view_matrix[1][2] := %f\n",view_matrix[1][2]; printf "view_matrix[1][3] := %f\n",view_matrix[1][3]; printf "view_matrix[1][4] := %f\n",view_matrix[1][4]; printf "view_matrix[2][1] := %f\n",view_matrix[2][1]; printf "view_matrix[2][2] := %f\n",view_matrix[2][2]; printf "view_matrix[2][3] := %f\n",view_matrix[2][3]; printf "view_matrix[2][4] := %f\n",view_matrix[2][4]; printf "view_matrix[3][1] := %f\n",view_matrix[3][1]; printf "view_matrix[3][2] := %f\n",view_matrix[3][2]; printf "view_matrix[3][3] := %f\n",view_matrix[3][3]; printf "view_matrix[3][4] := %f\n",view_matrix[3][4]; printf "view_matrix[4][1] := %f\n",view_matrix[4][1]; printf "view_matrix[4][2] := %f\n",view_matrix[4][2]; printf "view_matrix[4][3] := %f\n",view_matrix[4][3]; printf "view_matrix[4][4] := %f\n",view_matrix[4][4]; printf "if oldautodisplay then autodisplay on\n"; } evolver-2.30c.dfsg/fe/band.cmd0000755000175300017530000001214511410765113016417 0ustar hazelscthazelsct// band.cmd // Surface Evolver command to create triangulated band bordering // deisgnated edges and vertices. Purpose is to make extremely // accurate PostScript files without notching on thick edges. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // WARNING: This command modifies the surface by creating a lot of // tiny facets. You should use only on a disposable copy of your surface. // Usage: set bandcolor and bandwidth to desired values; bandwidth is // the width of the band in surface coordinates on one // side of an edge. // set the edge attribute inband to nonzero for those edges // to have band drawn along them. // run makeband // When creating PostScript file, use the NN response to P 3 "Show grid lines?" // IMPORTANT NOTE: The algorithm has trouble where inband edges meet // at an angle, since each vertex has a single band distance attribute // and the vertex may be a neighbor to two different bands. This results // in ragged bands near corners. A cure is to do banding in stages, // doing one "original" edge at a time. // Not wise to run more than once along a given edge. Numerical // variations due to new vertices lead to a lot more unnecessary // refinements. define edge attribute inband integer // 1 if band center, 0 if not define vertex attribute banddist real // distance from band center. define facet attribute bandused integer // so use each facet at most once bandcolor := black // Set this to desired color before makeband. bandwidth := 0.010 // Set this to desired half-width before makeband. makeband := { local maxbanddist,changed,eps; maxbanddist := 100000*bandwidth; set vertex banddist maxbanddist; foreach edge ee where inband do set ee.vertex banddist 0; foreach edge ee where (ee.inband==0) and (ee.vertex[1].banddist==0) and (ee.vertex[2].banddist==0) do { refine ee; ee.vertex[2].banddist := maxbanddist; } ; set facet bandused 0; // Calculate vertex distances from band center do { changed := 0; foreach facet ff do { local s1,s2,s3,d1,d2,d3; local newd1,newd2,newd3; if ff.bandused then continue; s1 := ff.edge[1].length; s2 := ff.edge[2].length; s3 := ff.edge[3].length; d1 := ff.vertex[1].banddist; d2 := ff.vertex[2].banddist; d3 := ff.vertex[3].banddist; if ( (d2 < d1 or d3 < d1) and ( ( (d2 < bandwidth) && (d3 < maxbanddist) ) or ( (d2 < maxbanddist) && (d3 < bandwidth) ) ) ) then { newd1 := d3 + s3*sin(asin((d2-d3)/s2)+acos((-s1^2+s2^2+s3^2)/2/s2/s3)); if ( newd1 > 0 and newd1 < d1 ) then { ff.vertex[1].banddist := newd1; changed += 1; ff.bandused := 1; continue; }; }; if ( (d1 < d2 or d3 < d2 ) and ( ( (d1 < bandwidth) && (d3 < maxbanddist) ) or ( (d1 < maxbanddist) && (d3 < bandwidth) ) ) ) then { newd2 := d1 + s1*sin(asin((d3-d1)/s3)+acos((-s2^2+s3^2+s1^2)/2/s3/s1)); if ( newd2 > 0 and newd2 < d2 ) then { ff.vertex[2].banddist := newd2; changed += 1; ff.bandused := 1; continue; }; }; if ( (d1 < d3 or d2 < d3 ) and ( ( (d1 < bandwidth) && (d2 < maxbanddist) ) or ( (d1 < maxbanddist) && (d2 < bandwidth) ) ) ) then { newd3 := d2 + s2*sin(asin((d1-d2)/s1)+acos((-s3^2+s1^2+s2^2)/2/s1/s2)); if ( newd3 > 0 and newd3 < d3 ) then { ff.vertex[3].banddist := newd3; changed += 1; ff.bandused := 1; continue; }; }; }; } while ( changed > 0 ); printf "\n"; // Subdivide edges spanning band boundary eps := 1e-4*bandwidth; // numerical margin for error foreach edge ee do { local d1,d2; local new_x,new_y,new_z; local lambda; d1 := ee.vertex[1].banddist; d2 := ee.vertex[2].banddist; if ( ((d1<=bandwidth+eps)&&(d2<=bandwidth+eps)) || ((d1>=bandwidth-eps)&&(d2>=bandwidth-eps))) then continue; lambda := (bandwidth - d2)/(d1 - d2); if ( lambda < .00001 or lambda > .99999 ) then continue; new_x := lambda*ee.vertex[1].x + (1-lambda)*ee.vertex[2].x; new_y := lambda*ee.vertex[1].y + (1-lambda)*ee.vertex[2].y; new_z := lambda*ee.vertex[1].z + (1-lambda)*ee.vertex[2].z; refine ee; ee.vertex[2].x := new_x; ee.vertex[2].y := new_y; ee.vertex[2].z := new_z; ee.vertex[2].banddist := bandwidth; }; // Color band triangles foreach facet ff do { if avg(ff.vertex,banddist) < bandwidth then set ff color bandcolor; }; } // to do edges on different constraints one constraint at a time. cband := { local connum; for ( connum := 1 ; connum <= high_constraint ; connum += 1 ) { if valid_constraint(connum) then { set edge inband on_constraint connum; makeband; } }; } // makeband usage: // set bandwidth to desired half-width (in surface coordinates) // set edge inband to 1 along band center, 0 elsewhere // set bandcolor to desired color // run makeband // In making postscript, answer N to gridlines, and y to colors. evolver-2.30c.dfsg/fe/strdup.cmd0000755000175300017530000000265711410765113017043 0ustar hazelscthazelsct// strdup.cmd // Evolver command to write a datafile that is an n-fold // covering of a string. Meant to generate multiple coverings // of elastic figure 8's. // Usage: set dupnum to desired multiple and run "strdup". // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke dupnum := 2 // covering order strdup := { local sheet,vbase,connectflag,swapedge,ebase; list topinfo; printf "\nvertices\n"; sheet := 0; while ( sheet < dupnum ) do { vbase := sheet*vertex_count; foreach vertex do printf "%g %g %g\n",id+vbase,x,y; sheet := sheet + 1; }; printf "\nedges\n"; connectflag := 0; sheet := 0; swapedge := -1; while ( sheet < dupnum ) do { ebase := sheet*edge_count; vbase := sheet*vertex_count; foreach edge ee do { if connectflag and (swapedge != ee.id) then printf "%g %g %g\n", ee.id+ebase,ee.vertex[1].id+vbase,ee.vertex[2].id+vbase else { printf "%g %g %g\n", ee.id+ebase,ee.vertex[1].id+vbase, ee.vertex[2].id+((sheet+1)imod dupnum)*vertex_count; connectflag := 1; swapedge := ee.id; } }; sheet := sheet + 1; }; printf "\nread\n"; list procedures; } // end strdup evolver-2.30c.dfsg/fe/README-cmd.txt0000644000175300017530000000625411410765113017266 0ustar hazelscthazelsctA selection of Evolver commands for your edification. Consult remarks at the top of each file for further information and instructions on use. Use with caution; no attempt has been made to bulletproof these. Note: These scripts do not necessarily take advantage of all the latest Evolver features, and if I were writing them from scratch now they might turn out quite different. adjoint.cmd ansurf.cmd Produces input file for ANSYS. band.cmd Make extremely accurate thick borders for graphics. bonnet_movie.cmd Show Bonnet rotation of minimal surface. cmccousin.cmd Create constant mean curvature "cousin" surface. dirichlet_to_disk.cmd Makes conformal map to disk by minimizing Dirichlet energy. dxf.cmd Produce AutoCad DXF file. equi.cmd To color edges subject to equiangulation. equi2.cmd To color edges subject to equiangulation. fourier.cmd Calculate Fourier components for closed curve gaussequi.cmd Equiangulation using Gauss map for swap criterion. gaussmap.cmd Converts each vertex coordinate to unit normal. gaussref.cmd Refining using Gauss map as criterion. iges.cmd Create file in IGES text format. iges128.cmd Create IGES file using B-spline entity (type 128). iges136.cmd Create IGES file using finite element entity (type 136). intersec.cmd Detect intersection of linear edges and facets. jvx.cmd Creates JVX format file for JavaView. multiplicate.cmd Create datafile with multiple copies of surface. neville.cmd Neville's algorithm for interpolating b-splines in 1D and 2D. off.cmd Prints OFF format file. order.cmd Numbers string vertices consecutively. orderdmp.cmd Number string vertices consecutively and create an ordered dump file. orderlis.cmd Number string vertices consecutively and list them polyfilm.cmd Produce Polycut format file povray.cmd Produces POV-Ray input file. povrays.cmd POV-Ray file with smooth shading. quadbbox.cmd Finds bounding boxes for quadratic facets in 3D quadint2.cmd For detecting intersection of quadratic facets in 3D quadmeet.cmd For detecting intersection of quadratic facets in 3D reorder.cmd Example of using "reorder_storage" command. rewrap.cmd Adjusting wraps of edges in 3D torus model. rewrap2.cmd Adjusting wraps of edges in 2D torus model. rib.cmd Creates Renderman RIB file. rotate.cmd Rotate unfixed vertices by angle about z axis. saveview.cmd Saves view matrix in easily reloadable form. slice.cmd Calculate length of intersection of plane with surface. slice2.cmd Calculates length of intersection of plane with one body. slicer.cmd Create physical intersection of surface and plane. stl.cmd Creates file in STL stereolithography format. strdup.cmd Writes a datafile that is an n-fold covering of a string. tuber.cmd Creates tubes around certain edges. unshear.cmd Morphs 2D torus fundamental region to rectangle. vrml.cmd Makes VRML file for surface. wavefront.cmd Creates Wavefront format file. wetfoam2.cmd Creates Plateau borders on a dry foam. x3d.cmd Creates X3D format file. xray.cmd Creates PostScript file with x-ray image. zebra.cmd Colors string edges alternately black and white evolver-2.30c.dfsg/fe/iges.cmd0000755000175300017530000001753611410765113016453 0ustar hazelscthazelsct// iges.cmd // Surface Evolver script to write IGES file for surface, using IGES // finite element entity (type 136), which handles linear, quadratic, // and cubic triangles (among many other types not of interest). // Finite element type not official "geometry"???? Rhino says can't // find any independent geometry, and http://www.iges5x.org/taxonomy/ has // FEM types included under "non-geometry taxonomy'. // Also not listed in Table 3 on p. 38 of IGES-6 documentation. // So we try using parametric patches, type 114. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // usage: iges >>> "filename.igs" // Set up color translation array define iges_colors integer [16]; //iges_colors[black] := 1 iges_colors[red] := 2 iges_colors[green] := 3 iges_colors[blue] := 4 iges_colors[yellow] := 5 iges_colors[magenta] := 6 iges_colors[cyan] := 7 iges_colors[white] := 8 iges_colors[brown] := 2 // red iges_colors[lightgray] := 8 // white iges_colors[darkgray] := 1 // black iges_colors[lightblue] := 4 // blue iges_colors[lightcyan] := 7 // cyan iges_colors[lightred] := 2 // red iges_colors[lightmagenta] := 7 iges := { local start_counter,global_counter,message,xmax,ymax,zmax; local maxsize,entype,paramdata,structure,linefont,level,view; local transmat,label,status,directory_counter,lineweight; local colornum,paramcount,form,reserved,entlabel,entsubscr; local parameter_counter,ctype,ptype,mvalue,nvalue,minu,maxu; local minv,maxv,co00,co10,co01,co11,line,subcount; // Flag section // Don't need this since not doing binary or compressed format. // Start section start_counter := 0; start_counter += 1; printf "%-72sS%07d\n","IGES version of Surface Evolver surface",start_counter; start_counter += 1; printf " %-69sS%07d\n",datafilename,start_counter; start_counter += 1; printf "%-72sS%07d\n","Created using iges.cmd Evolver script.", start_counter; // Global section global_counter := 0; global_counter += 1; printf "%-72sG%07d\n","1H,,1H;,",global_counter; global_counter += 1; printf "%02dH%-69sG%07d\n",sizeof("Surface Evolver"),"Surface Evolver,", global_counter; global_counter += 1; message := sprintf "%s,",datafilename; printf "%02dH%-69sG%07d\n",sizeof(datafilename),message,global_counter; global_counter += 1; printf "%02dH%-69sG%07d\n",sizeof("Surface Evolver"),"Surface Evolver,", global_counter; global_counter += 1; printf "%02dH%-69sG%07d\n",sizeof("Surface Evolver"),"Surface Evolver,", global_counter; global_counter += 1; printf "%-72sG%07d\n","32,75,6,75,15,,1.0,1,2HIN,32768,0.0394,", global_counter; global_counter += 1; printf "%-72sG%07d\n","15H00000000.000000,", global_counter; // date xmax := max(vertex,abs(x)); ymax := max(vertex,abs(y)); zmax := max(vertex,abs(z)); maxsize := (xmax > ymax) ? xmax : ymax; maxsize := (zmax > maxsize) ? zmax : maxsize; message := sprintf "%g,%g,",maxsize/10000000,maxsize; global_counter += 1; printf "%-72sG%07d\n",message, global_counter; global_counter += 1; printf "%02dH%-69sG%07d\n",sizeof("Name of author"),"Name of author,", global_counter; global_counter += 1; printf "%02dH%-69sG%07d\n",sizeof("Author's organization"), "Author's organization,", global_counter; global_counter += 1; printf "%-72sG%07d\n","15H00000000.000000;", global_counter; // date // Directory entry section. Each entry 20 8-char fields on two lines. // Fields with default values entype := 0; // 1 and 11 paramdata := 1; // 2 structure := 0; // 3 linefont := 0; // 4 level := 0; // 5 view := 0; // 6 transmat := 0; // 7 label := 0; // 8 status := "00000000"; // 9; actually 4 two-digit numbers directory_counter := 0; // 10 and 20 lineweight := 0; // 12 colornum := 0; // 13 paramcount := 0; // 14 form := 0; // 15 reserved := " "; // 16 and 17 entlabel := "entity"; // 18 entsubscr := 0; // 19 // Facets as "parametric surface" types entype := 114; status := "00010001"; paramcount := 1; entlabel := " FACET"; define facet attribute fpdata integer; define facet attribute fdir integer; foreach facet ff do { ff.fpdata := paramdata; entsubscr := ff.id; directory_counter += 1; ff.fdir := directory_counter; printf "%8d%8d%8d%8d%8d%8d%8d%8d%8sD%7d\n",entype,paramdata,structure, linefont,level,view,transmat,label,status,directory_counter; directory_counter += 1; printf "%8d%8d%8d%8d%8d%8s%8s%8s%8dD%7d\n",entype,lineweight, iges_colors[ff.color], paramcount,form,reserved,reserved,entlabel,entsubscr,directory_counter; paramdata += 7; }; // Geometry element entype := 402; status := "00000301"; paramcount := ceil(facet_count/10); form := 7; entlabel := " SURFACE"; entsubscr := 1; directory_counter += 1; printf "%8d%8d%8d%8d%8d%8d%8d%8d%8sD%7d\n",entype,paramdata,structure, linefont,level,view,transmat,label,status,directory_counter; directory_counter += 1; printf "%8d%8d%8d%8d%8d%8s%8s%8s%8dD%7d\n",entype,lineweight,colornum, paramcount,form,reserved,reserved,entlabel,entsubscr,directory_counter; // Parameter data section parameter_counter := 0; // Facets entype := 114; ctype := 1; // linear ptype := 0; // unspecified mvalue := 1; // number of u segments nvalue := 1; // number of v segments minu := 0; maxu := 1; minv := 0; maxv := 1; foreach facet ff do { parameter_counter += 1; if ff.fpdata != parameter_counter then errprintf "ERROR: bad facet parameter line number, facet %d. Is %d, should be %d.\n", ff.id,parameter_counter,ff.fpdata; message := sprintf "%d,%d,%d,%d,%d,%g,%g,%g,%g,",entype,ctype,ptype, mvalue,nvalue,minu,maxu,minv,maxv; printf "%-64s%8dP%7d\n",message,ff.fdir,parameter_counter; co00 := ff.vertex[1].x; co10 := ff.vertex[2].x-ff.vertex[1].x; co01 := ff.vertex[3].x-ff.vertex[1].x; co11 := ff.vertex[1].x-ff.vertex[2].x; message := sprintf "%10.7f,%10.7f,0,0,%10.7f,%10.7f,0,0,", co00,co10,co01,co11; parameter_counter += 1; printf "%-64s%8dP%7d\n",message,ff.fdir,parameter_counter; message := "0,0,0,0,0,0,0,0,"; parameter_counter += 1; printf "%-64s%8dP%7d\n",message,ff.fdir,parameter_counter; co00 := ff.vertex[1].y; co10 := ff.vertex[2].y-ff.vertex[1].y; co01 := ff.vertex[3].y-ff.vertex[1].y; co11 := ff.vertex[1].y-ff.vertex[2].y; message := sprintf "%10.7f,%10.7f,0,0,%10.7f,%10.7f,0,0,", co00,co10,co01,co11; parameter_counter += 1; printf "%-64s%8dP%7d\n",message,ff.fdir,parameter_counter; message := "0,0,0,0,0,0,0,0,"; parameter_counter += 1; printf "%-64s%8dP%7d\n",message,ff.fdir,parameter_counter; co00 := ff.vertex[1].z; co10 := ff.vertex[2].z-ff.vertex[1].z; co01 := ff.vertex[3].z-ff.vertex[1].z; co11 := ff.vertex[1].z-ff.vertex[2].z; message := sprintf "%10.7f,%10.7f,0,0,%10.7f,%10.7f,0,0,", co00,co10,co01,co11; parameter_counter += 1; printf "%-64s%8dP%7d\n",message,ff.fdir,parameter_counter; message := "0,0,0,0,0,0,0,0,"; parameter_counter += 1; printf "%-64s%8dP%7d\n",message,ff.fdir,parameter_counter; }; // Geometry element line := sprintf "402,%d,",facet_count; subcount := 0; foreach facet ff do { if subcount == 10 then { parameter_counter += 1; printf "%-64s%8dP%7d\n",line,directory_counter-1,parameter_counter; line := "" ; subcount := 0; }; line := sprintf"%s%d,",line,ff.fdir; subcount += 1; }; line := sprintf "%s0,0;",line; // sample files ended with 2 extra 0's parameter_counter += 1; printf "%-64s%8dP%7d\n",line,directory_counter-1,parameter_counter; // Terminate section printf "S%07dG%07dD%07dP%07d%40sT0000001\n",start_counter,global_counter, directory_counter,parameter_counter," "; } evolver-2.30c.dfsg/fe/rewrap2.cmd0000755000175300017530000000240011410765113017066 0ustar hazelscthazelsct// rewrap2.cmd // Commands to rewrap torus vertices and edges to get them nicely // within unit cell. 2D version. For 3D version, see rewrap.cmd. // Moves vertices at most one period at a time, so you may have to // repeat if things are very bad to start with. // Uses torus wrap representation and wrap_vertex builtin command. // Usage: rewrap2 rewrap2 := { if space_dimension != 2 then { errprintf"rewrap2 is for space dimension 2; for 3D use rewrap3.cmd.\n"; return; }; define body attribute old_volume real; // so can adjust volconst set body old_volume volume; foreach vertex vv where vv.x*inverse_periods[1][1]+vv.y*inverse_periods[1][2] < 0 do wrap_vertex(vv.id,1); foreach vertex vv where vv.x*inverse_periods[1][1]+vv.y*inverse_periods[1][2] >= 1 do wrap_vertex(vv.id,31); foreach vertex vv where vv.x*inverse_periods[2][1]+vv.y*inverse_periods[2][2] < 0 do wrap_vertex(vv.id,64); foreach vertex vv where vv.x*inverse_periods[2][1]+vv.y*inverse_periods[2][2] >= 1 do wrap_vertex(vv.id,1984); recalc; local torvol; torvol := abs(torus_periods[1][1]*torus_periods[2][2] - torus_periods[1][2]*torus_periods[2][1]); set body volconst floor((old_volume - volume - volconst)/torvol+.5)*torvol; } evolver-2.30c.dfsg/fe/orderlis.cmd0000755000175300017530000000202211410765113017327 0ustar hazelscthazelsct// orderlist.cmd // Evolver command to number string vertices consecutively // by means of extra attribute 'number'. // And list them to stdout. Can be redirected to file. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // Usage: orderlist define vertex attribute number integer orderlist := { local ecount,e_id,first_e,v_id,newv_id,newe_id; ecount := 0; // for safety // get starting edge, since can't assume edge[1] exists foreach edge do { e_id := id; break; }; first_e := e_id; v_id := edge[e_id].vertex[1].id; // follow connected edges do { set vertex[v_id] number ecount+1; printf "%g %g %g\n",vertex[v_id].x,vertex[v_id].y,vertex[v_id].z; foreach edge[e_id].vertex vv do { if ( vv.id != v_id ) then { newv_id := vv.id; foreach vv.edge ee do if ee.id != e_id then newe_id := ee.id } }; e_id := newe_id; v_id := newv_id; ecount := ecount + 1; } while ( (e_id != first_e) and (ecount <= edge_count) ) } evolver-2.30c.dfsg/fe/neville.cmd0000755000175300017530000001405211410765113017150 0ustar hazelscthazelsct// neville.cmd // Neville's algorithm for computing Bspline values // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // Contents: // neville1 - interpolation and derivatives in one dimension // neville2 - interpolation and derivatives in two dimensions. // Neville algorithm for interpolating along one dimension. // Usage of neville1: // neville1_data should be set up by caller, redimensioning if necessary // Indexed by point number along curve and data dimension. define neville1_data real[0][0]; // not modified by neville1 // Returned from neville1. Index is data dimension. define neville1_value real[0]; // interpolated values define neville1_deriv real[0]; // interpolated deriv wrt u procedure neville1 ( real neville1_order, // order of interpolation polynomial real neville1_dim, // range dimension real neville1_u // interpolation spot, between 0 and 1 ) { local neville1_array; local neville1_darray; define neville1_array real[neville1_order+1][neville1_dim]; define neville1_darray real[neville1_order+1][neville1_dim]; define neville1_value real[neville1_dim]; // interpolated values define neville1_deriv real[neville1_dim]; // interpolated deriv wrt u local inx;local dinx; inx := 1; while ( inx <= neville1_order+1 ) do { dinx := 1; while ( dinx <= neville1_dim ) do { neville1_array[inx][dinx] := neville1_data[inx][dinx]; neville1_darray[inx][dinx] := 0; dinx += 1; }; inx += 1; }; // scale npoint to integer based value local nnpoint; nnpoint := neville1_u*neville1_order; local linx; linx := 1; while ( linx <= neville1_order ) do { inx := 1; while ( inx + linx <= neville1_order + 1 ) do { dinx := 1; while ( dinx <= neville1_dim ) do { // do derivatives first, since dependent on current value neville1_darray[inx][dinx] := ((inx-1+linx - nnpoint)*neville1_darray[inx][dinx] + (nnpoint - (inx-1))*neville1_darray[inx+1][dinx])/linx + (-neville1_array[inx][dinx] + neville1_array[inx+1][dinx])/linx; neville1_array[inx][dinx] := ((inx-1+linx - nnpoint)*neville1_array[inx][dinx] + (nnpoint - (inx-1))*neville1_array[inx+1][dinx])/linx; dinx += 1; }; inx += 1; }; linx += 1; }; dinx := 1; while ( dinx <= neville1_dim ) do { neville1_value[dinx] := neville1_array[1][dinx]; neville1_deriv[dinx] := neville1_darray[1][dinx]*neville1_order; dinx += 1; }; } // Neville algorithm for interpolating in 2D // Usage of neville2: // Initialize neville2_data, neville2_u arrays. // Call neville2. // Input data, indexed by (i,j) node coordinate and data dimension. define neville2_data real[0][0][0]; define neville2_u real[2]; // incoming; should sum to at most 1 neville2_u[1] := 1/3; neville2_u[2] := 1/3; // Return values, indexed by data dimension. define neville2_value real[0] // return values of position define neville2_deriv real[0][2] // return values of partials procedure neville2 ( real neville2_order, // of polynomial real neville2_dim // dimension of values ) { local neville2_array; local neville2_darray; local unpoint; define neville2_array real[neville2_order+1][neville2_order+1][neville2_dim]; define neville2_darray real[neville2_order+1][neville2_order+1][neville2_dim][2]; define uupoint real[2]; // scaled target coordinates define neville2_value real[neville2_dim]; // return values of position define neville2_deriv real[neville2_dim][2]; // return values of partials // initialize data; kludge due to fact that indexing on vertices // only does the three corners. local inx; local jnx; local dinx; inx := 1; while ( inx <= neville2_order+1 ) do { jnx := 1; while ( inx + jnx <= neville2_order+2 ) do { dinx := 1; while ( dinx <= neville2_dim ) do { neville2_array[inx][jnx][dinx] := neville2_data[inx][jnx][dinx]; neville2_darray[inx][jnx][dinx][1] := 0; neville2_darray[inx][jnx][dinx][2] := 0; dinx += 1; }; jnx += 1; }; inx += 1; }; // scale npoint to integer based value uupoint[1] := neville2_u[1]*neville2_order; uupoint[2] := neville2_u[2]*neville2_order; local linx; linx := 1; while ( linx <= neville2_order ) do { inx := 1; while ( inx + linx <= neville2_order + 1 ) do { jnx := 1; while ( inx + jnx + linx <= neville2_order + 2 ) do { dinx := 1; while ( dinx <= neville2_dim ) do { // do derivatives first, since dependent on current value neville2_darray[inx][jnx][dinx][1] := ((inx+jnx-2+linx-uupoint[1]-uupoint[2]) *neville2_darray[inx][jnx][dinx][1] + (uupoint[1]-(inx-1))*neville2_darray[inx+1][jnx][dinx][1] + (uupoint[2]-(jnx-1))*neville2_darray[inx][jnx+1][dinx][1])/linx + (-neville2_array[inx][jnx][dinx] + neville2_array[inx+1][jnx][dinx] )/linx; neville2_darray[inx][jnx][dinx][2] := ((inx+jnx-2+linx-uupoint[1]-uupoint[2]) *neville2_darray[inx][jnx][dinx][2] + (uupoint[1]-(inx-1))*neville2_darray[inx+1][jnx][dinx][2] + (uupoint[2]-(jnx-1))*neville2_darray[inx][jnx+1][dinx][2])/linx + (-neville2_array[inx][jnx][dinx] + neville2_array[inx][jnx+1][dinx] )/linx; neville2_array[inx][jnx][dinx] := ((inx+jnx-2+linx-uupoint[1]-uupoint[2])*neville2_array[inx][jnx][dinx] + (uupoint[1] - (inx-1))*neville2_array[inx+1][jnx][dinx] + (uupoint[2] - (jnx-1))*neville2_array[inx][jnx+1][dinx])/linx; dinx += 1; }; jnx += 1; }; inx += 1; }; linx += 1; }; dinx := 1; while ( dinx <= neville2_dim ) do { neville2_value[dinx] := neville2_array[1][1][dinx]; neville2_deriv[dinx][1] := neville2_darray[1][1][dinx][1]*neville2_order; neville2_deriv[dinx][2] := neville2_darray[1][1][dinx][2]*neville2_order; dinx += 1; }; } evolver-2.30c.dfsg/fe/slicer.cmd0000755000175300017530000001566611410765113017007 0ustar hazelscthazelsct// slicer.cmd --- create intersection of surface with plane // plane eq: a*x + b*y + c*z = d // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // Commands included: // drawslice - make new edges across facets along plane. // slicer - make slice and dissolve all facets, edges, and vertices // on negative side of the plane. Calls drawslice. // slice_list - print coordinates of vertices along a slice. // "slicer" usage: // Set slice_a,slice_b,slice_c,slice_d coefficients so // slice_a*x + slice_b*y + slice_c*z >= slice_d // is the side you want, and do "slicer". // output: truncated surface on positive side of plane // Try not to slice exactly through vertices!! // Default plane is slice_a := 0, slice_b := 0, slice_c := 1, slice_d := .1 // To mark the vertices and edges created by the current slicing, // there are a vertex attribute v_timestamp and an edge attribute // e_timestamp that are set to slice_timestamp, which is incremented // each time slicer is called. // Works in torus by rewrapping wrapped edges that would be cut // so unwrapped part is on positive side of cut plane. // Slice_list: // Listing of vertices along the slice, beginning at user-designated vertex. // If the slice has multiple components, you will have to run separately // for each component. // Usage: set startv to the first vertex on the slice and call slice_list // Output is to stdout, so will usually be redirected, e.g. // slice_list >>> "slice.dat" //Set these coefficients for the slicing plane slice_a := 0; slice_b := 0; slice_c := 1; slice_d := .1; // Attributes for marking vertices and edges on slice with timestamp slice_timestamp := 0 define vertex attribute v_timestamp integer define edge attribute e_timestamp integer // First put in new edges along slicing plane drawslice := { local lambda; local denom; local xx1; local yy1; local zz1; local xx2; local yy2; local zz2; slice_timestamp += 1; // marker for this slice foreach edge ee do { xx1 := ee.vertex[1].x; yy1 := ee.vertex[1].y; zz1 := ee.vertex[1].z; xx2 := xx1 + ee.x; // using edge vector in case of torus wrap yy2 := yy1 + ee.y; zz2 := zz1 + ee.z; denom := slice_a*(xx1-xx2)+slice_b*(yy1-yy2)+slice_c*(zz1-zz2); if ( denom != 0.0 ) then { lambda := (slice_d-slice_a*xx2-slice_b*yy2-slice_c*zz2)/denom; if ( (lambda >= 0) and (lambda <= 1) ) then { if torus then if ee.wrap then { if denom > 0 then // tail on positive side wrap_vertex(ee.vertex[2].id,ee.wrap) else // head on positive side wrap_vertex(ee.vertex[1].id,wrap_inverse(ee.wrap)); }; xb := lambda*xx1+(1-lambda)*xx2; yb := lambda*yy1+(1-lambda)*yy2; zb := lambda*zz1+(1-lambda)*zz2; refine ee; ee.vertex[2].v_timestamp := slice_timestamp; ee.vertex[2].x := xb; ee.vertex[2].y := yb; ee.vertex[2].z := zb; } else if torus and ee.wrap then { // try wrapping from head xx2 := ee.vertex[2].x; yy2 := ee.vertex[2].y; zz2 := ee.vertex[2].z; xx1 := xx2 - ee.x; // using edge vector in case of torus wrap yy1 := yy2 - ee.y; zz1 := zz2 - ee.z; denom := slice_a*(xx1-xx2)+slice_b*(yy1-yy2)+slice_c*(zz1-zz2); if ( denom != 0.0 ) then { lambda := (slice_d-slice_a*xx2-slice_b*yy2-slice_c*zz2)/denom; if ( (lambda >= 0) and (lambda <= 1) ) then { if torus then if ee.wrap then { if denom > 0 then // tail on positive side wrap_vertex(ee.vertex[2].id,ee.wrap) else // head on positive side wrap_vertex(ee.vertex[1].id,wrap_inverse(ee.wrap)); }; xb := lambda*xx1+(1-lambda)*xx2; yb := lambda*yy1+(1-lambda)*yy2; zb := lambda*zz1+(1-lambda)*zz2; refine ee; ee.vertex[2].v_timestamp := slice_timestamp; ee.vertex[2].x := xb; ee.vertex[2].y := yb; ee.vertex[2].z := zb; } } } } ; } ; // mark edges created by slicing set edge ee e_timestamp min(ee.vertex,v_timestamp); } slicer := { drawslice; former_autodisplay := (autodisplay); autodisplay off; // prevent display while dissolving foreach facet ff where // again, careful of torus wraps slice_a*(ff.vertex[1].x+ff.edge[1].x/3-ff.edge[3].x/3) + slice_b*(ff.vertex[1].y+ff.edge[1].y/3-ff.edge[3].y/3) + slice_c*(ff.vertex[1].z+ff.edge[1].z/3-ff.edge[3].z/3) < slice_d do { unset ff frontbody; unset ff backbody; dissolve ff; }; dissolve bodies bbb where sum(bbb.facets,1) == 0; dissolve edges; // just does bare edges dissolve vertices; // just does bare vertices if former_autodisplay then autodisplay on; } color_slice := { foreach facet ff where // again, careful of torus wraps slice_a*(ff.vertex[1].x+ff.edge[1].x/3-ff.edge[3].x/3) + slice_b*(ff.vertex[1].y+ff.edge[1].y/3-ff.edge[3].y/3) + slice_c*(ff.vertex[1].z+ff.edge[1].z/3-ff.edge[3].z/3) < slice_d do set ff color magenta; } // Listing of vertices along the slice, beginning at user-designated vertex. // If the slice has multiple components, you will have to run separately // for each component. // Usage: set startv to the first vertex on the slice and call slice_list // Output is to stdout, so will usually be redirected, e.g. // slice_list >>> "slice.dat" startv := 1 slice_list := { if vertex[startv].v_timestamp != slice_timestamp then { errprintf "slice_list: starting vertex startv is %d, not on latest slice.\n", startv; return; }; thisv := startv; thisedge := 0; do { // print current vertex coordinates printf "%20.15f %20.15f %20.15f\n",vertex[thisv].x,vertex[thisv].y, vertex[thisv].z; nextedge := 0; foreach vertex[thisv].edge ee where ee.e_timestamp == slice_timestamp do { if ee.oid != -thisedge then { nextedge := ee.oid; break; }}; if nextedge == 0 then break; /* done */ thisv := edge[nextedge].vertex[2].id; thisedge := nextedge; } while thisv != startv; // done } // "slicer" usage: // Set slice_a,slice_b,slice_c,slice_d coefficients so // slice_a*x + slice_b*y + slice_c*z >= slice_d // is the side you want, and do "slicer". // Default plane is slice_a := 0, slice_b := 0, slice_c := 1, slice_d := .1 evolver-2.30c.dfsg/fe/relaxed.fe0000644000175300017530000000130311410765113016755 0ustar hazelscthazelsct// relaxed.fe // Test of relaxed_elastic energy space_dimension 5 // 2 extra for unstrained reference coordinates quantity stretchy energy method relaxed_elastic global quantity stretchy1 info_only method relaxed_elastic1 global quantity stretchy2 info_only method relaxed_elastic2 global define facet attribute poisson_ratio real define vertex attribute ref_coord real[2] constraint 1 formula: x4 = ref_coord[1] constraint 2 formula: x5 = ref_coord[2] vertices 1 0 0 0 0 0 constraint 1,2 ref_coord 0 0 2 1 0 0 1 0 constraint 1,2 ref_coord 1 0 3 0 1 0 1 1 constraint 1,2 ref_coord 0 1 edges 1 1 2 2 2 3 3 3 1 faces 1 1 2 3 read set facet poisson_ratio 0.8 set facet tension 0.0 evolver-2.30c.dfsg/fe/column.fe0000644000175300017530000000704311410765113016635 0ustar hazelscthazelsct// column.fe // Example of calculating forces exerted by a // column of liquid solder in shape of skewed catenoid. // All units cgs parameter RAD = 0.05 // ring radius parameter ZH = 0.08 // total height parameter SHIFT = 0.025 // shift #define SG 8 // specific gravity of solder #define TENS 460 // surface tension of solder #define GR 980 // acceleration of gravity gravity_constant GR BOUNDARY 1 PARAMETERS 1 X1: RAD*cos(P1) X2: RAD*sin(P1) + SHIFT X3: ZH CONTENT // used to compensate for missing top facets c1: 0 c2: ZH*x c3: 0 ENERGY // used to compensate for gravitational energy under top facets e1: 0 e2: GR*ZH^2/2*x e3: 0 BOUNDARY 2 PARAMETERS 1 X1: RAD*cos(P1) X2: RAD*sin(P1) X3: 0 vertices // given in terms of boundary parameter 1 0.00 boundary 1 fixed 2 pi/3 boundary 1 fixed 3 2*pi/3 boundary 1 fixed 4 pi boundary 1 fixed 5 4*pi/3 boundary 1 fixed 6 5*pi/3 boundary 1 fixed 7 0.00 boundary 2 fixed 8 pi/3 boundary 2 fixed 9 2*pi/3 boundary 2 fixed 10 pi boundary 2 fixed 11 4*pi/3 boundary 2 fixed 12 5*pi/3 boundary 2 fixed edges 1 1 2 boundary 1 fixed 2 2 3 boundary 1 fixed 3 3 4 boundary 1 fixed 4 4 5 boundary 1 fixed 5 5 6 boundary 1 fixed 6 6 1 boundary 1 fixed 7 7 8 boundary 2 fixed 8 8 9 boundary 2 fixed 9 9 10 boundary 2 fixed 10 10 11 boundary 2 fixed 11 11 12 boundary 2 fixed 12 12 7 boundary 2 fixed 13 1 7 14 2 8 15 3 9 16 4 10 17 5 11 18 6 12 faces 1 -1 13 7 -14 density TENS 2 -2 14 8 -15 density TENS 3 -3 15 9 -16 density TENS 4 -4 16 10 -17 density TENS 5 -5 17 11 -18 density TENS 6 -6 18 12 -13 density TENS bodies 1 1 2 3 4 5 6 volume 0.00045 density SG read // horizontal force on upper pad by central differences dy := .0001 do_yforce := { oldshift := shift; shift := shift + dy; set vertex y y+dy*z/zh; // uniform shear recalc; energy1 := total_energy - body[1].pressure*(body[1].volume - body[1].target); oldshift := shift; shift := shift - 2*dy; set vertex y y-2*dy*z/zh; // uniform shear recalc; energy2 := total_energy - body[1].pressure*(body[1].volume - body[1].target); yforce := -(energy1-energy2)/2/dy; printf "restoring force: %20.15f\n",yforce; // restore everything oldshift := shift; shift := shift + dy; set vertex y y+dy*z/zh; // uniform shear recalc; } // vertical force on upper pad by central differences. dz := .0001 do_zforce := { oldzh := zh; zh := zh + dz; set vertex z z+dz*z/oldzh; recalc; // uniform stretch energy1 := total_energy - body[1].pressure*(body[1].volume - body[1].target); oldzh := zh; zh := zh - 2*dz; set vertex z z-2*dz*z/oldzh; recalc; // uniform stretch energy2 := total_energy - body[1].pressure*(body[1].volume - body[1].target); zforce := -(energy1-energy2)/2/dz; printf "vertical force: %20.15f\n",zforce; // restore everything oldzh := zh; zh := zh + dz; set vertex z z+dz*z/oldzh; recalc; // uniform stretch } // Sample evolution and force calculation gogo := { u; g 5; r; g 5 ; r; g 5; hessian; hessian; do_yforce; do_zforce; } evolver-2.30c.dfsg/fe/popstr.fe0000644000175300017530000000041411410765113016662 0ustar hazelscthazelsct// popstr.fe // evolver string vertex popping test STRING vertices 1 0.0 0.0 0.0 fixed 2 1.0 0.0 0.0 fixed 3 1.0 0.0 1.0 fixed 4 0.0 0.0 1.0 fixed 5 0.5 0.0 0.5 edges 1 1 5 2 2 5 3 3 5 4 4 5 read // Typical evolution gogo := { o; g 5 } evolver-2.30c.dfsg/fe/povrays.cmd0000755000175300017530000001354711410765113017225 0ustar hazelscthazelsct// povrays.cmd // Surface Evolver command for producing POV-Ray input file. // Output is to stdout, so redirect to desired file. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // Like povray.cmd, but with vertex normals for a smooth surface. // A normal at a vertex of a facet is the average of all adjacent // facet normals within a certain angle of the original facet normal // Usage: // Use the "show edge where ..." command to declare which // edges are to be depicted as thin cylinders. // Set "edge_radius" to desired radius of edge cylinders. // Set "critcos" to critical cosine of angle for facets // regarded as smoothly joined. // Run "povray" and redirect to desired file, e.g. // Enter command: povray >>> "something.pov" // Run "povrays" with output redirected to a file, e.g. // Enter command: povrays >>> "something.pov" edge_radius := 0.003; // adjust this for desired radius of edge cylinders critcos := 0.8; // criterion for vertex normal averaging. povrays := { local ffx,ffy,ffz,ffnorm,vernum,nx,ny,nz,fffx,fffy,fffz,fffnorm; local costheta,nnorm; printf "// %s in POV-Ray format.\n\n",datafilename; printf "light_source { <0,0,300> color rgb <1,1,1> }\n"; printf "light_source { <100,0,0> color rgb <1,1,1> }\n"; printf "camera { location <12,0,0> sky <0,0,1> // right handed \n"; printf " up <0,0,1> right <1.3,0,0> look_at <0,0,0> angle 15 }\n"; printf "background { color <0.3,0.8,1.0> } // light blue\n\n"; printf "// Textures corresponding to Evolver colors\n\n"; printf "#declare t_black = texture { pigment { rgb <0.0,0.0,0.0> }}\n"; printf "#declare t_blue = texture { pigment { rgb <0.0,0.0,1.,> }}\n"; printf "#declare t_green = texture { pigment { rgb <0.0,1.,0.0,> }}\n"; printf "#declare t_cyan = texture { pigment { rgb <0.0,1.,1.,> }}\n"; printf "#declare t_red = texture { pigment { rgb <1.,0.0,0.0,> }}\n"; printf "#declare t_magenta = texture { pigment { rgb <1.,0.0,1.,> }}\n"; printf "#declare t_brown = texture { pigment { rgb <1.,0.5,0.,> }}\n"; printf "#declare t_lightgray = texture { pigment { rgb <.6,.6,.6,> }}\n"; printf "#declare t_darkgray = texture { pigment { rgb <.3,.3,.3,> }}\n"; printf "#declare t_lightblue = texture { pigment { rgb <.3,.8,1.,> }}\n"; printf "#declare t_lightgreen = texture { pigment { rgb <.5,1.,.5,> }}\n"; printf "#declare t_lightcyan = texture { pigment { rgb <.5,1.,1.,> }}\n"; printf "#declare t_lightred = texture { pigment { rgb <1.,.5,.5,> }}\n"; printf "#declare t_lightmagenta = texture { pigment { rgb <1.,.5,1.,> }}\n"; printf "#declare t_yellow = texture { pigment { rgb <1.,1.,.0,> }}\n"; printf "#declare t_white = texture { pigment { rgb <1.,1.,1.,> }}\n"; printf "\n//One overall object.\n"; printf "union {\n"; printf "// All facets in one big mesh object for efficiency.\n"; printf " mesh { \n"; foreach facet ff do { printf " smooth_triangle { "; ffx := ff.x; ffy := ff.y; ffz := ff.z; ffnorm := sqrt(ffx^2 + ffy^2 + ffz^2); vernum := 1; while ( vernum <= 3 ) do { nx := 0; ny := 0; nz := 0; foreach ff.vertex[vernum].facet fff do { fffx := fff.x; fffy := fff.y; fffz := fff.z; fffnorm := sqrt(fffx^2 + fffy^2 + fffz^2); costheta := (fffx*ffx+fffy*ffy+fffz*ffz)/ffnorm/fffnorm; if ( costheta > critcos ) then { nx := nx + fffx/fffnorm; ny := ny + fffy/fffnorm; nz := nz + fffz/fffnorm; } else if ( costheta < -critcos ) then // in case nonorientable { nx := nx - fffx/fffnorm; ny := ny - fffy/fffnorm; nz := nz - fffz/fffnorm; } }; nnorm := sqrt(nx^2+ny^2+nz^2); if vernum > 1 then printf ","; printf "<%f,%f,%f>,<%f,%f,%f>", ff.vertex[vernum].x,ff.vertex[vernum].y,ff.vertex[vernum].z, nx/nnorm,ny/nnorm,nz/nnorm; vernum := vernum + 1; }; printf " texture {"; if ( ff.color == white ) then printf " t_white " else if ( ff.color == black ) then printf " t_black " else if ( ff.color == blue) then printf " t_blue " else if ( ff.color == green ) then printf " t_green " else if ( ff.color == cyan ) then printf " t_cyan " else if ( ff.color == red ) then printf " t_red " else if ( ff.color == magenta ) then printf " t_magenta " else if ( ff.color == brown ) then printf " t_brown " else if ( ff.color == lightgray ) then printf " t_lightgray " else if ( ff.color == darkgray ) then printf " t_darkgray " else if ( ff.color == lightblue ) then printf " t_lightblue " else if ( ff.color == lightgreen ) then printf " t_lightgreen " else if ( ff.color == lightcyan ) then printf " t_lightcyan " else if ( ff.color == lightred ) then printf " t_lightred " else if ( ff.color == lightmagenta ) then printf " t_lightmagenta " else if ( ff.color == yellow ) then printf " t_yellow "; printf " } }\n"; }; printf " } // end of mesh object\n"; // Do desired edges printf "#declare edge_radius = %f;\n",edge_radius; foreach edge ee where ee.show do { printf "cylinder { <%f,%f,%f>,<%f,%f,%f> edge_radius texture { t_black } }\n", ee.vertex[1].x,ee.vertex[1].y,ee.vertex[1].z, ee.vertex[2].x,ee.vertex[2].y,ee.vertex[2].z; }; // Windup printf "// overall viewing transformation\n"; printf " matrix < %f,%f,%f,\n", view_matrix[1][1],view_matrix[2][1],view_matrix[3][1]; printf " %f,%f,%f,\n", view_matrix[1][2],view_matrix[2][2],view_matrix[3][2]; printf " %f,%f,%f,\n", view_matrix[1][3],view_matrix[2][3],view_matrix[3][3]; printf " %f,%f,%f>\n", view_matrix[1][4],view_matrix[2][4],view_matrix[3][4]; printf " } // end of all objects\n"; } evolver-2.30c.dfsg/fe/cmccousin.cmd0000755000175300017530000003431211410765113017476 0ustar hazelscthazelsct// CMCcousin.cmd // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // A constant mean curvature surface in R^3 has a "cousin" minimal // surface in S^3, which is isometric to it and has tangent planes // rotated by 90 degrees. In S^3, the translation is done through // the quaternion group. // This representation uses the 4th coordinate as the quaternion // scalar component, for better mapping between R^3 and S^3 at quaternion unit. // Datafiles should be set up in 4 dimensions, with S^3 implemented as // level set constraint x^2 + y^2 + z^2 + w^2 = 1 // Works best if starting edge starte is toward the center of the surface // rather than on the outside. // Procedures contained in this file: // s3_to_r3: Converts minimal surface in S^3 to CMC 1 surface in R^3. // Remove all constraints and boundaries before invoking. // r3_to_s3: Converts CMC 1 surface in R^3 to minimal surface in S^3. // Remove all constraints and boundaries before invoking. // procedure centralize(integer v_id): translates S^3 so given vertex // is at (0,0,0,1). // For converting to adjoint define vertex attribute newx real [4]; define edge attribute eflag integer; define edge attribute enewx real [4]; // Angle of Bonnet rotation, degrees bangle := 90 // unit normal of S^3 facet define s3points real[3][4] // input define s3normal real[4] // for return calc_s3_normal := { // triple product in R^4 s3normal[1] := s3points[1][2]*(s3points[2][3]*s3points[3][4] - s3points[2][4]*s3points[3][3]) + s3points[1][3]*(s3points[2][4]*s3points[3][2] - s3points[2][2]*s3points[3][4]) + s3points[1][4]*(s3points[2][2]*s3points[3][3] - s3points[2][3]*s3points[3][2]); s3normal[2] := -(s3points[1][1]*(s3points[2][3]*s3points[3][4] - s3points[2][4]*s3points[3][3]) + s3points[1][3]*(s3points[2][4]*s3points[3][1] - s3points[2][1]*s3points[3][4]) + s3points[1][4]*(s3points[2][1]*s3points[3][3] - s3points[2][3]*s3points[3][1])); s3normal[3] := s3points[1][1]*(s3points[2][2]*s3points[3][4] - s3points[2][4]*s3points[3][2]) + s3points[1][2]*(s3points[2][4]*s3points[3][1] - s3points[2][1]*s3points[3][4]) + s3points[1][4]*(s3points[2][1]*s3points[3][2] - s3points[2][2]*s3points[3][1]); s3normal[4] := -(s3points[1][1]*(s3points[2][2]*s3points[3][3] - s3points[2][3]*s3points[3][2]) + s3points[1][2]*(s3points[2][3]*s3points[3][1] - s3points[2][1]*s3points[3][3]) + s3points[1][3]*(s3points[2][1]*s3points[3][2] - s3points[2][2]*s3points[3][1])); mag := sqrt(s3normal[1]^2 + s3normal[2]^2 + s3normal[3]^2 + s3normal[4]^2); for ( inx := 1 ; inx < 4 ; inx += 1 ) s3normal[inx] /= mag; } // Swaps conjugate sets of coordinates. flip := { foreach vertex vv do { tmp := vv.x; vv.x := vv.newx[1]; vv.newx[1] := tmp; tmp := vv.y; vv.y := vv.newx[2]; vv.newx[2] := tmp; tmp := vv.z; vv.z := vv.newx[3]; vv.newx[3] := tmp; tmp := vv.w; vv.w := vv.newx[4]; vv.newx[4] := tmp; } } starte := 0 // user should set starte to set origin of adjoint. define rn real[3][4]; define emid real[3][4]; calc_s3_to_r3 := { set edge eflag 0; if starte == 0 then foreach edge ee do { starte := ee.id; break; }; // just to get starter edge[starte].enewx[1] := 0; edge[starte].enewx[2] := 0; edge[starte].enewx[3] := 0; edge[starte].eflag := 1; bs := sin(bangle*pi/180); bc := cos(bangle*pi/180); ecount := 1; endflag := 0; loopcount := 1; while ( !endflag ) do { endflag := 1; foreach facet ff do { enum := 1; while ( enum <= 5 ) do { thise := (enum imod 3) + 1; nexte := ((enum+1) imod 3) + 1; othere := ((enum+2) imod 3) + 1; if ( ff.edge[thise].eflag>=loopcount and !ff.edge[nexte].eflag ) then { // transform this facet // normal in S3 for ( inx := 1 ; inx <= 4 ; inx += 1 ) for ( vnx := 1 ; vnx <= 3 ; vnx += 1 ) s3points[vnx][inx] := ff.vertex[vnx].x[inx]; calc_s3_normal; // edge midpoints in S3 for ( enx := 1 ; enx <= 3 ; enx += 1 ) { for ( inx := 1 ; inx <= 4 ; inx += 1 ) emid[enx][inx] := avg(ff.edge[enx].vertex,x[inx]); mag := sqrt(emid[enx][1]^2 + emid[enx][2]^2 + emid[enx][3]^2 + emid[enx][4]^2); for ( inx := 1 ; inx <= 4 ; inx += 1 ) emid[enx][inx] /= mag; // Use inverse quaternion of each edge midpoint to convert // common normal to R3 normal rn[enx][1] := emid[enx][4]*s3normal[1] - s3normal[4]*emid[enx][1] - (emid[enx][2]*s3normal[3] - emid[enx][3]*s3normal[2]); rn[enx][2] := emid[enx][4]*s3normal[2] - s3normal[4]*emid[enx][2] - (emid[enx][3]*s3normal[1] - emid[enx][1]*s3normal[3]); rn[enx][3] := emid[enx][4]*s3normal[3] - s3normal[4]*emid[enx][3] - (emid[enx][1]*s3normal[2] - emid[enx][2]*s3normal[1]); }; for ( inx := 1 ; inx <= 3 ; inx += 1 ) ff.edge[nexte].enewx[inx] := ff.edge[thise].enewx[inx] + rn[nexte][inx] - rn[thise][inx]; ff.edge[nexte].eflag := loopcount+1; endflag := 0; ecount += 1; }; enum += 1; }; // end while }; if !quiet then printf "%g edges done.\n",ecount; loopcount += 1; }; set vertex newx[1] 0; set vertex newx[2] 0; set vertex newx[3] 0; foreach facet ff do { // extend center facet to original vertex; can't simply average // midedge vertices around an original vertex since that doesn't // work for vertices on the boundary. vva := 1; while ( vva <= 3 ) do { vvb := vva==3 ? 1 : vva+1; vvc := vva==1 ? 3 : vva-1; kk := 1; while ( kk <= 3 ) do { ff.vertex[vva].newx[kk] += ff.edge[vva].enewx[kk] - ff.edge[vvb].enewx[kk] + ff.edge[vvc].enewx[kk]; kk += 1; }; vva += 1; } }; foreach vertex vv do { nbrs := sum(vv.facet, 1); if ( nbrs != 0 ) then { vv.newx[1] /= nbrs; vv.newx[2] /= nbrs; vv.newx[3] /= nbrs; vv.newx[4] := 0; }; }; } // end calc_s3_to_r3 // try using average normal at edge midpoints calc_s3_to_r3_a := { define facet attribute snormal real[4]; define nmid real[4][4]; set edge eflag 0; if starte == 0 then foreach edge ee do { starte := ee.id; break; }; // just to get starter edge[starte].enewx[1] := 0; edge[starte].enewx[2] := 0; edge[starte].enewx[3] := 0; edge[starte].eflag := 1; foreach facet ff do { for ( inx := 1 ; inx <= 4 ; inx += 1 ) for ( vnx := 1 ; vnx <= 3 ; vnx += 1 ) s3points[vnx][inx] := ff.vertex[vnx].x[inx]; calc_s3_normal; for ( inx := 1 ; inx <= 4 ; inx += 1 ) ff.snormal[inx] := s3normal[inx]; }; bs := sin(bangle*pi/180); bc := cos(bangle*pi/180); ecount := 1; endflag := 0; loopcount := 1; while ( !endflag ) do { endflag := 1; foreach facet ff do { enum := 1; while ( enum <= 5 ) do { thise := (enum imod 3) + 1; nexte := ((enum+1) imod 3) + 1; othere := ((enum+2) imod 3) + 1; if ( ff.edge[thise].eflag>=loopcount and !ff.edge[nexte].eflag ) then { // transform this facet // edge midpoints in S3 for ( enx := 1 ; enx <= 3 ; enx += 1 ) { for ( inx := 1 ; inx <= 4 ; inx += 1 ) { emid[enx][inx] := avg(ff.edge[enx].vertex,x[inx]); nmid[enx][inx] := avg(ff.edge[enx].facet,snormal[inx]); }; mag := sqrt(emid[enx][1]^2 + emid[enx][2]^2 + emid[enx][3]^2 + emid[enx][4]^2); nmag := sqrt(nmid[enx][1]^2 + nmid[enx][2]^2 + nmid[enx][3]^2 + nmid[enx][4]^2); for ( inx := 1 ; inx <= 4 ; inx += 1 ) { emid[enx][inx] /= mag; nmid[enx][inx] /= nmag; }; // Use inverse quaternion of each edge midpoint to convert // common normal to R3 normal rn[enx][1] := emid[enx][4]*nmid[enx][1] - nmid[enx][4]*emid[enx][1] - (emid[enx][2]*nmid[enx][3] - emid[enx][3]*nmid[enx][2]); rn[enx][2] := emid[enx][4]*nmid[enx][2] - nmid[enx][4]*emid[enx][2] - (emid[enx][3]*nmid[enx][1] - emid[enx][1]*nmid[enx][3]); rn[enx][3] := emid[enx][4]*nmid[enx][3] - nmid[enx][4]*emid[enx][3] - (emid[enx][1]*nmid[enx][2] - emid[enx][2]*nmid[enx][1]); }; for ( inx := 1 ; inx <= 3 ; inx += 1 ) ff.edge[nexte].enewx[inx] := ff.edge[thise].enewx[inx] + rn[nexte][inx] - rn[thise][inx]; ff.edge[nexte].eflag := loopcount+1; endflag := 0; ecount += 1; }; enum += 1; }; // end while }; if !quiet then printf "%g edges done.\n",ecount; loopcount += 1; }; set vertex newx[1] 0; set vertex newx[2] 0; set vertex newx[3] 0; foreach facet ff do { // extend center facet to original vertex; can't simply average // midedge vertices around an original vertex since that doesn't // work for vertices on the boundary. vva := 1; while ( vva <= 3 ) do { vvb := vva==3 ? 1 : vva+1; vvc := vva==1 ? 3 : vva-1; kk := 1; while ( kk <= 3 ) do { ff.vertex[vva].newx[kk] += ff.edge[vva].enewx[kk] - ff.edge[vvb].enewx[kk] + ff.edge[vvc].enewx[kk]; kk += 1; }; vva += 1; } }; foreach vertex vv do { nbrs := sum(vv.facet, 1); if ( nbrs != 0 ) then { vv.newx[1] /= nbrs; vv.newx[2] /= nbrs; vv.newx[3] /= nbrs; vv.newx[4] := 0; }; }; } // end calc_s3_to_r3 calc_r3_to_s3 := { set edge eflag 0; if starte == 0 then foreach edge ee do { starte := ee.id; break; }; // just to get starter edge[starte].enewx[1] := 0; edge[starte].enewx[2] := 0; edge[starte].enewx[3] := 0; edge[starte].enewx[4] := 1; edge[starte].eflag := 1; bs := sin(bangle*pi/180); bc := cos(bangle*pi/180); ecount := 1; endflag := 0; loopcount := 1; while ( !endflag ) do { endflag := 1; foreach facet ff do { enum := 1; while ( enum <= 5 ) do { thise := (enum imod 3) + 1; nexte := ((enum+1) imod 3) + 1; othere := ((enum+2) imod 3) + 1; if ( ff.edge[thise].eflag>=loopcount and !ff.edge[nexte].eflag ) then { // facet unit normal nx := ff.x; ny := ff.y; nz := ff.z; norm := sqrt(nx^2+ny^2+nz^2); nx := nx/norm; ny := ny/norm; nz := nz/norm; // vector from thise to nexte vx := -ff.edge[othere].x/2; vy := -ff.edge[othere].y/2; vz := -ff.edge[othere].z/2; // rotate 90 degrees about normal (using cross product) qx := -(ny*vz - nz*vy); qy := -(nz*vx - nx*vz); qz := -(nx*vy - ny*vx); qw := sqrt(1 - (qx^2 + qy^2 + qz^2)); // quaternion multiplication by position of thise midpoint tx := ff.edge[thise].enewx[1]; ty := ff.edge[thise].enewx[2]; tz := ff.edge[thise].enewx[3]; tw := ff.edge[thise].enewx[4]; ff.edge[nexte].enewx[1] := tw*qx + qw*tx + (ty*qz - tz*qy); ff.edge[nexte].enewx[2] := tw*qy + qw*ty + (tz*qx - tx*qz); ff.edge[nexte].enewx[3] := tw*qz + qw*tz + (tx*qy - ty*qx); ff.edge[nexte].enewx[4] := tw*qw - (tx*qx + ty*qy + tz*qz); ff.edge[nexte].eflag := loopcount+1; endflag := 0; ecount += 1; }; enum += 1; }; // end while }; if !quiet then printf "%g edges done.\n",ecount; loopcount += 1; }; set vertex newx[1] 0; set vertex newx[2] 0; set vertex newx[3] 0; set vertex newx[4] 0; foreach facet ff do { // extend center facet to original vertex; can't simply average // midedge vertices around an original vertex since that doesn't // work for vertices on the boundary. for ( vva := 1; vva <= 3 ; vva += 1 ) { vvb := vva==3 ? 1 : vva+1; vvc := vva==1 ? 3 : vva-1; for ( kk := 1; kk <= 4 ; kk += 1 ) { ff.vertex[vva].newx[kk] += ff.edge[vva].enewx[kk] - ff.edge[vvb].enewx[kk] + ff.edge[vvc].enewx[kk]; }; }; }; foreach vertex vv do { nbrs := sum(vv.facet, 1); if ( nbrs != 0 ) then { vv.newx[1] /= nbrs; vv.newx[2] /= nbrs; vv.newx[3] /= nbrs; vv.newx[4] /= nbrs; }; }; } // end calc_r3_to_s3 s3_to_r3 := { foreach vertex vv do if vv.__v_constraint_list[1] != 0 then { errprintf "s3_to_r3 error: vertex %d is still on a constraint.\n", vv.id; return; }; autodisplay_state := (autodisplay); autodisplay off; calc_s3_to_r3; flip; if ( autodisplay_state ) then autodisplay; } r3_to_s3 := { foreach vertex vv do if vv.__v_constraint_list[1] != 0 then { errprintf "s3_to_r3 error: vertex %d is still on a constraint.\n", vv.id; return; }; autodisplay_state := (autodisplay); autodisplay off; calc_r3_to_s3; flip; if ( autodisplay_state ) then autodisplay; } // Utility function for translating surface in sphere to get // desired vertex at identity element (0,0,0,1). procedure centralize ( integer v_id ) { ax := vertex[v_id].x; ay := vertex[v_id].y; az := vertex[v_id].z; aw := vertex[v_id].w; foreach vertex vv do { vx := vv.x; vy := vv.y; vz := vv.z; vw := vv.w; vv.x := aw*vx - vw*ax - (ay*vz - az*vy); vv.y := aw*vy - vw*ay - (az*vx - ax*vz); vv.z := aw*vz - vw*az - (ax*vy - ay*vx); vv.w := aw*vw + ax*vx + ay*vy + az*vz; }; } evolver-2.30c.dfsg/fe/order.cmd0000755000175300017530000000163411410765113016627 0ustar hazelscthazelsct// order.cmd // Evolver command to number string-model vertices consecutively. // Result is order number in vertex extra attribute 'number'. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // Usage: order define vertex attribute number integer order := { local ecount,e_id,first_e,v_id,newv_id,newe_id; ecount := 0; // for safety // get starting edge, since can't assume edge[1] exists foreach edge do { e_id := id; break; }; first_e := e_id; v_id := edge[e_id].vertex[1].id; // follow connected edges do { set vertex[v_id] number ecount+1; foreach edge[e_id].vertex vv do { if ( vv.id != v_id ) then { newv_id := vv.id; foreach vv.edge ee do if ee.id != e_id then newe_id := ee.id } }; e_id := newe_id; v_id := newv_id; ecount := ecount + 1; } while ( (e_id != first_e) and (ecount <= edge_count) ) } evolver-2.30c.dfsg/fe/symtest.fe0000644000175300017530000000303611410765113017046 0ustar hazelscthazelsct// symtest.fe // test of quotient space mechanism // same surface as in twointor.fe // Two Kelvin tetrakaidecahedra in a torus. SYMMETRY_GROUP "torus" // same name as in quotient.c vertices 1 0.50 0.00 0.75 2 0.25 0.00 0.50 3 0.00 0.25 0.50 4 0.75 0.00 0.50 5 0.00 0.50 0.75 6 0.50 0.00 0.25 7 0.00 0.75 0.50 8 0.50 0.25 0.00 9 0.25 0.50 0.00 10 0.00 0.50 0.25 11 0.50 0.75 0.00 12 0.75 0.50 0.00 // wrap codes for group elements using scheme in quotient.c #define XWRAP 1 #define XWRAPNEG 31 #define YWRAP 64 #define YWRAPNEG 31*64 #define ZWRAP 64*64 #define ZWRAPNEG 31*64*64 edges /* with torus wrap symbols */ 1 1 2 2 2 3 3 1 4 4 3 5 5 2 6 6 2 7 wrap YWRAPNEG 7 1 8 wrap ZWRAP 8 4 6 9 5 9 wrap ZWRAP 10 3 10 11 3 4 wrap XWRAPNEG 12 6 8 13 6 11 wrap YWRAPNEG 14 7 4 wrap XWRAPNEG+YWRAP 15 8 12 16 9 8 17 9 11 18 10 7 19 11 1 wrap YWRAP+ZWRAPNEG 20 12 5 wrap XWRAP+ZWRAPNEG 21 5 7 22 11 12 23 10 12 wrap XWRAPNEG 24 9 10 faces 1 1 2 4 9 16 -7 2 -2 5 12 -16 24 -10 3 -4 10 18 -21 4 7 15 20 -4 11 -3 5 -1 3 8 -5 6 6 14 -11 -2 7 5 13 -17 24 18 -6 8 -12 13 19 7 9 -16 17 22 -15 10 -10 11 8 12 15 -23 11 -21 9 17 19 1 6 12 -14 -18 23 -22 -13 -8 13 -24 -9 -20 -23 14 -19 22 20 21 14 -3 bodies 1 -1 -2 -3 -4 -5 9 7 11 -9 10 12 5 14 3 // volume 0.500 2 2 -6 -7 8 -10 -12 -11 -13 1 13 -14 6 4 -8 // volume 0.500 evolver-2.30c.dfsg/fe/OCTA.WLF0000644000175300017530000000014611410765113016121 0ustar hazelscthazelsct 0 0 1 0 0 -1 0.707 0.707 0 -0.707 0.707 0 0.707 -0.707 0 -0.707 -0.707 0 evolver-2.30c.dfsg/fe/slice.cmd0000755000175300017530000000363011410765113016611 0ustar hazelscthazelsct// slice.cmd --- Calculate length of intersection of plane with surface. // Does not modify surface. // plane eq: aa*x + bb*y + cc*z = dd // output: prints length of slice, and area inside slice. // Note all area inside slice is counted as positive! // Try not to slice exactly through vertices!! // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // Usage: Set plane coefficients, then do "slice". // Results: Intersection length printed out, left in variable lensum. aa := 0; bb := 0; cc := 1; dd := .1; // set these for desired plane slice := { local any,xx1,yy1,zz1,xx2,yy2,zz2; local denom,lambda,zaa,zbb,darea; local xa,ya,za,xb,yb,zb; lensum := 0; areasum := 0; foreach facet ff do { any := 0; foreach ff.edge ee do { xx1 := ee.vertex[1].x; yy1 := ee.vertex[1].y; zz1 := ee.vertex[1].z; xx2 := ee.vertex[2].x; yy2 := ee.vertex[2].y; zz2 := ee.vertex[2].z; denom := aa*(xx1-xx2)+bb*(yy1-yy2)+cc*(zz1-zz2); if ( denom != 0.0 ) then { lambda := (dd-aa*xx2-bb*yy2-cc*zz2)/denom; if ( (lambda >= 0) and (lambda <= 1) ) then { xa := xb; ya := yb; za := zb; xb := lambda*xx1+(1-lambda)*xx2; yb := lambda*yy1+(1-lambda)*yy2; zb := lambda*zz1+(1-lambda)*zz2; any := any+1; } } } ; if any == 2 then { dx := xa-xb; dy := ya-yb; dz := za - zb; lensum := lensum + sqrt(dx^2+dy^2+dz^2); zaa := za - dd/cc; zbb := zb - dd/cc; darea := sqrt((ya*zbb-yb*zaa)^2+(zaa*xb-zbb*xa)^2+(xa*yb-ya*xb)^2); areasum := areasum + darea/2; } }; printf "Circumference: %18.15g Area: %18.15g\n",lensum,areasum; } // end slice evolver-2.30c.dfsg/fe/qmound.fe0000644000175300017530000000412711410765113016643 0ustar hazelscthazelsct// qmound.fe // Evolver data for drop of prescribed volume sitting on plane with gravity. // Contact angle with plane can be varied. // Volume constraint implemented as quantity constraint PARAMETER angle = 90 // interior angle between plane and surface, degrees #define T (-cos(angle*pi/180)) // virtual tension of facet on plane quantity vol fixed = 1 method facet_vector_integral global // is volume vector_integrand: q1: 0 q2: 0 q3: z constraint 1 /* the table top */ formula: x3 = 0 energy: // for contact angle e1: -T*y e2: 0 e3: 0 vertices 1 0.0 0.0 0.0 constraint 1 /* 4 vertices on plane */ 2 1.0 0.0 0.0 constraint 1 3 1.0 1.0 0.0 constraint 1 4 0.0 1.0 0.0 constraint 1 5 0.0 0.0 1.0 6 1.0 0.0 1.0 7 1.0 1.0 1.0 8 0.0 1.0 1.0 9 2.0 2.0 0.0 fixed /* for table top */ 10 2.0 -1.0 0.0 fixed 11 -1.0 -1.0 0.0 fixed 12 -1.0 2.0 0.0 fixed edges /* given by endpoints and attribute */ 1 1 2 constraint 1 /* 4 edges on plane */ 2 2 3 constraint 1 3 3 4 constraint 1 4 4 1 constraint 1 5 5 6 6 6 7 7 7 8 8 8 5 9 1 5 10 2 6 11 3 7 12 4 8 13 9 10 fixed /* for table top */ 14 10 11 fixed 15 11 12 fixed 16 12 9 fixed faces /* given by oriented edge loop */ 1 1 10 -5 -9 2 2 11 -6 -10 3 3 12 -7 -11 4 4 9 -8 -12 5 5 6 7 8 7 13 14 15 16 density 0 fixed /* table top for display */ bodies /* one body, defined by its oriented faces */ 1 1 2 3 4 5 density 1 read re := {refine edges where on_constraint 1 } // Typical evolution gogo := { re; g 5; r; g 5; r; g 5; hessian; hessian; } // Evolution with 45 degree contact angle gogo2 := { angle := 45; re; g 5; V;V; r; g 5; V;V; r; g 5; hessian; hessian; } // Evolution with 90 contact and high gravity gogo3 := { angle := 90; G 5; re; g 5; r; g 5; r; g 5; hessian; hessian; } // Evolution with 90 contact and negative gravity, i.e. pendant drop gogo4 := { angle := 90; G -2; re; g 5; r; g 5; r; g 5; hessian; hessian; } // Pendant drop falling off ceiling gogo5 := { angle := 90; G -5; re; g 10; t .1; unset vertex constraint 1; g 3; } evolver-2.30c.dfsg/fe/tuber.cmd0000755000175300017530000001415311410765113016635 0ustar hazelscthazelsct// tuber.cmd // Surface Evolver command to put tubes around certain edges, for more // reliable display of chosen edges. Note that this procedure does // modify the current surface, rather than write a datafile. // Usage: Define the "intube" edge attribute to be 1 for the edges // you want tubed, and call "tuber" with appropriate parameters, i.e. // Enter command: set edge intube 0 // Enter command: set edge intube 1 where on_constraint 1 // Enter command: tuber(0.01,4,1) // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke define edge attribute intube integer // set positive if want tube procedure tuber (real tube_radius, // radius of tube integer tube_sides, // how many sides on each tube, >= 3 integer tube_caps // 0 for no caps, 1 for cone caps ) { local ax,ay,az,bx,by,bz,mag,elength,tailcapv,headcapv,t_angle,vv1,ww1; local ee1,start_tailcape,start_headcape,prev_tailcape,prev_headcape; local vstart,wstart,starte,inx,vv2,ww2,ee2,vend,wend,diag,newf; local tailcape,headcape; // Some parameter checking if tube_sides < 3 then tube_sides := 3; if tube_radius <= 0 then { errprintf "tuber: tube_radius is %f; must be positive.\n",tube_radius; return; }; // Now the tubes. foreach edge ee where intube > 0 do { // find two orthogonal directions to edge; if possible, // one along adjacent facet if ee.valence > 0 then { ax := ee.facet[1].x; ay := ee.facet[1].y; az := ee.facet[1].z; } else { if abs(ee.x) < abs(ee.y) and abs(ee.x) < abs(ee.z) then { ax := 1; ay := 0; az := 0;} else if abs(ee.y) < abs(ee.z) then { ax := 0; ay := 1; az := 0; } else { ax := 0; ay := 0; az := 1; }; }; bx := ay*ee.z - az*ee.y; by := az*ee.x - ax*ee.z; bz := ax*ee.y - ay*ee.x; ax := by*ee.z - bz*ee.y; ay := bz*ee.x - bx*ee.z; az := bx*ee.y - by*ee.x; // normalize to radius mag := sqrt(ax^2+ay^2+az^2); if mag <= 0 then continue; ax *= tube_radius/mag; ay *= tube_radius/mag; az *= tube_radius/mag; mag := sqrt(bx^2+by^2+bz^2); if mag <= 0 then continue; bx *= tube_radius/mag; by *= tube_radius/mag; bz *= tube_radius/mag; // Tube cap tips, if wanted if tube_caps then { elength := ee.length; tailcapv := new_vertex(ee.vertex[1].x - ee.x/elength*tube_radius, ee.vertex[1].y - ee.y/elength*tube_radius, ee.vertex[1].z - ee.z/elength*tube_radius); headcapv := new_vertex(ee.vertex[1].x + ee.x + ee.x/elength*tube_radius, ee.vertex[1].y + ee.y + ee.y/elength*tube_radius, ee.vertex[1].z + ee.z + ee.z/elength*tube_radius); fix vertex[tailcapv]; fix vertex[headcapv]; }; // Construct tubes t_angle := 2*pi/tube_sides; vv1 := new_vertex(ee.vertex[1].x+ax*cos(0*t_angle)+bx*sin(0*t_angle), ee.vertex[1].y+ay*cos(0*t_angle)+by*sin(0*t_angle), ee.vertex[1].z+az*cos(0*t_angle)+bz*sin(0*t_angle)); ww1 := new_vertex(ee.vertex[1].x+ee.x+ax*cos(0*t_angle)+bx*sin(0*t_angle), ee.vertex[1].y+ee.y+ay*cos(0*t_angle)+by*sin(0*t_angle), ee.vertex[1].z+ee.z+az*cos(0*t_angle)+bz*sin(0*t_angle)); fix vertex[vv1]; fix vertex[ww1]; ee1 := new_edge(vv1,ww1); edge[ee1].color := ee.color; if tube_caps then { start_tailcape := new_edge(tailcapv,vv1); start_headcape := new_edge(ww1,headcapv); edge[start_tailcape].color := ee.color; edge[start_headcape].color := ee.color; prev_tailcape := start_tailcape; prev_headcape := start_headcape; }; vstart := vv1; wstart := ww1; starte := ee1; for ( inx := 1 ; inx <= tube_sides ; inx += 1 ) { if inx == tube_sides then { vv2 := vstart; ww2 := wstart; ee2 := starte; } else { vv2 := new_vertex(ee.vertex[1].x+ax*cos(inx*t_angle)+bx*sin(inx*t_angle), ee.vertex[1].y+ay*cos(inx*t_angle)+by*sin(inx*t_angle), ee.vertex[1].z+az*cos(inx*t_angle)+bz*sin(inx*t_angle)); ww2 := new_vertex(ee.vertex[1].x+ee.x+ax*cos(inx*t_angle)+bx*sin(inx*t_angle), ee.vertex[1].y+ee.y+ay*cos(inx*t_angle)+by*sin(inx*t_angle), ee.vertex[1].z+ee.z+az*cos(inx*t_angle)+bz*sin(inx*t_angle)); ee2 := new_edge(vv2,ww2); edge[ee2].color := ee.color; }; vend := new_edge(vv1,vv2); wend := new_edge(ww1,ww2); diag := new_edge(vv2,ww1); edge[vend].color := ee.color; edge[wend].color := ee.color; edge[diag].color := ee.color; set edge[vend] no_refine; set edge[wend] no_refine; set edge[diag] no_refine; fix edge[vend]; fix edge[wend]; fix edge[diag]; newf := new_facet(vend,diag,-ee1); set facet[newf] color ee.color; fix facet[newf]; set facet[newf] no_refine; set facet[newf] tension 0; newf := new_facet(ee2,-wend,-diag); set facet[newf] color ee.color; fix facet[newf]; set facet[newf] no_refine; set facet[newf] tension 0; if tube_caps then { if inx == tube_sides then { tailcape := start_tailcape; headcape := start_headcape; } else { tailcape := new_edge(tailcapv,vv2); headcape := new_edge(ww2,headcapv); edge[tailcape].color := ee.color; edge[headcape].color := ee.color; }; fix edge[tailcape]; fix edge[headcape]; newf := new_facet(tailcape,-vend,-prev_tailcape); set facet[newf] color ee.color; fix facet[newf]; set facet[newf] no_refine; set facet[newf] tension 0; newf := new_facet(headcape,-prev_headcape,wend); set facet[newf] color ee.color; fix facet[newf]; set facet[newf] no_refine; set facet[newf] tension 0; prev_tailcape := tailcape; prev_headcape := headcape; }; vv1 := vv2; ww1 := ww2; ee1 := ee2; } } } // end tuber() evolver-2.30c.dfsg/fe/cat.fe0000644000175300017530000000447111410765113016111 0ustar hazelscthazelsct// cat.fe // Evolver data for catenoid. PARAMETER RMAX = 1.5088795 // minimum radius for height PARAMETER ZMAX = 1.0 boundary 1 parameters 1 // upper ring x1: RMAX * cos(p1) x2: RMAX * sin(p1) x3: ZMAX boundary 2 parameters 1 // lower ring x1: RMAX * cos(p1) x2: RMAX * sin(p1) x3: -ZMAX vertices // given in terms of boundary parameter 1 0.00 boundary 1 fixed 2 pi/3 boundary 1 fixed 3 2*pi/3 boundary 1 fixed 4 pi boundary 1 fixed 5 4*pi/3 boundary 1 fixed 6 5*pi/3 boundary 1 fixed 7 0.00 boundary 2 fixed 8 pi/3 boundary 2 fixed 9 2*pi/3 boundary 2 fixed 10 pi boundary 2 fixed 11 4*pi/3 boundary 2 fixed 12 5*pi/3 boundary 2 fixed edges 1 1 2 boundary 1 fixed 2 2 3 boundary 1 fixed 3 3 4 boundary 1 fixed 4 4 5 boundary 1 fixed 5 5 6 boundary 1 fixed 6 6 1 boundary 1 fixed 7 7 8 boundary 2 fixed 8 8 9 boundary 2 fixed 9 9 10 boundary 2 fixed 10 10 11 boundary 2 fixed 11 11 12 boundary 2 fixed 12 12 7 boundary 2 fixed 13 1 7 14 2 8 15 3 9 16 4 10 17 5 11 18 6 12 faces 1 1 14 -7 -13 2 2 15 -8 -14 3 3 16 -9 -15 4 4 17 -10 -16 5 5 18 -11 -17 6 6 13 -12 -18 read // Evolution to collapse and pop neck, as in Manual tutorial gogo := { r; u; g 120; t .05; o; g 5; } // Demonstrating saddle point due to triangulation arrangement. // First setting parameters to stable values. gogo2 := { rmax := 1; zmax := 0.55; recalc; g; u; r; g 50; // at this point have nearly a saddle point g 200; // triangulation twists around to lower energy } // Faster version of the above using conjugate gradient gogo3 := { rmax := 1; zmax := 0.55; recalc; g; u; r; U; g 25; // at this point have nearly a saddle point g 35; } // High accuracy evolution, using higher-order Lagrange elements. gogo4 := { u; zmax := 0.55; rmax := cosh(zmax); recalc; r; g 5; hessian; r; g 5; hessian; lagrange 2; g 5; hessian; lagrange 4; g 5; hessian; lagrange 6; g 5; hessian; lagrange 8; g 5; hessian; true_area := 2*pi*(zmax + 0.5*sinh(2*zmax)); printf"Difference from true area: %g\n",total_area - true_area; } evolver-2.30c.dfsg/fe/gaussmap.cmd0000755000175300017530000000443411410765113017335 0ustar hazelscthazelsct// gaussmap.cmd // Converts each vertex coordinate to unit normal. // No vertices should be on constraints or fixed. // Saves original coordinates so "revert" restores original surface. // Before running "gaussmap", vertices should be freed from all // constraints and boundaries. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // Usage: gaussmap define vertex attribute gaussx real[3] define vertex attribute oldx real[3] gaussmap := { foreach vertex vv do { local connum; connum := 1; do { unset vv constraint connum; connum += 1; } while ( connum < 30 ); vv.oldx[1] := vv.x[1]; vv.oldx[2] := vv.x[2]; vv.oldx[3] := vv.x[3]; vv.gaussx[1] := vv.vertexnormal[1]; vv.gaussx[2] := vv.vertexnormal[2]; vv.gaussx[3] := vv.vertexnormal[3]; }; set vertex x gaussx[1]; set vertex y gaussx[2]; set vertex z gaussx[3]; } revert := { set vertex x oldx[1]; set vertex y oldx[2]; set vertex z oldx[3]; } // Command to find spherical area of gauss map, Lagrange model calc_gaussarea := { local jnx, jnxx; if quadratic then lagrange 2; gaussarea := 0; jnx := 2; jnxx := 3; foreach facet ff do { local angle_a, angle_b, angle_c; angle_a := asin(sqrt( (ff.vertex[1].vertexnormal[1]-ff.vertex[jnx].vertexnormal[1])^2 +(ff.vertex[1].vertexnormal[2]-ff.vertex[jnx].vertexnormal[2])^2 +(ff.vertex[1].vertexnormal[3]-ff.vertex[jnx].vertexnormal[3])^2)/2); angle_b := asin(sqrt( (ff.vertex[jnxx].vertexnormal[1]-ff.vertex[jnx].vertexnormal[1])^2 +(ff.vertex[jnxx].vertexnormal[2]-ff.vertex[jnx].vertexnormal[2])^2 +(ff.vertex[jnxx].vertexnormal[3]-ff.vertex[jnx].vertexnormal[3])^2)/2); angle_c := asin(sqrt( (ff.vertex[jnxx].vertexnormal[1]-ff.vertex[1].vertexnormal[1])^2 +(ff.vertex[jnxx].vertexnormal[2]-ff.vertex[1].vertexnormal[2])^2 +(ff.vertex[jnxx].vertexnormal[3]-ff.vertex[1].vertexnormal[3])^2)/2); gaussarea += 4*atan(sqrt(tan((angle_a+angle_b+angle_c)/2) *tan((angle_a+angle_b-angle_c)/2) *tan((angle_a-angle_b+angle_c)/2) *tan((-angle_a+angle_b+angle_c)/2))); }; printf "Gauss map area: %18.15f or %18.15f*pi\n",gaussarea,gaussarea/pi; } evolver-2.30c.dfsg/fe/quad.fe0000644000175300017530000000045511410765113016272 0ustar hazelscthazelsct// quad.fe // simple skew quadrilateral vertices 1 0 0 1 fixed 2 2 0 0 fixed 3 2 2 1 fixed 4 0 2 0 fixed edges 1 1 2 fixed 2 2 3 fixed 3 3 4 fixed 4 4 1 fixed faces 1 1 2 3 4 read // Typical evolution gogo := { r; g 5; r; g 5; r; g 5; hessian; hessian; } evolver-2.30c.dfsg/fe/equi.cmd0000755000175300017530000000160311410765113016453 0ustar hazelscthazelsct// equi.cmd -- to color edges green subject to equiangulation // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // Usage: showequi showequi := { local ecount, al, nn, cl, dl, el, bl; ecount := 0; foreach edge ee do { al := ee.length; nn := 1; foreach ee.facet ff do { foreach ff.edge fe do { if ( fe.id != ee.id ) then { if ( nn == 1 ) then bl := fe.length else if ( nn == 2 ) then cl := fe.length else if ( nn == 3 ) then dl := fe.length else if ( nn == 4 ) then el := fe.length; nn := nn + 1; } } }; if ( nn == 5 ) then if (bl^2+cl^2-al^2)/bl/cl + (dl^2+el^2-al^2)/dl/el < -1e-5 then { set ee color green; ecount := 1 + ecount } }; printf "Found %g edges.\n",ecount; show_expr edges where 1 } evolver-2.30c.dfsg/fe/slidestr.fe0000644000175300017530000000324511410765113017171 0ustar hazelscthazelsct// slidestr.fe // Evolver data for drop of prescribed volume sitting // on hyperbolic slide with gravity. Illustrates corner // holding drop against gravity. // Instructions for simple use: // refine once // iterate until drop settles into curve (datafile starts with // gravity = 1 to pull drop down) // Use `G' command to reverse gravity. Blob is stable up to // about G = -.8 // // You can color the blob edges with // set edge color white where not fixed STRING // one-dimensional surface space_dimension 2 // Constraint of form m*x + x*y = k with m = 0.5, k = 0.1 // Integrals are used for volume, since zero density facets // don't move, and their stretching around the curve gives // an inaccurate volume. PARAMETER STEEP = 0.5 PARAMETER SHARP = 0.1 constraint 1 // the slide, constants are steepness and sharpness formula: STEEP*x + x*y = SHARP content: c1: log(x)*SHARP - STEEP*x // note this is indefinite integral of y constraint 2 // the slide, for display formula: STEEP*x + x*y = SHARP vertices 1 0.0 2.2 constraint 1 2 0.0 0.8 constraint 1 3 0.5 2.2 4 0.5 0.8 5 0.0 2.5 constraint 2 fixed // for slide display 6 0.3 0.2 constraint 2 fixed // for slide display 7 1.5 .0 constraint 2 fixed // for slide display edges /* given by endpoints and attribute */ 1 2 4 2 4 3 3 3 1 4 5 6 fixed constraint 2 // for slide display 5 6 7 fixed constraint 2 // for slide display faces /* given by oriented edge loop */ 1 1 2 3 bodies /* one body, defined by its oriented faces */ 1 1 volume 0.7 density 1.0 read // Typical evolution, doing V's to keep vertices spread. gogo := { r; {g 10; V;} 40 } evolver-2.30c.dfsg/fe/jvx.cmd0000755000175300017530000002026711410765113016326 0ustar hazelscthazelsct// jvx.cmd // Create jvx file for JavaView // Usage: jvx >>> "filename.jvx" // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke define vertex attribute jvx_number integer; // for implicit vertex order define facet attribute fjvxnum integer; // for implicit facet order define facetcolorcount integer[16]; // to see which color facets are present define edgecolorcount integer[16]; // to see which color edges are present define rgb integer[16][3]; // color definitions rgb[1][1] := 0; rgb[1][2] := 0; rgb[1][3] := 0; rgb[2][1] := 0; rgb[2][2] := 0; rgb[2][3] := 255; rgb[3][1] := 0; rgb[3][2] := 255; rgb[3][3] := 0; rgb[4][1] := 0; rgb[4][2] := 255; rgb[4][3] := 255; rgb[5][1] := 255; rgb[5][2] := 0; rgb[5][3] := 0; rgb[6][1] := 255; rgb[6][2] := 0; rgb[6][3] := 255; rgb[7][1] := 255; rgb[7][2] := 127; rgb[7][3] := 0; rgb[8][1] := 160; rgb[8][2] := 160; rgb[8][3] := 160; rgb[9][1] := 80; rgb[9][2] := 80; rgb[9][3] := 80; rgb[10][1] := 80; rgb[10][2] := 200; rgb[10][3] := 255; rgb[11][1] := 127; rgb[11][2] := 255; rgb[11][3] := 127; rgb[12][1] := 127; rgb[12][2] := 255; rgb[12][3] := 255; rgb[13][1] := 255; rgb[13][2] := 127; rgb[13][3] := 127; rgb[14][1] := 255; rgb[13][2] := 127; rgb[13][3] := 255; rgb[15][1] := 255; rgb[15][2] := 255; rgb[15][3] := 0; rgb[16][1] := 255; rgb[16][2] := 255; rgb[16][3] := 255; jvx := { // First, bounding box. maxx := max(vertex,x); minx := min(vertex,x); maxy := max(vertex,y); miny := min(vertex,y); maxz := max(vertex,z); minz := min(vertex,z); printf "\n"; printf "\n"; printf "\n"; // printf "\n",date_and_time; printf " %s \n",datafilename; printf "1.0\n"; /* printf "\n"; printf " \n"; printf " Kenneth\n"; printf " Brakke\n"; printf " \n"; printf " Susquehanna University\n"; printf "
\n"; printf " Mathematics Department\n"; printf " Susquehanna University\n"; printf " Selinsgrove, PA 17870\n"; printf " USA\n"; printf "
\n"; printf "
\n"; printf " brakke@susqu.edu\n"; printf " http://www.susqu.edu/brakke\n"; printf "
\n"; printf "
\n"; printf "\n"; printf " One-line description of surface.\n"; printf " \n"; printf " Paragraph of description here.\n"; printf " \n"; printf " \n"; printf " minimal surface\n"; printf " \n"; printf " \n"; printf " 49Q20\n"; printf " optional \n"; printf " optional \n"; printf " \n"; printf " Surface Evolver 2.16\n"; printf "\n"; */ printf "\n"; printf " \n"; printf " \n"; printf " \n",vertex_count; jvxnum := 0; foreach vertex vv do { printf "

%f %f %f

\n",vv.x,vv.y,vv.z; vv.jvx_number := jvxnum; jvxnum += 1; }; printf "
\n"; printf "
\n"; facetnum := 0; foreach facet ff where ff.show and color != clear do { ff.fjvxnum := facetnum; facetnum += 1; }; colornum := 1; while colornum <= 16 do { facetcolorcount[colornum] := 0; colornum += 1; }; foreach facet ff where ff.show and color != clear do facetcolorcount[ff.color+1] += 1; colornum := 1; numcolors := 0; while colornum <= 16 do { if facetcolorcount[colornum] > 0 then numcolors += 1; colornum += 1; }; if numcolors > 1 then printf " \n" else printf " \n"; printf " \n",sum(facets,show); foreach facet ff where ff.show and color != clear do { printf " %d %d %d \n",ff.vertex[1].jvx_number, ff.vertex[2].jvx_number, ff.vertex[3].jvx_number; }; if numcolors <= 1 then { colornum := 1; while colornum <= 16 do { if facetcolorcount[colornum] > 0 then printf " %d %d %d \n",rgb[colornum][1], rgb[colornum][2],rgb[colornum][3]; colornum += 1; }; }; printf " \n"; if numcolors > 1 then { printf " \n"; foreach facet ff where ff.show and color != clear do { printf " %d %d %d \n",rgb[ff.color+1][1], rgb[ff.color+1][2],rgb[ff.color+1][3]; }; printf " \n"; }; printf " \n",sum(facets,show); foreach facet ff where ff.show and color != clear do printf " %d %d %d \n", ff.edge[2].valence == 2 ? max(ff.edge[2].facet where id != ff.id,fjvxnum) : -1, ff.edge[3].valence == 2 ? max(ff.edge[3].facet where id != ff.id,fjvxnum) : -1, ff.edge[1].valence == 2 ? max(ff.edge[1].facet where id != ff.id,fjvxnum) : -1; printf " \n"; // printf " \n",sum(edges,show); // foreach edge ee where ee.show do // { printf " %d %d \n",ee.vertex[1].jvx_number, // ee.vertex[2].jvx_number; // }; // printf " 0 0 0 \n"; // printf " \n"; printf " \n"; printf " 1.0 \n"; printf " 0 0 0\n"; printf " \n"; printf " \n"; printf " \n"; printf "

%f %f %f

\n",minx,miny,minz; printf "

%f %f %f

\n",maxx,maxy,maxz; printf "
\n"; printf "
\n"; printf "

%f %f %f

\n",(maxx+minx)/2,(maxy+miny)/2, (maxz+minz)/2; printf "
\n"; printf "
\n"; colornum := 1; while colornum <= 16 do { edgecolorcount[colornum] := 0; colornum += 1; }; foreach edge ee where ee.show do edgecolorcount[ee.color+1] += 1; colornum := 1; numcolors := 0; while colornum <= 16 do { if edgecolorcount[colornum] > 0 then numcolors += 1; colornum += 1; }; printf " \n"; printf " \n"; printf " \n",2*sum(edges,show); foreach edge ee where ee.show do { printf "

%f %f %f

\n",ee.vertex[1].x,ee.vertex[1].y,ee.vertex[1].z; printf "

%f %f %f

\n",ee.vertex[2].x,ee.vertex[2].y,ee.vertex[2].z; }; printf "
\n"; printf "
\n"; if numcolors > 1 then printf " \n" else printf " \n"; printf " \n",sum(edges,show); jvxnum := 0; foreach edge ee where ee.show do { printf " %d %d \n",jvxnum,jvxnum+1; jvxnum += 2; }; printf " 2 \n"; if numcolors <= 1 then { colornum := 1; while colornum <= 16 do { if edgecolorcount[colornum] > 0 then printf " %d %d %d \n",rgb[colornum][1], rgb[colornum][2],rgb[colornum][3]; colornum += 1; }; }; printf " \n"; if ( numcolors > 1 ) then { printf " \n"; foreach edge ee where ee.show do { printf " %d %d %d \n", rgb[ee.color+1][1], rgb[ee.color+1][2],rgb[ee.color+1][3]; }; printf " \n"; }; printf " \n"; printf " \n"; printf "

%f %f %f

\n",minx,miny,minz; printf "

%f %f %f

\n",maxx,maxy,maxz; printf "
\n"; printf "
\n"; printf "

%f %f %f

\n",(maxx+minx)/2,(maxy+miny)/2, (maxz+minz)/2; printf "
\n"; printf "
\n"; printf "
\n"; printf "
\n"; } evolver-2.30c.dfsg/fe/gzipdump.cmd0000755000175300017530000000204111410765113017344 0ustar hazelscthazelsct// gzipdump.cmd // Evolver command for dumping datafile directly to gzip form // Needs Evolver version 1.99e or later. // Usage: gzipdump // As is, this will dump to a gzipped file with a default name. // You may change the filename in the command below to your own liking. // gzipped file may be reloaded directly with shell command like // /etc/mknod epipe p; ((gunzip epipe;rm epipe)& evolver epipe) // I suggest defining a csh alias like // alias gzev '/etc/mknod epipe p;((gzip -d < \!$ >epipe;rm epipe)& evolver \!:1- epipe)' // so you can just do // gzev cube.dmp.gz // The alias even works with options, as in // gzev -q -p2 cube.dmp.gz dodump := { list topinfo; print "\nvertices\n"; list vertices; print "\nedges\n"; list edges; print "\nfaces\n"; list facets; print "\nbodies\n"; list bodies; list bottominfo } gzipdump := { filename := sprintf "%s.dmp.gz",datafilename; dumpcmd := sprintf "gzip >%s",filename; printf "Dumping to %s\n",filename; dodump | dumpcmd } evolver-2.30c.dfsg/fe/vrml.cmd0000755000175300017530000000477111410765113016501 0ustar hazelscthazelsct// vrml.cmd // Makes VRML file for surface. // Usage: Set edge_flag to 1 if you want do see all edges. // Run "vrml" and re-direct output to file, e.g. // Enter command: vrml >>> "myfile.wrl"; // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke define vertex attribute order_num integer edge_flag := 0 // 1 for all edges, 0 for special edges only vrml := { local counter; counter := 0; printf "#VRML V1.0 ascii\n\n"; printf "Separator {\n"; printf " DEF Title Info { string \"%s\" }\n",datafilename; printf " DEF SceneInfo Info { string \"Created by Surface Evolver\" }\n"; printf " DEF BackgroundColor Info { string \".5 .6 1\" } \n"; printf " DirectionalLight { intensity .5 direction 0 0 -1 } \n"; printf " MaterialBinding { value PER_FACE_INDEXED }\n"; printf " Material { \n"; printf " diffuseColor [ 0.0 0.0 0.0 , 0.0 0.0 0.5 ,\n"; printf " 0.0 0.5 0.0 , 0.0 0.5 0.5 , 0.5 0.0 0.0 , 0.5 0.0 0.5 ,\n"; printf " 0.5 0.25 0. , .3 .3 .3 , .15 .15 .15 , .25 .25 .5 , .25 .5 .25 ,\n"; printf " .25 .5 .5 , .5 .25 .25 , .5 .25 .5 , .5 .5 .0 , .5 .5 .5 ] \n"; printf " emissiveColor [ 0.0 0.0 0.0 , 0.0 0.0 0.5 ,\n"; printf " 0.0 0.5 0.0 , 0.0 0.5 0.5 , 0.5 0.0 0.0 , 0.5 0.0 0.5 ,\n"; printf " 0.5 0.25 0. , .3 .3 .3 , .15 .15 .15 , .25 .25 .5 , .25 .5 .25 ,\n"; printf " .25 .5 .5 , .5 .25 .25 , .5 .25 .5 , .5 .5 .0 , .5 .5 .5 ] \n"; printf " }\n"; printf " Separator {\n"; printf " Coordinate3 { point [\n"; foreach vertex jvv do { printf " %f %f %f,\n",jvv.x,jvv.y,jvv.z; set jvv order_num counter; counter := counter + 1; }; printf " ]\n }\n"; printf " IndexedFaceSet { coordIndex [\n"; foreach facet jff do printf " %g,%g,%g,-1,\n", jff.vertex[1].order_num,jff.vertex[2].order_num,jff.vertex[3].order_num; printf " ] \n"; printf " materialIndex [\n"; foreach facet jff do printf " %g,\n",jff.color; printf " ]\n"; printf " }\n"; printf " Material { ambientColor 0 0 0 diffuseColor 0 0 0 }\n"; printf " IndexedLineSet { coordIndex [\n"; if edge_flag then foreach edge jee do printf " %g,%g,-1,\n", jee.vertex[1].order_num,jee.vertex[2].order_num else foreach edge jee where valence != 2 do printf " %g,%g,-1,\n", jee.vertex[1].order_num,jee.vertex[2].order_num; printf " ] } \n"; printf " }\n"; printf "}\n"; } // end vrml evolver-2.30c.dfsg/fe/phelanc.fe0000644000175300017530000001730611410765113016755 0ustar hazelscthazelsct// phelanc.fe // Structure that beats Kelvin's partition of space. // In 1887, Lord Kelvin posed the problem of finding the partition // of space into equal volume cells minimizing the interface area. // He suggested the cell shown in twointor.fe, which is basically // the voronoi cell for a bcc lattice. Now Robert Phelan and Denis // Weaire of Trinity College, Dublin, have found a structure using // two types of cells that has 0.3% less area than Kelvin's. This is // their Evolver datafile. There are 8 cells in a // cubic 2x2x2 flat torus, which start as Voronoi cells on centers // // 0 0 0 // 1 1 1 // 0.5 0 1 // 1.5 0 1 // 0 1 0.5 // 0 1 1.5 // 1 0.5 0 // 1 1.5 0 // Just evolve to get the volumes all to 1, and Kelvin is beat. // With more evolution, the ratio V^2/A^3 beats Kelvin by a // whopping 1%. The Weaire-Phelan structure has its tetrakaidecahedra // stacked on their hexagonal faces in three sets of perpendicular, // mutually interlocking columns, with interstices filled by the // dodecahedra. // phelanc.fe with colored bodies TORUS_FILLED periods 2.000000 0.000000 0.000000 0.000000 2.000000 0.000000 0.000000 0.000000 2.000000 view_transform_generators 6 1 0 0 2 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 1 0 2 0 0 1 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 1 2 0 0 0 1 1 0 0 -2 0 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0 0 1 0 -2 0 0 1 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 1 -2 0 0 0 1 vertices 1 1.374833 0.000542 0.313036 2 1.582639 1.583805 0.417091 3 1.999414 1.687884 0.625562 4 0.999778 0.000517 0.500564 5 1.686693 1.374893 1.999381 6 1.999036 0.312928 0.625224 7 0.416118 1.583554 0.417247 8 1.416641 1.417638 0.583002 9 0.999380 1.626008 0.687643 10 1.374528 0.000836 1.688167 11 1.582887 1.582882 1.583776 12 1.583228 0.416633 0.417188 13 0.415660 0.417170 0.416774 14 0.312152 1.375055 0.000199 15 1.999782 1.500468 1.000033 16 1.625132 1.312811 1.000465 17 1.312290 1.000953 0.374434 18 0.999015 1.625244 1.312907 19 0.582337 1.417418 0.583502 20 0.999205 0.000988 1.500509 21 1.582954 0.417290 1.583964 22 1.499589 1.000835 1.999244 23 1.687315 0.625137 1.999668 24 0.624322 0.000725 0.312769 25 0.416475 1.583942 1.583184 26 0.374830 1.313082 0.999521 27 1.624817 0.687664 1.000333 28 0.686621 1.000835 0.374834 29 1.416503 1.416444 1.417442 30 0.624634 0.000964 1.687531 31 1.999937 1.687755 1.375079 32 1.312386 1.000647 1.625031 33 0.499725 1.000658 0.000553 34 0.311830 0.624975 0.000488 35 0.375186 0.688291 0.999389 36 1.416715 0.583171 0.583818 37 0.582556 0.584101 0.583805 38 0.583642 1.417297 1.416440 39 1.999485 0.312616 1.375471 40 0.416489 0.416552 1.584015 41 1.999161 0.500331 0.999822 42 0.999925 0.375397 0.688204 43 0.688262 1.000529 1.624602 44 1.416155 0.584231 1.417123 45 0.584307 0.583998 1.416583 46 0.999499 0.376190 1.313024 edges 1 1 2 * - * 2 2 3 * * * 3 1 4 * * * 4 2 5 * * - 5 3 6 * + * 6 3 7 + * * 7 2 8 * * * 8 4 9 * - * 9 1 10 * * - 10 5 11 * * * 11 6 12 * * * 12 6 13 + * * 13 7 14 * * * 14 3 15 * * * 15 8 16 * * * 16 8 17 * * * 17 9 8 * * * 18 9 18 * * * 19 9 19 * * * 20 10 20 * * * 21 10 21 * * * 22 11 10 * + * 23 5 22 * * * 24 5 14 + * + 25 12 1 * * * 26 12 23 * * - 27 13 24 * * * 28 7 19 * * * 29 14 25 * * - 30 15 26 + * * 31 16 15 * * * 32 16 27 * * * 33 17 22 * * - 34 17 28 * * * 35 18 29 * * * 36 19 26 * * * 37 20 30 * * * 38 21 23 * * * 39 11 31 * * * 40 11 29 * * * 41 22 32 * * * 42 14 33 * * * 43 23 34 + * + 44 24 7 * - * 45 24 30 * * - 46 19 28 * * * 47 25 30 * + * 48 25 31 - * * 49 26 35 * * * 50 16 29 * * * 51 27 36 * * * 52 17 36 * * * 53 28 33 * * * 54 28 37 * * * 55 18 38 * * * 56 29 32 * * * 57 26 38 * * * 58 20 18 * - * 59 31 39 * + * 60 22 23 * * * 61 33 34 * * * 62 34 13 * * * 63 34 40 * * - 64 30 40 * * * 65 35 41 - * * 66 35 37 * * * 67 36 12 * * * 68 36 42 * * * 69 33 43 * * - 70 37 42 * * * 71 37 13 * * * 72 38 25 * * * 73 32 43 * * * 74 32 44 * * * 75 24 4 * * * 76 35 45 * * * 77 21 39 * * * 78 39 40 + * * 79 41 27 * * * 80 42 46 * * * 81 43 38 * * * 82 42 4 * * * 83 44 27 * * * 84 44 46 * * * 85 15 31 * * * 86 45 43 * * * 87 45 46 * * * 88 41 39 * * * 89 21 44 * * * 90 6 41 * * * 91 46 20 * * * 92 45 40 * * * faces 1 1 2 5 11 25 color 1 backcolor 4 2 -1 3 8 17 -7 color 8 backcolor 4 3 2 6 13 -24 -4 color 5 backcolor 1 4 5 12 27 44 -6 color 3 backcolor 1 5 11 26 43 62 -12 color 5 backcolor 1 6 1 4 10 22 -9 color 8 backcolor 1 7 17 16 34 -46 -19 color 2 backcolor 8 8 7 16 33 -23 -4 color 8 backcolor 5 9 -2 7 15 31 -14 color 5 backcolor 4 10 -6 14 30 -36 -28 color 5 backcolor 3 11 -13 28 46 53 -42 color 5 backcolor 8 12 24 29 48 -39 -10 color 6 backcolor 1 13 44 13 29 47 -45 color 1 backcolor 8 14 62 27 45 64 -63 color 1 backcolor 7 15 25 9 21 38 -26 color 7 backcolor 1 16 -3 9 20 37 -45 75 color 8 backcolor 7 17 -10 23 41 -56 -40 color 8 backcolor 6 18 -22 39 59 -77 -21 color 4 backcolor 1 19 8 19 -28 -44 75 color 3 backcolor 8 20 -18 19 36 57 -55 color 2 backcolor 3 21 -17 18 35 -50 -15 color 2 backcolor 4 22 34 53 69 -73 -41 -33 color 8 backcolor 7 23 -46 36 49 66 -54 color 5 backcolor 2 24 -16 15 32 51 -52 color 2 backcolor 5 25 31 30 49 65 79 -32 color 6 backcolor 5 26 -53 54 71 -62 -61 color 5 backcolor 7 27 42 61 -43 -60 -23 24 color 5 backcolor 6 28 48 59 78 -64 -47 color 1 backcolor 3 29 43 63 -78 -77 38 color 1 backcolor 6 30 -41 60 -38 89 -74 color 7 backcolor 6 31 -56 -35 55 -81 -73 color 2 backcolor 8 32 40 -35 -58 -20 -22 color 8 backcolor 4 33 -57 -30 85 -48 -72 color 6 backcolor 3 34 50 56 74 83 -32 color 2 backcolor 6 35 34 54 70 -68 -52 color 7 backcolor 2 36 69 81 72 -29 42 color 6 backcolor 8 37 -33 52 67 26 -60 color 7 backcolor 5 38 49 76 86 81 -57 color 2 backcolor 6 39 66 71 -12 90 -65 color 3 backcolor 5 40 51 67 -11 90 79 color 5 backcolor 4 41 -37 58 55 72 47 color 8 backcolor 3 42 -89 -21 20 -91 -84 color 7 backcolor 4 43 74 84 -87 86 -73 color 7 backcolor 2 44 83 51 68 80 -84 color 4 backcolor 2 45 70 82 -75 -27 -71 color 3 backcolor 7 46 -68 67 25 3 -82 color 4 backcolor 7 47 -69 61 63 -92 86 color 6 backcolor 7 48 -76 65 88 78 -92 color 3 backcolor 6 49 -66 76 87 -80 -70 color 3 backcolor 2 50 90 88 -59 -85 -14 5 color 4 backcolor 3 51 79 -83 -89 77 -88 color 4 backcolor 6 52 -91 -87 92 -64 -37 color 3 backcolor 7 53 -85 -31 50 -40 39 color 6 backcolor 4 54 -82 80 91 58 -18 -8 color 3 backcolor 4 bodies 1 1 -3 -4 -5 -6 -12 13 14 -15 -18 28 29 volume 0.976527 2 7 20 21 -23 24 31 34 -35 38 -43 -44 -49 volume 0.976538 3 39 45 48 49 -50 4 54 -28 52 -33 19 -20 -41 -10 volume 1.007698 4 -40 44 46 50 51 -1 -42 -54 -53 -9 18 -2 -32 -21 volume 1.007902 5 3 -8 9 10 11 -24 -25 23 26 27 -37 40 -39 5 volume 1.007528 6 33 -38 25 12 36 -48 53 -51 -34 -27 47 -17 -30 -29 volume 1.008217 7 -22 30 35 -26 37 42 43 -45 -46 -47 15 -52 -16 -14 volume 1.008071 8 2 6 -7 8 16 17 -19 22 -31 32 -36 -11 41 -13 volume 1.007564 read set body target 1 column_color := { /* colors bodies alike that are in same columns */ set facet frontcolor green where frontcolor==blue; set facet backcolor green where backcolor==blue; set facet frontcolor red where frontcolor==cyan; set facet backcolor red where backcolor==cyan; set facet frontcolor lightgray where frontcolor==darkgray; set facet backcolor lightgray where backcolor==darkgray; set facet frontcolor magenta where frontcolor==brown; set facet backcolor magenta where backcolor==brown; } dodecs:= show facet ff where max(ff.body,id==1 or id==2) column1:= show facet ff where max(ff.body,id==3 or id==4) column2:= show facet ff where max(ff.body,id==5 or id==6) column3:= show facet ff where max(ff.body,id==7 or id==8) bunch := transform_expr "abc" lots := transform_expr "abcdef" few := transforms off all := show facet // Typical evolution gogo := { g 5; r; g 5; hessian; r; g 5; hessian; } evolver-2.30c.dfsg/fe/iges136.cmd0000755000175300017530000001476611410765113016707 0ustar hazelscthazelsct// iges136.cmd // Surface Evolver script to write IGES file for surface, using IGES // finite element entity (type 136), which handles linear, quadratic, // and cubic triangles (among many other types not of interest). // Finite element type not official "geometry"???? Rhino says can't // find any independent geometry, and http://www.iges5x.org/taxonomy/ has // FEM types included under "non-geometry taxonomy'. // Also not listed in Table 3 on p. 38 of IGES-6 documentation. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // usage: iges >>> "filename.igs" iges := { // Flag section // Don't need this since not doing binary or compressed format. // Start section start_counter := 0; start_counter += 1; printf "%-72sS%07d\n","IGES version of Surface Evolver surface",start_counter; start_counter += 1; printf " %-69sS%07d\n",datafilename,start_counter; start_counter += 1; printf "%-72sS%07d\n","Created using iges.cmd Evolver script.", start_counter; // Global section global_counter := 0; global_counter += 1; printf "%-72sG%07d\n","1H,,1H;,",global_counter; global_counter += 1; printf "%02dH%-69sG%07d\n",sizeof("Surface Evolver"),"Surface Evolver,", global_counter; global_counter += 1; message := sprintf "%s,",datafilename; printf "%02dH%-69sG%07d\n",sizeof(datafilename),message,global_counter; global_counter += 1; printf "%02dH%-69sG%07d\n",sizeof("Surface Evolver"),"Surface Evolver,", global_counter; global_counter += 1; printf "%02dH%-69sG%07d\n",sizeof("Surface Evolver"),"Surface Evolver,", global_counter; global_counter += 1; printf "%-72sG%07d\n","32,75,6,75,15,,1.0,1,2HIN,32768,0.0394,", global_counter; global_counter += 1; printf "%-72sG%07d\n","15H00000000.000000,", global_counter; // date xmax := max(vertex,abs(x)); ymax := max(vertex,abs(y)); zmax := max(vertex,abs(z)); maxsize := (xmax > ymax) ? xmax : ymax; maxsize := (zmax > maxsize) ? zmax : maxsize; message := sprintf "%g,%g,",maxsize/10000000,maxsize; global_counter += 1; printf "%-72sG%07d\n",message, global_counter; global_counter += 1; printf "%02dH%-69sG%07d\n",sizeof("Name of author"),"Name of author,", global_counter; global_counter += 1; printf "%02dH%-69sG%07d\n",sizeof("Author's organization"), "Author's organization,", global_counter; global_counter += 1; printf "%-72sG%07d\n","15H00000000.000000;", global_counter; // date // Directory entry section. Each entry 20 8-char fields on two lines. // Fields with default values entype := 0; // 1 and 11 paramdata := 0; // 2 structure := 0; // 3 linefont := 0; // 4 level := 0; // 5 view := 0; // 6 transmat := 0; // 7 label := 0; // 8 status := "00000000"; // 9; actually 4 two-digit numbers directory_counter := 0; // 10 and 20 lineweight := 0; // 12 colornum := 0; // 13 paramcount := 0; // 14 form := 0; // 15 reserved := " "; // 16 and 17 entlabel := "entity"; // 18 entsubscr := 0; // 19 // Vertices as "node" types entype := 135; status := "00010401"; paramcount := 1; entlabel := " VERTEX"; define vertex attribute pdata integer; define vertex attribute dir integer; foreach vertex vv do { paramdata += 1; vv.pdata := paramdata; entsubscr := vv.id; directory_counter += 1; vv.dir := directory_counter; printf "%8d%8d%8d%8d%8d%8d%8d%8d%8sD%7d\n",entype,paramdata,structure, linefont,level,view,transmat,label,status,directory_counter; directory_counter += 1; printf "%8d%8d%8d%8d%8d%8s%8s%8s%8dD%7d\n",entype,lineweight,colornum, paramcount,form,reserved,reserved,entlabel,entsubscr,directory_counter; }; // Facets as "finite element" types entype := 136; status := "00010001"; paramcount := 1; entlabel := " FACET"; define facet attribute fpdata integer; define facet attribute fdir integer; foreach facet ff do { paramdata += 1; ff.fpdata := paramdata; entsubscr := ff.id; directory_counter += 1; ff.fdir := directory_counter; printf "%8d%8d%8d%8d%8d%8d%8d%8d%8sD%7d\n",entype,paramdata,structure, linefont,level,view,transmat,label,status,directory_counter; directory_counter += 1; printf "%8d%8d%8d%8d%8d%8s%8s%8s%8dD%7d\n",entype,lineweight,colornum, paramcount,form,reserved,reserved,entlabel,entsubscr,directory_counter; }; // Geometry element entype := 402; status := "00000301"; paramcount := ceil(facet_count/10); form := 7; entlabel := " SURFACE"; paramdata += 1; entsubscr := 1; directory_counter += 1; printf "%8d%8d%8d%8d%8d%8d%8d%8d%8sD%7d\n",entype,paramdata,structure, linefont,level,view,transmat,label,status,directory_counter; directory_counter += 1; printf "%8d%8d%8d%8d%8d%8s%8s%8s%8dD%7d\n",entype,lineweight,colornum, paramcount,form,reserved,reserved,entlabel,entsubscr,directory_counter; // Parameter data section parameter_counter := 0; // Vertices entype := 134; foreach vertex vv do { parameter_counter += 1; if vv.pdata != parameter_counter then errprintf "ERROR: bad vertex parameter line number, vertex %d. Is %d, should be %d.\n", vv.id,parameter_counter,vv.pdata; message := sprintf "%d,%13.10f,%13.10f,%13.10f,0;",entype,vv.x,vv.y,vv.z; printf "%-64s%8dP%7d\n",message,vv.dir,parameter_counter; }; // Facets entype := 136; itop := 2; // Linear TRIAngle etyp := "5HLTRIA"; numnodes := 3; foreach facet ff do { parameter_counter += 1; if ff.fpdata != parameter_counter then errprintf "ERROR: bad facet parameter line number, facet %d. Is %d, should be %d.\n", ff.id,parameter_counter,ff.fpdata; message := sprintf "%d,%d,%d,%d,%d,%d,%s;",entype,itop,numnodes, ff.vertex[1].dir,ff.vertex[2].dir,ff.vertex[3].dir,etyp; printf "%-64s%8dP%7d\n",message,ff.fdir,parameter_counter; }; // Geometry element line := sprintf "402,%d,",facet_count; subcount := 0; foreach facet ff do { if subcount == 10 then { parameter_counter += 1; printf "%-64s%8dP%7d\n",line,directory_counter-1,parameter_counter; line := "" ; }; line := sprintf"%s%d,",line,ff.fdir; }; line := sprintf "%s0,0;",line; // sample files ended with 2 extra 0's parameter_counter += 1; printf "%-64s%8dP%7d\n",line,directory_counter-1,parameter_counter; // Terminate section printf "S%07dG%07dD%07dP%07d%40sT0000001\n",start_counter,global_counter, directory_counter,parameter_counter," "; } evolver-2.30c.dfsg/fe/wavefront.cmd0000755000175300017530000000300011410765113017514 0ustar hazelscthazelsct// wavefront.cmd // Surface Evolver command file for producing Wavefront format file // for surface, suitable for feeding to JavaView. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // Usage: // wavefront >>> "filename.obj" // or, for version with normals at vertices, // wavefrontn >>> "filename.obj" define vertex attribute v_num integer wavefront := { // Make sure vertices consecutively numbered new_v_num := 1; foreach vertex vv do { vv.v_num := new_v_num; new_v_num += 1 }; printf "# Wavefront .obj file for %s\n",datafilename; printf "# Produced by the Surface Evolver.\n\n"; foreach vertex vv do printf "v %f %f %f\n",vv.x,vv.y,vv.z; printf "\n"; foreach facet ff do printf "f %g %g %g\n",ff.vertex[1].v_num, ff.vertex[2].v_num, ff.vertex[3].v_num; } // version with normals at vertices wavefrontn := { // Make sure vertices consecutively numbered new_v_num := 1; foreach vertex vv do { vv.v_num := new_v_num; new_v_num += 1 }; printf "# Wavefront .obj file for %s\n",datafilename; printf "# Produced by the Surface Evolver.\n\n"; foreach vertex vv do printf "v %f %f %f\n",vv.x,vv.y,vv.z; printf "\n"; foreach vertex vv do printf "vn %f %f %f\n",vv.vertexnormal[1], vv.vertexnormal[2],vv.vertexnormal[3]; printf "\n"; foreach facet ff do printf "f %g/%g %g/%g %g/%g\n",ff.vertex[1].v_num, ff.vertex[1].v_num, ff.vertex[2].v_num, ff.vertex[2].v_num, ff.vertex[3].v_num, ff.vertex[3].v_num; } evolver-2.30c.dfsg/fe/polyfilm.cmd0000755000175300017530000000222511410765113017344 0ustar hazelscthazelsct// polyfilm.cmd // command to produce Polycut format file // usage: polyfilm >>> "filename" // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke define vertex attribute offnumber integer; // command to print OFF format part (the vertices and facets) verlist := { local offnum; offnum := 0; foreach vertex vv do { printf "%f %f %f \n",x,y,z; vv.offnumber := offnum; offnum += 1; } } // end verlist facelist := { foreach facet ff do { printf "3 "; foreach ff.vertex vv do printf "%g ",vv.offnumber; printf "\n"; } } // end facelist do_off := { printf "OFF\n%g %g %g\n",vertex_count,facet_count,edge_count; verlist; facelist } // end do_off // vect-like file command for triple lines traa := { trcount ::= sum(edges where valence==3,1)} trbb := { foreach edge ee where ee.valence==3 do { foreach ee.vertex vv do printf "%f %f %f ",x,y,z ; printf "\n"; } } trips := { printf "TRIPLE\n"; traa; printf "%g\n",trcount; trbb } polyfilm := {do_off; trips} evolver-2.30c.dfsg/fe/100grain.fe0000644000175300017530000003515411410765113016665 0ustar hazelscthazelsct// 2D Voronoi diagram of 100 cells in unit torus. // With automatic popping and chopping. // Usage: just load in Evolver and do 'g' steps. You can do them // in big chunks, say 10000 at a time. STRING SPACE_DIMENSION 2 TORUS PERIODS 1.0 0.0 0.0 1.0 autopop autochop 0.015000 area_normalization scale 5e-006 fixed VERTICES 1 -0.030008 0.140444 2 -0.053561 0.142482 3 -0.053946 0.142913 4 -0.043174 0.262670 5 -0.030826 0.272623 6 -0.002732 0.263058 7 0.030515 0.186263 8 0.229526 0.892844 9 0.197250 0.830725 10 0.164128 0.845516 11 0.155140 0.891287 12 0.175151 0.917947 13 0.013457 0.904425 14 0.019242 0.903750 15 0.099088 0.822584 16 0.099238 0.797112 17 0.038472 0.769136 18 0.345511 0.867172 19 0.316990 0.895976 20 0.302884 0.933134 21 0.305616 0.970797 22 0.502700 0.988511 23 0.513272 0.938981 24 0.467254 0.873503 25 0.358794 0.760227 26 0.421715 0.745342 27 0.447609 0.623441 28 0.386207 0.547841 29 0.379759 0.543637 30 0.327500 0.540091 31 0.301920 0.560712 32 0.038356 0.911818 33 0.074852 0.917690 34 0.109014 0.031672 35 0.082139 0.059863 36 0.116979 0.117814 37 0.169077 0.147739 38 0.249566 0.102936 39 0.265711 0.083110 40 0.263516 0.061681 41 0.157972 0.022724 42 0.668828 0.702216 43 0.657217 0.709184 44 0.648690 0.726695 45 0.639862 0.849299 46 0.671332 0.879221 47 0.795602 0.874217 48 0.825169 0.863559 49 0.822140 0.854479 50 0.051843 0.463089 51 -0.008885 0.547199 52 -0.009018 0.554344 53 0.064609 0.593106 54 0.195952 0.584518 55 0.715969 0.035473 56 0.720991 0.077810 57 0.759825 0.154628 58 0.803906 0.131127 59 0.832758 0.097005 60 0.840144 0.056532 61 0.828317 0.038418 62 0.793569 0.016632 63 0.866798 0.634036 64 0.874315 0.608119 65 0.810168 0.563294 66 0.722718 0.611416 67 0.686430 0.661210 68 0.674258 0.697043 69 0.736065 0.704853 70 0.799285 0.697204 71 0.128274 0.421961 72 0.151241 0.424619 73 0.270736 0.385808 74 0.252679 0.348670 75 0.163426 0.278401 76 0.120465 0.335411 77 0.923424 0.139123 78 0.957650 0.073212 79 0.935431 0.025421 80 0.671668 0.089803 81 0.620645 0.141321 82 0.653768 0.179083 83 0.714382 0.195884 84 0.749149 0.170561 85 0.897672 0.454661 86 0.900534 0.482589 87 1.053106 0.457102 88 1.015103 0.398474 89 0.966433 0.356965 90 0.960711 0.360602 91 0.913244 0.405778 92 0.153045 0.773386 93 0.209507 0.805331 94 0.238979 0.775913 95 0.165918 0.722482 96 0.102759 1.006934 97 0.147549 0.957675 98 0.132490 0.954404 99 0.034313 0.180479 100 0.029867 0.134185 101 0.025138 0.128845 102 0.432624 0.437038 103 0.434238 0.453639 104 0.529974 0.419934 105 0.533843 0.416214 106 0.542179 0.349833 107 0.493588 0.324922 108 0.020543 0.263635 109 0.087614 0.335281 110 0.593847 0.456960 111 0.654644 0.445022 112 0.648907 0.417561 113 0.615874 0.343447 114 0.589189 0.330605 115 1.035042 0.079044 116 0.836275 0.779507 117 0.870909 0.890102 118 0.879339 0.892276 119 0.945582 0.887996 120 0.943789 0.834657 121 0.906471 0.738313 122 0.904679 0.737826 123 0.471347 0.306478 124 0.393143 0.335959 125 0.833147 0.245908 126 0.830435 0.241033 127 0.716296 0.231275 128 0.730788 0.289488 129 0.734095 0.289792 130 0.770476 0.418024 131 0.763693 0.431898 132 0.818061 0.530796 133 0.106657 0.781975 134 0.203174 0.820975 135 0.075784 0.139866 136 0.068339 0.176548 137 0.305266 0.880414 138 0.354191 0.763363 139 0.265994 0.753654 140 0.246446 0.772642 141 0.159245 0.265211 142 0.138670 0.234806 143 0.861916 0.186777 144 0.174082 0.940799 145 0.279948 1.009278 146 0.273393 0.288033 147 0.271448 0.386266 148 0.370404 0.332813 149 0.361161 0.275986 150 0.323080 0.261652 151 0.829893 0.311422 152 0.858517 0.273344 153 0.588934 0.250300 154 0.520092 0.070927 155 0.454613 0.150364 156 0.470693 0.190299 157 0.271767 0.226323 158 0.265901 0.219436 159 0.556483 0.563129 160 0.546308 0.638700 161 0.631007 0.553525 162 0.821470 0.375664 163 0.770607 0.417231 164 0.758282 0.954513 165 0.780616 0.971172 166 0.977825 0.939932 167 0.979590 0.912835 168 0.138699 0.234511 169 0.235663 0.594778 170 0.592619 0.309939 171 0.398289 0.170783 172 0.409605 0.154346 173 0.311022 0.227565 174 0.672435 0.458037 175 0.695586 0.463138 176 0.728091 0.343070 177 0.145115 0.682641 178 0.099338 0.699126 179 0.719575 0.318548 180 0.058977 0.057878 181 0.895069 0.729562 182 0.624958 0.535856 183 0.575125 0.513126 184 0.554961 0.523143 185 0.524879 0.511588 186 0.522569 0.050489 187 0.977322 0.677757 188 0.964115 0.598347 189 0.695639 0.943549 190 0.082714 0.694723 191 0.218466 0.595225 192 0.206052 0.587706 193 0.832707 0.335253 194 0.021993 0.747569 195 0.937054 0.260196 196 0.867310 0.188595 197 0.680799 1.007336 198 0.656663 0.017536 199 0.461951 0.779741 200 1.023159 0.705505 EDGES 1 1 2 * * 2 2 3 * * 3 3 4 * * 4 4 5 * * 5 5 6 * * 6 6 7 * * 7 7 1 * * 8 8 9 * * 9 9 10 * * 10 10 11 * * 11 11 12 * * 12 12 8 * * 13 13 14 * * 14 14 15 * * 15 15 16 * * 16 16 17 * * 17 17 13 * * 18 18 19 * * 19 19 20 * * 20 20 21 * * 21 21 22 * * 22 22 23 * * 23 23 24 * * 24 24 18 * * 25 25 26 * * 26 26 27 * * 27 27 28 * * 28 28 29 * * 29 29 30 * * 30 30 31 * * 31 31 25 * * 32 10 15 * * 33 14 32 * * 34 32 33 * * 35 33 11 * * 36 34 35 * * 37 35 36 * * 38 36 37 * * 39 37 38 * * 40 38 39 * * 41 39 40 * * 42 40 41 * * 43 41 34 * * 44 42 43 * * 45 43 44 * * 46 44 45 * * 47 45 46 * * 48 46 47 * * 49 47 48 * * 50 48 49 * * 51 49 42 * * 52 50 51 * * 53 51 52 * * 54 52 53 * * 55 53 54 * * 56 54 50 * * 57 55 56 * * 58 56 57 * * 59 57 58 * * 60 58 59 * * 61 59 60 * * 62 60 61 * * 63 61 62 * * 64 62 55 * * 65 63 64 * * 66 64 65 * * 67 65 66 * * 68 66 67 * * 69 67 68 * * 70 68 69 * * 71 69 70 * * 72 70 63 * * 73 71 72 * * 74 72 73 * * 75 73 74 * * 76 74 75 * * 77 75 76 * * 78 76 71 * * 79 77 3 + * 80 2 78 - * 81 78 79 * * 82 79 60 * * 83 59 77 * * 84 80 81 * * 85 81 82 * * 86 82 83 * * 87 83 84 * * 88 84 57 * * 89 56 80 * * 90 85 86 * * 91 86 51 + * 92 50 87 - * 93 87 88 * * 94 88 89 * * 95 89 90 * * 96 90 91 * * 97 91 85 * * 98 92 93 * * 99 93 94 * * 100 94 95 * * 101 95 92 * * 102 96 34 * + 103 41 97 * - 104 97 98 * * 105 98 96 * * 106 7 99 * * 107 99 100 * * 108 100 101 * * 109 101 1 * * 110 102 103 * * 111 103 104 * * 112 104 105 * * 113 105 106 * * 114 106 107 * * 115 107 102 * * 116 108 6 * * 117 5 89 - * 118 88 109 + * 119 109 108 * * 120 110 111 * * 121 111 112 * * 122 112 113 * * 123 113 114 * * 124 114 106 * * 125 105 110 * * 126 101 115 - * 127 115 78 * * 128 116 49 * * 129 48 117 * * 130 117 118 * * 131 118 119 * * 132 119 120 * * 133 120 121 * * 134 121 122 * * 135 122 116 * * 136 107 123 * * 137 123 124 * * 138 124 102 * * 139 68 42 * * 140 116 69 * * 141 125 126 * * 142 126 84 * * 143 83 127 * * 144 127 128 * * 145 128 129 * * 146 129 125 * * 147 130 131 * * 148 131 132 * * 149 132 86 * * 150 85 130 * * 151 92 133 * * 152 133 16 * * 153 9 134 * * 154 134 93 * * 155 135 100 * * 156 99 136 * * 157 136 135 * * 158 137 19 * * 159 18 138 * * 160 138 139 * * 161 139 140 * * 162 140 137 * * 163 87 71 + * 164 76 109 * * 165 75 141 * * 166 141 142 * * 167 142 108 * * 168 143 58 * * 169 126 143 * * 170 33 98 * * 171 97 144 * * 172 144 12 * * 173 40 145 * - 174 145 144 * * 175 146 74 * * 176 73 147 * * 177 147 148 * * 178 148 149 * * 179 149 150 * * 180 150 146 * * 181 8 20 * * 182 137 134 * * 183 129 151 * * 184 151 152 * * 185 152 125 * * 186 153 82 * * 187 81 154 * * 188 154 155 * * 189 155 156 * * 190 156 153 * * 191 146 157 * * 192 157 158 * * 193 158 141 * * 194 159 160 * * 195 160 43 * * 196 67 161 * * 197 161 159 * * 198 91 162 * * 199 162 163 * * 200 163 130 * * 201 164 165 * * 202 165 117 * * 203 47 164 * * 204 79 166 * - 205 166 167 * * 206 167 119 * * 207 118 61 * + 208 136 168 * * 209 168 37 * * 210 36 135 * * 211 31 169 * * 212 169 139 * * 213 138 25 * * 214 153 170 * * 215 170 127 * * 216 171 172 * * 217 172 39 * * 218 38 158 * * 219 157 173 * * 220 173 171 * * 221 111 174 * * 222 174 175 * * 223 175 131 * * 224 163 176 * * 225 176 112 * * 226 95 177 * * 227 177 178 * * 228 178 133 * * 229 170 114 * * 230 113 179 * * 231 179 128 * * 232 145 21 * * 233 13 167 - * 234 166 180 + + 235 180 35 * * 236 96 32 * * 237 70 181 * * 238 181 63 * * 239 161 182 * * 240 182 183 * * 241 183 184 * * 242 184 159 * * 243 185 184 * * 244 183 110 * * 245 104 185 * * 246 66 175 * * 247 174 182 * * 248 140 94 * * 249 180 115 - * 250 172 155 * * 251 154 186 * * 252 186 22 * - 253 181 122 * * 254 121 187 * * 255 187 188 * * 256 188 64 * * 257 150 173 * * 258 149 171 * * 259 46 189 * * 260 189 164 * * 261 156 123 * * 262 190 178 * * 263 177 191 * * 264 191 192 * * 265 192 54 * * 266 53 190 * * 267 169 191 * * 268 179 176 * * 269 162 193 * * 270 193 151 * * 271 17 194 * * 272 194 120 - * 273 165 62 * + 274 103 29 * * 275 28 185 * * 276 152 195 * * 277 195 196 * * 278 196 143 * * 279 189 197 * * 280 197 55 * + 281 124 148 * * 282 147 30 * * 283 198 186 * * 284 80 198 * * 285 24 199 * * 286 199 26 * * 287 160 27 * * 288 199 44 * * 289 45 23 * * 290 198 197 * - 291 4 195 - * 292 193 90 * * 293 192 72 * * 294 65 132 * * 295 52 188 - * 296 187 200 * * 297 200 190 + * 298 142 168 * * 299 200 194 + * 300 196 77 * * FACES 1 -7 -6 -5 -4 -3 -2 -1 2 -12 -11 -10 -9 -8 3 -17 -16 -15 -14 -13 4 -24 -23 -22 -21 -20 -19 -18 5 -31 -30 -29 -28 -27 -26 -25 6 -35 -34 -33 14 -32 10 7 -43 -42 -41 -40 -39 -38 -37 -36 8 -51 -50 -49 -48 -47 -46 -45 -44 9 -56 -55 -54 -53 -52 10 -64 -63 -62 -61 -60 -59 -58 -57 11 -72 -71 -70 -69 -68 -67 -66 -65 12 -78 -77 -76 -75 -74 -73 13 -83 61 -82 -81 -80 2 -79 14 -89 58 -88 -87 -86 -85 -84 15 -97 -96 -95 -94 -93 -92 52 -91 -90 16 -101 -100 -99 -98 17 -105 -104 -103 43 -102 18 7 -109 -108 -107 -106 19 -115 -114 -113 -112 -111 -110 20 -119 -118 94 -117 5 -116 21 -125 113 -124 -123 -122 -121 -120 22 -127 -126 109 1 80 23 -135 -134 -133 -132 -131 -130 -129 50 -128 24 115 -138 -137 -136 25 -140 128 51 -139 70 26 -146 -145 -144 -143 87 -142 -141 27 -150 90 -149 -148 -147 28 98 -154 -153 9 32 15 -152 -151 29 -157 -156 107 -155 30 -162 -161 -160 -159 18 -158 31 118 -164 78 -163 93 32 119 -167 -166 -165 77 164 33 -169 142 88 59 -168 34 35 11 -172 -171 104 -170 35 103 171 -174 -173 42 36 -180 -179 -178 -177 -176 75 -175 37 -182 158 19 -181 8 153 38 146 -185 -184 -183 39 -190 -189 -188 -187 85 -186 40 -193 -192 -191 175 76 165 41 -197 -196 69 139 44 -195 -194 42 150 -200 -199 -198 97 43 -203 49 129 -202 -201 44 -207 131 -206 -205 -204 82 62 45 157 -210 38 -209 -208 46 31 -213 160 -212 -211 47 -215 -214 186 86 143 48 -220 -219 192 -218 40 -217 -216 49 -225 -224 200 147 -223 -222 -221 121 50 101 151 -228 -227 -226 51 215 144 -231 -230 123 -229 52 12 181 20 -232 174 172 53 -236 102 36 -235 -234 205 -233 13 33 54 -238 -237 72 55 197 -242 -241 -240 -239 56 -245 112 125 -244 241 -243 57 -247 222 -246 68 196 239 58 182 154 99 -248 162 59 155 108 126 -249 235 37 210 60 21 -252 -251 188 -250 217 41 173 232 61 238 65 -256 -255 -254 134 -253 62 -257 180 191 219 63 257 220 -258 179 64 203 -260 -259 48 65 190 214 229 124 114 136 -261 66 -266 55 -265 -264 -263 227 -262 67 100 226 263 -267 212 161 248 68 183 -270 -269 199 224 -268 231 145 69 105 236 34 170 70 127 81 204 234 249 71 17 233 206 132 -272 -271 72 202 130 207 63 -273 73 245 -275 28 -274 111 74 169 -278 -277 -276 185 141 75 140 71 237 253 135 76 201 273 64 -280 -279 260 77 225 122 230 268 78 138 110 274 29 -282 177 -281 79 -284 84 187 251 -283 80 24 159 213 25 -286 -285 81 -288 286 26 -287 195 45 82 279 -290 283 252 22 -289 47 259 83 -292 270 184 276 -291 4 117 95 84 -293 265 56 92 163 73 85 284 290 280 57 89 86 247 240 244 120 221 87 246 223 148 -294 67 88 242 194 287 27 275 243 89 292 96 198 269 90 266 -297 -296 255 -295 54 91 156 208 -298 167 116 6 106 92 16 271 -299 297 262 228 152 93 -300 278 168 60 83 94 293 74 176 282 30 211 267 264 95 300 79 3 291 277 96 193 166 298 209 39 218 97 272 133 254 296 299 98 149 91 53 295 256 66 294 99 288 46 289 23 285 100 258 216 250 189 261 137 281 178 BODIES 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 11 11 12 12 13 13 14 14 15 15 16 16 17 17 18 18 19 19 20 20 21 21 22 22 23 23 24 24 25 25 26 26 27 27 28 28 29 29 30 30 31 31 32 32 33 33 34 34 35 35 36 36 37 37 38 38 39 39 40 40 41 41 42 42 43 43 44 44 45 45 46 46 47 47 48 48 49 49 50 50 51 51 52 52 53 53 54 54 55 55 56 56 57 57 58 58 59 59 60 60 61 61 62 62 63 63 64 64 65 65 66 66 67 67 68 68 69 69 70 70 71 71 72 72 73 73 74 74 75 75 76 76 77 77 78 78 79 79 80 80 81 81 82 82 83 83 84 84 85 85 86 86 87 87 88 88 89 89 90 90 91 91 92 92 93 93 94 94 95 95 96 96 97 97 98 98 99 99 100 100 read clipped // for clipped unit cell display evolver-2.30c.dfsg/fe/equi2.cmd0000755000175300017530000000157111410765113016541 0ustar hazelscthazelsct// equi2.cmd -- to color edges subject to equiangulation // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // useage: showequi showequi := { local ecount, al,bl,cl,dl,el,nn; ecount := 0; foreach edge ee do { al := ee.length; nn := 1; foreach ee.facet ff do { foreach ff.edge fe do { if ( fe.id != ee.id ) then { if ( nn == 1 ) then bl := fe.length else if ( nn == 2 ) then cl := fe.length else if ( nn == 3 ) then dl := fe.length else if ( nn == 4 ) then el := fe.length; nn := nn + 1; } } }; if ( nn == 5 ) then if (bl^2+cl^2-al^2)/bl/cl + (dl^2+el^2-al^2)/dl/el < 0 then { set ee color green; ecount := 1 + ecount } }; printf "Found %g edges.\n",ecount; show_expr edges where 1 } evolver-2.30c.dfsg/fe/catman.fe0000644000175300017530000000451011410765113016577 0ustar hazelscthazelsct// catman.fe // Evolver datafile for catenoid with parameters as in Manual tutorial. PARAMETER radius = 1 // ring radius; runtime adjustable PARAMETER height = 0.55 // ring height; runtime adjustable boundary 1 parameters 1 // upper ring; p1 is ring parameter x1: radius * cos(p1) x2: radius * sin(p1) x3: height boundary 2 parameters 1 // lower ring x1: radius * cos(p1) x2: radius * sin(p1) x3: -height vertices /* given by parameter value on a boundary */ 1 0*pi/3 boundary 1 fixed 2 1*pi/3 boundary 1 fixed 3 2*pi/3 boundary 1 fixed 4 3*pi/3 boundary 1 fixed 5 4*pi/3 boundary 1 fixed 6 5*pi/3 boundary 1 fixed 7 0*pi/3 boundary 2 fixed 8 1*pi/3 boundary 2 fixed 9 2*pi/3 boundary 2 fixed 10 3*pi/3 boundary 2 fixed 11 4*pi/3 boundary 2 fixed 12 5*pi/3 boundary 2 fixed edges /* given by endpoint vertices */ 1 1 2 boundary 1 fixed 2 2 3 boundary 1 fixed 3 3 4 boundary 1 fixed 4 4 5 boundary 1 fixed 5 5 6 boundary 1 fixed 6 6 1 boundary 1 fixed 7 7 8 boundary 2 fixed 8 8 9 boundary 2 fixed 9 9 10 boundary 2 fixed 10 10 11 boundary 2 fixed 11 11 12 boundary 2 fixed 12 12 7 boundary 2 fixed 13 1 7 14 2 8 15 3 9 16 4 10 17 5 11 18 6 12 faces /* given by oriented edge list */ 1 1 14 -7 -13 2 2 15 -8 -14 3 3 16 -9 -15 4 4 17 -10 -16 5 5 18 -11 -17 6 6 13 -12 -18 read // Demonstrating saddle point due to triangulation arrangement. // First setting parameters to stable values. gogo := { g; u; r; g 50; // at this point have nearly a saddle point g 200; // triangulation twists around to lower energy } // Faster version of the above using conjugate gradient gogo2 := { g; u; r; U; g 25; // at this point have nearly a saddle point g 35; } // High accuracy evolution, using higher-order Lagrange elements. gogo3 := { u; rmax := cosh(height); recalc; r; g 5; hessian; r; g 5; hessian; lagrange 2; g 5; hessian; lagrange 4; g 5; hessian; lagrange 6; g 5; hessian; lagrange 8; g 5; hessian; true_area := 2*pi*(height + 0.5*sinh(2*height)); printf"Difference from true area: %g\n",total_area - true_area; } evolver-2.30c.dfsg/fe/multiplicate.cmd0000755000175300017530000002205011410765113020203 0ustar hazelscthazelsct// multiplicate.cmd // Surface Evolver script to create datafile with surface duplicated // according to view transforms in effect. Writes datafile to stdout. // Does not create new elements in current surface, since together // with view transforms, that would result in quadratic explosion. // WARNING: This loses all element attributes in the output datafile. // But does preserve "edgetype" attribute // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // usage: multiplicate >>> "newdatafile.fe" define edge attribute edgetype integer // in case doesn't exist eps := 1e-4 // tolerance for identifying vertices // Hash table for finding identified vertices. Generates hash value // from skew plane. mskew1 := 0.361234123413728768 mskew2 := 0.725423451231725277 mskew3 := 0.5227723243451234514 vertex_hash_init := { vertex_hash_table_size := vertex_count*transform_count; define vertex_hash_table integer[vertex_hash_table_size]; define vertex_hash_chain integer[vertex_hash_table_size]; define newcoord real[vertex_hash_table_size][3]; vertex_entry_count := 0; } // deallocate memory vertex_hash_end := { define vertex_hash_table integer[0]; define vertex_hash_chain integer[0]; define newcoord real[0][0]; } // Returns sequential number of found or new vertex. function integer vertex_hash_add(real xx, real yy, real zz) { local hashval,hashspot; local vnum,dist; hashval := floor((mskew1*xx + mskew2*yy + mskew3*zz)/eps); hashspot := hashval imod vertex_hash_table_size; // See if there for ( vnum := vertex_hash_table[hashspot] ; vnum != 0 ; vnum := vertex_hash_chain[vnum] ) { dist := sqrt((xx-newcoord[vnum][1])^2 + (yy-newcoord[vnum][2])^2 + (zz-newcoord[vnum][3])^2); if dist < eps then return vnum; }; // Not there, so add vertex_entry_count += 1; vnum := vertex_entry_count; vertex_hash_chain[vnum] := vertex_hash_table[hashspot]; vertex_hash_table[hashspot] := vnum; newcoord[vnum][1] := xx; newcoord[vnum][2] := yy; newcoord[vnum][3] := zz; return vnum; } // Hash table for finding identified edges. edge_hash_init := { edge_hash_table_size := edge_count*transform_count; define edge_hash_table integer[edge_hash_table_size]; define edge_hash_chain integer[edge_hash_table_size]; define new_edge_verts integer[edge_hash_table_size][2]; edge_entry_count := 0; } // deallocate memory edge_hash_end := { define edge_hash_table integer[0]; define edge_hash_chain integer[0]; define new_edge_verts integer[0][0]; } // Edge hasher. Returns sequential number (signed) of found or new edge. function integer edge_hash_add(integer tailv, integer headv) { local temp,signflag; local hashval,hashspot; local edgenum; // get vertices in canonical order if tailv > headv then { temp := tailv; tailv := headv; headv := temp; signflag := -1; } else signflag := 1; hashval := tailv*737 + headv; hashspot := hashval imod edge_hash_table_size; // See if there for ( edgenum := edge_hash_table[hashspot] ; edgenum != 0 ; edgenum := edge_hash_chain[edgenum] ) { if (tailv == new_edge_verts[edgenum][1]) and (headv == new_edge_verts[edgenum][2]) then return signflag*edgenum; }; // Not there, so add edge_entry_count += 1; edgenum := edge_entry_count; edge_hash_chain[edgenum] := edge_hash_table[hashspot]; edge_hash_table[hashspot] := edgenum; new_edge_verts[edgenum][1] := tailv; new_edge_verts[edgenum][2] := headv; return signflag*edgenum; } multiplicate := { local tcount,high_vertex,vx,vy,vz; local high_edge,thistail,thishead,fstride,tdet,edge1,edge2,edge3; list topinfo; define vertex attribute tx real[transform_count]; define vertex attribute ty real[transform_count]; define vertex attribute tz real[transform_count]; define vertex attribute valias integer[transform_count]; define edge attribute ehead integer[transform_count]; define edge attribute etail integer[transform_count]; define edge attribute ealias integer[transform_count]; printf "\nVertices\n"; vertex_hash_init; tcount := 1; high_vertex := 0; while ( tcount <= transform_count ) do { foreach vertex vv do { vx := view_transforms[tcount][1][1]*vv.x + view_transforms[tcount][1][2]*vv.y + view_transforms[tcount][1][3]*vv.z + view_transforms[tcount][1][4]*1; vy := view_transforms[tcount][2][1]*vv.x + view_transforms[tcount][2][2]*vv.y + view_transforms[tcount][2][3]*vv.z + view_transforms[tcount][2][4]*1; vz := view_transforms[tcount][3][1]*vv.x + view_transforms[tcount][3][2]*vv.y + view_transforms[tcount][3][3]*vv.z + view_transforms[tcount][3][4]*1; vv.tx[tcount] := vx; vv.ty[tcount] := vy; vv.tz[tcount] := vz; // search for alias vv.valias[tcount] := vertex_hash_add(vx,vy,vz); if vv.valias[tcount] > high_vertex then { printf "%d %18.15f %18.15f %18.15f ",vv.valias[tcount], vx,vy,vz; printf "\n"; high_vertex := vv.valias[tcount]; }; }; tcount += 1; }; vertex_hash_end; printf "\nEdges\n"; tcount := 1; edge_hash_init; high_edge := 0; while ( tcount <= transform_count ) do { foreach edge ee do { thistail := vertex[ee.vertex[1].id].valias[tcount]; thishead := vertex[ee.vertex[2].id].valias[tcount]; ee.ehead[tcount] := thishead; ee.etail[tcount] := thistail; // search for aliases ee.ealias[tcount] := edge_hash_add(thistail,thishead); if ( abs(ee.ealias[tcount]) > high_edge ) then { printf "%d %d %d edgetype %d",abs(ee.ealias[tcount]), minimum(thistail,thishead),maximum(thistail,thishead),edgetype; if ee.bare then printf " bare "; printf "\n"; high_edge := abs(ee.ealias[tcount]); }; }; tcount += 1; }; edge_hash_end; printf "\nFaces\n"; fstride := max(facet,id); tcount := 1; while ( tcount <= transform_count ) do { tdet := view_transforms[tcount][1][1]* (view_transforms[tcount][2][2]*view_transforms[tcount][3][3] - view_transforms[tcount][3][2]*view_transforms[tcount][2][3]) - view_transforms[tcount][1][2]* (view_transforms[tcount][2][1]*view_transforms[tcount][3][3] - view_transforms[tcount][3][1]*view_transforms[tcount][2][3]) + view_transforms[tcount][1][3]* (view_transforms[tcount][2][1]*view_transforms[tcount][3][2] - view_transforms[tcount][3][1]*view_transforms[tcount][2][2]); foreach facet ff do { edge1 := edge[ff.edge[1].id].ealias[tcount]; edge2 := edge[ff.edge[2].id].ealias[tcount]; edge3 := edge[ff.edge[3].id].ealias[tcount]; if ( view_transform_swap_colors[tcount] != (tdet < 0.0) ) then // inverted printf "%d %d %d %d\n",ff.id + (tcount-1)*fstride, ((ff.edge[3].oid > 0) ? -edge3 : edge3), ((ff.edge[2].oid > 0) ? -edge2 : edge2), ((ff.edge[1].oid > 0) ? -edge1 : edge1) else printf "%d %d %d %d\n",ff.id + (tcount-1)*fstride, ((ff.edge[1].oid > 0) ? edge1 : -edge1), ((ff.edge[2].oid > 0) ? edge2 : -edge2), ((ff.edge[3].oid > 0) ? edge3 : -edge3); }; tcount += 1; }; // not listing bottominfo on purpose; too much extraneous stuff // free attribute storage define vertex attribute tx real[0]; define vertex attribute ty real[0]; define vertex attribute tz real[0]; define vertex attribute valias integer[0]; define edge attribute ehead integer[0]; define edge attribute etail integer[0]; define edge attribute ealias integer[0]; } aa := 1 pview := { printf "%f %f %f %f\n",view_transforms[aa][1][1],view_transforms[aa][1][2], view_transforms[aa][1][3],view_transforms[aa][1][4]; printf "%f %f %f %f\n",view_transforms[aa][2][1],view_transforms[aa][2][2], view_transforms[aa][2][3],view_transforms[aa][2][4]; printf "%f %f %f %f\n",view_transforms[aa][3][1],view_transforms[aa][3][2], view_transforms[aa][3][3],view_transforms[aa][3][4]; printf "%f %f %f %f\n",view_transforms[aa][4][1],view_transforms[aa][4][2], view_transforms[aa][4][3],view_transforms[aa][4][4]; } // Paste things together that didn't get pasted, by finding edges that // come out of vertices in the same direction. paste := { local paste_count; paste_count := 0; // track how many found, so know when no more left foreach vertex vv do { foreach vv.edge ee do { foreach vv.edge eee do { if ee.id == eee.id then continue; if (ee.x-eee.x)^2 + (ee.y-eee.y)^2 + (ee.z-eee.z)^2 < eps^2 then { edge_merge(ee.oid,eee.oid); paste_count += 1; break; } } } }; printf "Edges pasted: %d\n",paste_count; } // usage: Set view transforms as desired, then do // multiplicate >>> "newdatafile.fe" evolver-2.30c.dfsg/fe/metric.fe0000644000175300017530000000073411410765113016623 0ustar hazelscthazelsct// metric.fe // Demonstration of background metric with strings // Metric turns punctured plane into catenoid. Initial string // shrinks to loop around throat. Note that graphing uses just // the plane coordinates without metric. SPACE_DIMENSION 2 STRING #define G11 (1 + 1/(x^2 + y^2))^2 METRIC G11 0 0 G11 vertices 1 1 0 2 0 2 3 -2 0 4 0 -1 edges 1 1 2 2 2 3 3 3 4 4 4 1 read // Typical evolution gogo := { g 5; r; g 5; r; g 5; r; g 5; } evolver-2.30c.dfsg/fe/slice2.cmd0000755000175300017530000000435611410765113016701 0ustar hazelscthazelsct// slice2.cmd --- produce intersection of surface with plane // plane eq: aa*x + bb*y + cc*z = dd // output: length of slice, and area inside slice. // Does not modify surface. // Note all area inside slice is counted as positive! // Do not to slice exactly through vertices!! Avoid special // values for dd like 0 or .5! // Fixed to do oriented facets of a particular body. // Usage: set plane coefficients, then do slice2(body number). // Results: Intersection length printed out, left in variable lensum. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke procedure slice2(integer body_num) := { local any,xx1,yy1,zz1,xx2,yy2,zz2; local denom,lambda,zaa,zbb,darea; lensum := 0; areasum := 0; xb := 0; yb := 0; zb := 0; // just for declaration before use. foreach body[body_num].facet ff do { any := 0; foreach ff.edge ee do { xx1 := ee.vertex[1].x; yy1 := ee.vertex[1].y; zz1 := ee.vertex[1].z; xx2 := ee.vertex[2].x; yy2 := ee.vertex[2].y; zz2 := ee.vertex[2].z; denom := aa*(xx1-xx2)+bb*(yy1-yy2)+cc*(zz1-zz2); if ( denom != 0.0 ) then { lambda := (dd-aa*xx2-bb*yy2-cc*zz2)/denom; if ( (lambda >= 0) and (lambda <= 1) ) then { xa := xb; ya := yb; za := zb; xb := lambda*xx1+(1-lambda)*xx2; yb := lambda*yy1+(1-lambda)*yy2; zb := lambda*zz1+(1-lambda)*zz2; any := any+1; } } } ; if any == 2 then { local triple; dx := xa-xb; dy := ya-yb; dz := za - zb; lensum := lensum + sqrt(dx^2+dy^2+dz^2); zaa := za - dd/cc; zbb := zb - dd/cc; fx := ff.x; fy := ff.y; fz := ff.z; triple := fx*(dy*cc-dz*bb)+fy*(dz*aa-dx*cc)+fz*(dx*bb-dy*aa); darea := (xa*yb-xb*ya)/2; if ( triple > 0 ) then areasum := areasum + darea else areasum := areasum - darea; } }; areasum := areasum*sqrt(aa*aa+bb*bb+cc*cc)/cc; printf "Circumference: %18.15g Area: %18.15g\n",lensum,areasum; } // end slice evolver-2.30c.dfsg/fe/quadint2.cmd0000755000175300017530000001557011410765113017247 0ustar hazelscthazelsct// quadint2.cmd // For detecting intersection of quadratic facets in 3D // Not suitable for torus model. // Needs quadbbox.cmd to be loaded first. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // Usage: quadmeet // Prints id numbers of pairs of intersecting facets and colors them green. a_refine_order := 4 b_refine_order := 5 eps := 1e-10 // tolerance for being equal to zero ax := 0; bx := 0; cx := 0; ay := 0; by := 0; cy := 0; az := 0; bz := 0; cz := 0; px := 0; py := 0; pz := 0; qx := 0; qy := 0; qz:= 0; uuu := 0; vvv := 0; xa1 := 0; xa2 := 0; xa3 := 0; xa4 := 0; xa5 := 0; xa6 := 0 ya1 := 0; ya2 := 0; ya3 := 0; ya4 := 0; ya5 := 0; ya6 := 0 za1 := 0; za2 := 0; za3 := 0; za4 := 0; za5 := 0; za6 := 0 intersection_found := 0; // detection of triangle and edge intersection detect := { local acx,acy,acz,bcx,bcy,bcz,qpx,qpy,qpz,qcx,qcy,qcz; local denom,lambda,mu,sigma; do { acx := ax-cx; acy := ay-cy; acz := az-cz; bcx := bx-cx; bcy := by-cy; bcz := bz-cz; qpx := qx-px; qpy := qy-py; qpz := qz-pz; qcx := qx-cx; qcy := qy-cy; qcz := qz-cz; denom := qpx*(acy*bcz-acz*bcy) - qpy*(acx*bcz-acz*bcx) + qpz*(acx*bcy-acy*bcx); if ( abs(denom) < eps ) then break; lambda := (qpx*(qcy*bcz-qcz*bcy) - qpy*(qcx*bcz-qcz*bcx) + qpz*(qcx*bcy-qcy*bcx))/denom; if ( lambda <= eps ) then break; mu := (qpx*(acy*qcz-acz*qcy) - qpy*(acx*qcz-acz*qcx) + qpz*(acx*qcy-acy*qcx))/denom; if ( mu <= eps ) then break; if ( lambda + mu > 1-eps ) then break; sigma := (qcx*(acy*bcz-acz*bcy) - qcy*(acx*bcz-acz*bcx) + qcz*(acx*bcy-acy*bcx))/denom; if ( (sigma <= eps) or (sigma > 1-eps) ) then break; // now have intersection intersection_found := 1; } while 0; // so we could use break } // end detect calccoords := { xx := 0.5*(uuu+vvv-1)*(uuu+vvv-2)*xa1 + uuu*(2-uuu-vvv)*xa2 + 0.5*uuu*(uuu-1)*xa3 + vvv*(2-uuu-vvv)*xa4 + uuu*vvv*xa5 + 0.5*vvv*(vvv-1)*xa6; yy := 0.5*(uuu+vvv-1)*(uuu+vvv-2)*ya1 + uuu*(2-uuu-vvv)*ya2 + 0.5*uuu*(uuu-1)*ya3 + vvv*(2-uuu-vvv)*ya4 + uuu*vvv*ya5 + 0.5*vvv*(vvv-1)*ya6; zz := 0.5*(uuu+vvv-1)*(uuu+vvv-2)*za1 + uuu*(2-uuu-vvv)*za2 + 0.5*uuu*(uuu-1)*za3 + vvv*(2-uuu-vvv)*za4 + uuu*vvv*za5 + 0.5*vvv*(vvv-1)*za6; } // end calccoords refine_and_test := { // Test intersections of planar subtriangles and sub edges adelta := 2/a_refine_order; bdelta := 2/b_refine_order; ua := 0; while ( ua < 1.9999 ) do { va := 0; while ( va < 1.9999 - ua ) do { uuu := ua; vvv := va; calccoords; ax := xx; ay := yy; az := zz; uuu := ua+adelta; vvv := va; calccoords; bx := xx; by := yy; bz := zz; uuu := ua; vvv := va+adelta; calccoords; cx := xx; cy := yy; cz := zz; ub := 0; while ( ub < 1.9999 ) do { vb := 0; while ( vb < 1.9999 - ub ) do { uuu := ub; vvv := vb; calccoords; px := xx; py := yy; pz := zz; uuu := ub+bdelta; vvv := vb; calccoords; qx := xx; qy := yy; qz := zz; detect; if intersection_found then break; uuu := ub; vvv := vb+bdelta; calccoords; px := xx; py := yy; pz := zz; detect; if intersection_found then break; uuu := ub; vvv := vb; calccoords; qx := xx; qy := yy; qz := zz; detect; if intersection_found then break; vb := vb + bdelta; }; if intersection_found then break; ub := ub + bdelta; }; if intersection_found then break; va := va + adelta; }; if intersection_found then break; ua := ua + adelta; } } // end refine_and_test quadmeet := { intersect_total := 0; fboxes; // find facet bounding boxes // test all facet pairs foreach facet fa do { foreach facet fb where fb.id > fa.id do { // check adjacency if ( fa.vertex[1].id == fb.vertex[1].id ) then continue; if ( fa.vertex[2].id == fb.vertex[1].id ) then continue; if ( fa.vertex[3].id == fb.vertex[1].id ) then continue; if ( fa.vertex[1].id == fb.vertex[2].id ) then continue; if ( fa.vertex[2].id == fb.vertex[2].id ) then continue; if ( fa.vertex[3].id == fb.vertex[2].id ) then continue; if ( fa.vertex[1].id == fb.vertex[3].id ) then continue; if ( fa.vertex[2].id == fb.vertex[3].id ) then continue; if ( fa.vertex[3].id == fb.vertex[3].id ) then continue; // first, check bounding boxes if ( fb.fbox[1] >= fa.fbox[2] or fb.fbox[2] <= fa.fbox[1] or fb.fbox[3] >= fa.fbox[4] or fb.fbox[4] <= fa.fbox[3] or fb.fbox[5] >= fa.fbox[6] or fb.fbox[6] <= fa.fbox[5] ) then continue; // extract coordinates xa1 := fa.edge[1].vertex[1].x; xa2 := fa.edge[1].vertex[3].x; xa3 := fa.edge[1].vertex[2].x; xa4 := fa.edge[3].vertex[3].x; xa5 := fa.edge[2].vertex[3].x; xa6 := fa.edge[2].vertex[2].x; ya1 := fa.edge[1].vertex[1].y; ya2 := fa.edge[1].vertex[3].y; ya3 := fa.edge[1].vertex[2].y; ya4 := fa.edge[3].vertex[3].y; ya5 := fa.edge[2].vertex[3].y; ya6 := fa.edge[2].vertex[2].y; za1 := fa.edge[1].vertex[1].z; za2 := fa.edge[1].vertex[3].z; za3 := fa.edge[1].vertex[2].z; za4 := fa.edge[3].vertex[3].z; za5 := fa.edge[2].vertex[3].z; za6 := fa.edge[2].vertex[2].z; xb1 := fb.edge[1].vertex[1].x; xb2 := fb.edge[1].vertex[3].x; xb3 := fb.edge[1].vertex[2].x; xb4 := fb.edge[3].vertex[3].x; xb5 := fb.edge[2].vertex[3].x; xb6 := fb.edge[2].vertex[2].x; yb1 := fb.edge[1].vertex[1].y; yb2 := fb.edge[1].vertex[3].y; yb3 := fb.edge[1].vertex[2].y; yb4 := fb.edge[3].vertex[3].y; yb5 := fb.edge[2].vertex[3].y; yb6 := fb.edge[2].vertex[2].y; zb1 := fb.edge[1].vertex[1].z; zb2 := fb.edge[1].vertex[3].z; zb3 := fb.edge[1].vertex[2].z; zb4 := fb.edge[3].vertex[3].z; zb5 := fb.edge[2].vertex[3].z; zb6 := fb.edge[2].vertex[2].z; refine_and_test; if intersection_found then { intersect_total := intersect_total + 1; set fa color green; set fb color green; printf "Facets %g and %g intersect.\n",fa.id,fb.id; } } }; printf "Intersections found: %g\n",intersect_total; } // end quadmeet evolver-2.30c.dfsg/fe/gaussequi.cmd0000755000175300017530000000327711410765113017527 0ustar hazelscthazelsct// gaussequi.cmd // Equiangulation using Gauss map for swap criterion. // But has problems; can produce zero area facets. Also, swapping an // edge changes the Gauss map, so maybe things don't improve. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // Usage: gaussequi gaussequi := { local ax,ay,az,bx,by,bz,ffnum,cx,cy,cz,dx,dy,dz,aa,bb,cc,dd,ee; swapcount := 0; foreach edge eee where not fixed and valence == 2 do { ax := eee.vertex[1].vertexnormal[1]; ay := eee.vertex[1].vertexnormal[2]; az := eee.vertex[1].vertexnormal[3]; bx := eee.vertex[2].vertexnormal[1]; by := eee.vertex[2].vertexnormal[2]; bz := eee.vertex[2].vertexnormal[3]; ffnum := eee.facet[1].id; foreach facet[ffnum].vertex vv do { if vv.id != eee.vertex[1].id and vv.id != eee.vertex[2].id then { vvnum := vv.id; break; } }; cx := vertex[vvnum].vertexnormal[1]; cy := vertex[vvnum].vertexnormal[2]; cz := vertex[vvnum].vertexnormal[3]; ffnum := eee.facet[2].id; foreach facet[ffnum].vertex vv do { if vv.id != eee.vertex[1].id and vv.id != eee.vertex[2].id then { vvnum := vv.id; break; } }; dx := vertex[vvnum].vertexnormal[1]; dy := vertex[vvnum].vertexnormal[2]; dz := vertex[vvnum].vertexnormal[3]; aa := (ax-bx)^2+(ay-by)^2+(az-bz)^2; bb := (ax-cx)^2+(ay-cy)^2+(az-cz)^2; cc := (bx-cx)^2+(by-cy)^2+(bz-cz)^2; dd := (ax-dx)^2+(ay-dy)^2+(az-dz)^2; ee := (bx-dx)^2+(by-dy)^2+(bz-dz)^2; if ( (bb+cc-aa)*sqrt(dd*ee) + (dd+ee-aa)*sqrt(bb*cc) < 0 ) then { edgeswap eee; swapcount += 1; }; }; printf "Edges gaussequi swapped: %d\n",swapcount; } evolver-2.30c.dfsg/fe/rotate.cmd0000755000175300017530000000073611410765113017014 0ustar hazelscthazelsct// rotate.cmd // Surface Evolver procedure to rotate unfixed vertices by angle about z axis. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // Usage: rotate(angle) // where angle is in radians. procedure rotate(real roth) := { local sss,ccc,rrad,olds; sss := sin(roth); ccc := cos(roth); foreach vertex vv where not fixed do { rrad := vv.x^2+vv.y^2; oldx := vv.x; set vv x vv.x*ccc-vv.y*sss; set vv y oldx*sss+vv.y*ccc; } } evolver-2.30c.dfsg/fe/reorder.cmd0000755000175300017530000000107011410765113017150 0ustar hazelscthazelsct// reorder.cmd define vertex attribute vertex_order_key real define edge attribute edge_order_key real define facet attribute facet_order_key real define body attribute body_order_key real define facetedge attribute facetedge_order_key real reorder := { set vertex vertex_order_key x+y+z; set edge ee edge_order_key min(ee.vertex,vertex_order_key); set facetedge fe facetedge_order_key fe.edge[1].edge_order_key; set facet ff facet_order_key min(ff.vertex,vertex_order_key); set body bb body_order_key min(bb.facet,facet_order_key); reorder_storage; } evolver-2.30c.dfsg/fe/knotty.fe0000644000175300017530000000767111410765113016677 0ustar hazelscthazelsct// knotty.fe // Evolver data for 3,2 torus knot (trefoil) with various knot energies. // You need to set one type of energy and one type of length constraint; // see the set_* commands in the "read" section of the datafile. // Not all combinations are workable. string // this is a 1D surface parameter maj_r = 2 // torus major radius parameter min_r = 0.5 // torus minor radius boundary 1 parameter 1 // parametric curve around torus x1: cos(2*p1)*(maj_r + min_r*cos(3*p1)) x2: sin(2*p1)*(maj_r + min_r*cos(3*p1)) x3: min_r*sin(3*p1) // Here follow six different types of energy. Switch between them // with the set_* commands in the bottom section of this datafile. // elastic bending energy, i.e. squared curvature quantity bending_energy ENERGY modulus 0 method sqcurve_string global // conducting wire vertex energy quantity conduct_energy ENERGY modulus 1 method knot_energy global // insulating wire vertex energy quantity insul_energy ENERGY modulus 0 method edge_knot_energy global // Greg Buck's 1/(d1+d2+d3+d4-2(L1+L2)) energy quantity buck_energy ENERGY modulus 0 method buck_knot_energy global // have to start with modulus 0 since edges too long at start */ // Greg Buck's normal projection energy quantity proj_energy ENERGY modulus 0 method proj_knot_energy global // Peter Doyle's conformal circle energy quantity circle_energy ENERGY modulus 0 method circle_knot_energy global // Here follow three ways to control the length of edges. // Switch between them with the set_* commands below. // Elastic stretching energy to encourage edges to all have the same length. // Use "hooke_length := value" command to set hooke_length to desired // edge length quantity hooke ENERGY modulus 0 method hooke_energy global // Individually set edge equilibrium lengths. // Use "set edge hooke_size expr" to set desired edge lengths // When used, the modulus needs to be set high, like 1000 or 10000, // to provide decent stiffness. define edge attribute hooke_size real quantity hooke2 ENERGY modulus 0 method hooke2_energy global // to keep total length fixed quantity knot_length FIXED = 24 modulus 1 method edge_tension global vertices 1 0.0*pi boundary 1 2 0.2*pi boundary 1 3 0.4*pi boundary 1 4 0.6*pi boundary 1 5 0.8*pi boundary 1 6 1.0*pi boundary 1 7 1.2*pi boundary 1 8 1.4*pi boundary 1 9 1.6*pi boundary 1 10 1.8*pi boundary 1 edges 1 1 2 boundary 1 2 2 3 boundary 1 3 3 4 boundary 1 4 4 5 boundary 1 5 5 6 boundary 1 6 6 7 boundary 1 7 7 8 boundary 1 8 8 9 boundary 1 9 9 10 boundary 1 10 10 1 boundary 1 read // some initialization commands r 2 // refining twice produces 40 vertices unset vertices boundary 1 // release from parametric curve unset edges boundary 1 set edge tension 0 // get rid of default tension energy set edge color white where id <= edge_count/2 // dashed coloring // Set all quantity moduli to zero zero_moduli := { bending_energy.modulus := 0; conduct_energy.modulus := 0; insul_energy.modulus := 0; buck_energy.modulus := 0; proj_energy.modulus := 0; circle_energy.modulus := 0; } // Set up individual types of energy set_bending := { zero_moduli; bending_energy.modulus := 1; } set_conduct := { zero_moduli; conduct_energy.modulus := 1; } set_insul := { zero_moduli; insul_energy.modulus := 1; } set_buck := { zero_moduli; buck_energy.modulus := 1; } set_proj := { zero_moduli; proj_energy.modulus := 1; } set_circle := { zero_moduli; circle_energy.modulus := 1; } // Set up type of length constraint hooke_length := total_length/edge_count; set edge hooke_size total_length/edge_count; set_length := { fix knot_length; hooke.modulus := 0; hooke2.modulus := 0; } set_hooke := { unfix knot_length; hooke.modulus := 1000; hooke2.modulus := 0; } set_hooke2 := { unfix knot_length; hooke.modulus := 0; hooke2.modulus := 1000; } // Sample evolution gogo := { g 50; U; g 50; } evolver-2.30c.dfsg/fe/zebra.cmd0000755000175300017530000000301111410765113016606 0ustar hazelscthazelsct// zebra.cmd // Evolver command to alternately color string edges black and white // Usage: Set color1 and color2 to the two colors you want, if the // default black and white are not suitable. Then run "zebra". // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke color1 := white color2 := black zebra := { local ecount,first_e,v_id,zcolor,zinx,e_id; ecount := 0; // for safety // Look for valence 2 vertices with same color edges and // propagate zebra coloring in both directions. foreach vertex vv where valence==2 and vv.edge[1].color == vv.edge[2].color do { first_e := vv.edge[1].oid; v_id := vv.id; zcolor := color1; for ( zinx := 1 ; zinx <= 2 ; zinx += 1 ) { // follow connected edges e_id := first_e; do { set edge[e_id] color zcolor; v_id := edge[e_id].vertex[2].id; if vertex[v_id].valence != 2 then break; if vertex[v_id].edge[1].oid == -e_id then e_id := vertex[v_id].edge[2].oid else e_id := vertex[v_id].edge[1].oid; zcolor := (zcolor == color1) ? color2 : color1; ecount := ecount + 1; } while ( (v_id != vv.id) and (ecount <= edge_count) ); if ( v_id == vv.id ) then break; // don't need to go around the other way // set things up to go around other way next time through the loop. v_id := vv.id; zcolor := color2; first_e := vv.edge[2].oid; } } // end valence 2 vertex loop } // end zebra evolver-2.30c.dfsg/fe/mylarcube.fe0000644000175300017530000001463611410765113017331 0ustar hazelscthazelsct// mylarcube.fe // By Kenneth Brakke, Feb. 17, 2002 // Evolver data for mylar cube. Idea is to deform cube // without stretching to maximize volume. Implemented with // relaxed_elastic_A method, which permits a little stretching, but // shrinks freely. Can crank up the elastic modulus to get // effective no stretching. The relaxed_elastic_A method calculates // the Cauchy-Green strain tensor of a facet, and then uses // Poisson's ratio to get the stress tensor. Then it calculates // the stress eigenvalues, and assigns energy proportional to the // square of positive eigenvalues, but zero energy for negative // eigenvalues. So it permits effective wrinkling. The original // unstretched shape of a facet is remembered in an extra facet // attribute named form_factors, which has the components of the // Gram matrix of the unstretched facet. This datafile assumes the // initial surface is unstretched, and sets the form_factors in // the "read" section below. To recompute form factors after refining, // there is also a vertex attribute that holds the unstretched // vertex coordinates. The "r" command has been redefined in the // "read" section to properly adjust the form factors. DO NOT refine // or modify the triangulation in any other way than using the "r" // command, unless you want to take responsibility for fixing up // the form factors. // The Poisson ratio in this datafile is set to be 0.80 in the "read" // section. This was the Poisson ratio used in the project that // relaxed_elastic_A was originally developed for: modeling high-altitude // balloons. A couple of test runs with lower Poisson ratio led to // indefinite hessians at the end, so I am leaving the ratio as 0.80. // Evolving hints: See the sample evolution "gogo" below. The idea is // to ramp up the elastic modulus as you refine and approach the final // shape. For final convergence, use "hessian_seek" instead of "hessian", // since "hessian" has a tendency to blow up the surface here, and // "hessian_seek" prevents that by doing a minimum-energy search along // the direction to the putative hessian minimum. Note that the energy // contribution of the elastic is inversely proportional to the elastic // modulus. I'm not sure how high you can get the modulus effectively; // at least 1000000. Extrapolate to infinite modulus for unstretchable // mylar. // If you want confirmation at the end that there is little stretching // going on, the command "echeck" below will print out a histogram of // edge stretch factors. // Do not try to do quadratic or lagrange models; relaxed_elastic_A is // defined only for the linear model. /***************************************************************************/ // Set volume to be maximized. Do this by setting energy to negative // volume, since Evolver minimizes energy. quantity cube_volume energy modulus -1 method facet_volume global // Elastic energy. // Stuff to do relaxed_elastic_A method define facet attribute poisson_ratio real // For defining unstretched facet shape define facet attribute form_factors real[3] // For resetting form_factors in refining define vertex attribute ref_coord real[3] define vertex attribute old_vid integer define edge attribute old_eid integer // This is the key energy to permit wrinkling but discourage stretching. quantity stretch energy modulus 10 method relaxed_elastic_A global // For components of stress quantity stretch1 info_only method relaxed_elastic1_A global quantity stretch2 info_only method relaxed_elastic2_A global vertices 1 0.0 0.0 0.0 2 1.0 0.0 0.0 3 1.0 1.0 0.0 4 0.0 1.0 0.0 5 0.0 0.0 1.0 6 1.0 0.0 1.0 7 1.0 1.0 1.0 8 0.0 1.0 1.0 edges /* given by endpoints and attribute */ 1 1 2 2 2 3 3 3 4 4 4 1 5 5 6 6 6 7 7 7 8 8 8 5 9 1 5 10 2 6 11 3 7 12 4 8 faces /* given by oriented edge loop */ 1 1 10 -5 -9 tension 0 2 2 11 -6 -10 tension 0 3 3 12 -7 -11 tension 0 4 4 9 -8 -12 tension 0 5 5 6 7 8 tension 0 6 -4 -3 -2 -1 tension 0 bodies /* one body, defined by its oriented faces */ 1 1 2 3 4 5 6 read // refine original edges so better suited to detect creases refine edge where id >= 1 and id <=12 set facet poisson_ratio 0.80 set vertex ref_coord[1] x; set vertex ref_coord[2] y; set vertex ref_coord[3] z; // Set form factors so initial cube is unstretched. set_form_factors := { foreach facet ff do { set ff.form_factors[1] (ff.vertex[2].ref_coord[1] - ff.vertex[1].ref_coord[1])^2 + (ff.vertex[2].ref_coord[2] - ff.vertex[1].ref_coord[2])^2 + (ff.vertex[2].ref_coord[3] - ff.vertex[1].ref_coord[3])^2; set ff.form_factors[2] (ff.vertex[2].ref_coord[1] - ff.vertex[1].ref_coord[1]) *(ff.vertex[3].ref_coord[1] - ff.vertex[1].ref_coord[1]) + (ff.vertex[2].ref_coord[2] - ff.vertex[1].ref_coord[2]) *(ff.vertex[3].ref_coord[2] - ff.vertex[1].ref_coord[2]) + (ff.vertex[2].ref_coord[3] - ff.vertex[1].ref_coord[3]) *(ff.vertex[3].ref_coord[3] - ff.vertex[1].ref_coord[3]); set ff.form_factors[3] (ff.vertex[3].ref_coord[1] - ff.vertex[1].ref_coord[1])^2 + (ff.vertex[3].ref_coord[2] - ff.vertex[1].ref_coord[2])^2 + (ff.vertex[3].ref_coord[3] - ff.vertex[1].ref_coord[3])^2; } } set_form_factors; // redefine the "r" command to adjust form factors. r :::= { set vertex old_vid id; set edge old_eid id; 'r'; foreach vertex vv where old_vid == 0 do { vv.ref_coord[1] := avg(vv.edge ee where old_eid != 0,sum(ee.vertex where old_vid != 0, ref_coord[1])); vv.ref_coord[2] := avg(vv.edge ee where old_eid != 0,sum(ee.vertex where old_vid != 0, ref_coord[2])); vv.ref_coord[3] := avg(vv.edge ee where old_eid != 0,sum(ee.vertex where old_vid != 0, ref_coord[3])); }; set_form_factors; recalc; } // check of how edge lengths stretched echeck := { histogram(edge ee, ee.length/sqrt( (ee.vertex[2].ref_coord[1] - ee.vertex[1].ref_coord[1])^2 + (ee.vertex[2].ref_coord[2] - ee.vertex[1].ref_coord[2])^2 + (ee.vertex[2].ref_coord[3] - ee.vertex[1].ref_coord[3])^2)); } // Typical evolution gogo := { g 10; r; g 10; stretch.modulus := 100; g 10; r; g 10; conj_grad; g 20; r; g 50; stretch.modulus := 1000; g 50; stretch.modulus := 10000; g 150; g 200; hessian_seek; hessian_seek; hessian_seek; hessian_seek; } evolver-2.30c.dfsg/fe/dirichlet_to_disk.cmd0000755000175300017530000001260111410765113021173 0ustar hazelscthazelsct// dirichlet_to_disk.cmd // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // For mapping simply connected regions to unit disk minimizing Dirichlet // energy to get conformal mappings. // Prerequisites: Dirichlet energy defined, but no others. // Usage: Remove all constraints and boundaries. Run to_disk. Then evolve. define constraint circle_con formula: x^2 + y^2 = 1 define facet attribute form_factors real[3] set_ff := { foreach facet ff do { ff.form_factors[1] := ff.edge[1].length^2; ff.form_factors[2] := -(ff.edge[1].x*ff.edge[3].x + ff.edge[1].y*ff.edge[3].y); ff.form_factors[3] := ff.edge[3].length^2; } } // Mapping to triangle. Especially nice since can use the three conformal // degrees of freedom to map particular vertices to corners, and straight // sides should let Hessian minimize Dirichlet energy in one step. define cornerx real[4]; cornerx[1] := 1.0; cornerx[2] := 0.0; cornerx[3] := -1.0; cornerx[4] := 1.0; define cornery real[4]; cornery[1] := 0.0; cornery[2] := sqrt(3); cornery[3] := 0.0; cornery[4] := 0.0; define constraint side_1_con formula: sqrt(3)*x + y = sqrt(3); define constraint side_2_con formula: -sqrt(3)*x + y = sqrt(3); define constraint side_3_con formula: y = 0; define cornerv integer[3] cornerv[1] := 0 cornerv[2] := 0 cornerv[3] := 0 define sidecounts integer[3] // how many sides mapped to each triangle side to_triangle := { // Check simple connectivity if vertex_count - edge_count + facet_count != 1 then { errprintf "to_disk error: Surface not simply connected. Aborting.\n"; return; }; set_ff; // set form factors set facet tension 0; // Go around outside edges, mapping to unit circle unfix vertices; if ( valid_element(vertex[cornerv[1]]) and (sum(vertex[cornerv[1]].edge,valence==1) == 2) and valid_element(vertex[cornerv[2]]) and (sum(vertex[cornerv[2]].edge,valence==1) == 2) and valid_element(vertex[cornerv[3]]) and (sum(vertex[cornerv[3]].edge,valence==1) == 2) ) then { foreach vertex[cornerv[1]].edge where valence == 1 do { thise := oid; break; }; starte := thise; counter := 0; sidenumber := 1; do { foreach edge[thise].vertex[2].edge eee where valence == 1 and eee.id != thise do { nexte := eee.oid; break; }; thise := nexte; counter += 1; if ( (edge[thise].vertex[1].id == cornerv[1]) or (edge[thise].vertex[1].id == cornerv[2]) or (edge[thise].vertex[1].id == cornerv[3]) ) then { sidecounts[sidenumber] := counter; counter := 0; sidenumber += 1; }; } while thise != starte; } else { printf "Legal corner vertices not given, so doing my own.\n"; rimcount := sum(edge,valence==1); sidecounts[1] := rimcount idiv 3; sidecounts[2] := rimcount idiv 3; sidecounts[3] := rimcount - sidecounts[1] - sidecounts[2]; foreach edge ee where valence == 1 do { starte := ee.id; break; }; }; // Now go around moving vertices to triangle sides thise := starte; for ( sidenum := 1 ; sidenum <= 3; sidenum += 1 ) { for ( counter := 1; counter <= sidecounts[sidenum] ; counter += 1 ) { lambda := counter/sidecounts[sidenum]; edge[thise].vertex[1].x := (1-lambda)*cornerx[sidenum] + lambda*cornerx[sidenum+1]; edge[thise].vertex[1].y := (1-lambda)*cornery[sidenum] + lambda*cornery[sidenum+1]; if ( space_dimension > 2) then edge[thise].vertex[1].x[3] := 0; if sidenum == 1 then set edge[thise].vertex[1] constraint side_1_con; if sidenum == 2 then set edge[thise].vertex[1] constraint side_2_con; if sidenum == 3 then set edge[thise].vertex[1] constraint side_3_con; foreach edge[thise].vertex[2].edge eee where valence == 1 and eee.id != thise do { nexte := eee.oid; break; }; thise := nexte; }; }; // Big move with rim fixed, since movable rim can have negative // eigenvalues. fix vertex where on_constraint circle_con; hessian; unfix vertex; } to_disk := { // Check simple connectivity if vertex_count - edge_count + facet_count != 1 then { errprintf "to_disk error: Surface not simply connected. Aborting.\n"; return; }; set_ff; // set form factors set facet tension 0; // Go around outside edges, mapping to unit circle unfix vertices; rimcount := sum(edge,valence==1); foreach edge ee where valence == 1 do { starte := ee.id; break; }; thise := starte; counter := 0; do { edge[thise].vertex[1].x := cos(counter/rimcount*2*pi); edge[thise].vertex[1].y := sin(counter/rimcount*2*pi); if ( space_dimension > 2) then edge[thise].vertex[1].x[3] := 0; set edge[thise].vertex[1] constraint circle_con; foreach edge[thise].vertex[2].edge eee where valence == 1 and eee.id != thise do { nexte := eee.oid; break; }; thise := nexte; counter += 1; } while thise != starte; // Big move with rim fixed, since movable rim can have negative // eigenvalues. fix vertex where on_constraint circle_con; hessian; unfix vertex; } // Prerequisites: Dirichlet energy defined, but no others. // Usage: Remove all constraints and boundaries. Run to_disk. Then evolve. evolver-2.30c.dfsg/fe/off.cmd0000755000175300017530000000073511410765113016267 0ustar hazelscthazelsct// off.cmd // Surface Evolver command to print OFF format file. // usage: // do_off >>> "filename.off" // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke do_off := { // file header printf "OFF\n%g %g %g\n",vertex_count,facet_count,edge_count; // vertex list foreach vertex do { printf "%f %f %f \n",x,y,z }; // triangle list foreach facet ff do { printf "3 "; foreach ff.vertex do printf "%g ",id-1; printf "\n"; } } evolver-2.30c.dfsg/fe/twointor.fe0000644000175300017530000000264011410765113017223 0ustar hazelscthazelsct// twointor.fe // Two Kelvin tetrakaidecahedra in a torus. TORUS_FILLED periods 1.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 1.000000 vertices 1 0.50 0.00 0.75 2 0.25 0.00 0.50 3 0.00 0.25 0.50 4 0.75 0.00 0.50 5 0.00 0.50 0.75 6 0.50 0.00 0.25 7 0.00 0.75 0.50 8 0.50 0.25 0.00 9 0.25 0.50 0.00 10 0.00 0.50 0.25 11 0.50 0.75 0.00 12 0.75 0.50 0.00 edges /* with torus wrap symbols */ 1 1 2 * * * 2 2 3 * * * 3 1 4 * * * 4 3 5 * * * 5 2 6 * * * 6 2 7 * - * 7 1 8 * * + 8 4 6 * * * 9 5 9 * * + 10 3 10 * * * 11 3 4 - * * 12 6 8 * * * 13 6 11 * - * 14 7 4 - + * 15 8 12 * * * 16 9 8 * * * 17 9 11 * * * 18 10 7 * * * 19 11 1 * + - 20 12 5 + * - 21 5 7 * * * 22 11 12 * * * 23 10 12 - * * 24 9 10 * * * faces 1 1 2 4 9 16 -7 2 -2 5 12 -16 24 -10 3 -4 10 18 -21 4 7 15 20 -4 11 -3 5 -1 3 8 -5 6 6 14 -11 -2 7 5 13 -17 24 18 -6 8 -12 13 19 7 9 -16 17 22 -15 10 -10 11 8 12 15 -23 11 -21 9 17 19 1 6 12 -14 -18 23 -22 -13 -8 13 -24 -9 -20 -23 14 -19 22 20 21 14 -3 bodies 1 -1 -2 -3 -4 -5 9 7 11 -9 10 12 5 14 3 volume 0.500 2 2 -6 -7 8 -10 -12 -11 -13 1 13 -14 6 4 -8 volume 0.500 read // Typical evolution gogo := { g 5; r; g 5; hessian; r; g 5; hessian; hessian; } evolver-2.30c.dfsg/fe/orderdmp.cmd0000755000175300017530000000235211410765113017326 0ustar hazelscthazelsct// orderdmp.cmd // Evolver command to number string vertices consecutively // by means of extra attribute 'number', // and create an ordered dump file. // Pipe output to file to save it. // Programmer: Ken Brakke, brakke@susqu.edu, http://www.susqu.edu/brakke // Usage: oderdmp define vertex attribute number integer orderdmp := { local e_id,newv_id,ecount,first_e,v_id,newe_id; list topinfo; ecount := 0; // for safety // get starting edge, since can't assume edge[1] exists foreach edge do e_id := id; first_e := e_id; v_id := edge[e_id].vertex[1].id; // follow connected edges printf "\nVertices\n"; do { set vertex[v_id] number ecount+1; printf "%g %g %g %g\n",ecount+1,vertex[v_id].x,vertex[v_id].y, vertex[v_id].z; foreach edge[e_id].vertex vv do { if ( vv.id != v_id ) then { newv_id := vv.id; foreach vv.edge ee do if ee.id != e_id then newe_id := ee.id } }; e_id := newe_id; v_id := newv_id; ecount := ecount + 1; } while ( (e_id != first_e) and (ecount <= edge_count) ); printf "\nEdges\n"; foreach edge ee do { printf"%g %g %g \n",ee.id,ee.vertex[1].number,ee.vertex[2].number; }; list bottominfo; } evolver-2.30c.dfsg/src/0000755000175300017530000000000011410765113015215 5ustar hazelscthazelsctevolver-2.30c.dfsg/src/popfilm.c0000644000175300017530000051341111410765113017034 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /****************************************************************** * * File: popfilm.c * * Purpose: Convert all tangent cones to minimal cones in * soapfilm model. */ #include "include.h" /********************************************************************* * * Function: popfilm() * * Purpose: Overall control of tangent cone minimization. * Creates sorted list of vertex-facet incidences * sorted on vertex. Analyzes tangent cone for each * vertex and calls minimizer for the non-minimal. */ /* comparison routine for qsort */ static int vfcomp(a,b) struct verfacet *a,*b; { if ( a->v_id < b->v_id ) return -1; if ( a->v_id > b->v_id ) return 1; return 0; } int popfilm() { int popped = 0; if ( web.counts_reported & edge_pop_count_bit ) web.edge_pop_count = 0; popped = edgepop_film(); web.counts_reported |= edge_pop_count_bit; sprintf(msg,"Edges popped: %d\n",popped); outstring(msg); popped = verpop_film(); return popped; } /******************************************************************** * * Function: edgepop_film() * * Purpose: Reconfigure all edges with more than 3 facets into * triple edges. Also does two or more facets with * common edge on boundary or constraint. */ int edgepop_film() { edge_id e_id,sentinel; int popped = 0; /* Loop through all edges, popping as you go. New edges created during popping will be scanned to see if they are still pop-worthy. */ e_id = NULLEDGE; while ( generate_all(EDGE,&e_id,&sentinel) ) { popped += pop_one_edge(e_id); } return popped; } /*************************************************************************** * * function: vertex_degfree() * * purpose: find degrees of freedom left by a vertex's constraints * */ int vertex_degfree(v_id) vertex_id v_id; { int degfree; int vattr = get_vattr(v_id); struct boundary *vbdry; conmap_t *vmap; int i; /* Get degrees of freedom of vertex */ degfree = SDIM; if ( vattr & FIXED ) degfree = 0; else if ( vattr & BOUNDARY ) { vbdry = get_boundary(v_id); if ( (vbdry->attr & NONWALL) ) degfree = vbdry->pcount; } else if ( vattr & CONSTRAINT ) { vmap = get_e_constraint_map(v_id); for ( i = 1; i <= (int)vmap[0] ; i++ ) if (!((get_constraint(vmap[i])->attr)&(NONPOSITIVE|NONNEGATIVE|NONWALL))) degfree--; } return degfree; } /*************************************************************************** * * function: edge_degfree() * * purpose: find degrees of freedom left by a edge's constraints * */ int edge_degfree(e_id) edge_id e_id; { int degfree; int eattr = get_eattr(e_id); struct boundary *ebdry; conmap_t *emap; int i; /* Get degrees of freedom of edge */ degfree = SDIM; if ( eattr & FIXED ) degfree = 0; else if ( eattr & BOUNDARY ) { ebdry = get_edge_boundary(e_id); if ( (ebdry->attr & NONWALL) ) degfree = ebdry->pcount; } else if ( eattr & CONSTRAINT ) { emap = get_e_constraint_map(e_id); for ( i = 1; i <= (int)emap[0] ; i++ ) if (!((get_constraint(emap[i])->attr)&(NONPOSITIVE|NONNEGATIVE|NONWALL))) degfree--; } return degfree; } /*************************************************************************** * * function: facet_degfree() * * purpose: find degrees of freedom left by a facet's constraints * */ int facet_degfree(f_id) facet_id f_id; { int degfree; int fattr = get_fattr(f_id); struct boundary *fbdry; conmap_t *fmap; int i; /* Get degrees of freedom of edge */ degfree = SDIM; if ( fattr & FIXED ) degfree = 0; else if ( fattr & BOUNDARY ) { fbdry = get_facet_boundary(f_id); if ( (fbdry->attr & NONWALL) ) degfree = fbdry->pcount; } else if ( fattr & CONSTRAINT ) { fmap = get_f_constraint_map(f_id); for ( i = 1; i <= (int)fmap[0] ; i++ ) if (!((get_constraint(fmap[i])->attr)&(NONPOSITIVE|NONNEGATIVE|NONWALL))) degfree--; } return degfree; } /******************************************************************** * * Function: pop_one_edge() * * Purpose: Try popping one edge. Tests for more than 3 facets. * Also does two or more facets with * common edge on boundary or constraint. */ REAL new_displacement[MAXCOORD]; /* one normal of splittin wedge */ int pop_one_edge(e_id) edge_id e_id; { int facet_count = 0; facetedge_id fe_id,fe,key_fe=NULLID,new_key; REAL side[MAXCOORD],sideA[MAXCOORD],sideB[MAXCOORD]; REAL normA,normB,normalA[MAXCOORD],normalB[MAXCOORD]; REAL maxcos,newcos; int didsplit; int foundwedge; facetedge_id first_fe; int eattr = get_eattr(e_id); facet_id f1,f2; int attr1,attr2; int i,k; REAL midnormal[MAXCOORD]; int popped = 0; int degfree; /* degrees of freedom of edge */ int fdegfree,next_fdegfree; int popattr = -1; /* in case of kraynikpop */ int kraynikwedge; /* whether current wedge has kraynik preference */ int foundkraynik; int maxffree; /* maximum degrees of freedom of adjacent facets */ int maxffreen; /* number of adjacent facets with that freedom */ int septum_flag=1; /* whether to put in septum joining popped edges */ /* count facets around edge */ fe_id = first_fe = get_edge_fe(e_id); maxffree = 0; maxffreen = 0; if ( valid_id(fe_id) ) do { int ffree = facet_degfree(get_fe_facet(fe_id)); if ( ffree > maxffree ) { maxffree = ffree; maxffreen = 1; } else if ( ffree == maxffree ) maxffreen ++; facet_count++; fe_id = get_next_facet(fe_id); } while ( valid_id(fe_id) && !equal_id(fe_id,first_fe) ); /* only pop if more than 3 facets, or a wall or wire */ if ( !(eattr & (FIXED|BOUNDARY|CONSTRAINT) && (maxffreen >= 2)) && !(maxffreen > 3) ) return 0; /* Get degrees of freedom of edge */ degfree = edge_degfree(e_id); if ( (facet_count == 3) && (degfree == SDIM) ) return 0; if ( kraynikpopedge_flag ) { /* find facets that disagree on edge_pop_attribute */ popattr = find_attribute(FACET,"edge_pop_attribute"); } foundwedge = 0; foundkraynik = 0; if ( pop_disjoin_flag && (facet_count == 4) ) { /* test for merging Plateau borders */ facetedge_id fea,feb,fec,fed; body_id ba,bb,bc,bd; fea = get_edge_fe(e_id); feb = get_next_facet(fea); fec = get_next_facet(feb); fed = get_next_facet(fec); ba = get_facet_body(get_fe_facet(fea)); bb = get_facet_body(get_fe_facet(feb)); bc = get_facet_body(get_fe_facet(fec)); bd = get_facet_body(get_fe_facet(fed)); if ( valid_id(ba) && equal_id(ba,bc) ) { key_fe = feb; foundwedge = 1; septum_flag = 0; } else if ( valid_id(bb) && equal_id(bb,bd) ) { key_fe = fea; foundwedge = 1; septum_flag = 0; } } if ( !foundwedge ) { /* find narrowest wedge to pull out */ get_edge_side(e_id,side); maxcos = -2.0; /* for finding minimum angle between facets */ /* find first facet normal */ fe = get_edge_fe(e_id); get_fe_side(get_next_edge(fe),sideA); cross_prod(side,sideA,normalA); normA = sqrt(SDIM_dot(normalA,normalA)); next_fdegfree = fdegfree = facet_degfree(get_fe_facet(fe)); for ( i = 0 ; i < facet_count ; i++ ) { facetedge_id next_fe; next_fe = get_next_facet(fe); fdegfree = next_fdegfree; next_fdegfree = facet_degfree(get_fe_facet(next_fe)); /* test wedge for compatibility */ if ( fdegfree < 3 ) continue; if ( next_fdegfree < 3 ) continue; f1 = get_fe_facet(fe); f2 = get_fe_facet(next_fe); attr1 = get_fattr(f1); attr2 = get_fattr(f2); if ( get_facet_boundary(f1) != get_facet_boundary(f2) ) continue; if ( (attr1 & CONSTRAINT) || (attr2 & CONSTRAINT) ) { conmap_t *map1 = get_f_constraint_map(f1); conmap_t *map2 = get_f_constraint_map(f2); conmap_t ii,jj,found; found = 0; for ( ii = 1 ; ii <= map1[0] ; ii++ ) { for ( jj = 1; jj <= map2[0] ; jj++ ) if ( map1[ii] == map2[jj] ) found++; } if ( (found != map1[0]) || ( found != map2[0]) ) continue; } if ( (facet_count <= 3) && ((fdegfree <= degfree)||(next_fdegfree <= degfree) ) ) continue; /* fake wall */ /* get normal */ get_fe_side(get_next_edge(next_fe),sideB); cross_prod(side,sideB,normalB); normB = sqrt(SDIM_dot(normalB,normalB)); newcos = SDIM_dot(normalA,normalB)/normA/normB; /* If phases in effect, can estimate force */ if ( phase_flag ) { REAL tensionA = get_facet_density(f1); REAL tensionB = get_facet_density(f2); REAL tensionC; /* for the proposed interface */ REAL force1; REAL sum; int ii = get_b_phase(get_facet_body(f1)); int jj = get_b_phase(get_facet_body(inverse_id(f2))); tensionC = phase_data[ii][jj]; for ( k = 0, sum = 0.0 ; k < SDIM ; k++ ) { REAL term = (tensionA*normalA[0]/normA + tensionB*normalB[0]/normB); sum += term; } force1 = sqrt(sum); newcos = force1 - tensionC; } if ( popattr >= 0 ) /* kraynikpop test */ { kraynikwedge = ( *(int*)get_extra(f1,popattr) != *(int*)get_extra(f2,popattr) ); } else kraynikwedge = 0; if ( ((newcos > maxcos) && (kraynikwedge >= foundkraynik)) || (kraynikwedge > foundkraynik) ) { key_fe = next_fe; maxcos = newcos; for ( k = 0 ; k < SDIM ; k++ ) midnormal[k] = normalA[k]/normA + normalB[k]/normB; cross_prod(midnormal,side,new_displacement); } if ( kraynikwedge ) foundkraynik = 1; /* set up for next angle */ normA = normB; memcpy((char *)normalA,(char *)normalB,sizeof(normalA)); fe = next_fe; } /* now a little check just to make sure things happened as planned */ if ( (facet_count >= 4) && (maxcos < 0.0) && !foundkraynik ) { sprintf(errmsg, "Can't find poppable pair of facets on poppable edge %s.\n", ELNAME(e_id)); kb_error(1299,errmsg, RECOVERABLE); } } if ( !valid_id(key_fe) ) return 0; /* check boundary and constraint compatibility of wedge */ /* test to see if we really want to do this pop */ f1 = get_fe_facet(key_fe); f2 = get_fe_facet(get_next_edge(key_fe)); attr1 = get_fattr(f1); attr2 = get_fattr(f2); if ( (attr1 & FIXED) || (attr2 & FIXED) ) return 0; if ( get_facet_boundary(f1) != get_facet_boundary(f2) ) return 0; if ( (attr1 & CONSTRAINT) || (attr2 & CONSTRAINT) ) { conmap_t *map1 = get_e_constraint_map(f1); conmap_t *map2 = get_e_constraint_map(f2); conmap_t ii,jj,found; found = 0; for ( ii = 1 ; ii <= map1[0] ; ii++ ) { for ( jj = 1; jj <= map2[0] ; jj++ ) if ( map1[ii] == map2[jj] ) found++; } if ( (found != map1[0]) || ( found != map2[0]) ) return 0; } if ( verbose_flag ) { sprintf(msg,"Popping edge %s\n",ELNAME(e_id)); outstring(msg); } didsplit = 0; if ( (facet_count == 2) && (degfree==2) ) { /* special treatment, since may split either way */ popped = two_split(key_fe,septum_flag); if ( popped ) return popped; /* else do Y pull-out */ } /* try propagating split forward */ new_key = key_fe; while ( try_prop(&new_key,key_fe,septum_flag) ) { didsplit = 1; if ( verbose_flag ) { sprintf(msg," Propagating pop to edge %s\n",ELNAME(get_fe_edge(new_key))); outstring(msg); } popped++; } /* try propagating split backward */ new_key = inverse_id(get_prev_facet(key_fe)); while ( try_prop(&new_key,inverse_id(get_prev_facet(key_fe)),septum_flag) ) { didsplit = 1; if ( verbose_flag ) { sprintf(msg," Propagating pop to edge %s\n",ELNAME(get_fe_edge(key_fe))); outstring(msg); } popped++; } /* if can't split forward or backward, divide edge and split */ if ( !didsplit ) { edge_refine(e_id); popped += pop_one_edge(e_id); /* try again */ } return popped; } /* end pop_one_edge */ /*********************************************************************** * * Function: try_prop() * * Purpose: Sees if vertex at head of *pass_key can be split. * If it can, it does. If split can be propagated * further, it returns the next edge to try in *pass_key. * If the vertex was split, it returns 1, else 0. * Does not split FIXED vertices. Or BOUNDARY vertices. */ int try_prop(pass_key,start_key,septum_flag) facetedge_id *pass_key; facetedge_id start_key; /* to check for complete loop */ int septum_flag; /* whether to put septum between popped edges */ { int splitflag; int propflag; facetedge_id wing_fe; facetedge_id flip_fe; facetedge_id new_key=0; facetedge_id key_fe = *pass_key; edge_id e_id = get_fe_edge(key_fe); edge_id next_e=0; if ( !valid_id(*pass_key) ) return 0; /* if ( get_vattr(get_fe_headv(key_fe)) & FIXED ) return 0; */ /* don't even try if valence is already <= 3 */ /*if ( get_edge_valence(e_id) <= 3 ) return 0; */ /* swing forward on one side until find multiple edge */ flip_fe = inverse_id(key_fe); for (;;) { wing_fe = get_next_edge(inverse_id(flip_fe)); if ( equal_id(wing_fe,start_key) ) { /* have made complete loop around */ splitflag = 1; propflag = 0; break; } flip_fe = get_next_facet(wing_fe); if ( equal_id(wing_fe,flip_fe) ) { /* dead end on wing, so OK to split vertex maybe */ if ( edge_degfree(get_fe_edge(wing_fe)) >= 2 ) splitflag = 1; else splitflag = 0; propflag = 0; break; } if ( !equal_id(wing_fe,get_next_facet(flip_fe)) || (get_eattr(get_fe_edge(wing_fe)) & (FIXED|BOUNDARY|CONSTRAINT)) ) { /* have found multiple edge, or wall or wire */ next_e = get_fe_edge(wing_fe); if ( !equal_element(e_id,next_e) ) { /* have found legitimate next edge */ splitflag = 1; propflag = 1; new_key = wing_fe; break; } if ( equal_element(wing_fe,get_prev_facet(key_fe)) ) { /* have cirque, so OK to split, but no propagation */ splitflag = 1; propflag = 0; } else { /* have cirque crossing proposed split */ /* which ends propagation */ splitflag = 0; propflag = 0; } break; } } if ( !splitflag ) return 0; /* swing forward on other side until find multiple edge */ flip_fe = inverse_id(get_prev_facet(key_fe)); for (;;) { wing_fe = get_next_edge(inverse_id(flip_fe)); flip_fe = get_next_facet(wing_fe); if ( equal_id(wing_fe,flip_fe) ) { /* dead end on wing, so OK to split vertex maybe */ if ( edge_degfree(get_fe_edge(wing_fe)) < 2 ) splitflag = 0; propflag = 0; break; } if ( !equal_id(wing_fe,get_next_facet(flip_fe)) || (get_eattr(get_fe_edge(wing_fe)) & (FIXED|BOUNDARY|CONSTRAINT)) ) { /* have found multiple edge */ if ( !equal_id(get_fe_edge(wing_fe),next_e) ) { /* can't split; have serious nonminimal vertex */ splitflag = 0; propflag = 0; break; } else /* check normals */ { } break; } } if ( !propflag ) new_key = NULLFACETEDGE; /* check to see if new_key is already just triple */ { facetedge_id feb = get_next_facet(new_key); facetedge_id fec = get_next_facet(feb); facetedge_id fed = get_next_facet(fec); if ( equal_id(fec,new_key) ) { /* can't split further */ splitflag = 0; } else if ( equal_id(fed,new_key) ) { /* already triple edge; split and stop */ propflag = 0; } } if ( splitflag ) versplit(key_fe,new_key,septum_flag); if ( !propflag ) new_key = NULLFACETEDGE; *pass_key = new_key; return splitflag; } /* end try_prop */ /********************************************************************** * * Function: versplit() * * Purpose: Split a wedge of facets off a vertex along two edges. * Old edges go with split wedge (old edges now proper * minimal cones) and new edges have rest of old facets * and await scanning for minimality. Arguments are * facetedges for the two edges inside the wedge; if * second is null, means there is no second edge. * This is essentially the reverse of eliminating an edge. */ void versplit(fe_a,fe_b,septum_flag) facetedge_id fe_a,fe_b; int septum_flag; { vertex_id old_v = get_fe_headv(fe_a); vertex_id new_v; edge_id new_e=0,new_a=0,new_b=0,e_id; edge_id old_a = get_fe_edge(fe_a); edge_id old_b = get_fe_edge(fe_b); facetedge_id fe_aa = get_prev_facet(fe_a); facetedge_id fe_bb = get_prev_facet(fe_b); facetedge_id fe_a_old,fe_a_new,fe_a_e; facetedge_id fe_b_old=0,fe_b_new=0,fe_b_e=0; facet_id new_fa,new_fb=0; body_id b_id; int i; REAL *x; int halfflag; facetedge_id fe,first_fe; int valence = get_edge_valence(old_a); conmap_t *vmap,*emap,*fmap; facetedge_id halffe; /* create a new vertex, which will be split away with wedge */ new_v = dup_vertex(old_v); x = get_coord(new_v); for ( i = 0 ; i < SDIM ; i++ ) x[i] += 0.01*new_displacement[i]; if ( septum_flag ) { /* create new edge between vertices */ new_e = new_edge(old_v,new_v,NULLID); } /* create new edges split off from wedge */ if ( !equal_id(get_prev_facet(fe_aa),fe_a) ) { new_a = dup_edge(old_a); insert_vertex_edge(get_edge_tailv(old_a),new_a); insert_vertex_edge(old_v,inverse_id(new_a)); } /* fix up attributes of old edge, in case pulling off wall */ unset_attr(old_a,FIXED); set_edge_boundary_num(old_a,0); unset_attr(old_a,BOUNDARY); fmap = get_f_constraint_map(get_fe_facet(fe_a)); emap = get_e_constraint_map(old_a); for ( i = 1 ; i <= (int)fmap[0] ; i++ ) emap[i] = fmap[i]; for ( ; i <= (int)emap[0] ; i++ ) emap[i] = 0; emap[0] = fmap[0]; if ( emap[0] == 0 ) unset_attr(old_a,CONSTRAINT); else set_attr(old_a,CONSTRAINT); if ( valid_id(fe_b) ) { new_b = dup_edge(old_b); insert_vertex_edge(old_v,new_b); insert_vertex_edge(get_edge_headv(old_b),inverse_id(new_b)); /* fix up attributes of old edge, in case pulling off wall */ unset_attr(old_b,FIXED); set_edge_boundary_num(old_b,0); unset_attr(old_b,BOUNDARY); fmap = get_f_constraint_map(get_fe_facet(fe_b)); emap = get_e_constraint_map(old_b); for ( i = 1 ; i <= (int)fmap[0] ; i++ ) emap[i] = fmap[i]; for ( ; i <= (int)emap[0] ; i++ ) emap[i] = 0; emap[0] = fmap[0]; if ( emap[0] == 0 ) unset_attr(old_b,CONSTRAINT); else set_attr(old_b,CONSTRAINT); } /* reset edge endpoints coming into new_v */ fe = fe_a; halfflag = 0; halffe = NULLID; /* for attributes of new_v */ do { e_id = get_fe_edge(fe); remove_vertex_edge(old_v,inverse_id(e_id)); set_edge_headv(e_id,new_v); if ( equal_id(fe,get_next_facet(fe)) ) { halfflag = 1; /* need to go back and do other side of wedge */ halffe = fe; break; } fe = get_next_facet(inverse_id(get_next_edge(fe))); if ( !valid_id(halffe) ) halffe = fe; } while ( !equal_id(fe,fe_a) ); if ( halfflag ) { fe = get_prev_facet(inverse_id(get_next_edge(fe_aa))); if ( !valid_id(halffe) ) halffe = fe; do { e_id = get_fe_edge(fe); remove_vertex_edge(old_v,inverse_id(e_id)); set_edge_headv(e_id,new_v); if ( equal_id(fe,get_next_facet(fe)) ) break; fe = get_prev_facet(inverse_id(get_next_edge(fe))); } while ( !equal_id(fe,fe_aa) ); } /* fix up attributes of new_v, which was clone of old_v */ unset_attr(new_v,FIXED); if ( valid_id(halffe) ) { /* set attributes to same as halffe */ e_id = get_fe_edge(halffe); if ( get_eattr(e_id) & BOUNDARY ) { if ( get_boundary(new_v) != get_edge_boundary(e_id) ) kb_error(2440,"Trying to pop vertex not on same boundary as incoming edge.",RECOVERABLE); } else { set_boundary_num(new_v,0); unset_attr(new_v,BOUNDARY); } vmap = get_v_constraint_map(new_v); emap = get_e_constraint_map(e_id); for ( i = 1 ; i <= (int)emap[0] ; i++ ) vmap[i] = emap[i]; for ( ; i <= (int)vmap[0] ; i++ ) vmap[i] = 0; vmap[0] = emap[0]; if ( vmap[0] ) set_attr(new_v,CONSTRAINT); else unset_attr(new_v,CONSTRAINT); } if ( septum_flag ) { /* fix up attributes of new_e, which was brand new */ if ( valid_id(halffe) ) { /* set attributes to same as halffe */ e_id = get_fe_edge(halffe); if ( get_eattr(e_id) & BOUNDARY ) { set_edge_boundary_num(new_e,get_edge_boundary_num(e_id)); set_attr(new_e,BOUNDARY); } if ( get_eattr(e_id) & CONSTRAINT ) { conmap_t *eemap = get_e_constraint_map(new_e); emap = get_e_constraint_map(e_id); for ( i = 1 ; i <= (int)emap[0] ; i++ ) eemap[i] = emap[i]; eemap[0] = emap[0]; set_attr(new_e,CONSTRAINT); } } /* create two new facets */ new_fa = dup_facet(get_fe_facet(fe_a)); if ( valid_id(fe_b) ) { new_fb = dup_facet(get_fe_facet(fe_b)); } /* create new facet-edges */ fe_a_e = new_facetedge(new_fa,new_e); fe_a_new = new_facetedge(new_fa,new_a); fe_a_old = new_facetedge(new_fa,inverse_id(old_a)); if ( valid_id(fe_b) ) { fe_b_e = new_facetedge(new_fb,inverse_id(new_e)); fe_b_new = new_facetedge(new_fb,new_b); fe_b_old = new_facetedge(new_fb,inverse_id(old_b)); } /* link elements to facet-edges */ set_edge_fe(old_a,inverse_id(fe_a_old)); set_edge_fe(new_a,fe_a_new); set_edge_fe(new_e,fe_a_e); set_facet_fe(new_fa,fe_a_new); if ( valid_id(fe_b) ) { set_edge_fe(old_b,inverse_id(fe_b_old)); set_edge_fe(new_b,fe_b_new); set_facet_fe(new_fb,fe_b_new); } /* link edges around new facets */ set_next_edge(fe_a_e,fe_a_old); set_next_edge(fe_a_old,fe_a_new); set_next_edge(fe_a_new,fe_a_e); set_prev_edge(fe_a_old,fe_a_e); set_prev_edge(fe_a_new,fe_a_old); set_prev_edge(fe_a_e,fe_a_new); if ( valid_id(fe_b) ) { set_next_edge(fe_b_e,fe_b_new); set_next_edge(fe_b_old,fe_b_e); set_next_edge(fe_b_new,fe_b_old); set_prev_edge(fe_b_old,fe_b_new); set_prev_edge(fe_b_new,fe_b_e); set_prev_edge(fe_b_e,fe_b_old); } /* link facets around edges */ /* new middle edge */ if ( valid_id(fe_b) ) { set_next_facet(fe_a_e,inverse_id(fe_b_e)); set_next_facet(fe_b_e,inverse_id(fe_a_e)); set_prev_facet(fe_a_e,inverse_id(fe_b_e)); set_prev_facet(fe_b_e,inverse_id(fe_a_e)); } else { set_next_facet(fe_a_e,fe_a_e); set_prev_facet(fe_a_e,fe_a_e); } /* facet A old edge */ set_next_facet(fe_a_old,inverse_id(fe_a)); set_prev_facet(fe_a_old,inverse_id(fe_aa)); /* facet A new edge */ if ( valence >= 3 ) { set_next_facet(fe_a_new,get_next_facet(fe_a)); set_prev_facet(fe_a_new,get_prev_facet(fe_aa)); } else { set_next_facet(fe_a_new,fe_a_new); set_prev_facet(fe_a_new,fe_a_new); } /* reset old links */ set_next_facet(get_prev_facet(fe_a_old),fe_a_old); set_prev_facet(get_next_facet(fe_a_old),fe_a_old); set_next_facet(get_prev_facet(fe_a_new),fe_a_new); set_prev_facet(get_next_facet(fe_a_new),fe_a_new); /* reset edges of facet-edges around new edge */ fe = first_fe = get_edge_fe(new_a); if ( valid_id(fe) ) do { set_fe_edge(fe,new_a); fe = get_next_facet(fe); } while ( valid_id(fe) && !equal_id(fe,first_fe) ); if ( valid_id(fe_b) ) { /* facet B old edge */ set_next_facet(fe_b_old,inverse_id(fe_b)); set_prev_facet(fe_b_old,inverse_id(fe_bb)); /* facet B new edge */ set_next_facet(fe_b_new,get_next_facet(fe_b)); set_prev_facet(fe_b_new,get_prev_facet(fe_bb)); /* reset old links */ set_next_facet(get_prev_facet(fe_b_old),fe_b_old); set_prev_facet(get_next_facet(fe_b_old),fe_b_old); set_next_facet(get_prev_facet(fe_b_new),fe_b_new); set_prev_facet(get_next_facet(fe_b_new),fe_b_new); /* reset edges of facet-edges around new edge */ fe = first_fe = get_edge_fe(new_b); if ( valid_id(fe) ) do { set_fe_edge(fe,new_b); fe = get_next_facet(fe); } while ( valid_id(fe) && !equal_id(fe,first_fe) ); } /* set new facet bodies */ fe = get_next_facet(fe_a_new); b_id = get_facet_body(get_fe_facet(fe)); set_facet_body(inverse_id(new_fa),b_id); if ( valid_id(fe_b) ) set_facet_body(inverse_id(new_fb),b_id); fe = inverse_id(get_prev_facet(fe_a_new)); b_id = get_facet_body(get_fe_facet(fe)); set_facet_body(new_fa,b_id); if ( valid_id(fe_b) ) set_facet_body(new_fb,b_id); /* Tension, if phases */ if ( phase_flag ) { set_f_phase_density(new_fa); if ( valid_id(fe_b) ) set_f_phase_density(new_fb); } /* set velocity, so can tell edge is supposed to grow */ { REAL *velocity; velocity = get_velocity(old_v); for ( i = 0 ; i < SDIM ; i++ ) velocity[i] = -new_displacement[i]; velocity = get_velocity(new_v); for ( i = 0 ; i < SDIM ; i++ ) velocity[i] = new_displacement[i]; } } /* end if septum_flag */ else { /* no septum, so just reset facetedge links */ facetedge_id fe_a_next = get_next_facet(fe_a); facetedge_id fe_aa_prev = get_prev_facet(fe_aa); if ( !equal_id(fe_a_next,fe_aa) ) { set_next_facet(fe_a,fe_aa); set_prev_facet(fe_aa,fe_a); set_next_facet(fe_aa_prev,fe_a_next); set_prev_facet(fe_a_next,fe_aa_prev); set_fe_edge(fe_a_next,new_a); set_fe_edge(fe_aa_prev,new_a); set_edge_fe(old_a,fe_a); set_edge_fe(new_a,fe_a_next); } set_edge_headv(old_a,new_v); if ( valid_id(fe_b) ) { facetedge_id fe_b_next = get_next_facet(fe_b); facetedge_id fe_bb_prev = get_prev_facet(fe_bb); set_next_facet(fe_b,fe_bb); set_prev_facet(fe_bb,fe_b); set_next_facet(fe_bb_prev,fe_b_next); set_prev_facet(fe_b_next,fe_bb_prev); set_fe_edge(fe_b_next,new_b); set_fe_edge(fe_bb_prev,new_b); set_edge_tailv(old_b,new_v); set_edge_fe(old_b,fe_b); set_edge_fe(new_b,fe_b_next); } } } /*************************************************************************** * * function: two_split() * * purpose: Handle special case of two facets meeting at an edge on a wall, * with two degrees of freedom on the wall so splitting apart * is legal. * * input: facetedge of edge known to have 2 facets and 2 degrees of freedom. * * return: Number of edges split. */ int two_split(key_fe,septum_flag) facetedge_id key_fe; /* starting edge */ int septum_flag; /* no septum not implemented yet */ { facetedge_id headsplitlist[10]; int headsplitcount = 0; int headsplitflag = 0; /* whether to split end vertex */ facetedge_id tailsplitlist[10]; int tailsplitcount = 0; int tailsplitflag = 0; /* 7hether to split end vertex */ facetedge_id fe_a,fe_aa,fe_aaa; int stopflag; int popped = 0; int splitcount; int i; /* See how far potential split can be traced in each direction, checking that Y split is not more suitable all the way. */ /* Try forward */ fe_a = key_fe; headsplitflag = 1; for (stopflag=0;!stopflag;) { int head_degfree; int aa_degfree; headsplitlist[headsplitcount++] = fe_a; /* test head vertex */ head_degfree = vertex_degfree(get_fe_headv(fe_a)); if ( head_degfree == 0 ) { /* can't split */ headsplitflag = 0; break; } /* find next wall edge forward */ fe_aaa = inverse_id(fe_a); do { fe_aa = inverse_id(get_next_edge(fe_aaa)); fe_aaa = get_next_facet(fe_aa); if ( !equal_id(fe_aa,get_next_facet(fe_aaa)) ) { /* run into valence more than 2, can't split head */ stopflag = 1; headsplitflag = 0; break; } if ( equal_id(fe_aa,fe_aaa) ) { /* valence 1, could still split head */ aa_degfree = edge_degfree(get_fe_edge(fe_aa)); if ( aa_degfree < 2 ) headsplitflag = 0; else headsplitflag = 1; stopflag = 1; break; } /* now have valence 2 */ aa_degfree = edge_degfree(get_fe_edge(fe_aa)); if ( aa_degfree == 2 ) { /* back to wall */ if ( (facet_degfree(get_fe_facet(fe_aa)) != 3) || (facet_degfree(get_fe_facet(get_next_facet(fe_aa))) != 3) ) stopflag = 1; break; } else if ( aa_degfree != 3 ) { /* end of the line */ stopflag = 1; } } while ( aa_degfree == 3 ); fe_a = fe_aa; } /* end of going forward */ /* go backward */ fe_a = inverse_id(key_fe); tailsplitflag = 1; for (stopflag=0;!stopflag;) { int tail_degfree; int aa_degfree; tailsplitlist[tailsplitcount++] = fe_a; /* test tail vertex */ tail_degfree = vertex_degfree(get_fe_headv(fe_a)); if ( tail_degfree == 0 ) { /* can't split */ tailsplitflag = 0; break; } /* find next wall edge forward */ fe_aaa = inverse_id(fe_a); do { fe_aa = inverse_id(get_next_edge(fe_aaa)); fe_aaa = get_next_facet(fe_aa); if ( !equal_id(fe_aa,get_next_facet(fe_aaa)) ) { /* run into valence more than 2, can't split tail */ stopflag = 1; tailsplitflag = 0; break; } if ( equal_id(fe_aa,fe_aaa) ) { /* valence 1, could still split tail */ aa_degfree = edge_degfree(get_fe_edge(fe_aa)); if ( aa_degfree < 2 ) tailsplitflag = 0; else tailsplitflag = 1; stopflag = 1; break; } /* now have valence 2 */ aa_degfree = edge_degfree(get_fe_edge(fe_aa)); if ( aa_degfree == 2 ) { /* back to wall */ if ( (facet_degfree(get_fe_facet(fe_aa)) != 3) || (facet_degfree(get_fe_facet(get_next_facet(fe_aa))) != 3) ) stopflag = 1; break; } else if ( aa_degfree != 3 ) { /* end of the line */ stopflag = 1; } } while ( aa_degfree == 3 ); fe_a = fe_aa; } /* end of going backward */ /* split things */ splitcount = headsplitcount + tailsplitcount - 1; if ( (splitcount == 1) && !headsplitflag && !tailsplitflag ) return 0; /* now get into one list for splitting */ for ( i = headsplitcount-1 ; i >= 0 ; i-- ) headsplitlist[i+tailsplitcount-1] = headsplitlist[i]; for ( i = 1 ; i < tailsplitcount ; i++ ) headsplitlist[tailsplitcount-i] = inverse_id(tailsplitlist[i]); /* Test to see if all edges have their facets less than 90 degrees apart, in which case we don't split, but let Y pull-out happen. */ for ( i = 0 ; i < splitcount ; i++ ) { facetedge_id fe = headsplitlist[i]; facetedge_id fe_a = get_next_facet(fe); REAL normal[MAXCOORD]; REAL normala[MAXCOORD]; get_facet_normal(get_fe_facet(fe),normal); get_facet_normal(get_fe_facet(fe_a),normala); if ( SDIM_dot(normal,normala) < 0 ) break; /* too wide */ } if ( i == splitcount ) return 0; /* split new edge off with facetedges in list */ for ( i = 0 ; i < splitcount ; i++ ) { facetedge_id fe,fe_b,fe_c; edge_id new_e,old_e; fe = headsplitlist[i]; old_e = get_fe_edge(fe); new_e = dup_edge(old_e); if ( verbose_flag ) { sprintf(msg,"Popping valence 2 edge %s on a constraint.\n",ELNAME(old_e)); outstring(msg); } insert_vertex_edge(get_edge_tailv(old_e),new_e); insert_vertex_edge(get_edge_headv(old_e),inverse_id(new_e)); fe_a = get_next_facet(fe); set_fe_edge(fe,new_e); set_edge_fe(new_e,fe); set_edge_fe(old_e,fe_a); set_next_facet(fe,fe); set_prev_facet(fe,fe); set_next_facet(fe_a,fe_a); set_prev_facet(fe_a,fe_a); if ( (i > 0) || tailsplitflag ) { vertex_id old_tail = get_edge_tailv(old_e); vertex_id new_tail = dup_vertex(old_tail); fe_b = fe; fe_c = NULLID; /* reconnect edges */ for (;;) { remove_vertex_edge(old_tail,get_fe_edge(fe_b)); set_edge_tailv(get_fe_edge(fe_b),new_tail); if ( equal_id(fe_b,fe_c) ) break; fe_c = inverse_id(get_prev_edge(fe_b)); fe_b = get_next_facet(fe_c); } } if ( (i == splitcount-1) && headsplitflag ) { vertex_id old_head = get_edge_headv(old_e); vertex_id new_head = dup_vertex(old_head); fe_b = fe; fe_c = NULLID; /* reconnect edges */ for (;;) { remove_vertex_edge(old_head,inverse_id(get_fe_edge(fe_b))); set_edge_headv(get_fe_edge(fe_b),new_head); if ( equal_id(fe_b,fe_c) ) break; fe_c = inverse_id(get_next_edge(fe_b)); fe_b = get_next_facet(fe_c); } } } popped = splitcount; return popped; } /********************************************************************** Vertex popping. *************************************************************************/ #define OTHERCONE 0 #define WIREBOUNDARY 1 #define PLANECONE 2 #define WIREWING 3 #define WIRECORNER 4 #define TRIPLE_EDGER 5 #define TETRAHEDRAL 6 #define KRAYNIKCONE 7 #define CUBECONE 8 #define ODD4CONE 9 static facetedge_id *felist; /* for cell-ordered list */ /************************************************************************* * * Function: verpop_film() * * Purpose: Pop vertices with non-minimal tangent cones. Assumes * all edges are at most triple edges. */ int verpop_film() { int popcount = 0; while ( find_vertex_to_pop() ) { popcount++; } return popcount; } /************************************************************************ * * function: find_vertex_to_pop * * purpose: Looks through all vertices and finds one to pop. Whole * process restarts after each pop since popping makes * search lists obsolete. * * Returns number popped (0 or 1) */ int find_vertex_to_pop() { struct verfacet *vflist; facetedge_id fe; facet_id f_id; int count,maxcount; int spot,dups,conetype; int popcount = 0; /* Allocate list, with room for each facet thrice */ maxcount = 3*web.skel[FACET].count; vflist = (struct verfacet *)temp_calloc(sizeof(struct verfacet),maxcount); /* create unsorted list */ count = 0; FOR_ALL_FACETS(f_id) { vflist[count].f_id = f_id; fe = get_facet_fe(f_id); vflist[count].v_id = get_fe_tailv(fe); count++; fe = get_next_edge(fe); vflist[count].f_id = f_id; vflist[count].v_id = get_fe_tailv(fe); count++; fe = get_next_edge(fe); vflist[count].f_id = f_id; vflist[count].v_id = get_fe_tailv(fe); count++; if ( count > maxcount ) { kb_error(1300, "Internal error: Not enough structures allocated for vertex-facet list.\n", RECOVERABLE); return 0; } } /* sort by vertex order */ qsort((char *)vflist,count,sizeof(struct verfacet),FCAST vfcomp); /* go through list and pop appropriate vertices */ for ( spot = 0 ; spot < count ; spot += dups ) { vertex_id v_id = vflist[spot].v_id; /* find how many successive duplicates of current vertex */ for ( dups = 1 ; spot+dups < count ; dups++ ) if ( !equal_id(vflist[spot+dups].v_id,v_id) ) break; if ( get_vattr(v_id) & FIXED ) continue; conetype = cone_analyze(vflist + spot,dups); if ( conetype == KRAYNIKCONE ) popcount += kraynik_pop(v_id,dups); else if ( conetype == CUBECONE ) popcount += cubecone_pop(v_id,dups); else if ( conetype == ODD4CONE ) popcount += odd4cone_pop(v_id,dups); else if ( conetype == OTHERCONE ) popcount += pop_vertex(v_id,dups); temp_free((char *)felist); if ( popcount ) break; /* one vertex at a time */ } temp_free((char *)vflist); return popcount; } /************************************************************************** * * function: pop_given_vertex() * * purpose: pop vertex specified by user. * */ int pop_given_vertex(v_id) vertex_id v_id; { struct verfacet *vflist; facetedge_id fe,start_fe,loop_fe; facet_id f_id; int count,maxcount; int spot,dups,conetype; int popcount = 0; int con_hits; if ( !valid_id(get_vertex_edge(v_id)) ) { if ( !(get_vattr(v_id) & FIXED) ) { if ( verbose_flag ) { sprintf(msg,"Dissolving bare vertex %s\n",ELNAME(v_id)); outstring(msg); } free_element(v_id); return 1; } } if ( web.representation == STRING ) return pop_string_vertex(v_id); con_hits = v_hit_constraint_count(v_id); if ( con_hits ) return pop_constrained_vertex(v_id); /* Allocate list, with room for each facet thrice */ maxcount = 3*web.skel[FACET].count; vflist = (struct verfacet *)temp_calloc(sizeof(struct verfacet),maxcount); /* create unsorted list */ count = 0; start_fe = get_vertex_first_facet(v_id); loop_fe = start_fe; if ( !valid_id(loop_fe) ) return 0; do { f_id = get_fe_facet(loop_fe); vflist[count].f_id = f_id; fe = get_facet_fe(f_id); vflist[count].v_id = get_fe_tailv(fe); count++; fe = get_next_edge(fe); vflist[count].f_id = f_id; vflist[count].v_id = get_fe_tailv(fe); count++; fe = get_next_edge(fe); vflist[count].f_id = f_id; vflist[count].v_id = get_fe_tailv(fe); count++; if ( count > maxcount ) { kb_error(2526, "Internal error: Not enough structures allocated for vertex-facet list.\n", RECOVERABLE); return 0; } loop_fe = get_next_vertex_facet(v_id,loop_fe); } while ( !equal_element(loop_fe,start_fe)); /* sort by vertex order */ qsort((char *)vflist,count,sizeof(struct verfacet),FCAST vfcomp); /* go through list and pop appropriate vertices */ for ( spot = 0 ; spot < count ; spot += dups ) { if ( v_id != vflist[spot].v_id ) { dups = 1; continue; } /* find how many successive duplicates of current vertex */ for ( dups = 1 ; spot+dups < count ; dups++ ) if ( !equal_id(vflist[spot+dups].v_id,v_id) ) break; if ( get_vattr(v_id) & FIXED ) continue; conetype = cone_analyze(vflist + spot,dups); if ( conetype == KRAYNIKCONE ) popcount += kraynik_pop(v_id,dups); else if ( conetype == CUBECONE ) popcount += cubecone_pop(v_id,dups); else if ( conetype == ODD4CONE ) popcount += odd4cone_pop(v_id,dups); else if ( conetype == OTHERCONE ) popcount += pop_vertex(v_id,dups); temp_free((char *)felist); break; /* one vertex at a time */ } temp_free((char *)vflist); return popcount; } /************************************************************************** * * function: figure_type() * * purpose: figure out what type of tangent cone a vertex has. * */ #define CELLMAX 300 #define ARCMAX 600 static struct cell { int start; /* arc list start in arclist */ int festart; /* starting place in felist */ int num; /* number of arcs */ int fenum; /* number of facetedges */ REAL area; /* external angle deficit */ body_id b_id; /* which body, if any */ } cell[CELLMAX]; static struct arc { int start; /* edge list start in felist */ int num; /* number of edges */ int valence; /* number of arcs into head node */ /* int headtype; */ /* type of head node */ } arclist[ARCMAX]; static int cells; int figure_type ARGS((int)); int figure_type(arcs) int arcs; { int k,type; /* Classifying cone */ switch ( cells ) { case 1: /* wire boundary */ type = WIREBOUNDARY; break; case 2: switch ( arcs ) { case 2: /* internal plane */ type = PLANECONE; break; case 4: /* two wings on wire boundary */ type = WIREWING; /* minimality depends on angle */ break; default: /* n boundary wires meet at center */ type = WIRECORNER; break; } break; case 3: switch ( arcs ) { case 6: /* triple edge */ type = TRIPLE_EDGER; for ( k = 0 ; k < cells ; k++ ) if ( cell[k].num != 2 ) type = OTHERCONE; break; default: type = OTHERCONE; break; } break; case 4: switch ( arcs ) { case 12: /* maybe tetrahedral cone */ { int twos=0,threes=0,fours=0; for ( k = 0 ; k < cells ; k++ ) { switch ( cell[k].num ) { case 2: twos++; break; case 3: threes++; break; case 4: fours++; break; } } if ( threes == 4 ) type = TETRAHEDRAL; else if ( twos==2 && fours==2 ) type = ODD4CONE; else type = OTHERCONE; } break; default: type = OTHERCONE; break; } break; case 5: if ( !kraynikpopvertex_flag || (arcs != 18) ) { type = OTHERCONE; break; } type = KRAYNIKCONE; for ( k = 0 ; k < cells ; k++ ) if ( (cell[k].num < 3) || (cell[k].num > 4) ) { type = OTHERCONE; break; } break; case 6: type = CUBECONE; if ( arcs != 24 ) { type = OTHERCONE; break; } for ( k = 0 ; k < cells ; k++ ) if ( cell[k].num != 4 ) { type = OTHERCONE; break; } break; default: type = OTHERCONE; break; } return type; } /*************************************************************************** * * function: cone_analyze() * * purpose: construct tangent cone structure around vertex and * return cone type from figure_type(). * */ int cone_analyze(vf,count) struct verfacet *vf; int count; { int type; /* final classification */ facetedge_id fe,nextfe,ray,nextray; vertex_id v_id = vf->v_id; int cellstart,arcstart,cellsides; int arclen,valence; int arcs; int k,j; int nodeflag; /* whether any interesting node has been found for cell */ /* First, set up network structure of edges opposite central vertex */ felist = (facetedge_id *)temp_calloc(sizeof(facetedge_id),2*count); /* fill with outside edges of facets */ for ( k = 0 ; k < count ; k++ ) { fe = get_facet_fe(vf[k].f_id); if ( equal_id(v_id,get_fe_tailv(fe)) ) fe = get_next_edge(fe); else if ( equal_id(v_id,get_fe_headv(fe)) ) fe = get_prev_edge(fe); felist[2*k] = fe; felist[2*k+1] = inverse_id(fe); } /* order into arcs and cell boundaries */ k = 0; /* current edge number */ cells = 0; /* current cell number */ arcs = 0; /* current arc number */ while ( k < 2*count ) { CELLSTART: /* starting new cell */ if ( k >= 2*count ) break; if ( cells >= CELLMAX ) { sprintf(errmsg,"pop: Too many cells around vertex %s in cone_analyze().\n", ELNAME(v_id)); kb_error(1301,errmsg, RECOVERABLE); } nodeflag = 0; /* whether interesting node found on cell */ cellstart = k; cellsides = 0; cell[cells].start = arcs; ARCSTART: /* starting new arc */ if ( arcs >= ARCMAX ) { sprintf(errmsg,"pop: Too many arcs around vertex %s in cone_analyze().\n", ELNAME(v_id)); kb_error(1302,errmsg, RECOVERABLE); } arcstart = k; arclist[arcs].start = arcstart; ARCRESTART: arclen = 0; for (k = arcstart; k < 2*count ;k++) { /* starting next edge */ arclen++; fe = felist[k]; nextray = ray = get_next_edge(fe); for ( valence = 1 ; ; valence++ ) { nextray = get_next_facet(nextray); if ( equal_id(nextray,ray) ) break; } nextray = get_next_facet(ray); nextfe = get_next_edge(inverse_id(nextray)); if ( equal_id(nextfe,felist[cellstart]) ) /* have gone around */ { arclist[arcs].num = arclen; arclist[arcs].valence = valence; arcs++; cellsides++; cell[cells].num = cellsides; cells++; k++; goto CELLSTART; } /* linear search list until nextfe found */ for ( j = k+1 ; j < 2*count ; j++ ) if ( equal_id(nextfe,felist[j]) ) break; if ( !equal_id(nextfe,felist[j]) ) { sprintf(errmsg, "Internal error: cone_analyze vertex %s: can't find nextfe in felist.\n", ELNAME(v_id)); kb_error(1303,errmsg,RECOVERABLE); } if ( j > k+1 ) /* swap into place */ { felist[j] = felist[k+1]; felist[k+1] = nextfe; } /* see if node between is interesting */ if ( (valence != 2)) { /* have interesting node */ if ( nodeflag == 0 ) { /* first interesting node for cell */ /* reset felist so cell and arc start are same */ felist[k] = felist[arcstart]; felist[arcstart] = nextfe; felist[k+1] = fe; nodeflag = 1; goto ARCRESTART; } arclist[arcs].num = arclen; arclist[arcs].valence = valence; arcs++; cellsides++; k++; goto ARCSTART; } } } type = figure_type(arcs); /* so stupid SUN can optimize */ return type; } /******************************************************************* * * Function: pop_vertex() * * Purpose: pops one non-minimal vertex using info found by * cone_analyze(). Finds areas of each face. All * face cones are pulled out to sphere except largest * area. Special treatment for face with disjoint * boundary. */ int pop_vertex(v_id,count) vertex_id v_id; int count; /* number of entries in vflist */ { int i,j,k,m; struct arc *ar; int fenum; facetedge_id fe; REAL cosdef; REAL prevnormal[MAXCOORD],prevnorm; REAL thisnormal[MAXCOORD],thisnorm; REAL maxarea = 0.0; int bigcell=0; REAL total_area = 0.0; REAL total_angle,angle; REAL side[MAXCOORD],ray[MAXCOORD]; REAL *vx,newx[MAXCOORD]; edge_id ray_e; facetedge_id ray_fe; vertex_id new_v; conmap_t *old_conmap; int old_bdrynum = -1; int samecell2, othercell2; body_id bs1,bs2,bo1,bo2; REAL totside1length,totside2length; int flipflag = 0; /* if cones switched in widecones */ if ( verbose_flag ) { sprintf(msg,"Popping vertex %s\n",ELNAME(v_id)); outstring(msg); } /* fix up cell structures */ for ( i = 0 ; i < cells ; i++ ) { cell[i].fenum = 0; ar = arclist + cell[i].start; cell[i].festart = ar->start; for ( j = 0 ; j < cell[i].num ; j++, ar++ ) cell[i].fenum += ar->num; } /* check for special configuration of touching disjoint cones that may want to be merged rather than split */ if ( (cells == 4) && (cell[0].num == 1) && (cell[1].num == 1) && (cell[2].num == 1) && (cell[3].num == 1) ) { REAL cosa,bestcosa; facetedge_id bestray1=NULLID, bestray2=NULLID,fe_1,fe_2; int samecell,othercell; REAL area1,area2,normal1[MAXCOORD],normal2[MAXCOORD]; REAL totnorm1[MAXCOORD],totnorm2[MAXCOORD]; REAL axis1[MAXCOORD], axis2[MAXCOORD]; REAL side1[MAXCOORD], side2[MAXCOORD]; facet_id f_1,f_2,f_septum=NULLID; REAL ratio; int middleflag = 0; /* whether to put in septum */ facetedge_id first_fe3=NULLID, prev_fe3, newfe3; body_id b_1, b_2; /* first, see which cells are really the same cones */ samecell = 0; for ( i = 1 ; i < cells ; i++ ) { for ( j = 0 ; j < cell[0].fenum ; j++ ) for ( k = 0 ; k < cell[i].fenum ; k++ ) if (equal_element(felist[cell[0].festart+j],felist[cell[i].festart+k])) { samecell = i; break; } } if ( samecell == 0 ) { sprintf(errmsg,"Vertex %s looks like double cones but isn't.\n", ELNAME(v_id)); kb_error(2890,errmsg,RECOVERABLE); } othercell = (samecell == 1) ? 2 : 1; /* get axis and see if cones wide enough to pop this way */ area1 = area2 = 0.0; memset((char*)axis1,0,sizeof(axis1)); memset((char*)axis2,0,sizeof(axis2)); memset((char*)totnorm1,0,sizeof(totnorm1)); memset((char*)totnorm2,0,sizeof(totnorm2)); totside1length = totside2length = 0.0; for ( i = 0 ; i < cell[samecell].fenum ; i++ ) { fe_1 = felist[cell[samecell].festart+i]; f_1 = get_fe_facet(fe_1); get_facet_normal(f_1,normal1); area1 += sqrt(dot(normal1,normal1,SDIM)); for ( j = 0 ; j < SDIM ; j++ ) totnorm1[j] += normal1[j]; get_fe_side(get_prev_edge(fe_1),side1); for ( j = 0 ; j < SDIM ; j++ ) axis1[j] += side1[j]; totside1length += sqrt(dot(side1,side1,SDIM)); } for ( i = 0 ; i < cell[othercell].fenum ; i++ ) { fe_2 = felist[cell[othercell].festart+i]; f_2 = get_fe_facet(fe_2); get_facet_normal(f_2,normal2); area2 += sqrt(dot(normal2,normal2,SDIM)); for ( j = 0 ; j < SDIM ; j++ ) totnorm2[j] += normal2[j]; get_fe_side(get_prev_edge(fe_2),side2); for ( j = 0 ; j < SDIM ; j++ ) axis2[j] += side2[j]; totside2length += sqrt(dot(side2,side2,SDIM)); } if ( pop_disjoin_flag ) goto narrowcones; if ( pop_enjoin_flag ) goto widecones; /* test wideness */ ratio = (sqrt(dot(totnorm1,totnorm1,SDIM))+sqrt(dot(totnorm2,totnorm2,SDIM))) /(area1 + area2); if ( ratio < (middleflag?0.94:0.88) ) goto narrowcones; widecones: /* choose cones to get facet normals going same way */ for ( j = 1 ; j < cells ; j++ ) if ( (j != samecell) && (j != othercell) ) break; othercell2 = j; samecell2 = 0; /* if ( dot(axis2,totnorm2,SDIM) < 0 ) { int tmp = othercell; othercell = othercell2; othercell2 = tmp; } if ( dot(axis1,totnorm1,SDIM) > 0 ) { samecell2 = samecell; samecell = 0; } */ if ( dot(totnorm1,totnorm2,SDIM) < 0 ) { int tmp = othercell; othercell = othercell2; othercell2 = tmp; for ( i = 0 ; i < SDIM ; i++ ) { axis2[i] *= -1; totnorm2[i] *= -1; } } if ( dot(axis1,totnorm1,SDIM)/totside1length > dot(axis2,totnorm1,SDIM)/totside2length ) { int tmp = othercell; othercell = samecell; samecell = tmp; tmp = samecell2; samecell2 = othercell2; othercell2 = tmp; } bs1 = get_facet_body(get_fe_facet(felist[cell[samecell].festart])); bs2 = get_facet_body(get_fe_facet(felist[cell[samecell2].festart])); bo1 = get_facet_body(get_fe_facet(felist[cell[othercell].festart])); bo2 = get_facet_body(get_fe_facet(felist[cell[othercell2].festart])); if ( !equal_id(bs2,bo1) ) { /* have to flip sides */ int tmp; body_id btmp; if ( !equal_id(bs1,bo2) ) { /* no matching sides. Shouldn't happen. */ return 0; } tmp = othercell; othercell = othercell2; othercell2 = tmp; btmp = bs1; bs1 = bs2; bs2 = btmp; tmp = samecell; samecell = samecell2; samecell2 = tmp; btmp = bo1; bo1 = bo2; bo2 = btmp; } /* test bodies to see if we want septum */ fe_1 = felist[cell[samecell].festart]; f_1 = get_fe_facet(fe_1); fe_2 = felist[cell[othercell].festart]; f_2 = get_fe_facet(fe_2); b_1 = get_facet_body(f_1); b_2 = get_facet_body(inverse_id(f_2)); if ( !valid_id(b_1) || !valid_id(b_2) || !equal_id(b_1,b_2) ) { middleflag = 1; f_septum = dup_facet(f_1); set_facet_body(f_septum,b_1); set_facet_body(inverse_id(f_septum),b_2); } /* now find closest rays on opposite cells */ bestcosa = -1.0; for ( i = 0 ; i < cell[samecell].fenum ; i++ ) { REAL edge1[MAXCOORD],edge2[MAXCOORD]; facetedge_id ray1 = get_prev_edge(felist[cell[samecell].festart+i]); get_fe_side(ray1,edge1); for ( j = 0 ; j < cell[othercell].fenum ; j++ ) { facetedge_id ray2 = get_prev_edge(felist[cell[othercell].festart+j]); get_fe_side(ray2,edge2); cosa = dot(edge1,edge2,SDIM)/sqrt(dot(edge1,edge1,SDIM)* dot(edge2,edge2,SDIM)); if ( cosa > bestcosa ) { bestray1 = ray1; bestray2 = ray2; bestcosa = cosa; } } } /* get lower number of sides in first cone */ if ( cell[samecell].fenum > cell[othercell].fenum ) { int tmp = samecell; facetedge_id fetmp = bestray1; samecell = othercell; othercell = tmp; bestray1 = bestray2; bestray2 = fetmp; flipflag = 1; } /* go around, inserting edges */ fe_1 = bestray1; fe_2 = bestray2; prev_fe3 = NULLID; for ( j = 0 ; j < cell[samecell].fenum ; j++ ) { edge_id e_1,e_1next,e_2next,newe; facetedge_id fe_1next,fe_2next,newfe1,newfe2; facet_id f_1,f_2; vertex_id newv; REAL *x; e_1 = get_fe_edge(fe_1); fe_1next = inverse_id(get_prev_edge(fe_1)); e_1next = get_fe_edge(fe_1next); fe_2next = inverse_id(get_prev_edge(fe_2)); e_2next = get_fe_edge(fe_2next); f_1 = get_fe_facet(fe_1next); f_2 = get_fe_facet(fe_2next); if ( j < cell[samecell].fenum - 1 ) { newv = dup_vertex(v_id); remove_vertex_edge(v_id,e_1next); remove_vertex_edge(v_id,e_2next); set_edge_tailv(e_1next,newv); set_edge_tailv(e_2next,newv); } else newv = v_id; get_edge_side(e_1next,side1); get_edge_side(e_2next,side2); x = get_coord(newv); for ( k = 0 ; k < SDIM ; k++ ) x[k] += 0.25*(side1[k]+side2[k]); newe = new_edge(get_edge_tailv(e_1),newv,e_1); newfe1 = new_facetedge(f_1,newe); newfe2 = new_facetedge(f_2,newe); set_edge_fe(newe,newfe1); set_prev_facet(newfe1,newfe2); set_next_facet(newfe2,newfe1); set_prev_facet(newfe2,newfe1); set_next_facet(newfe1,newfe2); if ( middleflag ) { newfe3 = new_facetedge(f_septum,newe); if ( flipflag ) { set_prev_facet(newfe3,newfe2); set_next_facet(newfe2,newfe3); set_prev_facet(newfe1,newfe3); set_next_facet(newfe3,newfe1); } else { set_prev_facet(newfe3,newfe1); set_next_facet(newfe1,newfe3); set_prev_facet(newfe2,newfe3); set_next_facet(newfe3,newfe2); } if ( valid_id(prev_fe3) ) { set_next_edge(prev_fe3,newfe3); set_prev_edge(newfe3,prev_fe3); prev_fe3 = newfe3; } else { first_fe3 = newfe3; set_facet_fe(f_septum,first_fe3); prev_fe3 = newfe3; } } set_next_edge(newfe1,fe_1next); set_prev_edge(fe_1next,newfe1); set_prev_edge(newfe1,inverse_id(fe_1)); set_next_edge(inverse_id(fe_1),newfe1); set_next_edge(newfe2,fe_2next); set_prev_edge(fe_2next,newfe2); set_prev_edge(newfe2,inverse_id(fe_2)); set_next_edge(inverse_id(fe_2),newfe2); cross_cut(newfe1,fe_1next); cross_cut(newfe2,fe_2next); fe_1 = get_next_facet(fe_1next); fe_2 = get_next_facet(fe_2next); } if ( middleflag ) { set_next_edge(prev_fe3,first_fe3); set_prev_edge(first_fe3,prev_fe3); if ( cell[samecell].fenum > 3 ) face_triangulate(f_septum,cell[samecell].fenum); } return 1; narrowcones: ; /* bailout if not poppable this way */ } /* see if any cells are totally detachable */ for ( i = 0 ; i < cells ; i++ ) { vertex_id newv; ar = arclist + cell[i].start; if ( (cell[i].num != 1) || (ar->valence != 2) ) continue; /* now have one */ newv = dup_vertex(v_id); for ( j = 0 ; j < ar->num ; j++ ) { fe = felist[ar->start+j]; ray_e = get_fe_edge(get_next_edge(fe)); remove_vertex_edge(v_id,inverse_id(ray_e)); set_edge_headv(ray_e,newv); } return 1; /* safe to do only one at a time */ } /* calculate areas of each cell */ for ( i = 0 ; i < cells ; i++ ) { total_angle = 0.0; fenum = cell[i].festart; fe = felist[fenum+cell[i].fenum-1]; cell[i].b_id = get_facet_body(get_fe_facet(inverse_id(fe))); get_fe_side(fe,side); get_fe_side(get_prev_edge(fe),ray); cross_prod(ray,side,prevnormal); prevnorm = sqrt(SDIM_dot(prevnormal,prevnormal)); for ( k = 0 ; k < cell[i].fenum ; k++,fenum++ ) { fe = felist[fenum]; get_fe_side(fe,side); get_fe_side(get_prev_edge(fe),ray); cross_prod(ray,side,thisnormal); thisnorm = sqrt(SDIM_dot(thisnormal,thisnormal)); cosdef = SDIM_dot(prevnormal,thisnormal)/prevnorm/thisnorm; if ( cosdef > 1.0 ) angle = 0.0; else if ( cosdef < -1.0 ) angle = M_PI; else angle = acos(cosdef); if ( SDIM_dot(side,prevnormal) > 0.0 ) total_angle += angle; else total_angle -= angle; /* in case cell not convex */ /* set up for next loop */ prevnorm = thisnorm; memcpy((char *)prevnormal,(char *)thisnormal,sizeof(prevnormal)); } cell[i].area = 2*M_PI - total_angle; total_area += cell[i].area; } /* kludge to adjust for 2*pi ambiguity in turning angle that can affect badly shaped cells */ while ( total_area > 4*M_PI + 1 ) { int maxi = -1; REAL maxa = -1e30; for ( i = 0 ; i < cells ; i++ ) if ( cell[i].area > maxa ) { maxi = i; maxa = cell[i].area; } cell[maxi].area -= 2*M_PI; total_area -= 2*M_PI; } while ( total_area < 4*M_PI - 1) { int mini = -1; REAL mina = 1e30; for ( i = 0 ; i < cells ; i++ ) if ( cell[i].area < mina ) { mini = i; mina = cell[i].area; } cell[mini].area += 2*M_PI; total_area += 2*M_PI; } if ( total_area < 4*M_PI - 0.00001 ) { sprintf(errmsg, "Solid angle deficit %g found around vertex %s. Not popped.\n", (double)(4*M_PI-total_area), ELNAME(v_id)); kb_error(2176,errmsg,WARNING); return 0; } if ( total_area > 4*M_PI + 0.00001 ) { sprintf(errmsg, "Solid angle excess %g found around vertex %s. Not popped.\n", (double)(total_area-4*M_PI), ELNAME(v_id)); kb_error(2177,errmsg,WARNING); return 0; } /* see which cell is the largest */ for ( i = 0 ; i < cells ; i++ ) if ( cell[i].area > maxarea ) { bigcell = i; maxarea = cell[i].area; } /* Now go around covering over all cells but bigcell or cells bigger than hemisphere */ /* First, put new endpoint on all edges coming into v_id */ vx = get_coord(v_id); old_conmap = get_v_constraint_map(v_id); if ( get_vattr(v_id) & BOUNDARY ) { old_bdrynum = get_vertex_boundary_num(v_id); } for ( j = 0 ; j < 2*count ; j++ ) { REAL rayvec[MAXCOORD]; fe = felist[j]; ray_fe = get_prev_edge(fe); if ( !equal_id(v_id,get_fe_tailv(ray_fe)) ) continue; /* done this */ ray_e = get_fe_edge(ray_fe); get_edge_side(ray_e,rayvec); for ( m = 0 ; m < SDIM ; m++ ) newx[m] = 0.5*rayvec[m] + vx[m]; new_v = new_vertex(newx,ray_e); remove_vertex_edge(v_id,ray_e); set_edge_tailv(ray_e,new_v); if ( old_bdrynum >= 0 ) set_vertex_boundary_num(new_v,old_bdrynum); else set_v_conmap(new_v,old_conmap); } /* divide up old facets */ for ( j = 0 ; j < 2*count ; j++ ) { facetedge_id new_fe,new_fe_next,new_fe_prev; facetedge_id inray_fe; vertex_id v1,v2; edge_id new_e; fe = felist[j]; ray_fe = get_prev_edge(fe); inray_fe = get_next_edge(fe); v1 = get_fe_tailv(ray_fe); v2 = get_fe_headv(inray_fe); /* Each facet gets covered twice. First time we put in edge; second time triangulate the quadrilateral. This way we can substitute the new inner fe for the old outer fe in felist. */ if ( equal_id(inray_fe,get_prev_edge(ray_fe)) ) { /* first time around */ /* put in new edge */ new_e = new_edge(v2,v1,get_fe_facet(fe)); new_fe = new_facetedge(get_fe_facet(fe),new_e); set_edge_fe(new_e,new_fe); set_next_edge(inray_fe,new_fe); set_prev_edge(new_fe,inray_fe); set_next_edge(new_fe,ray_fe); set_prev_edge(ray_fe,new_fe); if ( old_bdrynum >= 0 ) set_edge_boundary_num(new_e,old_bdrynum); else set_e_conmap(new_e,old_conmap); /* stub fe's for cells on each side */ new_fe_prev = new_facetedge(NULLFACET,new_e); new_fe_next = new_facetedge(NULLFACET,new_e); set_next_facet(new_fe,new_fe_next); set_next_facet(new_fe_next,new_fe_prev); set_next_facet(new_fe_prev,new_fe); set_prev_facet(new_fe,new_fe_prev); set_prev_facet(new_fe_prev,new_fe_next); set_prev_facet(new_fe_next,new_fe); felist[j] = inverse_id(new_fe_next); /* new inner fe */ } else { felist[j] = inverse_id(get_next_facet(get_next_edge(inray_fe))); divide_quad(fe); } } /* now connect up insides of cells */ for ( i = 0 ; i < cells ; i++ ) { facet_id new_f=0,old_f; facetedge_id prev_fe=0; /* last in edge chain */ fenum = cell[i].festart; if ( (i != bigcell) && (cell[i].area < 2*M_PI) ) { prev_fe = felist[fenum+cell[i].fenum-1]; old_f = facet_inverse(get_fe_facet(get_next_facet(prev_fe))); new_f = dup_facet(old_f); set_facet_body(new_f,cell[bigcell].b_id); set_facet_body(inverse_id(new_f),cell[i].b_id); set_facet_fe(new_f,prev_fe); } for ( k = 0 ; k < cell[i].fenum ; k++,fenum++ ) { fe = felist[fenum]; if ( (i == bigcell) || (cell[i].area >= 2*M_PI) ) { /* have to excise fe */ set_prev_facet(get_next_facet(fe),get_prev_facet(fe)); set_next_facet(get_prev_facet(fe),get_next_facet(fe)); free_element(fe); } else { /* hook into edge loop around facet */ set_prev_edge(fe,prev_fe); set_next_edge(prev_fe,fe); set_fe_facet(fe,new_f); prev_fe = fe; } } /* if necessary, triangulate new cell facet */ if ( (i != bigcell) && (cell[i].area < 2*M_PI) ) { if ( cell[i].fenum >= 5 ) face_triangulate(new_f,cell[i].fenum); else if ( cell[i].fenum == 4 ) divide_quad(get_facet_fe(new_f)); } } /* free original central vertex */ free_element(v_id); return 1; } /******************************************************************* * * Function: kraynik_pop() * * Purpose: pops one non-minimal vertex of type KRAYNIKCONE. */ int kraynik_pop(v_id,count) vertex_id v_id; int count; /* number of entries in vflist */ { int i,j,k,m,kk; struct arc *ar; facetedge_id fe; vertex_id newv; edge_id newe; edge_id head_triples[3],tail_triples[3]; REAL tailside[MAXCOORD],headside[MAXCOORD]; REAL tailforce[MAXCOORD],headforce[MAXCOORD]; REAL mag; /* find the 3-arc cells */ for ( k = 0 ; k < cells ; k++ ) if ( cell[k].num == 3 ) break; for ( kk = k+1 ; kk < cells ; kk++ ) if ( cell[kk].num == 3 ) break; if ( kk == cells ) return 0; /* failure */ for ( i = 0 ; i < SDIM ; i++ ) tailforce[i] = headforce[i] = 0.0; /* get triple lines and vectors to see which way to pop */ for ( m = 0 ; m < 3 ; m++ ) { ar = arclist + cell[k].start + m; fe = felist[ar->start]; tail_triples[m] = get_fe_edge(get_prev_edge(fe)); get_edge_side(tail_triples[m],tailside); mag = sqrt(dot(tailside,tailside,SDIM)); if ( mag != 0 ) for ( i = 0 ; i < SDIM ; i++ ) tailforce[i] += tailside[i]/mag; ar = arclist + cell[kk].start + m; fe = felist[ar->start]; head_triples[m] = get_fe_edge(get_prev_edge(fe)); get_edge_side(head_triples[m],headside); mag = sqrt(dot(headside,headside,SDIM)); if ( mag != 0 ) for ( i = 0 ; i < SDIM ; i++ ) headforce[i] += headside[i]/mag; } if ( pop_to_face_flag || ( !pop_to_edge_flag && (dot(headforce,headforce,SDIM)+dot(tailforce,tailforce,SDIM) < 3)) ) return pop_vertex_to_tri(v_id,tail_triples); newv = dup_vertex(v_id); newe = new_edge(newv,v_id,NULLID); /* go around the 3 arcs and reconfigure */ for ( m = 0 ; m < 3 ; m++ ) { facetedge_id ray_fe,other_ray_fe,third_ray_fe; facetedge_id newfe_a,newfe_b,newfe_c; edge_id ray_e,newray_e; facet_id newf; ar = arclist + cell[k].start + m; for ( j = 0 ; j < ar->num ; j++ ) { fe = felist[ar->start+j]; ray_e = get_fe_edge(get_next_edge(fe)); remove_vertex_edge(v_id,inverse_id(ray_e)); set_edge_headv(ray_e,newv); } /* last one for splitting triple edge */ j = ar->num-1; fe = felist[ar->start+j]; ray_fe = get_next_edge(fe); ray_e = get_fe_edge(ray_fe); newray_e = dup_edge(ray_e); set_edge_headv(newray_e,v_id); set_edge_tailv(newray_e,get_edge_tailv(ray_e)); other_ray_fe = inverse_id(get_prev_edge(felist[(ar+(m==2?-2:1))->start])); third_ray_fe = get_next_facet(ray_fe); if ( equal_id(third_ray_fe,other_ray_fe) ) third_ray_fe = get_prev_facet(ray_fe); newf = dup_facet(get_fe_facet(third_ray_fe)); newfe_a = new_facetedge(newf,newe); newfe_b = new_facetedge(newf,inverse_id(newray_e)); newfe_c = new_facetedge(newf,ray_e); set_facet_fe(newf,newfe_a); if ( m == 0 ) set_edge_fe(newe,newfe_a); set_next_edge(newfe_a,newfe_b); set_next_edge(newfe_b,newfe_c); set_next_edge(newfe_c,newfe_a); set_prev_edge(newfe_a,newfe_c); set_prev_edge(newfe_b,newfe_a); set_prev_edge(newfe_c,newfe_b); if ( equal_id(ray_fe,get_next_facet(other_ray_fe)) ) { set_next_facet(ray_fe,newfe_c); set_prev_facet(newfe_c,ray_fe); set_prev_facet(other_ray_fe,newfe_c); set_next_facet(newfe_c,other_ray_fe); } else { set_prev_facet(ray_fe,newfe_c); set_next_facet(newfe_c,ray_fe); set_next_facet(other_ray_fe,newfe_c); set_prev_facet(newfe_c,other_ray_fe); } set_edge_fe(ray_e,ray_fe); set_edge_fe(newray_e,third_ray_fe); set_next_facet(third_ray_fe,inverse_id(newfe_b)); set_prev_facet(third_ray_fe,inverse_id(newfe_b)); set_next_facet(inverse_id(newfe_b),third_ray_fe); set_prev_facet(inverse_id(newfe_b),third_ray_fe); set_fe_edge(third_ray_fe,newray_e); set_edge_fe(newray_e,third_ray_fe); if ( m == 1 ) { set_next_facet(newfe_a,get_edge_fe(newe)); set_prev_facet(newfe_a,get_edge_fe(newe)); set_next_facet(get_edge_fe(newe),newfe_a); set_prev_facet(get_edge_fe(newe),newfe_a); } else if ( m == 2 ) { facetedge_id fe1 = get_edge_fe(newe); facetedge_id fe2 = get_next_facet(fe1); set_next_facet(newfe_a,fe2); set_prev_facet(fe2,newfe_a); set_prev_facet(newfe_a,fe1); set_next_facet(fe1,newfe_a); } } /* move a bit to avoid zero area facets */ new_vertex_average(newv,RAWEST); new_vertex_average(v_id,RAWEST); return 1; /* success */ } /******************************************************************* * * Function: cubecone_pop() * * Purpose: pops one non-minimal vertex of type CUBECONE. * Inserts central square in most favorable orientation. */ int cubecone_pop(v_id,count) vertex_id v_id; int count; /* number of entries in vflist */ { int i,k,m; struct arc *ar; facetedge_id fe; REAL side[MAXCOORD]; REAL faceforce[MAXCOORD]; REAL facemag,bestmag,mag; int bestk=0; edge_id triples[6][4]; /* find mean triple edge vectors for each face */ /* get triple lines and vectors to see which way to pop */ bestmag = 1e20; for ( k = 0 ; k < 6 ; k++ ) { for ( i = 0 ; i < SDIM ; i++ ) faceforce[i] = 0.0; for ( m = 0 ; m < 4 ; m++ ) { ar = arclist + cell[k].start + m; fe = felist[ar->start]; triples[k][m] = get_fe_edge(get_prev_edge(fe)); get_edge_side( triples[k][m],side); mag = sqrt(dot(side,side,SDIM)); if ( mag != 0 ) for ( i = 0 ; i < SDIM ; i++ ) faceforce[i] += side[i]/mag; } facemag = sqrt(dot(faceforce,faceforce,SDIM)); if ( facemag < bestmag ) { bestmag = facemag; bestk = k; } } return pop_vertex_to_quad(v_id,triples[bestk]); } /******************************************************************** * * Function: facet_force_on_vertex() * * Purpose: Calculate force of a facet on one of its vertices, * force due to surface tension with density. For * determining pop directions. Note the vector in fvec * is the force, i.e. the negative of the area gradient. */ void facet_force_on_vertex(f_id,v_id,fvec) facet_id f_id; vertex_id v_id; REAL *fvec; /* returned force */ { REAL sides[2][MAXCOORD]; MAT2D(vx,FACET_VERTS,MAXCOORD); int i,j,basei=0; facetedge_id fe; REAL s1s1,s1s2,s2s2,det; REAL tension = get_facet_density(f_id); REAL coeff; get_facet_verts(f_id,vx,NULL); fe = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { if ( equal_id(get_fe_tailv(fe),v_id) ) basei = i; fe = get_next_edge(fe); } for ( j = 0 ; j < SDIM ; j++ ) { sides[0][j] = vx[1][j] - vx[0][j]; sides[1][j] = vx[2][j] - vx[0][j]; } s1s1 = dot(sides[0],sides[0],SDIM); s2s2 = dot(sides[1],sides[1],SDIM); s1s2 = dot(sides[0],sides[1],SDIM); det = s1s1*s2s2 - s1s2*s1s2; if ( det <= 0.0 ) { for ( j = 0 ; j < SDIM ; j++ ) fvec[j] = 0.0; } coeff = tension/2/sqrt(det); if ( basei == 0 ) { for ( j = 0 ; j < SDIM ; j++ ) fvec[j] = coeff*((s2s2*sides[0][j] - s1s2*sides[1][j]) + (s1s1*sides[1][j] - s1s2*sides[0][j])); } else if ( basei == 1 ) { for ( j = 0 ; j < SDIM ; j++ ) fvec[j] = -coeff*(s2s2*sides[0][j] - s1s2*sides[1][j]); } else { for ( j = 0 ; j < SDIM ; j++ ) fvec[j] = -coeff*(s1s1*sides[1][j] - s1s2*sides[0][j]); } } /* end facet_force_on_vertex() */ /******************************************************************* * * Function: odd4cone_pop() * * Purpose: pops one non-minimal vertex of type ODD4CONE, * which is two triple lines touching at the vertex. * Can either detach the triple lines or split into * two tetrahedral points with septum between. */ int odd4cone_pop(v_id,count) vertex_id v_id; int count; /* number of entries in vflist */ { int i,j,k; facetedge_id fe_a=NULLID,fe_b=NULLID; /* triple edge fe's on septum side*/ facetedge_id fe_c=NULLID,fe_d=NULLID; /* other pair of triple edge fe's */ edge_id e_a, e_b, e_c, e_d; /* triple edges */ facet_id f_a,f_b,f_c, f_d; /* septum facets */ REAL veca[MAXCOORD],vecb[MAXCOORD],vecc[MAXCOORD],vecd[MAXCOORD]; REAL vecab[MAXCOORD],vecac[MAXCOORD],veccd[MAXCOORD],vecbd[MAXCOORD]; int flag; int split_cell=0; int cdswap = 0; REAL tripnetforce; /* force towards separate triples; pos favorable */ REAL quadnetforce; /* force towards quad line; pos favorable */ facetedge_id next_fe,fe; REAL ee,ea,eb,ec,ed,aa,bb,cc,dd,bd,ac,acac,bdbd; REAL eside[MAXCOORD]; int counter; /* identify key parts */ /* find two triple lines on one side */ for ( i = 0, flag = 0 ; i < cells ; i++ ) { if ( cell[i].num == 2 ) { if ( flag ) { fe_c = felist[arclist[cell[i].start].start]; /* outer fe */ fe_c = get_prev_edge(fe_c); /* radial fe */ fe_c = get_prev_facet(fe_c); /* on septum */ fe_d = felist[arclist[cell[i].start+1].start]; fe_d = get_prev_edge(fe_d); /* radial fe */ fe_d = get_prev_facet(fe_d); /* on septum */ } else { fe_a = felist[arclist[cell[i].start].start]; /* outer fe */ fe_a = get_prev_edge(fe_a); /* radial fe */ fe_a = get_prev_facet(fe_a); /* on septum */ fe_b = felist[arclist[cell[i].start+1].start]; fe_b = get_prev_edge(fe_b); /* radial fe */ fe_b = get_prev_facet(fe_b); /* on septum */ split_cell = i; flag = 1; } } } /* get a and c on same side of vertex */ next_fe = fe_a; counter = 0; for(;;) { next_fe = inverse_id(get_prev_edge(next_fe)); if ( equal_id(next_fe,fe_c) || equal_id(next_fe,fe_d)) break; if ( !equal_id(get_next_facet(next_fe),get_prev_facet(next_fe)) || (++counter > 100) ) { sprintf(errmsg,"Internal: Cannot match fe_c with fe_a at vertex %s; skipping pop.\n", ELNAME(v_id)); kb_error(3692,errmsg,RECOVERABLE); return 0; } next_fe = get_next_facet(next_fe); } if ( equal_id(next_fe,fe_d) ) { /* swap c and d */ facetedge_id temp = fe_c; fe_c = fe_d; fe_d = temp; cdswap = 1; } /* more parts */ e_a = get_fe_edge(fe_a); e_b = get_fe_edge(fe_b); e_c = get_fe_edge(fe_c); e_d = get_fe_edge(fe_d); f_a = get_fe_facet(fe_a); f_b = get_fe_facet(fe_b); f_c = get_fe_facet(fe_c); f_d = get_fe_facet(fe_d); get_edge_side(e_a,veca); get_edge_side(e_b,vecb); get_edge_side(e_c,vecc); get_edge_side(e_d,vecd); for ( i = 0 ; i < SDIM ; i++ ) { vecab[i] = (veca[i] + vecb[i])/4; veccd[i] = (vecc[i] + vecd[i])/4; vecac[i] = (veca[i] + vecc[i])/4; vecbd[i] = (vecb[i] + vecd[i])/4; } /* determine which way to split */ /* First, if pull triple lines apart*/ tripnetforce = 0.0; for ( i = 0, flag = 0 ; i < cells ; i++ ) { /* forces from existing facets */ if ( cell[i].num == 2 ) { for ( k = 0 ; k < cell[i].num ; k++ ) { struct arc *a = arclist + cell[i].start + k; for ( j = 0 ; j < a->num ; j++ ) { facetedge_id fe = felist[a->start + j]; REAL force[MAXCOORD]; facet_force_on_vertex(get_fe_facet(fe),v_id,force); if ( flag ) tripnetforce += dot(veccd,force,SDIM); else tripnetforce += dot(vecab,force,SDIM); } } flag = 1; } } /* force from introduced facets */ ee = dot(vecab,vecab,SDIM); aa = dot(veca,veca,SDIM); ea = dot(veca,vecab,SDIM); tripnetforce -= sqrt(aa*ee - ea*ea)/2*get_facet_density(f_a); bb = dot(vecb,vecb,SDIM); eb = dot(vecb,vecab,SDIM); tripnetforce -= sqrt(bb*ee - eb*eb)/2*get_facet_density(f_b); ee = dot(veccd,veccd,SDIM); cc = dot(vecc,vecc,SDIM); ec = dot(vecc,veccd,SDIM); tripnetforce -= sqrt(cc*ee - ec*ec)/2*get_facet_density(f_c); dd = dot(vecd,vecd,SDIM); ed = dot(vecd,veccd,SDIM); tripnetforce -= sqrt(dd*ee - ed*ed)/2*get_facet_density(f_d); /* now force from expanding vertex to quad line */ /* assuming splits at the a->num/2 edge in the side arcs */ quadnetforce = 0.0; for ( i = 0, flag = 0 ; i < cells ; i++ ) { if ( cell[i].num == 2 ) { for ( k = 0 ; k < cell[i].num ; k++ ) { struct arc *a = arclist + cell[i].start + k; for ( j = 0 ; j < a->num ; j++ ) { facetedge_id fe = felist[a->start + j]; REAL force[MAXCOORD]; facet_force_on_vertex(get_fe_facet(fe),v_id,force); if ( !flag || !cdswap ) { if ( ((k==0) && (j < a->num/2)) || ((k==1)&&(j >= a->num/2)) ) quadnetforce += dot(vecac,force,SDIM); else quadnetforce += dot(vecbd,force,SDIM); } else { if ( ((k==0) && (j < a->num/2)) || ((k==1)&&(j >= a->num/2)) ) quadnetforce += dot(vecbd,force,SDIM); else quadnetforce += dot(vecac,force,SDIM); } } } flag = 1; } } /* force from old septa */ ac = dot(veca,vecc,SDIM); quadnetforce += sqrt(aa*cc-ac*ac)/4*get_facet_density(f_a); bd = dot(vecb,vecd,SDIM); quadnetforce += sqrt(bb*dd-bd*bd)/4*get_facet_density(f_b); /* force from introduced facets */ for ( i = 0 ; i < cells ; i++ ) { if ( cell[i].num == 2 ) { for ( k = 0 ; k < cell[i].num ; k++ ) { struct arc *a = arclist + cell[i].start + k; REAL tension; fe = felist[a->start + a->num/2]; fe = get_prev_edge(fe); get_edge_side(get_fe_edge(fe),eside); ee = dot(eside,eside,SDIM); tension = get_facet_density(get_fe_facet(fe)); acac = dot(vecac,vecac,SDIM); ea = dot(vecac,eside,SDIM); quadnetforce -= sqrt(acac*ee - ea*ea)/2*tension; bdbd = dot(vecbd,vecbd,SDIM); eb = dot(vecbd,eside,SDIM); quadnetforce -= sqrt(bdbd*ee - eb*eb)/2*tension; } } } if ( (tripnetforce < 0) && (quadnetforce < 0) ) { if ( verbose_flag ) { sprintf(msg,"Not popping touching-triple-lines vertex %s since stable.\n", ELNAME(v_id)); outstring(msg); } return 0; /* no profit from splitting either way */ } /* if separate triple lines */ if ( tripnetforce > quadnetforce ) { vertex_id newv = dup_vertex(v_id); edge_id newe; facetedge_id fe_aa,fe_bb,new_fe_a,new_fe_b; REAL *xold = get_coord(v_id); REAL *xnew = get_coord(newv); if ( verbose_flag ) { sprintf(msg,"Separating triple lines at vertex %s.\n", ELNAME(v_id)); outstring(msg); } /* spread vertices apart */ for ( i = 0 ; i < SDIM ; i++ ) { xnew[i] += vecab[i]; xold[i] += veccd[i]; } /* reconnected other edges on split-off part */ for ( i = 0 ; i < cell[split_cell].num ; i++ ) { struct arc *a = arclist + cell[split_cell].start + i; for ( j = 0 ; j < a->num ; j++ ) { facetedge_id fe = felist[a->start + j]; edge_id e_id; fe = get_prev_edge(fe); e_id = get_fe_edge(fe); set_edge_tailv(e_id,newv); } } /* new edge and its facetedges */ newe = new_edge(v_id,newv,v_id); new_fe_a = new_facetedge(f_a,newe); new_fe_b = new_facetedge(f_b,newe); set_edge_fe(newe,new_fe_a); set_next_facet(new_fe_a,new_fe_b); set_prev_facet(new_fe_a,new_fe_b); set_next_facet(new_fe_b,new_fe_a); set_prev_facet(new_fe_b,new_fe_a); fe_aa = get_prev_edge(fe_a); set_next_edge(new_fe_a,fe_a); set_prev_edge(fe_a,new_fe_a); set_prev_edge(new_fe_a,fe_aa); set_next_edge(fe_aa,new_fe_a); fe_bb = get_prev_edge(fe_b); set_next_edge(new_fe_b,fe_b); set_prev_edge(fe_b,new_fe_b); set_prev_edge(new_fe_b,fe_bb); set_next_edge(fe_bb,new_fe_b); /* subdivide septum quadrilaterals */ cross_cut(new_fe_a,fe_a); cross_cut(new_fe_b,fe_b); } else { /* split vertex to form quadruple line */ vertex_id newv = dup_vertex(v_id); edge_id newe; REAL *xold = get_coord(v_id); REAL *xnew = get_coord(newv); if ( verbose_flag ) { sprintf(msg,"Inserting new septum at vertex %s.\n", ELNAME(v_id)); outstring(msg); } /* spread vertices apart */ for ( i = 0 ; i < SDIM ; i++ ) { xnew[i] += vecac[i]; xold[i] += vecbd[i]; } /* new edge and its facetedges */ newe = new_edge(v_id,newv,v_id); /* reconnected other edges on split-off part */ set_edge_tailv(e_a,newv); set_edge_tailv(e_c,newv); for ( i = 0, flag = 0 ; i < cells ; i++ ) { if ( cell[i].num != 2 ) continue; for ( k = 0 ; k < cell[i].num ; k++ ) { struct arc *a = arclist + cell[i].start + k; int upflag = (!flag && (k==0)) || (flag && !cdswap && (k==0)) || (flag && cdswap && (k==1)) ; facet_id ff; facetedge_id new_fe,fe_prev,e_fe; for ( j = 1 ; j < a->num ; j++ ) { facetedge_id fe = felist[a->start + j]; edge_id e_id; fe = get_prev_edge(fe); e_id = get_fe_edge(fe); if ( (upflag && (j <= a->num/2)) || (!upflag && (j >= a->num/2)) ) set_edge_tailv(e_id,newv); } /* now expand some facets to quads */ fe = felist[a->start + a->num/2]; fe = get_prev_edge(fe); if ( !upflag ) fe = get_next_facet(fe); ff = get_fe_facet(fe); new_fe = new_facetedge(ff,newe); fe_prev = get_prev_edge(fe); set_prev_edge(new_fe,fe_prev); set_next_edge(fe_prev,new_fe); set_next_edge(new_fe,fe); set_prev_edge(fe,new_fe); e_fe = get_edge_fe(newe); if ( valid_id(e_fe) ) { fe_prev = get_prev_facet(e_fe); set_next_facet(fe_prev,new_fe); set_prev_facet(new_fe,fe_prev); set_prev_facet(e_fe,new_fe); set_next_facet(new_fe,e_fe); } else { set_edge_fe(newe,new_fe); set_next_facet(new_fe,new_fe); set_prev_facet(new_fe,new_fe); } cross_cut(new_fe,fe); } flag = 1; } fe_reorder(newe); pop_one_edge(newe); } return 1; } /* end odd4cone_pop() */ /***************************************************************************** * * function: pop_tri_to_edge() * * purpose: Topology change of small triangle to edge perpendicular to it. * Implements pop_tri_to_edge command. * * return: 1 for success, 0 for failure. */ int pop_tri_to_edge(f_id) facet_id f_id; { edge_id ea,eb,ec,newe; vertex_id va,vb,vc,keepv=NULLID,newv; facetedge_id fe,next_fe,fa,fb,fc; int retval,i,j; int legs; REAL side[MAXCOORD],separation[MAXCOORD]; edge_id trip[3]; REAL *keepx,*newx; edge_id tredges[12]; int ti; if ( (web.modeltype != LINEAR) && (web.modeltype != QUADRATIC) ) kb_error(3366,"pop_tri_to_edge only for LINEAR or QUADRATIC models.\n", RECOVERABLE); /* Test geometry is appropriate */ next_fe = fe = get_facet_fe(f_id); ti = 0; do { int triples; ea = get_fe_edge(next_fe); ec = inverse_id(get_fe_edge(get_prev_edge(next_fe))); if ( get_edge_valence(ea) != 3 ) { if ( verbose_flag ) { sprintf(msg, "pop_tri_to_edge fails on facet %s; not all its edges triple .\n", ELNAME(f_id)); outstring(msg); } return 0; } va = get_fe_tailv(next_fe); eb = get_next_tail_edge(ea); triples = 1; while ( !equal_id(ea,eb) ) { int val = get_edge_valence(eb); if ( (val < 2) || ( val > 3 ) ) { if ( verbose_flag ) { sprintf(msg, "pop_tri_to_edge fails on facet %s due to edge %s having valence %d.\n", ELNAME(f_id),ELNAME1(eb),val); outstring(msg); } return 0; } if ( (val == 3) && !equal_id(eb,ec) ) { tredges[ti++] = eb; triples++; } eb = get_next_tail_edge(eb); } if ( triples != 3 ) { if ( verbose_flag ) { sprintf(msg, "pop_tri_to_edge fails on facet %s due to %d triple edges on vertex %s; need 4.\n", ELNAME(f_id),triples+1,ELNAME1(get_edge_tailv(eb))); outstring(msg); } return 0; } next_fe = get_next_edge(next_fe); } while ( !equal_id(fe,next_fe) ); fe = get_facet_fe(f_id); ea = get_fe_edge(fe); eb = get_fe_edge(get_next_edge(fe)); ec = get_fe_edge(get_prev_edge(fe)); va = get_edge_tailv(ea); vb = get_edge_tailv(eb); vc = get_edge_tailv(ec); /* check for disjoint endpoints of triple edges */ for ( i = 1 ; i < ti ; i++ ) for ( j = 0 ; j < i ; j++ ) if ( equal_id(get_edge_headv(tredges[i]),get_edge_headv(tredges[j])) ) { if ( verbose_flag ) { sprintf(msg,"pop_tri_to_edge fails on facet %s due to outer triple edges with common endpoint.\n",ELNAME(f_id)); outstring(msg); } return 0; } if ( verbose_flag ) { sprintf(msg,"pop_tri_to_edge on facet %s\n",ELNAME(f_id)); outstring(msg); } retval = eliminate_facet(f_id); if ( retval == 0 ) return 0; /* now finish elimination by eliminating the remaining facet edge */ if ( valid_element(ea) ) { retval = eliminate_edge(ea); free_element(ea); } else if ( valid_element(eb) ) { retval = eliminate_edge(eb); free_element(eb); } else if ( valid_element(ec) ) { retval = eliminate_edge(ec); free_element(ec); } else retval = 0; if ( retval == 0 ) { if ( verbose_flag ) { sprintf(msg,"pop_tri_to_edge failed on facet %s; failed to delete edge remaining after deleting facet.\n",ELNAME(f_id)); outstring(msg); } return 0; } if ( valid_element(va) ) keepv = va; else if ( valid_element(vb) ) keepv = vb; else if ( valid_element(vc) ) keepv = vc; /* Identify the three downward triple lines */ trip[0] = ea = tredges[0]; trip[1] = trip[2] = NULLID; fe = get_edge_fe(ea); get_edge_side(ea,side); for ( j = 0 ; j < SDIM ; j++ ) separation[j] = side[j]; for ( i = 0 ; i < 3 ; i++,fe=get_next_facet(fe) ) { next_fe = inverse_id(get_prev_edge(fe)); while ( equal_id(get_next_facet(next_fe),get_prev_facet(next_fe)) ) next_fe = inverse_id(get_prev_edge(get_next_facet(next_fe))); eb = get_fe_edge(next_fe); get_edge_side(eb,side); if ( equal_id(eb,tredges[2]) || equal_id(eb,tredges[3]) ) { trip[1] = eb; for ( j = 0 ; j < SDIM ; j++ ) separation[j] += side[j]; } if ( equal_id(eb,tredges[4]) || equal_id(eb,tredges[5]) ) { trip[2] = eb; for ( j = 0 ; j < SDIM ; j++ ) separation[j] += side[j]; } } if ( !valid_id(trip[1]) || !valid_id(trip[2]) ) { if ( verbose_flag ) { sprintf(msg,"pop_tri_to_edge failed after deleting facet %s; didn't find triple of edges in same direction.\n",ELNAME(f_id)); outstring(msg); } return 0; } legs = 3; /* Insert new vertex and edge */ newv = dup_vertex(keepv); newe = new_edge(keepv,newv,NULLID); keepx = get_coord(keepv); newx = get_coord(newv); for ( j = 0 ; j < SDIM ; j++ ) { newx[j] = keepx[j] + separation[j]/12; keepx[j] -= separation[j]/12; } /* Reconnect lower edge endpoints */ for ( i = 0 ; i < legs ; i++ ) /* lower triple edges */ { facetedge_id next_fe,start_fe; remove_vertex_edge(keepv,trip[i]); set_edge_tailv(trip[i],newv); fe = start_fe = get_edge_fe(trip[i]); do /* around the multiple edge */ { next_fe = inverse_id(get_prev_edge(fe)); for (;;) { /* traverse fan of edges */ edge_id eg; if ( !equal_id(get_fe_tailv(next_fe),keepv) ) break; if ( get_next_facet(next_fe) != get_prev_facet(next_fe) ) { /* have reached next triple edge */ eg = get_fe_edge(next_fe); if ( equal_id(eg,trip[0]) || equal_id(eg,trip[1]) || equal_id(eg,trip[2]) ) { /* all lower edges, so go back and reconnect */ next_fe = inverse_id(get_prev_edge(next_fe)); while ( !equal_id(next_fe,fe) ) { edge_id e_id = get_fe_edge(next_fe); remove_vertex_edge(keepv,e_id); set_edge_tailv(e_id,newv); next_fe = get_prev_facet(next_fe); next_fe = inverse_id(get_prev_edge(next_fe)); } } else { /* vertical, so insert new edge above trip[i] */ facetedge_id fea = get_prev_edge(fe); facet_id fa = get_fe_facet(fe); facetedge_id newfe = new_facetedge(fa,newe); facetedge_id newefe = get_edge_fe(newe); set_next_edge(fea,newfe); set_prev_edge(newfe,fea); set_prev_edge(fe,newfe); set_next_edge(newfe,fe); if ( valid_id(newefe) ) { set_next_facet(newfe,newefe); set_prev_facet(newefe,newfe); set_prev_facet(newfe,get_next_facet(newefe)); set_next_facet(get_next_facet(newefe),newfe); } else { set_edge_fe(newe,newfe); set_next_facet(newfe,newfe); set_prev_facet(newfe,newfe); } cross_cut(newfe,fe); /* triangulate */ } break; /* done with this way out of triple edge */ } next_fe = get_next_facet(next_fe); next_fe = inverse_id(get_prev_edge(next_fe)); } fe = get_next_facet(fe); } while ( !equal_id(fe,start_fe) ); } /* Fix up facet edge orders */ fa = get_edge_fe(newe); fb = get_next_facet(fa); fc = get_prev_facet(fa); if ( equal_id(get_facet_body(get_fe_facet(fb)), get_facet_body(inverse_id(get_fe_facet(fc)))) ) { /* have to switch order around edge */ set_prev_facet(fa,fb); set_prev_facet(fb,fc); set_prev_facet(fc,fa); set_next_facet(fa,fc); set_next_facet(fc,fb); set_next_facet(fb,fa); } if ( web.modeltype == QUADRATIC ) { /* fix up edge midpoints */ vertex_id vv[2]; int bailcount = 0; vv[0] = keepv; vv[1] = newv; /* fix up edge midpoints */ for ( i = 0 ; i < 2 ; i++ ) { edge_id start_e = get_vertex_edge(vv[i]); edge_id ea; ea = start_e; do { new_vertex_average(get_edge_midv(ea),VOLKEEP); ea = get_next_tail_edge(ea); if ( bailcount++ > 1000 ) break; } while ( !equal_id(ea,start_e)); } } fe_reorder(newe); return 1; } /***************************************************************************** * * function: pop_edge_to_tri() * * purpose: Topology change of edge to small triangle perpendicular to it. * Implements pop_edge_to_tri command. * * return: 1 for success, 0 for failure. */ int pop_edge_to_tri(e_id) edge_id e_id; { int triples; vertex_id tail_triples[3],head_triples[3]; vertex_id headv,tailv; int retval; int i,j; conmap_t *hmap,*tmap; int thit=0,hhit=0; unsigned int k; if ( (web.modeltype != LINEAR) && (web.modeltype != QUADRATIC) ) kb_error(2835,"pop_edge_to_tri only for LINEAR or QUADRATIC models.\n", RECOVERABLE); /* Check valences and gather info on triple edges */ if ( get_edge_valence(e_id) != 3 ) { if ( verbose_flag ) { sprintf(msg, "pop_edge_to_tri fails on edge %s; it is not a triple edge.\n", ELNAME(e_id)+1); outstring(msg); } return 0; } tailv = get_edge_tailv(e_id); tmap = get_v_constraint_map(tailv); for ( k = 1 ; k <= tmap[0] ; k++ ) if ( tmap[k] & CON_HIT_BIT ) { thit = 1; break; } if ( !thit ) { /* check for proper number of triple edges */ edge_id eb = get_next_tail_edge(e_id); triples = 0; while ( !equal_id(e_id,eb) ) { int val = get_edge_valence(eb); if ( (val < 2) || ( val > 3 ) ) { if ( verbose_flag ) { sprintf(msg, "pop_edge_to_tri fails on edge %s due to edge %s having valence %d.\n", ELNAME(e_id),ELNAME1(eb),val); outstring(msg); } return 0; } if ( val == 3 ) { if ( triples >= 3 ) { if ( verbose_flag ) { sprintf(msg, "pop_edge_to_tri fails on edge %s due to too many triple edges at end.\n", ELNAME(e_id)+1); outstring(msg); } return 0; } tail_triples[triples++] = eb; } eb = get_next_tail_edge(eb); } if ( triples != 3 ) { if ( verbose_flag ) { sprintf(msg, "pop_edge_to_tri fails on edge %s due to not enough triple edges at an endpoint.\n", ELNAME(e_id)); outstring(msg); } return 0; } } headv = get_edge_headv(e_id); hmap = get_v_constraint_map(headv); for ( k = 1 ; k <= hmap[0] ; k++ ) if ( hmap[k] & CON_HIT_BIT ) { hhit = 1; break; } if ( !hhit ) { /* check for proper number of triples */ edge_id eb = get_next_head_edge(e_id); triples = 0; while ( !equal_id(e_id,eb) ) { int val = get_edge_valence(eb); if ( (val < 2) || ( val > 3 ) ) { if ( verbose_flag ) { sprintf(msg, "pop_edge_to_tri fails on edge %s due to edge %s having valence %d.\n", ELNAME(e_id),ELNAME1(eb),val); outstring(msg); } return 0; } if ( val == 3 ) { if ( triples >= 3 ) { if ( verbose_flag ) { sprintf(msg,"pop_edge_to_tri fails on edge %s due to too many triple edges at one endpoint.\n",ELNAME(e_id)); outstring(msg); } return 0; } head_triples[triples++] = inverse_id(eb); } eb = get_next_head_edge(eb); } if ( triples != 3 ) { if ( verbose_flag ) { sprintf(msg,"pop_edge_to_tri fails on edge %s due to not enough triple edges at one endpoint.\n",ELNAME(e_id)); outstring(msg); } return 0; } } if ( thit && hhit ) { if ( verbose_flag ) { sprintf(msg,"pop_edge_to_tri fails on edge %s due to both endpoints on constraints.\n",ELNAME(e_id)); outstring(msg); } return 0; } if ( thit ) return pop_edge_to_tri_con(e_id); if ( hhit ) return pop_edge_to_tri_con(inverse_id(e_id)); /* check for common endpoints on triples */ for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < 3 ; j++ ) if ( equal_id(get_edge_headv(tail_triples[i]), get_edge_headv(head_triples[j])) ) { if ( verbose_flag ) { sprintf(msg,"pop_edge_to_tri fails on edge %s due to outer triple edges with common endpoint.\n",ELNAME(e_id)); outstring(msg); } return 0; } if ( verbose_flag ) { sprintf(msg,"pop_edge_to_tri on edge %s\n",ELNAME(e_id)); outstring(msg); } /* Delete the edge */ retval = eliminate_edge(e_id); if ( retval == 0 ) return 0; free_element(e_id); return pop_vertex_to_tri((valid_element(headv) ? headv : tailv),tail_triples); } /************************************************************************** * * function: pop_vertex_to_tri() * * purpose: main work for pop_edge_to_tri and kraynik pop. */ int pop_vertex_to_tri(v_id,tail_triples) vertex_id v_id; edge_id *tail_triples; { int i,j; vertex_id newv[3],keepv; edge_id newe[3]; facet_id newf; facetedge_id newfe[3],fe,fa,fb,fc; edge_id ea,eb,ec,ee,head_triples[3]; /* Create new vertices, edges, and facet */ newv[0] = v_id; newv[1] = dup_vertex(newv[0]); newv[2] = dup_vertex(newv[0]); newe[0] = new_edge(newv[0],newv[1],NULLID); newe[1] = new_edge(newv[1],newv[2],NULLID); newe[2] = new_edge(newv[2],newv[0],NULLID); fe = get_vertex_first_facet(newv[0]); newf = dup_facet(get_fe_facet(fe)); set_facet_body(newf,NULLID); set_facet_body(inverse_id(newf),NULLID); newfe[0] = new_facetedge(newf,newe[0]); set_edge_fe(newe[0],newfe[0]); newfe[1] = new_facetedge(newf,newe[1]); set_edge_fe(newe[1],newfe[1]); newfe[2] = new_facetedge(newf,newe[2]); set_edge_fe(newe[2],newfe[2]); set_next_edge(newfe[0],newfe[1]); set_next_edge(newfe[1],newfe[2]); set_next_edge(newfe[2],newfe[0]); set_prev_edge(newfe[0],newfe[2]); set_prev_edge(newfe[1],newfe[0]); set_prev_edge(newfe[2],newfe[1]); set_facet_fe(newf,newfe[0]); /* Reconnect things */ keepv = newv[0]; for ( i = 0 ; i < 3 ; i++ ) /* lower triple edges */ { facetedge_id next_fe; remove_vertex_edge(keepv,tail_triples[i]); set_edge_tailv(tail_triples[i],newv[i]); fe = get_edge_fe(tail_triples[i]); for ( j = 0 ; j < 3 ; j++ ) /* around the triple edge */ { fe = get_next_facet(fe); next_fe = inverse_id(get_prev_edge(fe)); for (;;) { /* traverse fan of edges */ vertex_id vv_id; edge_id eg,ee; eg = get_fe_edge(next_fe); if ( equal_element(eg,newe[0]) ) break; if ( equal_element(eg,newe[1]) ) break; if ( equal_element(eg,newe[2]) ) break; vv_id = get_edge_tailv(eg); if ( !equal_id(vv_id,keepv) && !equal_id(vv_id,newv[i]) ) { sprintf(errmsg, "pop_edge_to_tri bug: edge %s tail vertex is %s instead of %s.\n", ELNAME(eg),ELNAME1(get_edge_tailv(eg)),ELNAME2(keepv)); kb_error(2834,errmsg,RECOVERABLE); break; } if ( get_next_facet(next_fe) != get_prev_facet(next_fe) ) { /* have reached next triple edge */ if ( equal_id(eg,tail_triples[(i+2)%3]) ) break; else if ( equal_id(eg,tail_triples[(i+1)%3]) ) { /* all lower edges, so go back and split out to triangle edge */ facetedge_id feb = inverse_id(get_prev_edge(fe)); facetedge_id fea = get_prev_edge(fe); facet_id fa = get_fe_facet(fe); facetedge_id newfe = new_facetedge(fa,inverse_id(newe[i])); facetedge_id newefe = get_edge_fe(inverse_id(newe[i])); while ( !equal_id(feb,next_fe) ) { ee = get_fe_edge(feb); remove_vertex_edge(keepv,ee); set_edge_tailv(ee,newv[(i+1)%3]); feb = get_next_facet(feb); feb = inverse_id(get_prev_edge(feb)); } set_next_edge(fea,newfe); set_prev_edge(newfe,fea); set_prev_edge(fe,newfe); set_next_edge(newfe,fe); set_next_facet(newfe,newefe); set_prev_facet(newefe,newfe); set_next_facet(newefe,newfe); set_prev_facet(newfe,newefe); cross_cut(newfe,fe); /* triangulate */ } else /* hit one of the head_triples */ { /* vertical wall, so reconnect */ head_triples[i] = eg; next_fe = inverse_id(get_prev_edge(next_fe)); while ( !equal_id(next_fe,fe) ) { edge_id ee = get_fe_edge(next_fe); remove_vertex_edge(keepv,ee); set_edge_tailv(ee,newv[i]); next_fe = get_prev_facet(next_fe); next_fe = inverse_id(get_prev_edge(next_fe)); } } break; /* done with this way out of triple edge */ } next_fe = get_next_facet(next_fe); next_fe = inverse_id(get_prev_edge(next_fe)); } } } /* Now go around head end and reconnect, head_triples having been put in proper correspondence above */ for ( i = 0 ; i < 3 ; i++ ) /* upper triple edges */ { facetedge_id next_fe; remove_vertex_edge(keepv,head_triples[i]); set_edge_tailv(head_triples[i],newv[i]); fe = get_edge_fe(head_triples[i]); for ( j = 0 ; j < 3 ; j++ ) /* around the triple edge */ { fe = get_next_facet(fe); next_fe = inverse_id(get_prev_edge(fe)); for (;;) { /* traverse fan of edges */ edge_id eg; if ( !equal_id(get_fe_tailv(next_fe),keepv) ) break; eg = get_fe_edge(next_fe); if ( get_next_facet(next_fe) != get_prev_facet(next_fe) ) { /* have reached next triple edge */ if ( equal_id(eg,head_triples[(i+2)%3]) ) break; if ( equal_element(eg,newe[0]) ) break; if ( equal_element(eg,newe[1]) ) break; if ( equal_element(eg,newe[2]) ) break; else if ( equal_id(eg,head_triples[(i+1)%3]) ) { /* all upper edges, so go back and split out to triangle edge */ facetedge_id feb = inverse_id(get_prev_edge(fe)); facetedge_id fea = get_prev_edge(fe); facet_id fa = get_fe_facet(fe); facetedge_id newfe = new_facetedge(inverse_id(fa),newe[i]); facetedge_id newefe = get_edge_fe(newe[i]); while ( !equal_id(feb,next_fe) ) { ee = get_fe_edge(feb); remove_vertex_edge(keepv,ee); set_edge_tailv(ee,newv[(i+1)%3]); feb = get_next_facet(feb); feb = inverse_id(get_prev_edge(feb)); } set_next_edge(fea,inverse_id(newfe)); set_prev_edge(inverse_id(newfe),fea); set_prev_edge(fe,inverse_id(newfe)); set_next_edge(inverse_id(newfe),fe); set_next_facet(newfe,newefe); set_prev_facet(newefe,newfe); set_prev_facet(newfe,get_next_facet(newefe)); set_next_facet(get_next_facet(newefe),newfe); cross_cut(inverse_id(newfe),fe); /* triangulate */ } else /* hit one of the head_triples */ { /* vertical wall, but those already reconnected */ } break; /* done with this way out of triple edge */ } next_fe = get_next_facet(next_fe); next_fe = inverse_id(get_prev_edge(next_fe)); } } } /* Spread new vertices */ for ( i = 0 ; i < 3 ; i++ ) /* the vertices */ { REAL *x,side1[MAXCOORD],side2[MAXCOORD]; x = get_coord(newv[i]); get_edge_side(tail_triples[i],side1); get_edge_side(head_triples[i],side2); for ( j = 0 ; j < SDIM ; j++ ) x[j] += (side1[j]+side2[j])/6; } /* Fix up facet edge orders */ for ( i = 0 ; i < 3 ; i++ ) { facetedge_id next_fe; int count; vertex_id base_v; fa = newfe[i]; fb = get_next_facet(fa); fc = get_prev_facet(fa); ea = get_fe_edge(fa); eb = get_fe_edge(inverse_id(get_prev_edge(fa))); base_v = get_edge_tailv(ea); next_fe = inverse_id(get_prev_edge(fb)); for(count=0;count < 10000 ;count++) { ec = get_fe_edge(next_fe); if ( !equal_id(get_edge_tailv(ec),base_v) ) kb_error(2833,"pop_tri_to_edge failure.\n",RECOVERABLE); if ( equal_id(ec,ea) ) break; /* ok */ if ( equal_id(ec,eb) ) { /* have to switch order around edge */ set_prev_facet(fa,fb); set_prev_facet(fb,fc); set_prev_facet(fc,fa); set_next_facet(fa,fc); set_next_facet(fc,fb); set_next_facet(fb,fa); break; } next_fe = inverse_id(get_prev_edge(get_prev_facet(next_fe))); } if ( count >= 10000 ) { sprintf(errmsg,"Internal error after pop_vertex_to_tri edge %s, bad topology around vertex %s.\n",ELNAME(v_id),ELNAME1(get_edge_tailv(ea))); kb_error(2832,errmsg,RECOVERABLE); } } /* Fix up bodies on new facet */ fe = newfe[0]; set_facet_body(newf, get_facet_body(inverse_id(get_fe_facet(get_prev_facet(fe))))); set_facet_body(inverse_id(newf), get_facet_body(get_fe_facet(get_next_facet(fe)))); if ( web.modeltype == QUADRATIC ) { /* fix up edge midpoints */ int bailcount = 0; for ( i = 0 ; i < 3 ; i++ ) { edge_id start_e = get_vertex_edge(newv[i]); edge_id ea; ea = start_e; do { new_vertex_average(get_edge_midv(ea),VOLKEEP); ea = get_next_tail_edge(ea); if ( bailcount++ > 1000 ) break; } while ( !equal_id(ea,start_e)); } } return 1; } /* end pop_edge_to_tri */ /************************************************************************** * * function: pop_vertex_to_quad() * * purpose: main work for pop_cubecone. */ int pop_vertex_to_quad(v_id,tail_triples) vertex_id v_id; edge_id *tail_triples; { int i,j; vertex_id newv[4],keepv; edge_id newe[4]; facet_id newf; facetedge_id newfe[4],fe,fa,fb,fc; edge_id ea,eb,ec,ee,head_triples[4]; /* Create new vertices, edges, and facet */ newv[0] = v_id; newv[1] = dup_vertex(newv[0]); newv[2] = dup_vertex(newv[0]); newv[3] = dup_vertex(newv[0]); newe[0] = new_edge(newv[0],newv[1],NULLID); newe[1] = new_edge(newv[1],newv[2],NULLID); newe[2] = new_edge(newv[2],newv[3],NULLID); newe[3] = new_edge(newv[3],newv[0],NULLID); fe = get_vertex_first_facet(newv[0]); newf = dup_facet(get_fe_facet(fe)); set_facet_body(newf,NULLID); set_facet_body(inverse_id(newf),NULLID); newfe[0] = new_facetedge(newf,newe[0]); set_edge_fe(newe[0],newfe[0]); newfe[1] = new_facetedge(newf,newe[1]); set_edge_fe(newe[1],newfe[1]); newfe[2] = new_facetedge(newf,newe[2]); set_edge_fe(newe[2],newfe[2]); newfe[3] = new_facetedge(newf,newe[3]); set_edge_fe(newe[3],newfe[3]); set_next_edge(newfe[0],newfe[1]); set_next_edge(newfe[1],newfe[2]); set_next_edge(newfe[2],newfe[3]); set_next_edge(newfe[3],newfe[0]); set_prev_edge(newfe[0],newfe[3]); set_prev_edge(newfe[1],newfe[0]); set_prev_edge(newfe[2],newfe[1]); set_prev_edge(newfe[3],newfe[2]); set_facet_fe(newf,newfe[0]); /* Reconnect things */ keepv = newv[0]; for ( i = 0 ; i < 4 ; i++ ) /* lower triple edges */ { facetedge_id next_fe; remove_vertex_edge(keepv,tail_triples[i]); set_edge_tailv(tail_triples[i],newv[i]); fe = get_edge_fe(tail_triples[i]); for ( j = 0 ; j < 3 ; j++ ) /* around the triple edge */ { fe = get_next_facet(fe); next_fe = inverse_id(get_prev_edge(fe)); for (;;) { /* traverse fan of edges */ vertex_id vv_id; edge_id eg,ee; eg = get_fe_edge(next_fe); if ( equal_element(eg,newe[0]) ) break; if ( equal_element(eg,newe[1]) ) break; if ( equal_element(eg,newe[2]) ) break; if ( equal_element(eg,newe[3]) ) break; vv_id = get_edge_tailv(eg); if ( !equal_id(vv_id,keepv) && !equal_id(vv_id,newv[i]) ) { sprintf(errmsg, "pop_vertex_to_quad bug: edge %s tail vertex is %s instead of %s.\n", ELNAME(eg),ELNAME1(get_edge_tailv(eg)),ELNAME2(keepv)); kb_error(3930,errmsg,RECOVERABLE); break; } if ( get_next_facet(next_fe) != get_prev_facet(next_fe) ) { /* have reached next triple edge */ if ( equal_id(eg,tail_triples[(i+3)%4]) ) break; else if ( equal_id(eg,tail_triples[(i+1)%4]) ) { /* all lower edges, so go back and split out to triangle edge */ facetedge_id feb = inverse_id(get_prev_edge(fe)); facetedge_id fea = get_prev_edge(fe); facet_id fa = get_fe_facet(fe); facetedge_id newfe = new_facetedge(fa,inverse_id(newe[i])); facetedge_id newefe = get_edge_fe(inverse_id(newe[i])); while ( !equal_id(feb,next_fe) ) { ee = get_fe_edge(feb); remove_vertex_edge(keepv,ee); set_edge_tailv(ee,newv[(i+1)%4]); feb = get_next_facet(feb); feb = inverse_id(get_prev_edge(feb)); } set_next_edge(fea,newfe); set_prev_edge(newfe,fea); set_prev_edge(fe,newfe); set_next_edge(newfe,fe); set_next_facet(newfe,newefe); set_prev_facet(newefe,newfe); set_next_facet(newefe,newfe); set_prev_facet(newfe,newefe); cross_cut(newfe,fe); /* triangulate */ } else /* hit one of the head_triples */ { /* vertical wall, so reconnect */ head_triples[i] = eg; next_fe = inverse_id(get_prev_edge(next_fe)); while ( !equal_id(next_fe,fe) ) { edge_id ee = get_fe_edge(next_fe); remove_vertex_edge(keepv,ee); set_edge_tailv(ee,newv[i]); next_fe = get_prev_facet(next_fe); next_fe = inverse_id(get_prev_edge(next_fe)); } } break; /* done with this way out of triple edge */ } next_fe = get_next_facet(next_fe); next_fe = inverse_id(get_prev_edge(next_fe)); } } } /* Now go around head end and reconnect, head_triples having been put in proper correspondence above */ for ( i = 0 ; i < 4 ; i++ ) /* upper triple edges */ { facetedge_id next_fe; remove_vertex_edge(keepv,head_triples[i]); set_edge_tailv(head_triples[i],newv[i]); fe = get_edge_fe(head_triples[i]); for ( j = 0 ; j < 3 ; j++ ) /* around the triple edge */ { fe = get_next_facet(fe); next_fe = inverse_id(get_prev_edge(fe)); for (;;) { /* traverse fan of edges */ edge_id eg; if ( !equal_id(get_fe_tailv(next_fe),keepv) ) break; eg = get_fe_edge(next_fe); if ( get_next_facet(next_fe) != get_prev_facet(next_fe) ) { /* have reached next triple edge */ if ( equal_id(eg,head_triples[(i+3)%4]) ) break; if ( equal_element(eg,newe[0]) ) break; if ( equal_element(eg,newe[1]) ) break; if ( equal_element(eg,newe[2]) ) break; if ( equal_element(eg,newe[3]) ) break; else if ( equal_id(eg,head_triples[(i+1)%4]) ) { /* all upper edges, so go back and split out to triangle edge */ facetedge_id feb = inverse_id(get_prev_edge(fe)); facetedge_id fea = get_prev_edge(fe); facet_id fa = get_fe_facet(fe); facetedge_id newfe = new_facetedge(inverse_id(fa),newe[i]); facetedge_id newefe = get_edge_fe(newe[i]); while ( !equal_id(feb,next_fe) ) { ee = get_fe_edge(feb); remove_vertex_edge(keepv,ee); set_edge_tailv(ee,newv[(i+1)%4]); feb = get_next_facet(feb); feb = inverse_id(get_prev_edge(feb)); } set_next_edge(fea,inverse_id(newfe)); set_prev_edge(inverse_id(newfe),fea); set_prev_edge(fe,inverse_id(newfe)); set_next_edge(inverse_id(newfe),fe); set_next_facet(newfe,newefe); set_prev_facet(newefe,newfe); set_prev_facet(newfe,get_next_facet(newefe)); set_next_facet(get_next_facet(newefe),newfe); cross_cut(inverse_id(newfe),fe); /* triangulate */ } else /* hit one of the head_triples */ { /* vertical wall, but those already reconnected */ } break; /* done with this way out of triple edge */ } next_fe = get_next_facet(next_fe); next_fe = inverse_id(get_prev_edge(next_fe)); } } } /* Spread new vertices */ for ( i = 0 ; i < 4 ; i++ ) /* the vertices */ { REAL *x,side1[MAXCOORD],side2[MAXCOORD]; x = get_coord(newv[i]); get_edge_side(tail_triples[i],side1); get_edge_side(head_triples[i],side2); for ( j = 0 ; j < SDIM ; j++ ) x[j] += (side1[j]+side2[j])/6; } /* Fix up facet edge orders */ for ( i = 0 ; i < 4 ; i++ ) { facetedge_id next_fe; int count; vertex_id base_v; fa = newfe[i]; fb = get_next_facet(fa); fc = get_prev_facet(fa); ea = get_fe_edge(fa); eb = get_fe_edge(inverse_id(get_prev_edge(fa))); base_v = get_edge_tailv(ea); next_fe = inverse_id(get_prev_edge(fb)); for(count=0;count < 10000 ;count++) { ec = get_fe_edge(next_fe); if ( !equal_id(get_edge_tailv(ec),base_v) ) kb_error(4833,"pop_vertex_to_quad failure.\n",RECOVERABLE); if ( equal_id(ec,ea) ) break; /* ok */ if ( equal_id(ec,eb) ) { /* have to switch order around edge */ set_prev_facet(fa,fb); set_prev_facet(fb,fc); set_prev_facet(fc,fa); set_next_facet(fa,fc); set_next_facet(fc,fb); set_next_facet(fb,fa); break; } next_fe = inverse_id(get_prev_edge(get_prev_facet(next_fe))); } if ( count >= 10000 ) { sprintf(errmsg,"Internal error after pop_vertex_to_quad edge %s, bad topology around vertex %s.\n",ELNAME(v_id),ELNAME1(get_edge_tailv(ea))); kb_error(4832,errmsg,RECOVERABLE); } } /* Fix up bodies on new facet */ fe = newfe[0]; set_facet_body(newf, get_facet_body(inverse_id(get_fe_facet(get_prev_facet(fe))))); set_facet_body(inverse_id(newf), get_facet_body(get_fe_facet(get_next_facet(fe)))); if ( web.modeltype == QUADRATIC ) { /* fix up edge midpoints */ int bailcount = 0; for ( i = 0 ; i < 4 ; i++ ) { edge_id start_e = get_vertex_edge(newv[i]); edge_id ea; ea = start_e; do { new_vertex_average(get_edge_midv(ea),VOLKEEP); ea = get_next_tail_edge(ea); if ( bailcount++ > 1000 ) break; } while ( !equal_id(ea,start_e)); } } /* divide central square */ cross_cut(newfe[0],newfe[1]); return 1; } /* end pop_vertex_to_quad */ /***************************************************************************** * * function: pop_quad_to_quad() * * purpose: Topology change of narrow quadrilateral to quad perpendicular to it. * Implements pop_quad_to_quad command. * * return: 1 for success, 0 for failure. */ int pop_quad_to_quad(f_id) facet_id f_id; /* one facet of the quadrilateral */ { facetedge_id fe,next_fe,start_fe,quad_triples[4],newfe[4],fea,keepfe; edge_id e_id,ee_id,eee_id,other_triples[4][2],ea,eb,ec,keepe,newe[4]; vertex_id newv[4]; facet_id newf,fa,fb,fc; body_id b_id; int i,j,k,ii,jj; REAL len[4]; REAL sides[4][2][MAXCOORD]; REAL *x[4]; #define MAXQF 100 facet_id quadfacets[MAXQF]; int qfcount; if ( (web.modeltype != LINEAR) && (web.modeltype != QUADRATIC) ) kb_error(2836,"pop_quad_to_quad only for LINEAR or QUADRATIC models.\n", RECOVERABLE); /* quad could be four facets or two, or more */ /* Check geometry and gather info about triple edges */ /* find triple edge on original facet */ fe = get_facet_fe(f_id); for ( i = 0 ; i < 3 ; i++ ) { edge_id e_id = get_fe_edge(fe); if ( get_edge_valence(e_id) == 3 ) break; fe = get_next_edge(fe); } if ( i == 3 ) /* couldn't find a triple edge */ { if ( verbose_flag ) { sprintf(msg,"pop_quad_to_quad fails on facet %s since it doesn't have a triple edge.\n",ELNAME(f_id)); outstring(msg); } return 0; } /* march around inner quad */ start_fe = fe; for ( i = 0 ; i < 4 ; i++ ) { for(;;) /* seek next triple */ { fe = inverse_id(get_prev_edge(fe)); if ( equal_id(fe,get_next_facet(fe)) ) /* valence 1 edge */ { if ( verbose_flag ) { sprintf(msg,"pop_quad_to_quad fails on facet %s; edge %s has valence 1.\n",ELNAME(f_id),ELNAME1(get_fe_edge(fe))); outstring(msg); } return 0; } if ( !equal_id(get_next_facet(fe),get_prev_facet(fe)) ) break; /* found triple */ fe = get_next_facet(fe); } /* now have next triple */ e_id = get_fe_edge(fe); if ( get_edge_valence(e_id) != 3 ) { if ( verbose_flag ) { sprintf(msg,"pop_quad_to_quad fails on facet %s; edge %s valence too high.\n",ELNAME(f_id),ELNAME1(e_id)); outstring(msg); } return 0; } quad_triples[i] = fe; fe = inverse_id(fe); /* get ready for next triple line search */ } if ( !equal_id(start_fe,fe) ) { if ( verbose_flag ) { sprintf(msg,"pop_quad_to_quad fails on facet %s; didn't find quadrilateral of triple edges.\n",ELNAME(f_id)); outstring(msg); } return 0; } /* Find shorter pair of opposite edges, and swap if necessary so quad_triples[1,3] has short edges */ for ( i = 0 ; i < 4 ; i++ ) { e_id = get_fe_edge(quad_triples[i]); calc_edge(e_id); len[i] = get_edge_length(e_id); } if ( (len[0]+len[2]) < (len[1]+len[3]) ) { facetedge_id temp_fe = quad_triples[0]; quad_triples[0] = quad_triples[1]; quad_triples[1] = quad_triples[2]; quad_triples[2] = quad_triples[3]; quad_triples[3] = temp_fe; } /* Find the other triple lines at each corner */ for ( i = 0 ; i < 4 ; i++ ) { edge_id ee_id; int val; e_id = get_fe_edge(quad_triples[i]); ee_id = e_id; j = 0; do { ee_id = get_next_tail_edge(ee_id); val = get_edge_valence(ee_id); if ( (val < 2) || (val > 3) ) { if ( verbose_flag ) { sprintf(msg, "pop_quad_to_quad fails on facet %s since edge %s has valence %d.\n", ELNAME(f_id),ELNAME1(ee_id),val); outstring(msg); } return 0; } if ( val == 3 ) { if ( equal_element(ee_id,e_id) ) continue; if ( equal_element(ee_id,get_fe_edge(quad_triples[(i+3)%4])) ) continue; if ( j >= 2 ) { if ( verbose_flag ) { sprintf(msg,"pop_quad_to_quad fails on facet %s; too many triple edges on vertex %s.\n",ELNAME(f_id),ELNAME1(get_edge_tailv(ee_id))); outstring(msg); } return 0; } other_triples[i][j++] = ee_id; } } while ( !equal_id(e_id,ee_id)); if ( j != 2 ) { if ( verbose_flag ) { sprintf(msg,"pop_quad_to_quad fails on facet %s since vertex %s doesn't have enough triple edges.\n",ELNAME(f_id),ELNAME1(get_edge_tailv(e_id))); outstring(msg); } return 0; } } /* check outer triples for common endpoints, due to triangular * prisms and pyramids. */ for ( i = 0 ; i < 4 ; i++ ) for ( j = 0 ; j < 2 ; j++ ) for ( ii = i ; ii < 4 ; ii++ ) for ( jj = 0 ; jj < 2 ; jj++ ) { if ( (ii == i) && (jj == j) ) continue; if ( equal_id(get_edge_headv(other_triples[i][j]), get_edge_headv(other_triples[ii][jj])) ) { sprintf(msg,"Pop_quad_to_quad fails on facet %s\n",ELNAME(f_id)); sprintf(msg+strlen(msg), " since triple edges %s and %s have common far endpoint.\n", ELNAME(other_triples[i][j]),ELNAME1(other_triples[ii][jj])); strcat(msg," Probably a face of a pyramid or triangular prism.\n"); outstring(msg); return 0; } } if ( verbose_flag ) { sprintf(msg,"pop_quad_to_quad on facet %s\n",ELNAME(f_id)); outstring(msg); } ea = get_fe_edge(quad_triples[0]); /* for identifying kept edge */ eb = get_fe_edge(quad_triples[2]); /* use old facet to get attributes for new before deleting old */ /* newf = new_facet(); */ newf = dup_facet(f_id); set_facet_body(newf,NULLID); set_facet_body(inverse_id(newf),NULLID); /* get list of the quadrilateral's facets, in case quad is subdivided too much for simple edge deletion to take care of */ quadfacets[0] = f_id; qfcount = 1; for ( i = 0 ; i < qfcount ; i++ ) { fa = quadfacets[i]; fea = get_facet_fe(fa); for ( j = 0 ; j < 3 ; j++, fea = get_next_edge(fea) ) { if ( equal_id(get_next_facet(fea),get_prev_facet(fea)) ) { /* add neighbor facet to list if not already there */ fb = get_fe_facet(get_next_facet(fea)); for ( k = 0 ; k < qfcount ; k++ ) { if ( equal_element(fb,quadfacets[k]) ) break; } if ( k == qfcount ) /* not found, so add to list */ { if ( qfcount >= MAXQF ) { kb_error(2840,"pop_quad_to_quad: over 100 facets in quadrilateral.\n", RECOVERABLE); } quadfacets[qfcount++] = fb; } } } } /* Delete original quadrilateral by deleting the short edges */ keepfe = get_next_facet(quad_triples[0]); e_id = get_fe_edge(quad_triples[1]); eliminate_edge(e_id); free_element(e_id); e_id = get_fe_edge(quad_triples[3]); eliminate_edge(e_id); free_element(e_id); /* now delete any leftover quadrilateral facets */ for ( i = 0 ; i < qfcount ; i++ ) if ( valid_element(quadfacets[i]) ) eliminate_facet(quadfacets[i]); /* make sure all the triple lines are left */ for ( i = 0 ; i < 4 ; i++ ) for ( j = 0 ; j < 2 ; j++ ) if ( !valid_element(other_triples[i][j]) ) { sprintf(errmsg, "Unanticipated geometry after eliminating quad for facet %s.\n", ELNAME(f_id)); strcat(errmsg,"New quad not created.\n"); kb_error(2828,errmsg,WARNING); } /* Identify the remaining edge */ keepe = get_fe_edge(keepfe); /* Create new quad */ newv[0] = get_edge_tailv(keepe); newv[1] = get_edge_headv(keepe); newv[2] = dup_vertex(newv[1]); newv[3] = dup_vertex(newv[0]); newe[0] = keepe; newe[1] = dup_edge(keepe); set_edge_tailv(newe[1],newv[1]); set_edge_headv(newe[1],newv[2]); newe[2] = dup_edge(keepe); set_edge_tailv(newe[2],newv[2]); set_edge_headv(newe[2],newv[3]); newe[3] = dup_edge(keepe); set_edge_tailv(newe[3],newv[3]); set_edge_headv(newe[3],newv[0]); if ( web.symmetry_flag ) { set_edge_wrap(newe[1],0); set_edge_wrap(newe[2],(*sym_inverse)(get_edge_wrap(newe[0]))); set_edge_wrap(newe[3],0); } for ( i = 0 ; i < 4 ; i++ ) { newfe[i] = new_facetedge(newf,newe[i]); set_next_facet(newfe[i],newfe[i]); set_prev_facet(newfe[i],newfe[i]); set_edge_fe(newe[i],newfe[i]); } set_facet_fe(newf,newfe[0]); for ( i = 0 ; i < 4 ; i++ ) { set_next_edge(newfe[i],newfe[(i+1)%4]); set_prev_edge(newfe[i],newfe[(i+3)%4]); } cross_cut(newfe[0],newfe[1]); /* Connect in with old stuff */ /* start with first corner */ e_id = other_triples[0][0]; fe = get_edge_fe(e_id); for ( k = 0 ; k < 3 ; k++ ) { /* traverse fan in all three directions */ fe = get_next_facet(fe); next_fe = inverse_id(get_prev_edge(fe)); for (;;) { if ( !equal_id(get_next_facet(next_fe),get_prev_facet(next_fe)) ) { /* have found next triple edge */ ee_id = get_fe_edge(next_fe); if ( equal_element(ee_id,keepe) ) { /* everything already ok, but adjust facetedges */ fea = newfe[0]; set_next_facet(next_fe,fea); set_prev_facet(fea,next_fe); set_prev_facet(next_fe,fea); set_next_facet(fea,next_fe); /* and line up next corner */ for(;;) { next_fe = get_next_edge(next_fe); if ( !equal_id(get_next_facet(next_fe),get_prev_facet(next_fe)) ) { /* have found next triple edge */ eee_id = get_fe_edge(next_fe); if ( equal_element(eee_id,other_triples[1][0]) ) { /* all ok */ } else if ( equal_element(eee_id,other_triples[1][1]) ) { /* swap */ edge_id tempe = other_triples[1][0]; other_triples[1][0] = other_triples[1][1]; other_triples[1][1] = tempe; } break; } next_fe = inverse_id(get_next_facet(next_fe)); } } else if ( equal_id(ee_id,other_triples[0][1]) ) { /* spread out with new edge */ facetedge_id feb = inverse_id(get_prev_edge(fe)); facetedge_id fea = get_prev_edge(fe); facet_id fa = get_fe_facet(fe); facetedge_id newfe = new_facetedge(fa,newe[3]); facetedge_id newefe = get_edge_fe(newe[3]); while ( !equal_id(feb,next_fe) ) { eee_id = get_fe_edge(feb); remove_vertex_edge(newv[0],eee_id); set_edge_tailv(eee_id,newv[3]); feb = get_next_facet(feb); feb = inverse_id(get_prev_edge(feb)); } set_next_edge(fea,newfe); set_prev_edge(newfe,fea); set_prev_edge(fe,newfe); set_next_edge(newfe,fe); set_next_facet(newfe,newefe); set_prev_facet(newefe,newfe); set_next_facet(newefe,newfe); set_prev_facet(newfe,newefe); cross_cut(newfe,fe); } else if ( equal_id(ee_id,other_triples[3][0]) ) { /* everything ok */ } else if ( equal_id(ee_id,other_triples[3][1]) ) { /* swap */ edge_id tempe = other_triples[3][0]; other_triples[3][0] = other_triples[3][1]; other_triples[3][1] = tempe; } else /* shouldn't get here */ kb_error(2820,"Aborted pop_quad_to_quad halfway through.\n", RECOVERABLE); break; } next_fe = get_next_facet(next_fe); next_fe = inverse_id(get_prev_edge(next_fe)); } } /* other triple out of first corner */ e_id = other_triples[3][0]; fe = get_edge_fe(e_id); for ( k = 0 ; k < 3 ; k++ ) { /* traverse fan in all three directions */ fe = get_next_facet(fe); next_fe = inverse_id(get_prev_edge(fe)); for (;;) { if ( !equal_id(get_next_facet(next_fe),get_prev_facet(next_fe)) ) { /* have found next triple edge */ ee_id = get_fe_edge(next_fe); if ( equal_element(ee_id,keepe) ) { /* everything already ok, adjust facetedges */ fea = newfe[0]; set_next_facet(next_fe,fea); set_prev_facet(fea,next_fe); set_prev_facet(next_fe,get_next_facet(fea)); set_next_facet(get_next_facet(fea),next_fe); /* and line up next corner */ for(;;) { next_fe = get_next_edge(next_fe); if ( !equal_id(get_next_facet(next_fe),get_prev_facet(next_fe)) ) { /* have found next triple edge */ eee_id = get_fe_edge(next_fe); if ( equal_element(eee_id,other_triples[2][0]) ) { /* all ok */ } else if ( equal_element(eee_id,other_triples[2][1]) ) { /* swap */ edge_id tempe = other_triples[2][0]; other_triples[2][0] = other_triples[2][1]; other_triples[2][1] = tempe; } break; } next_fe = inverse_id(get_next_facet(next_fe)); } } else if ( equal_id(ee_id,other_triples[3][1]) ) { /* spread out with new edge */ facetedge_id feb = inverse_id(get_prev_edge(fe)); facetedge_id fea = get_prev_edge(fe); facet_id fa = get_fe_facet(fe); facetedge_id newfe = new_facetedge(fa,newe[3]); facetedge_id newefe = get_edge_fe(newe[3]); while ( !equal_id(feb,next_fe) ) { eee_id = get_fe_edge(feb); remove_vertex_edge(newv[0],eee_id); set_edge_tailv(eee_id,newv[3]); feb = get_next_facet(feb); feb = inverse_id(get_prev_edge(feb)); } set_next_edge(fea,newfe); set_prev_edge(newfe,fea); set_prev_edge(fe,newfe); set_next_edge(newfe,fe); set_next_facet(newfe,newefe); set_prev_facet(newefe,newfe); set_next_facet(get_next_facet(newefe),newfe); set_prev_facet(newfe,get_next_facet(newefe)); cross_cut(newfe,fe); } else if ( equal_id(ee_id,other_triples[0][0]) ) { /* everything ok */ } else /* shouldn't get here */ kb_error(2821,"Aborted pop_quad_to_quad halfway through.\n", RECOVERABLE); break; } next_fe = get_next_facet(next_fe); next_fe = inverse_id(get_prev_edge(next_fe)); } } /* another corner, at end of short edge from previous */ e_id = other_triples[0][1]; fe = get_edge_fe(e_id); for ( k = 0 ; k < 3 ; k++ ) { /* traverse fan in all three directions */ fe = get_next_facet(fe); next_fe = fe; for (;;) { ee_id = get_fe_edge(next_fe); if ( !equal_id(get_edge_tailv(ee_id),newv[3]) ) { remove_vertex_edge(newv[0],ee_id); set_edge_tailv(ee_id,newv[3]); } next_fe = inverse_id(get_prev_edge(next_fe)); if ( !equal_id(get_next_facet(next_fe),get_prev_facet(next_fe)) ) { /* have found next triple edge */ ee_id = get_fe_edge(next_fe); if ( equal_element(ee_id,newe[0]) ) { /* move fe to new edge */ set_fe_edge(next_fe,inverse_id(newe[2])); fea = inverse_id(newfe[2]); set_next_facet(next_fe,fea); set_prev_facet(fea,next_fe); set_prev_facet(next_fe,fea); set_next_facet(fea,next_fe); } else if ( equal_element(ee_id,newe[3]) ) { /* all ok */ } else if ( equal_id(ee_id,other_triples[3][1]) ) { /* everything ok */ } else /* shouldn't get here */ kb_error(2822,"Aborted pop_quad_to_quad halfway through.\n", WARNING); break; } next_fe = get_next_facet(next_fe); } } /* other part of same corner, at end of short edge from previous */ e_id = other_triples[3][1]; fe = get_edge_fe(e_id); for ( k = 0 ; k < 3 ; k++ ) { /* traverse fan in all three directions */ fe = get_next_facet(fe); next_fe = fe; for (;;) { ee_id = get_fe_edge(next_fe); if ( !equal_id(get_edge_tailv(ee_id),newv[3]) ) { remove_vertex_edge(newv[0],ee_id); set_edge_tailv(ee_id,newv[3]); } next_fe = inverse_id(get_prev_edge(next_fe)); if ( !equal_id(get_next_facet(next_fe),get_prev_facet(next_fe)) ) { /* have found next triple edge */ ee_id = get_fe_edge(next_fe); if ( equal_element(ee_id,newe[0]) ) { /* move fe to new edge */ set_fe_edge(next_fe,inverse_id(newe[2])); fea = inverse_id(newfe[2]); set_next_facet(next_fe,get_prev_facet(fea)); set_prev_facet(get_prev_facet(fea),next_fe); set_prev_facet(next_fe,fea); set_next_facet(fea,next_fe); } else if ( equal_id(ee_id,other_triples[0][1]) ) { /* all ok */ } else if ( equal_element(ee_id,newe[3]) ) { /* everything ok */ } else /* shouldn't get here */ kb_error(2823,"Aborted pop_quad_to_quad halfway through.\n", WARNING); break; } next_fe = get_next_facet(next_fe); } } /* corner at other end of long edge from first corner */ e_id = other_triples[1][0]; fe = get_edge_fe(e_id); for ( k = 0 ; k < 3 ; k++ ) { /* traverse fan in all three directions */ fe = get_next_facet(fe); next_fe = inverse_id(get_prev_edge(fe)); for (;;) { if ( !equal_id(get_next_facet(next_fe),get_prev_facet(next_fe)) ) { /* have found next triple edge */ ee_id = get_fe_edge(next_fe); if ( equal_element(ee_id,keepe) ) { /* everything already ok, but adjust facetedges */ } else if ( equal_id(ee_id,other_triples[1][1]) ) { /* spread out with new edge */ facetedge_id feb = inverse_id(get_prev_edge(fe)); facetedge_id fea = get_prev_edge(fe); facet_id fa = get_fe_facet(fe); facetedge_id newfe = new_facetedge(fa,inverse_id(newe[1])); facetedge_id newefe = get_edge_fe(inverse_id(newe[1])); while ( !equal_id(feb,next_fe) ) { eee_id = get_fe_edge(feb); remove_vertex_edge(newv[1],eee_id); set_edge_tailv(eee_id,newv[2]); feb = get_next_facet(feb); feb = inverse_id(get_prev_edge(feb)); } set_next_edge(fea,newfe); set_prev_edge(newfe,fea); set_prev_edge(fe,newfe); set_next_edge(newfe,fe); set_next_facet(newfe,newefe); set_prev_facet(newefe,newfe); set_next_facet(newefe,newfe); set_prev_facet(newfe,newefe); cross_cut(newfe,fe); } else if ( equal_id(ee_id,other_triples[2][0]) ) { /* everything ok */ } else /* shouldn't get here */ kb_error(2824,"Aborted pop_quad_to_quad halfway through.\n", WARNING); break; } next_fe = get_next_facet(next_fe); next_fe = inverse_id(get_prev_edge(next_fe)); } } /* other edge at corner at other end of long edge from first corner */ e_id = other_triples[2][0]; fe = get_edge_fe(e_id); for ( k = 0 ; k < 3 ; k++ ) { /* traverse fan in all three directions */ fe = get_next_facet(fe); next_fe = inverse_id(get_prev_edge(fe)); for (;;) { if ( !equal_id(get_next_facet(next_fe),get_prev_facet(next_fe)) ) { /* have found next triple edge */ ee_id = get_fe_edge(next_fe); if ( equal_element(ee_id,keepe) ) { /* everything already ok, but adjust facetedges */ } else if ( equal_id(ee_id,other_triples[2][1]) ) { /* spread out with new edge */ facetedge_id feb = inverse_id(get_prev_edge(fe)); facetedge_id fea = get_prev_edge(fe); facet_id fa = get_fe_facet(fe); facetedge_id newfe = new_facetedge(fa,inverse_id(newe[1])); facetedge_id newefe = get_edge_fe(inverse_id(newe[1])); while ( !equal_id(feb,next_fe) ) { eee_id = get_fe_edge(feb); remove_vertex_edge(newv[1],eee_id); set_edge_tailv(eee_id,newv[2]); feb = get_next_facet(feb); feb = inverse_id(get_prev_edge(feb)); } set_next_edge(fea,newfe); set_prev_edge(newfe,fea); set_prev_edge(fe,newfe); set_next_edge(newfe,fe); set_next_facet(newfe,get_prev_facet(newefe)); set_prev_facet(get_prev_facet(newefe),newfe); set_next_facet(newefe,newfe); set_prev_facet(newfe,newefe); cross_cut(newfe,fe); } else if ( equal_id(ee_id,other_triples[1][0]) ) { /* everything ok */ } else /* shouldn't get here */ kb_error(2825,"Aborted pop_quad_to_quad halfway through.\n", WARNING); break; } next_fe = get_next_facet(next_fe); next_fe = inverse_id(get_prev_edge(next_fe)); } } /* final corner */ e_id = other_triples[1][1]; fe = get_edge_fe(e_id); for ( k = 0 ; k < 3 ; k++ ) { /* traverse fan in all three directions */ fe = get_next_facet(fe); next_fe = fe; for (;;) { ee_id = get_fe_edge(next_fe); if ( !equal_id(get_edge_tailv(ee_id),newv[2]) ) { remove_vertex_edge(newv[1],ee_id); set_edge_tailv(ee_id,newv[2]); } next_fe = inverse_id(get_prev_edge(next_fe)); if ( !equal_id(get_next_facet(next_fe),get_prev_facet(next_fe)) ) { /* have found next triple edge */ break; } next_fe = get_next_facet(next_fe); } } /* other part of final corner */ e_id = other_triples[2][1]; fe = get_edge_fe(e_id); for ( k = 0 ; k < 3 ; k++ ) { /* traverse fan in all three directions */ fe = get_next_facet(fe); next_fe = fe; for (;;) { ee_id = get_fe_edge(next_fe); if ( !equal_id(get_edge_tailv(ee_id),newv[2]) ) { remove_vertex_edge(newv[1],ee_id); set_edge_tailv(ee_id,newv[2]); } next_fe = inverse_id(get_prev_edge(next_fe)); if ( !equal_id(get_next_facet(next_fe),get_prev_facet(next_fe)) ) { /* have found next triple edge */ break; } next_fe = get_next_facet(next_fe); } } /* move vertices apart */ for ( i = 0 ; i < 4 ; i++ ) { x[i] = get_coord(newv[i]); for ( j = 0 ; j < 2 ; j++ ) get_edge_side(other_triples[i][j],sides[i][j]); } for ( j = 0 ; j < SDIM ; j++ ) { REAL d; d = sides[0][0][j]+sides[1][0][j]+sides[2][0][j]+sides[3][0][j]; x[0][j] += d/12; x[1][j] += d/12; d = sides[0][1][j]+sides[1][1][j]+sides[2][1][j]+sides[3][1][j]; x[2][j] += d/12; x[3][j] += d/12; } /* Fix up facet edge orders */ for ( i = 0 ; i < 4 ; i++ ) { facetedge_id next_fe; int count; fa = newfe[i]; fb = get_next_facet(fa); fc = get_prev_facet(fa); ea = newe[i]; eb = newe[(i+3)%4]; next_fe = inverse_id(get_prev_edge(fb)); for(count=0;count < 10000 ;count++) { ec = get_fe_edge(next_fe); if ( equal_element(ec,ea) ) break; /* ok */ if ( equal_element(ec,eb) ) { /* have to switch order around edge */ set_prev_facet(fa,fb); set_prev_facet(fb,fc); set_prev_facet(fc,fa); set_next_facet(fa,fc); set_next_facet(fc,fb); set_next_facet(fb,fa); break; } next_fe = inverse_id(get_prev_edge(get_prev_facet(next_fe))); } if ( count >= 10000 ) { sprintf(errmsg,"Internal error after pop_quad_to_quad facet %s, bad topology around vertex %s.\n",ELNAME(f_id),ELNAME1(get_edge_tailv(ea))); kb_error(2831,errmsg,RECOVERABLE); } } /* fix up new facet bodies */ fa = get_fe_facet(newfe[0]); fe = get_prev_facet(newfe[0]); /* debugging */ fb = get_fe_facet(fe); b_id = get_facet_body(inverse_id(fb)); set_facet_body(fa,b_id); set_facet_body(inverse_id(get_fe_facet(newfe[0])), get_facet_body(get_fe_facet(get_next_facet(newfe[0])))); set_facet_body(get_fe_facet(newfe[2]), get_facet_body(inverse_id(get_fe_facet(get_prev_facet(newfe[2]))))); set_facet_body(inverse_id(get_fe_facet(newfe[2])), get_facet_body(get_fe_facet(get_next_facet(newfe[2])))); if ( web.modeltype == QUADRATIC ) { /* fix up edge midpoints */ int bailcount = 0; for ( i = 0 ; i < 4 ; i++ ) { edge_id start_e = get_vertex_edge(newv[i]); edge_id ea; ea = start_e; do { new_vertex_average(get_edge_midv(ea),VOLKEEP); ea = get_next_tail_edge(ea); if ( bailcount++ > 1000 ) break; } while ( !equal_id(ea,start_e)); } } return 1; } /* end pop_quad_to_quad */ /***************************************************************************** * * function: pop_constrained_vertex() * * purpose: analyze and pop a vertex on at least one constraint. * * return: 1 if popped, 0 if not. */ int pop_constrained_vertex(v_id) vertex_id v_id; { conmap_t *vmap = get_v_constraint_map(v_id); int vhits = v_hit_constraint_count(v_id); int kind_counts[5][4]; /* indexed by valence, constraints */ edge_id kind_lists[5][4][20]; /* actual edges, for easy reference */ int toohigh=0,triples=0; unsigned int i,j,val; edge_id e_id,start_e; if ( vhits == 3 ) return triple_con_pop(v_id); for ( i = 0; i < 5 ; i++ ) for ( j = 0 ; j < 4 ; j++ ) kind_counts[i][j] = 0; /* detect types of edges */ e_id = start_e = get_vertex_edge(v_id); do { int valence = get_edge_valence(e_id); conmap_t *emap = get_e_constraint_map(e_id); int hits = 0; /* see if constraints are at most those of v_id */ for ( i = 1 ; i <= emap[0] ; i++ ) { int found = 0; for ( j = 1 ; j <= vmap[0] ; j++ ) if ( emap[i] == (vmap[j] & ~CON_HIT_BIT) ) { if ( vmap[j] & CON_HIT_BIT ) hits++; found = 1; break; } if ( !found ) { if ( verbose_flag ) { sprintf(msg,"Pop vertex %s fails since constraints of edge %s are not a subset of those of the vertex.\n",ELNAME(v_id),ELNAME1(e_id)); outstring(msg); } return 0; } } if ( valence > 3 ) { valence = 4; toohigh++; } if ( valence == 3 ) triples++; if ( hits > 3 ) hits = 4; if ( kind_counts[valence][hits] > 20 ) { sprintf(errmsg,"Too many edges around vertex %s for pop to handle.\n", ELNAME(v_id)); kb_error(4001,errmsg,WARNING); return 0; } kind_lists[valence][hits][kind_counts[valence][hits]++] = e_id; e_id = get_next_tail_edge(e_id); } while ( !equal_id(e_id,start_e) ); /* see what we have */ if ( toohigh ) { if ( verbose_flag ) { sprintf(msg, "Vertex %s has edges of valence more than 3, so pop edges first.\n", ELNAME(v_id)); outstring(msg); } } if ( (vhits == 2) && (triples == 1)) return double_con_pop(v_id,kind_lists[3][0][0],kind_lists[1][1]); val = get_vertex_evalence(v_id); if ( (vhits == 1) && (kind_counts[1][1]==4) && (kind_counts[2][0] == val-4) ) return one_con_pop_4(v_id,kind_lists[1][1],POP_TO_BETTER); /* test for ok configurations */ if ( (vhits <= 2) && (triples == 0) ) return 0; if ( triples <= 1 ) return 0; /* so now have one constraint and at least two triple lines */ if ( (triples == 2) && (kind_counts[3][0] == 2) && (kind_counts[1][1]==4) ) { /* common case of two unconstrained triple lines with 4 constrained edges */ return one_con_pop_2(v_id,kind_lists[3][0],kind_lists[1][1],POP_TO_BETTER); } if ( (triples >= 3) && (kind_counts[3][0] == triples) && (kind_counts[1][1]==triples) ) { /* common case of N unconstrained triple lines with N constrained edges */ return one_con_pop_3(v_id,triples,kind_lists[3][0],kind_lists[1][1], POP_TO_BETTER); } return 0; } /***************************************************************************** * * function: triple_con_pop() * * purpose: Move film out of corner where three constraints meet. * Does nothing if one adjacent edge is on two constraints. * Just frees vertex from constraint not used by edges, * if not a one-sided constraint. Doesn't actually move vertex. * * return: 1 if successful, 0 if not */ int triple_con_pop(v_id) vertex_id v_id; { conmap_t *vmap = get_v_constraint_map(v_id); edge_id e_id, start_e; /* edge_id conedges[2]; */ conmap_t cons[2]; unsigned int ehits=0; /* number of adjacent edges on constraints */ unsigned int i,j; /* see if any adjacent edge is on two constraints */ /* and find out what constraints are involved */ e_id = start_e = get_vertex_edge(v_id); do { conmap_t *emap = get_e_constraint_map(e_id); int this_hits = 0; for ( i = 1 ; i <= emap[0] ; i++ ) { for ( j = 0 ; j <= vmap[0] ; j++ ) { if ( (emap[j]==(vmap[j]&~CON_HIT_BIT)) && (vmap[j] & CON_HIT_BIT) ) { if ( this_hits ) { /* now second constraint on edge */ return 0; } /* conedges[ehits] = e_id; */ cons[ehits] = emap[j]; ehits++; this_hits++; } } } e_id = get_next_tail_edge(e_id); } while ( !equal_id(e_id,start_e) ); /* see if constraint not used by edges is not one-sided */ for ( i = 1 ; i <= vmap[0] ; i++ ) { if ( !(vmap[i] & CON_HIT_BIT) ) continue; if ( get_constraint(vmap[i])->attr & (NONPOSITIVE|NONNEGATIVE) ) continue; for ( j = 0 ; j < ehits ; j++ ) if ( cons[j] == (vmap[i] & ~CON_HIT_BIT) ) continue; /* now have freeable constraint */ unset_v_constraint_map(v_id,vmap[i]); return 1; } return 0; } /***************************************************************************** * * function: double_con_pop() * * purpose: Pop triple line coming into junction of two constraints. * Moves triple line end to the constraint it is most * parallel to. Presumably triple edge got there by deletion * of a short edge, so probably wants to go on the constraint * with the single edge. * * return: 1 if successful, 0 if not */ int double_con_pop(v_id,triple,con_edges) vertex_id v_id; edge_id triple; /* the triple edge */ edge_id *con_edges; /* the edges on constraints */ { conmap_t *vmap = get_v_constraint_map(v_id); conmap_t cons[2]; conmap_t concounts[2]; unsigned int count; unsigned int i,j,k; edge_id cedges[2][2]; REAL tripvec[MAXCOORD]; REAL con_normal[2][MAXCOORD]; REAL dummy; if ( (get_vattr(v_id) & FIXED) || (get_eattr(con_edges[0]) & FIXED) || (get_eattr(con_edges[1]) & FIXED) || (get_eattr(con_edges[2]) & FIXED)) return 0; concounts[0] = concounts[1] = count = 0; /* find which constraint used twice */ for ( i = 0 ; i < 3 ; i++ ) { conmap_t *emap = get_e_constraint_map(con_edges[i]); for ( j = 1 ; j <= emap[0] ; j++ ) { /* see if hit by vertex */ for ( k = 1 ; k <= vmap[0] ; k++ ) if ( (emap[j] == (vmap[k] & ~CON_HIT_BIT)) && (vmap[k] & CON_HIT_BIT) ) break; if ( k > vmap[0] ) continue; for ( k = 0 ; k < count ; k++ ) { if ( emap[j] == cons[k] ) { cedges[k][concounts[k]++] = con_edges[i]; break; } } if ( k == count ) { cons[k] = emap[j]; concounts[k] = 1; cedges[k][0] = con_edges[i]; count++; } } } if ( concounts[0] == 1 ) { /* swap */ edge_id tempe; conmap_t temp; tempe = cedges[0][0]; cedges[0][0] = cedges[1][0]; cedges[0][1] = cedges[1][1]; cedges[1][0] = tempe; temp = cons[0]; cons[0] = cons[1]; cons[1] = temp; concounts[0] = 2; concounts[1] = 1; } /* calculate gradients */ for ( i = 0 ; i < 2 ; i++ ) eval_all(get_constraint(cons[i])->formula,get_coord(v_id),SDIM, &dummy,con_normal[i],v_id); if (fabs(dot(tripvec,con_normal[0],SDIM)) < fabs(dot(tripvec,con_normal[1],SDIM))) { /* v_id goes to single-edge constraint */ /* generated new vertices on junction by refining edges */ edge_refine(cedges[0][0]); edge_refine(cedges[0][1]); /* old edge is the one attached to v_id */ set_v_constraint_map(get_edge_headv(cedges[0][0]),cons[1]); set_v_constraint_map(get_edge_headv(cedges[0][1]),cons[1]); unset_v_constraint_map(v_id,cons[0]); unset_e_constraint_map(cedges[0][0],cons[0]); unset_e_constraint_map(cedges[0][1],cons[0]); set_e_constraint_map(cedges[0][0],cons[1]); set_e_constraint_map(cedges[0][1],cons[1]); } else { /* move to double-edge constraint */ edge_refine(cedges[1][0]); /* old edge is the one attached to v_id */ set_v_constraint_map(get_edge_headv(cedges[1][0]),cons[0]); unset_v_constraint_map(v_id,cons[1]); unset_e_constraint_map(cedges[1][0],cons[1]); set_e_constraint_map(cedges[1][0],cons[0]); } return 1; } /*************************************************************************** * * function: one_con_pop_2() * * purpose: pop a specific configuration of two triple edges on vertex * with one constraint. Favors simple pull apart of triple edges. * * return: 1 if successful */ int one_con_pop_2(v_id,triples,con_edges,mode) vertex_id v_id; edge_id *triples; edge_id *con_edges; int mode; /* POP_TO_OPEN or POP_TO_TWIST or POP_BETTER */ { vertex_id newv; edge_id newe,e_id; facetedge_id fe,start_fe,newfe; unsigned int i,j,k; REAL sides[2][2][MAXCOORD]; REAL mag[2][2]; REAL *x; edge_id attached[2][2]; /* which constraint edges attached to which trip */ int atcount[2]; conmap_t con=0,*vmap; /* which constraint involved */ int n; REAL pull[2][MAXCOORD]; /* separating forces for the two ways */ if ( get_vattr(v_id) & FIXED ) return 0; /* see which constraint edges attached to which triple, and get in order */ atcount[0] = atcount[1] = 0; for ( k = 0 ; k < 2 ; k++ ) /* for each triple edge */ { int triple_second_flag = 0; start_fe = get_edge_fe(triples[k]); for ( i = 0 ; i < 3 ; i++ ) { fe = inverse_id(get_prev_edge(start_fe)); for(;;) { if ( equal_id(triples[1-k],get_fe_edge(fe)) ) { if ( i == 1 ) triple_second_flag = 1; break; } e_id = get_fe_edge(fe); if ( equal_id(fe,get_next_facet(fe)) ) { /* found valence 1 edge */ attached[k][atcount[k]++] = e_id; break; } fe = inverse_id(get_prev_edge(get_next_facet(fe))); } start_fe = get_next_facet(start_fe); } if ( triple_second_flag ) { /* swap into standard order */ edge_id temp = attached[k][0]; attached[k][0] = attached[k][1]; attached[k][1] = temp; } for ( i = 0 ; i < 2 ; i++ ) get_edge_side(attached[k][i],sides[k][i]); } /* see which constraint involved */ vmap = get_v_constraint_map(v_id); for ( i = 1; i <= vmap[0] ; i++ ) if ( vmap[i] & CON_HIT_BIT ) { con = vmap[i] & ~CON_HIT_BIT; break; } /* figure out which way it should pop according to edges on constraints */ if ( mode == POP_TO_BETTER ) { for ( i = 0 ; i < 2 ; i++ ) for ( j = 0 ; j < 2 ; j++ ) mag[i][j] = dot(sides[i][j],sides[i][j],SDIM); for ( n = 0 ; n < SDIM ; n++ ) { pull[0][n] = sides[0][0][n]/mag[0][0]+sides[1][1][n]/mag[1][1] -sides[0][1][n]/mag[0][1]-sides[1][0][n]/mag[1][0]; pull[1][n] = sides[0][0][n]/mag[0][0]+sides[0][1][n]/mag[0][1] -sides[1][0][n]/mag[1][0]-sides[1][1][n]/mag[1][1]; } if ( dot(pull[0],pull[0],SDIM) < 1.2*dot(pull[1],pull[1],SDIM) ) mode = POP_TO_OPEN; else mode = POP_TO_TWIST; } if ( mode == POP_TO_OPEN ) { /* simple pull two triple edges apart */ newv = dup_vertex(v_id); newe = new_edge(v_id,newv,NULLID); set_e_conmap(newe,get_v_constraint_map(v_id)); /* move stuff around second triple edge to newv */ remove_vertex_edge(v_id,triples[1]); set_edge_tailv(triples[1],newv); start_fe = get_edge_fe(triples[1]); for ( i = 0 ; i < 3 ; i++ ) { fe = inverse_id(get_prev_edge(start_fe)); for(;;) { if ( equal_id(triples[0],get_fe_edge(fe)) ) { /* take care of splitting facet(s) between triples */ facet_id f_id = inverse_id(get_fe_facet(fe)); newfe = new_facetedge(f_id,newe); set_edge_fe(newe,newfe); set_next_facet(newfe,newfe); set_prev_facet(newfe,newfe); set_prev_edge(newfe,inverse_id(fe)); set_next_edge(newfe,inverse_id(get_prev_edge(fe))); set_prev_edge(inverse_id(get_prev_edge(fe)),newfe); set_next_edge(inverse_id(fe),newfe); cross_cut(inverse_id(fe),newfe); break; } e_id = get_fe_edge(fe); remove_vertex_edge(v_id,e_id); set_edge_tailv(e_id,newv); if ( equal_id(fe,get_next_facet(fe)) ) { /* found valence 1 edge */ break; } fe = inverse_id(get_prev_edge(get_next_facet(fe))); } start_fe = get_next_facet(start_fe); } /* move vertices apart a bit */ x = get_coord(newv); for ( n = 0 ; n < SDIM ; n++ ) x[n] += sides[0][0][n]/6 + sides[0][1][n]/6 + sides[1][0][n]/3 + sides[1][1][n]/3; x = get_coord(v_id); for ( n = 0 ; n < SDIM ; n++ ) x[n] += sides[0][0][n]/3 + sides[0][1][n]/3 + sides[1][0][n]/6 + sides[1][1][n]/6; } else { /* create twist facet in pull-apart */ vertex_id newv1,newv2; edge_id newe1,newe2,newe3; facet_id newf; facetedge_id fe,prevfe,newfe1,newfe2,newfe3,newfe4,newfe5; REAL tsides[2][MAXCOORD]; /* v_id will be released from constraint and moved off constraint */ newv1 = dup_vertex(v_id); newv2 = dup_vertex(v_id); newe1 = new_edge(v_id,newv1,triples[0]); newe2 = new_edge(v_id,newv2,triples[0]); newe3 = new_edge(newv1,newv2,NULLID); set_e_conmap(newe3,get_v_constraint_map(v_id)); unset_v_constraint_map(v_id,con); /* create twist facet */ newf = new_facet(); newfe1 = new_facetedge(newf,newe1); newfe2 = new_facetedge(inverse_id(newf),newe2); newfe3 = new_facetedge(newf,newe3); set_edge_fe(newe1,newfe1); set_edge_fe(newe2,newfe2); set_edge_fe(newe3,newfe3); set_facet_fe(newf,newfe1); set_next_edge(newfe1,newfe3); set_next_edge(newfe2,inverse_id(newfe3)); set_next_edge(newfe3,inverse_id(newfe2)); set_prev_edge(newfe1,inverse_id(newfe2)); set_prev_edge(newfe2,inverse_id(newfe1)); set_prev_edge(newfe3,newfe1); set_next_facet(newfe3,newfe3); set_prev_facet(newfe3,newfe3); /* reconnect wings */ remove_vertex_edge(v_id,attached[0][0]); remove_vertex_edge(v_id,attached[0][1]); remove_vertex_edge(v_id,attached[1][0]); remove_vertex_edge(v_id,attached[1][1]); set_edge_tailv(attached[0][0],newv2); set_edge_tailv(attached[0][1],newv1); set_edge_tailv(attached[1][0],newv1); set_edge_tailv(attached[1][1],newv2); fe = get_edge_fe(attached[0][0]); prevfe = get_prev_edge(fe); newfe4 = new_facetedge(get_fe_facet(fe),newe2); set_next_edge(prevfe,newfe4); set_prev_edge(newfe4,prevfe); set_next_edge(newfe4,fe); set_prev_edge(fe,newfe4); set_next_facet(newfe2,newfe4); set_prev_facet(newfe4,newfe2); cross_cut(newfe4,fe); fe = get_edge_fe(attached[1][1]); prevfe = get_prev_edge(fe); newfe5 = new_facetedge(get_fe_facet(fe),newe2); set_next_edge(prevfe,newfe5); set_prev_edge(newfe5,prevfe); set_next_edge(newfe5,fe); set_prev_edge(fe,newfe5); set_next_facet(newfe5,newfe2); set_prev_facet(newfe2,newfe5); set_next_facet(newfe4,newfe5); set_prev_facet(newfe5,newfe4); cross_cut(newfe5,fe); fe = get_edge_fe(attached[1][0]); prevfe = get_prev_edge(fe); newfe4 = new_facetedge(get_fe_facet(fe),newe1); set_next_edge(prevfe,newfe4); set_prev_edge(newfe4,prevfe); set_next_edge(newfe4,fe); set_prev_edge(fe,newfe4); set_next_facet(newfe1,newfe4); set_prev_facet(newfe4,newfe1); cross_cut(newfe4,fe); fe = get_edge_fe(attached[0][1]); prevfe = get_prev_edge(fe); newfe5 = new_facetedge(get_fe_facet(fe),newe1); set_next_edge(prevfe,newfe5); set_prev_edge(newfe5,prevfe); set_next_edge(newfe5,fe); set_prev_edge(fe,newfe5); set_next_facet(newfe5,newfe1); set_prev_facet(newfe1,newfe5); set_next_facet(newfe4,newfe5); set_prev_facet(newfe5,newfe4); cross_cut(newfe5,fe); set_facet_body(newf,get_facet_body(inverse_id(get_fe_facet(newfe5)))); set_facet_body(inverse_id(newf),get_facet_body(get_fe_facet(newfe4))); /* move vertices */ x = get_coord(newv1); for ( n = 0 ; n < SDIM ; n++ ) x[n] += sides[1][0][n]/3 + sides[0][1][n]/3 + sides[0][0][n]/6 + sides[1][1][n]/6; x = get_coord(newv2); for ( n = 0 ; n < SDIM ; n++ ) x[n] += sides[1][0][n]/6 + sides[0][1][n]/6 + sides[0][0][n]/3 + sides[1][1][n]/3; x = get_coord(v_id); get_edge_side(triples[0],tsides[0]); get_edge_side(triples[1],tsides[1]); for ( n = 0 ; n < SDIM ; n++ ) x[n] += 0.15*tsides[0][n] + 0.15*tsides[1][n]; } return 1; } /*************************************************************************** * * function: one_con_pop_3() * * purpose: pop a specific configuration of N triple edges on vertex * with one constraint. Each triple edge is on a fin with * one constrained edge. * * return: 1 if successful */ #define MAXTRIPS 20 int one_con_pop_3(v_id,tripcount,triples,con_edges,mode) vertex_id v_id; int tripcount; edge_id *triples; edge_id *con_edges; int mode; /* POP_TO_OPEN or POP_TO_TRIPLE or POP_BETTER */ { edge_id e_id; unsigned int i; REAL sides[MAXTRIPS][MAXCOORD]; REAL tsides[MAXTRIPS][MAXCOORD]; REAL mag[MAXTRIPS]; REAL net[MAXCOORD]; REAL *x; conmap_t con=0,*vmap; /* which constraint involved */ int j,n,m; facetedge_id fe; if ( get_vattr(v_id) & FIXED ) return 0; /* see which constraint involved */ vmap = get_v_constraint_map(v_id); for ( i = 1; i <= vmap[0] ; i++ ) if ( vmap[i] & CON_HIT_BIT ) { con = vmap[i] & ~CON_HIT_BIT; break; } /* get triple edges and constrained edges lined up */ fe = get_edge_fe(con_edges[0]); fe = inverse_id(get_prev_edge(fe)); while ( equal_id(get_next_facet(fe),get_prev_facet(fe)) ) fe = inverse_id(get_prev_edge(get_next_facet(fe))); fe = get_prev_facet(fe); for ( m = 0 ; m < tripcount ; m++ ) { facetedge_id ffe; triples[m] = get_fe_edge(fe); /* seek down to corresponding constrained edge */ ffe = get_next_facet(fe); ffe = inverse_id(get_prev_edge(ffe)); while ( !equal_id(get_next_facet(ffe),ffe) ) ffe = inverse_id(get_prev_edge(get_next_facet(ffe))); con_edges[m] = get_fe_edge(ffe); /* now over to next triple */ fe = get_prev_facet(fe); fe = inverse_id(get_prev_edge(fe)); while ( equal_id(get_next_facet(fe),get_prev_facet(fe)) ) fe = inverse_id(get_prev_edge(get_next_facet(fe))); } for ( m = 0 ; m < tripcount ; m++ ) get_edge_side(triples[m],tsides[m]); for ( m = 0 ; m < tripcount ; m++ ) { get_edge_side(con_edges[m],sides[m]); mag[m] = sqrt(dot(tsides[m],tsides[m],SDIM)); } /* figure out which way it should pop according to edges on constraints */ if ( mode == POP_TO_BETTER ) { for ( n = 0 ; n < SDIM ; n++ ) for ( m = 0, net[n] = 0.0 ; m < tripcount ; m++ ) net[n] += tsides[m][n]/mag[m]; if ( sqrt(dot(net,net,SDIM)) < 0.7*tripcount ) mode = POP_TO_OPEN; else mode = POP_TO_TRIPLE; } /* do the pop */ if ( mode == POP_TO_OPEN ) { /* make open triangle */ vertex_id newv[MAXTRIPS]; edge_id newe[MAXTRIPS]; facetedge_id next_fe,fe,start_fe,newfe; newv[0] = v_id; for ( m = 1 ; m < tripcount ; m++ ) newv[m] = dup_vertex(v_id); for ( m = 0 ; m < tripcount ; m++ ) { newe[m] = new_edge(newv[m],newv[(m+1)%tripcount],NULLID); set_e_conmap(newe[m],get_v_constraint_map(v_id)); } /* move stuff around last two constraint edges to new vertices */ /* and putting in new edges */ for ( m = 0 ; m < tripcount ; m++ ) { int found_triple; if ( m > 0 ) { remove_vertex_edge(v_id,con_edges[m]); set_edge_tailv(con_edges[m],newv[m]); } start_fe = get_edge_fe(con_edges[m]); fe = inverse_id(get_prev_edge(start_fe)); for( found_triple = 0 ; !found_triple ; fe = next_fe ) { next_fe = inverse_id(get_prev_edge(get_next_facet(fe))); if ( !equal_id(get_next_facet(fe),get_prev_facet(fe)) ) { /* take care of splitting facet(s) between triples */ facet_id f_id; facetedge_id ffe,prevfe; ffe = get_next_facet(fe); f_id = inverse_id(get_fe_facet(ffe)); newfe = new_facetedge(f_id,newe[m]); set_edge_fe(newe[m],newfe); set_next_facet(newfe,newfe); set_prev_facet(newfe,newfe); prevfe = inverse_id(get_prev_edge(ffe)); set_prev_edge(newfe,inverse_id(ffe)); set_next_edge(newfe,prevfe); set_prev_edge(prevfe,newfe); set_next_edge(inverse_id(ffe),newfe); /* continue on, resetting endpoints on crossing face */ if ( m != tripcount-1 ) while (equal_id(get_next_facet(prevfe),get_prev_facet(prevfe))) { e_id = get_fe_edge(prevfe); remove_vertex_edge(v_id,e_id); set_edge_tailv(e_id,newv[m+1]); prevfe = inverse_id(get_prev_edge(get_next_facet(prevfe))); } cross_cut(newfe,get_next_edge(newfe)); found_triple = 1; } e_id = get_fe_edge(fe); if ( m > 0 ) { remove_vertex_edge(v_id,e_id); set_edge_tailv(e_id,newv[m]); } } } /* move vertices apart a bit */ for ( m = 0 ; m < tripcount ; m++ ) { x = get_coord(newv[m]); for ( n = 0 ; n < SDIM ; n++ ) { x[n] += 0.5*sides[m][n]; for ( j = 0 ; j < tripcount ; j++ ) x[n] += 0.5*sides[j][n]/tripcount; } } } else { /* create pulled-out triple edge */ vertex_id newv; edge_id newe; facetedge_id fe,prevfe,newfe[MAXTRIPS]; /* v_id will be released from constraint and moved off constraint */ newv = dup_vertex(v_id); newe = new_edge(v_id,newv,triples[0]); unset_v_constraint_map(v_id,con); /* reconnect wings */ for ( m = 0 ; m < tripcount ; m++ ) { remove_vertex_edge(v_id,con_edges[m]); set_edge_tailv(con_edges[m],newv); fe = get_edge_fe(con_edges[m]); prevfe = get_prev_edge(fe); newfe[m] = new_facetedge(get_fe_facet(fe),newe); set_next_edge(prevfe,newfe[m]); set_prev_edge(newfe[m],prevfe); set_next_edge(newfe[m],fe); set_prev_edge(fe,newfe[m]); cross_cut(newfe[m],fe); } set_edge_fe(newe,newfe[0]); for ( m = 0 ; m < tripcount ; m++ ) { set_next_facet(newfe[m],newfe[(m+1)%tripcount]); set_prev_facet(newfe[(m+1)%tripcount],newfe[m]); } fe_reorder(newe); /* make sure in proper geometric order */ /* move vertex */ x = get_coord(v_id); for ( n = 0 ; n < SDIM ; n++ ) { for ( m= 0 ; m < tripcount ; m++ ) x[n] += 0.3*tsides[m][n]/tripcount; } } return 1; } /*************************************************************************** * * function: one_con_pop_4() * * purpose: pop a specific configuration of two films meeting at a * point on a constraint, i.e. parting mounds. * Or joining mounds that are touching at one vertex. * * return: 1 if successful */ int one_con_pop_4(v_id,con_edges,mode) vertex_id v_id; edge_id *con_edges; int mode; /* POP_TO_OPEN or POP_TO_TRIANGLE or POP_TO_BETTER */ { vertex_id newv[2]; int i; REAL sides[4][MAXCOORD]; REAL mag[4]; facetedge_id fe,next_fe; if ( get_vattr(v_id) & FIXED ) return 0; /* see which constraint edges belong together */ fe = get_edge_fe(con_edges[0]); do { fe = inverse_id(get_prev_edge(fe)); next_fe = get_next_facet(fe); if ( equal_id(fe,next_fe) ) { /* found it */ edge_id other_e = get_fe_edge(fe); for ( i = 2 ; i < 4 ; i++ ) /* get in second spot */ if ( equal_id(other_e,con_edges[i]) ) { con_edges[i] = con_edges[1]; con_edges[1] = other_e; } break; } fe = next_fe; } while (1); /* get side vectors */ for ( i = 0 ; i < 4 ; i++ ) { get_edge_side(con_edges[i],sides[i]); mag[i] = sqrt(dot(sides[i],sides[i],SDIM)); } /* figure out which way it should pop according to edges on constraints */ if ( mode == POP_TO_BETTER ) { if ( pop_disjoin_flag ) mode = POP_TO_OPEN; /* if included angles bigger than exterior, then merge */ else if ( acos(dot(sides[0],sides[1],SDIM)/mag[0]/mag[1]) + acos(dot(sides[2],sides[3],SDIM)/mag[2]/mag[3]) > M_PI ) mode = POP_TO_TRIANGLE; else mode = POP_TO_OPEN; } /* do the pop */ if ( mode == POP_TO_OPEN ) { /* make separate vertices */ edge_id other_e; newv[0] = v_id; newv[1] = dup_vertex(v_id); /* reconnect edges */ next_fe = get_edge_fe(con_edges[2]); do { fe = next_fe; other_e = get_fe_edge(fe); remove_vertex_edge(v_id,other_e); set_edge_tailv(other_e,newv[1]); fe = inverse_id(get_prev_edge(fe)); next_fe = get_next_facet(fe); } while ( !equal_id(other_e,con_edges[3]) ); set_vertex_edge(newv[0],con_edges[0]); set_vertex_edge(newv[1],con_edges[2]); /* no reason to move vertices apart a bit */ } else { /* create pulled-out triangle */ facetedge_id fe1,fe2,newfe1,newfe2,prev1,prev2; REAL *xold,*xnew; edge_id newe; facet_id f1,f2; facetedge_id fe_a,fe_b,cc_fe; body_id b1f,b1b,b2f,b2b; /* get con_edges in canonical order */ if ( dot(sides[0],sides[3],SDIM)*mag[1]*mag[2] + dot(sides[1],sides[2],SDIM)*mag[0]*mag[3] < dot(sides[0],sides[2],SDIM)*mag[1]*mag[3] + dot(sides[1],sides[3],SDIM)*mag[0]*mag[2]) { edge_id tmpe = con_edges[2]; con_edges[2] = con_edges[3]; con_edges[3] = tmpe; for ( i = 0 ; i < SDIM ; i++ ) { REAL t = sides[2][i]; sides[2][i] = sides[3][i]; sides[3][i] = t; } } /* first, split vertex and put edge between */ newv[0] = v_id; newv[1] = dup_vertex(v_id); newe = new_edge(newv[0],newv[1],NULLID); f1 = get_fe_facet(get_edge_fe(con_edges[1])); f2 = get_fe_facet(get_edge_fe(con_edges[2])); newfe1 = new_facetedge(f1,newe); newfe2 = new_facetedge(f2,newe); set_edge_fe(newe,newfe1); set_next_facet(newfe1,newfe2); set_prev_facet(newfe1,newfe2); set_next_facet(newfe2,newfe1); set_prev_facet(newfe2,newfe1); /* reconnect edges */ remove_vertex_edge(v_id,con_edges[1]); set_edge_tailv(con_edges[1],newv[1]); remove_vertex_edge(v_id,con_edges[2]); set_edge_tailv(con_edges[2],newv[1]); fe1 = get_edge_fe(con_edges[1]); prev1 = get_prev_edge(fe1); set_prev_edge(newfe1,prev1); set_next_edge(prev1,newfe1); set_next_edge(newfe1,fe1); set_prev_edge(fe1,newfe1); fe2 = get_edge_fe(con_edges[2]); prev2 = get_prev_edge(fe2); set_prev_edge(newfe2,prev2); set_next_edge(prev2,newfe2); set_next_edge(newfe2,fe2); set_prev_edge(fe2,newfe2); cross_cut(prev1,newfe1); cross_cut(prev2,newfe2); /* move vertices apart a bit */ xold = get_coord(v_id); xnew = get_coord(newv[1]); for ( i = 0 ; i < SDIM ; i++ ) { xold[i] += 0.1 * sides[0][i] + 0.1 * sides[3][i]; xnew[i] += 0.1 * sides[1][i] + 0.1 * sides[2][i]; } /* refine the new edge */ cc_fe = get_next_edge(newfe1); /* need to save for later */ edge_refine(newe); /* Septum if bodies disagree */ b1f = get_facet_body(f1); b1b = get_facet_body(inverse_id(f1)); b2f = get_facet_body(f2); b2b = get_facet_body(inverse_id(f2)); /* figure out which side needs the septum, if any */ fe_a = fe_b = NULLID; if ( !equal_id(b1b,b2f) ) { fe_a = newfe1; fe_b = get_prev_edge(cc_fe); } else if ( !equal_id(b1f,b2b) ) { fe_a = inverse_id(get_prev_edge(cc_fe)); fe_b = inverse_id(newfe1); } if ( valid_id(fe_a) ) { /* put in new facet */ edge_id span_e = new_edge( get_fe_headv(fe_b),get_fe_tailv(fe_a),v_id); facet_id span_f = new_facet(); facetedge_id span_fe = new_facetedge(span_f,span_e); facetedge_id fe_a_new = new_facetedge(span_f,get_fe_edge(fe_a)); facetedge_id fe_b_new = new_facetedge(span_f,get_fe_edge(fe_b)); facetedge_id fe_c,fe_d; set_facet_fe(span_f,span_fe); set_edge_fe(span_e,span_fe); set_facet_density(span_f,1.0); set_next_edge(span_fe,fe_a_new); set_next_edge(fe_a_new,fe_b_new); set_next_edge(fe_b_new,span_fe); set_prev_edge(span_fe,fe_b_new); set_prev_edge(fe_b_new,fe_a_new); set_prev_edge(fe_a_new,span_fe); set_next_facet(span_fe,span_fe); set_prev_facet(span_fe,span_fe); fe_c = get_next_facet(fe_a); fe_d = get_next_facet(fe_b); set_next_facet(fe_a,fe_a_new); set_next_facet(fe_a_new,fe_c); set_prev_facet(fe_c,fe_a_new); set_prev_facet(fe_a_new,fe_a); set_next_facet(fe_b,fe_b_new); set_next_facet(fe_b_new,fe_d); set_prev_facet(fe_d,fe_b_new); set_prev_facet(fe_b_new,fe_b); set_facet_body(span_f,get_facet_body(inverse_id(get_fe_facet(fe_a)))); set_facet_body(inverse_id(span_f),get_facet_body(get_fe_facet(fe_c))); } } /* end pulled-out triangle */ return 1; } /************************************************************************** * * function: pop_tri_to_edge_con() * * purpose: Implement pop_tri_to_edge for triple edge with tail on constraint. * Algorithm: deletes edge, calls one_con_pop_3 in proper mode. * Called from pop_tri_to_edge(), which has checked valence and * number of triple edges at head. * * return: 1 if successful, 0 if not. */ int pop_edge_to_tri_con(e_id) edge_id e_id; { vertex_id v_id = get_edge_tailv(e_id); edge_id ee_id, start_e; int retval; int concount = 0,tripcount = 0; edge_id triples[20]; edge_id con_edges[20]; retval = eliminate_edge(e_id); if ( retval == 0 ) return 0; free_element(e_id); /* quirk of eliminate_edge */ /* gather data for one_con_pop_3 */ ee_id = start_e = get_vertex_edge(v_id); do { int valence = get_edge_valence(ee_id); if ( valence == 3 ) triples[tripcount++] = ee_id; if ( valence == 1 ) con_edges[concount++] = ee_id; ee_id = get_next_tail_edge(ee_id); if ( tripcount >= 20 || concount >= 20 ) { if ( verbose_flag ) { sprintf(msg,"Can't handle over 20-valence edge %s! Sorry.\n", ELNAME(e_id)); outstring(msg); } return 0; } } while ( !equal_id(ee_id,start_e) ); if ( tripcount != concount ) { if ( verbose_flag ) { sprintf(msg,"Pop_edge_to_tri not applicable to edge %s.\n",ELNAME(e_id)); outstring(msg); } return 0; } return one_con_pop_3(v_id,tripcount,triples,con_edges,POP_TO_OPEN); } evolver-2.30c.dfsg/src/web.h0000644000175300017530000002231111410765113016142 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************** * * The ultimate structure for a whole surface , including all global * variables needed to export for distributed computing. */ #ifdef __cplusplus extern "C" { #endif /* structure type name different from structure variable name since Visual C is incompetent at distinguishing in debugger. */ struct webstruct { struct skeleton skel[NUMELEMENTS]; int sizes[NUMELEMENTS]; /* allocated space for element structure */ int usedsizes[NUMELEMENTS]; /* used space for element structure */ struct element **elhashtable; /* id hash list of element pointers */ int elhashcount; /* actual number of live entries */ int elhashmask; /* for picking off index bits of id hash */ int elhashsize; /* size of hash table; power of 2 */ int sdim; /* dimension of ambient space */ int dimension; /* where tension resides */ int representation; /* STRING, SOAPFILM, or SIMPLEX */ int modeltype; /* QUADRATIC, LINEAR, or LAGRANGE; see defines below */ int lagrange_order; /* polynomial order of elements */ int headvnum; /* number of head vertex in edge list */ int maxparam; /* maximum number of parameters in any boundary */ int maxcon; /* number of constraint structures allocated */ int highcon; /* highest constraint number used */ struct constraint *constraints; /* constraint definitions */ conmap_t con_global_map[MAXCONPER]; /* global vertex constraints */ int con_global_count; /* number of global vertex constraints */ REAL tolerance; /* constraint error tolerance */ REAL target_tolerance; /* error tolerance for extensive constraints */ int bdrymax; /* number of boundary structures allocated */ int highbdry; /* highest boundary number used */ struct boundary *boundaries; /* for free boundaries */ int diffusion_flag; /* whether diffusion in effect */ REAL diffusion_const; /* coefficient for diffusion */ REAL simplex_factorial; /* content correction factor for determinant */ int torus_clip_flag; int torus_body_flag; int symmetric_content; /* 1 if volumes use symmetric divergence */ int h_inverse_metric_flag; /* for laplacian of curvature */ REAL meritfactor; /* for multiplying figure of merit */ int gravflag; /* whether gravity is on */ REAL grav_const; /* multiplier for gravitational force */ int convex_flag; /* whether any convex boundaries present */ int pressflag; /* whether prescribed pressures present */ int constr_flag; /* set if there are any one-sided constraints */ int hide_flag; /* set for hidden surface removal */ int motion_flag; /* set for fixed scale of motion; otherwise seek minimum. */ int symmetry_flag; /* whether symmetry group in effect */ int torus_flag; /* whether working in toroidal domain */ int full_flag; /* whether torus solidly packed with bodies */ int pressure_flag; /* whether pressure used dynamically */ int projection_flag; /* whether to project */ int area_norm_flag; /* whether to normalize force by area surrounding vertex */ int norm_check_flag; /* whether area normalization checks normal deviation */ REAL norm_check_max; /* maximum allowable deviation */ int vol_flag; /* whether body volumes up to date */ int jiggle_flag; /* whether to jiggle vertices at each move */ int homothety; /* flag for homothety adjustment each iteration */ int wulff_flag; /* whether we are using wulff shapes for energy */ int wulff_count; /* number of Wulff vectors read in */ char wulff_name[60]; /* Wulff file or keyword */ vertex_id zoom_v; /* vertex to zoom on */ REAL zoom_radius; /* current zoom radius */ REAL total_area; REAL total_area_addends[MAXADDENDS]; /* for binary tree addition */ REAL total_energy; REAL total_energy_addends[MAXADDENDS]; /* for binary tree addition */ REAL spring_energy; int total_facets; int bodycount; /* number of bodies */ body_id outside_body; /* a body surrounding all others */ REAL scale; /* force to motion scale factor */ REAL scale_scale; /* over-relaxation factor */ REAL maxscale; /* upper limit on scale factor */ REAL pressure; /* ambient pressure */ REAL min_area; /* criterion on weeding out small triangles */ REAL min_length; /* criterion on weeding out small triangles */ REAL max_len; /* criterion for dividing long edges */ REAL max_angle; /* max allowed deviation from parallelism */ REAL temperature; /* "temperature" for jiggling */ REAL spring_constant; /* for forcing edges to conform to boundary */ int gauss1D_order; /* order for gaussian 1D integration */ int gauss2D_order; /* order for gaussian 2D integration */ REAL torusv; /* unit cell volume or area */ REAL **torus_period; REAL **inverse_periods;/* inverse matrix of torus periods */ REAL **torus_display_period; REAL display_origin[MAXCOORD]; REAL **inverse_display_periods;/* inverse of torus display periods */ int metric_flag; /* set if background metric in force */ int conformal_flag; /* set for conformal metrics */ struct expnode metric[MAXCOORD][MAXCOORD]; /* metric component functions */ /* Some counters. New scheme: Using word of flag bits for having-been- reported status and needing-reported status, so exec() doesn't have to zero these for each call to exec. */ /* Counts that are the result of mass action only are reported immediately */ int equi_count; int edge_delete_count; int facet_delete_count; int edge_refine_count; int facet_refine_count; int vertex_dissolve_count; int edge_dissolve_count; int facet_dissolve_count; int body_dissolve_count; int edge_reverse_count; int facet_reverse_count; int vertex_pop_count; int edge_pop_count; int pop_tri_to_edge_count; int pop_edge_to_tri_count; int pop_quad_to_quad_count; int where_count; int edgeswap_count; int t1_edgeswap_count; int fix_count; int unfix_count; int notch_count; /* flag words and bits */ int counts_reported; int counts_changed; #define equi_count_bit 0x00000001 #define weed_count_bit 0x00000002 #define edge_delete_count_bit 0x00000004 #define facet_delete_count_bit 0x00000008 #define edge_refine_count_bit 0x00000010 #define facet_refine_count_bit 0x00000020 #define notch_count_bit 0x00000040 #define vertex_dissolve_count_bit 0x00000080 #define edge_dissolve_count_bit 0x00000100 #define facet_dissolve_count_bit 0x00000200 #define body_dissolve_count_bit 0x00000400 #define vertex_pop_count_bit 0x00000800 #define edge_pop_count_bit 0x00001000 #define pop_tri_to_edge_count_bit 0x00004000 #define pop_edge_to_tri_count_bit 0x00008000 #define pop_quad_to_quad_count_bit 0x00010000 #define where_count_bit 0x00020000 #define edgeswap_count_bit 0x00040000 #define fix_count_bit 0x00080000 #define unfix_count_bit 0x00100000 #define t1_edgeswap_count_bit 0x00200000 #define edge_reverse_count_bit 0x00400000 #define facet_reverse_count_bit 0x00800000 /* here follows stuff moved from independent globals to inside web so as to be easily exported. Previous global names are defined to web fields elsewhere. */ DY_OFFSET dy_gen_quants_w; int gen_quant_count_w; int gen_quant_alloc_w; int global_count; int maxglobals; /* number allocated */ int perm_global_count; int max_perm_globals; /* number allocated */ DY_OFFSET dy_meth_inst_w; /* for storing instance structures */ int meth_inst_alloc_w; /* number allocated */ int meth_inst_count_w; /* number defined */ /* global method instances, applying to every element of type */ int global_meth_inst_w[NUMELEMENTS][MAXGLOBINST]; /* lists */ int global_meth_inst_count_w[NUMELEMENTS]; /* flags telling which quantity calculations necessary */ /* flag set for Q_ENERGY,Q_FIXED, or Q_INFO if any element needs a quantity calculated */ int quant_flags_w[NUMELEMENTS]; DY_OFFSET dy_freestart_w; /* initial block of freelist, 0 if none */ #define dy_freestart web.dy_freestart_w DY_OFFSET dy_globals_w; /* global variable table */ struct global *dy_perm_globals_w; DY_OFFSET dy_globalshash_w; /* hash list for global variables */ /* common */ int meth_attr[NUMELEMENTS] ; /* method instances list */ int mpi_export_attr[NUMELEMENTS] ; /* method instances list */ }; extern struct webstruct web; #ifdef __cplusplus } #endif evolver-2.30c.dfsg/src/softimag.c0000644000175300017530000000472111410765113017176 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /***************************************************************** * * File: softimag.c * * Purpose: Triangle list file output for Softimage input. */ #include "include.h" static FILE *fd; /***************************************************************** * * Function: softimage() * * Purpose: Write Softimage format files. */ void softimage() { char file_name[100]; char name[100]; vertex_id v_id; facet_id f_id; int *vnumber; int n; prompt("Enter file name (no suffix): ",name,sizeof(name)); /* model file */ strcpy(file_name,name); strcat(file_name,".mdl"); fd = fopen(file_name,"w"); if ( fd == NULL ) { perror(file_name); return; } fprintf(fd,"SOFTIMAGE 4D Creative Environment v 1.6 \"ASCII\"\n\n\n"); fprintf(fd," MODL \"%s\"\n {\n type PMSH\n",name); fprintf(fd," nbdef 1\n"); fprintf(fd," scal 1.000000 1.000000 1.000000\n"); fprintf(fd," rot 0.000000 0.000000 0.000000\n"); fprintf(fd," trans 0.000000 0.000000 0.000000\n"); fprintf(fd," }\n"); fclose(fd); strcpy(file_name,name); strcat(file_name,".def"); fd = fopen(file_name,"w"); if ( fd == NULL ) { perror(file_name); return; } fprintf(fd,"SOFTIMAGE 4D Creative Environment v 1.6 \"ASCII\"\n\n"); fprintf(fd,"PMSH \"%s\"\n {\n ",name); /* vertex list */ vnumber = (int*)temp_calloc(web.skel[VERTEX].max_ord+1,sizeof(int*)); fprintf(fd," vertex %ld\n",web.skel[VERTEX].count); n = 1; FOR_ALL_VERTICES(v_id) { REAL *x = get_coord(v_id); fprintf(fd,"%12d %14.12f %14.12f %14.12f\n",n, (DOUBLE)x[0],(DOUBLE)x[1],(DOUBLE)x[2]); vnumber[loc_ordinal(v_id)] = n; n++; } /* triangle list */ fprintf(fd,"\n polygon %ld\n",web.skel[FACET].count); FOR_ALL_FACETS(f_id) { facetedge_id fe = get_facet_fe(f_id); facetedge_id next_fe = get_next_edge(fe); int oh = vnumber[loc_ordinal(get_fe_headv(fe))]; fprintf(fd," %10d %10d %10d %10d\n",oh, vnumber[loc_ordinal(get_fe_headv(next_fe))], vnumber[loc_ordinal(get_fe_tailv(fe))], vnumber[loc_ordinal(get_fe_tailv(fe))]); } fprintf(fd," }\n\n"); fclose(fd); temp_free((char*)vnumber); } evolver-2.30c.dfsg/src/evaltree.c0000644000175300017530000062572111410765113017205 0ustar hazelscthazelsct/************************************************************ * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * ************************************************************/ /***************************************************************** * * File: evaltree.c * * Purpose: To execute expression and command trees. * Tree nodes are stored in postorder linear form * for fast postorder execution. * */ #include "include.h" #include "ytab.h" /* for breakpoints */ #define BREAKMAX 100 struct breakinfo { int name_id; int line; } breaklist[BREAKMAX]; int breakcount; struct eval_frame *subshell_frame[100]; /***************************************************************** * * Function eval() * * Purpose: runtime evaluation of expressions and commands. * The big switch statement is split between two functions to * get functions small enough for DOS compilers. All nodes * manipulating the stack are in the eval() function, and * others in other_stuff(). This should not impose any speed * penalty on expressions. * * Notes: The evaluation stack is a local variable, to permit * parallel evaluations on shared memory machines. To prevent * eval() from getting too long, many cases have been moved * to evalmore(), which is called by the default case of eval(). * But for efficiency, most frequently evaluated cases should * be in eval(). * * Uses one permanent stack per thread. Recursive calls to eval() * wind up using same stack (but progressively, of course). */ int current_debug_line; int debugging_flag; /* Number of stack slots occupied by a frame structure */ #define FRAME_SPACE ((int)((sizeof(struct eval_frame)+sizeof(REAL)-1)/sizeof(REAL))) REAL eval ARGS4((ex_original,params,self_id,parent_frame), struct expnode *ex_original, /* expression tree */ REAL *params, /* vector of parameters */ element_id self_id, /* reference element, if any */ struct eval_frame *parent_frame) /* not used anymore */ { struct treenode *node; /* currently executing node */ struct thread_data *td = GET_THREAD_DATA; #define newstack (td->eval_stack) #define stackmax (td->eval_stack_size) #define stacktop (td->stack_top) #define this_frame ((struct eval_frame*)(newstack + td->frame_spot)) int entry_flag = BASE_OF_EVAL; /* so can set frame flag marking entry to eval() */ /* variables that act like registers for stack gymnastics */ struct expnode *ex = ex_original; struct locallist_t *localbase; int localcount; element_id q_id = self_id; /* innermost loop element */ REAL return_value; struct treenode *return_node = NULL; REAL *localstack; /* base of local stack, after frame structure */ /* miscellaneous local variables useful in executing nodes */ int k,n,i; REAL x,y; element_id id; REAL *bins; REAL hi; REAL lo; REAL val; int old_flag = iterate_flag; facet_id f_id; REAL vect[MAXCOORD]; facetedge_id fe; vertex_id v_id; int recalc_flag = 0; int update_display_flag = 0; struct boundary *bdry; REAL *histo_data; int histo_max; int histo_count; struct global *g; int oldquiet; int eval_elapsed_time[2]; if ( ex == NULL ) return 0.0; if ( ex->start == NULL ) { sprintf(errmsg,"Trying to evaluate null expression for %s.\n",ex->name); kb_error(1253,errmsg,WARNING); return 0.0; } PROF_EVAL_START(ex); if ( !breakflag ) iterate_flag = 2; /* for interrupt handler */ if ( ex->start[1].type != SETUP_FRAME_ ) kb_error(3987,"no frame setup\n",RECOVERABLE); for ( node = ex->start+1 ; ; node++ ) { if ( single_step_debugging && (node->line_no != current_debug_line) ) { char prompt_string[100]; current_debug_line = node->line_no; subshell_depth++; subshell_frame[subshell_depth] = this_frame; setjmp(jumpbuf[subshell_depth]); if ( subshell_depth == 1 ) sprintf(prompt_string,"Debug (\"%s\" line %d): ",ex->name,node->line_no); else { sprintf(prompt_string,"Debug command(%d): ",subshell_depth); } /* command read and execute loop */ debugging_flag = 1; single_step_debugging = 0; exec_commands(NULL,prompt_string); subshell_depth--; debugging_flag = 0; } else /* have reached node set to a breakpoint */ if ( node->flags & BREAKPOINT_NODE ) { char prompt_string[100]; current_debug_line = node->line_no; subshell_depth++; subshell_frame[subshell_depth] = this_frame; setjmp(jumpbuf[subshell_depth]); if ( subshell_depth == 1 ) sprintf(prompt_string,"Debug (\"%s\" line %d): ",ex->name,node->line_no); else { sprintf(prompt_string,"Debug command(%d): ",subshell_depth); } /* command read and execute loop */ debugging_flag = 1; exec_commands(NULL,prompt_string); subshell_depth--; debugging_flag = 0; } switch ( node->type ) { struct treenode *where,*enode,*eroot; case SETUP_FRAME_: /* first node of any procedure */ { int parent_frame_spot; int stackused = stacktop-newstack; /* check stack space */ localbase = ex->locals; if ( ex->locals ) localcount = ex->locals->totalsize; else localcount = 0; if ( stackmax < stackused + localcount + ex->stack_max + FRAME_SPACE + 20 ) { stackmax = stackused + localcount + ex->stack_max + FRAME_SPACE + 300; newstack = (REAL*)realloc(newstack,stackmax*sizeof(REAL) ); stacktop = newstack + stackused; newstack[stackmax-1] = STACKMAGIC; /* sentinel */ } /* find parent frame, if any */ parent_frame_spot = td->frame_spot; if ( stacktop == newstack ) parent_frame = NULL; else parent_frame = (struct eval_frame*)(newstack + parent_frame_spot); stacktop++; /* Set up first frame at current stack position */ td->frame_spot = stacktop - newstack; /* sets this_frame by macro */ stacktop += FRAME_SPACE-1; this_frame->base_ex = ex; this_frame->basenode = &node; this_frame->parent_frame_spot = parent_frame_spot; td->frame_spot = (REAL*)this_frame - newstack; // redundant? this_frame->self_id = self_id; this_frame->return_node = return_node; this_frame->flags = entry_flag; entry_flag = 0; if ( parent_frame ) { this_frame->flags |= parent_frame->flags & IN_ELEMENT_LOOP; if ( return_node ) this_frame->flags |= return_node->flags & IN_ELEMENT_LOOP; } localstack = stacktop + 1; /* local variables after frame */ if ( localcount ) { stacktop++; memset((char*)stacktop,0,localcount*sizeof(REAL)); stacktop += localcount-1; /* room for local variables */ } } break; /* end SETUP_FRAME_ */ case ABORT_: /* if ( subshell_depth ) { exit_flag = 1; break; } */ breakflag = BREAKABORT; break; case SUBCOMMAND_: { char prompt_string[100]; char *pmpt; subshell_depth++; subshell_frame[subshell_depth] = this_frame; setjmp(jumpbuf[subshell_depth]); if ( subshell_depth == 1 ) pmpt = "Subcommand: "; else { sprintf(prompt_string,"Subcommand(%d): ",subshell_depth); pmpt = prompt_string; } /* command read and execute loop */ exec_commands(NULL,pmpt); subshell_depth--; break; } case SET_BREAKPOINT_: { int breakline = (int)(*stacktop--); struct expnode *proc; struct treenode *nodespot; int found = 0; /* now set flag bit in first node on line */ proc = &(globals(node->op1.name_id)->value.proc); for ( nodespot = proc->start+1 ; nodespot != proc->root ; nodespot++ ) if ( nodespot->line_no == breakline ) { nodespot->flags |= BREAKPOINT_NODE; /* record for unset */ breaklist[breakcount].name_id = node->op1.name_id; breaklist[breakcount].line = breakline; breakcount++; found = 1; break; } if ( !found ) { sprintf(msg,"Cannot find instruction on line %d of \"%s\".\n", breakline,&globals(node->op1.name_id)->name); outstring(msg); } break; } case UNSET_BREAKPOINT_: { struct expnode *proc; struct treenode *nodespot; if ( node->left ) /* particular */ { int breakline = (int)(*stacktop--); /* unmark breakpoint node */ proc = &(globals(node->op1.name_id)->value.proc); for ( nodespot = proc->start ; nodespot != proc->root ; nodespot++ ) if ( nodespot->line_no == breakline ) nodespot->flags &= ~BREAKPOINT_NODE; /* remove from breakpoint list */ for ( i = 0 ; i < breakcount ; i++ ) if ( (breaklist[i].name_id == node->op1.name_id) && (breaklist[i].line == breakline) ) { breaklist[i] = breaklist[--breakcount]; } } else /* all */ { for ( i = 0 ; i < breakcount ; i++ ) { proc = &(globals(breaklist[i].name_id)->value.proc); for ( nodespot = proc->start ; nodespot != proc->root ; nodespot++ ) nodespot->flags &= ~BREAKPOINT_NODE; } breakcount = 0; } break; } case WHEREAMI_COMMAND_: /* for use in debugging or subshells */ { struct eval_frame *frame = subshell_frame[subshell_depth]; while ( frame ) { sprintf(msg," %s:%d\n",frame->base_ex->name,frame->return_node->line_no); outstring(msg); if ( frame->flags & BASE_OF_WHOLE_STACK ) frame = NULL; else frame = (struct eval_frame*)(newstack + frame->parent_frame_spot); }; break; } case FREE_DISCARDS_: if ( (node->flags & IN_ELEMENT_LOOP) || ( this_frame->flags & IN_ELEMENT_LOOP ) ) kb_error(1904,"free_discards called inside element loop. Ignored.", WARNING); else free_discards(DISCARDS_ALL); break; case SINGLE_LETTER_: if ( debugging_flag && node->op1.letter == 'n' ) /* special for debugging prompt */ { single_step_debugging = 1; exit_flag = 1; goto the_exit; break; } if ( !(node->flags & IN_ELEMENT_LOOP) && !(this_frame->flags & IN_ELEMENT_LOOP) ) free_discards(DISCARDS_SOME); letter_command(node->op1.letter); break; case LINEAR_: if ( web.modeltype == QUADRATIC ) { quad_to_linear(); recalc(); break; } else if ( web.modeltype == LAGRANGE ) lagrange_to_linear(); break; case QUADRATIC_: if ( !(node->flags & IN_ELEMENT_LOOP) && !(this_frame->flags & IN_ELEMENT_LOOP) ) free_discards(DISCARDS_SOME); if ( web.modeltype == LINEAR ) { linear_to_quad(); recalc(); break; } else if ( web.modeltype == LAGRANGE ) lagrange_to_quad(); break; case LAGRANGE_: if ( !(node->flags & IN_ELEMENT_LOOP) && !(this_frame->flags & IN_ELEMENT_LOOP) ) free_discards(DISCARDS_SOME); if ( web.modeltype == LINEAR ) linear_to_lagrange((int)(*stacktop--)); else if ( web.modeltype == QUADRATIC ) quad_to_lagrange((int)(*stacktop--)); else lagrange_to_lagrange((int)(*stacktop--)); break; case ELINDEX_: /* id possibly with mpi task number */ /* creates typeless valid id */ { int task; if ( node->right ) task = (int)(*stacktop--); else task = this_task; if ( *stacktop == 0 ) id = NULLID; else { if ( *stacktop > 0.0 ) id = ((int)(*stacktop)-1); else { id = -((int)(*stacktop)+1); invert(id); } id |= VALIDMASK; } #ifdef MPI_EVOLVER if ( task < 0 || task >= mpi_nprocs ) { sprintf(errmsg, "Illegal task number %d. Must be between 1 and %d.\n",task,mpi_nprocs); kb_error(4324,errmsg,RECOVERABLE); } id |= (element_id)task << TASK_ID_SHIFT; #endif *(element_id *)stacktop = id; break; } case PUSH_ELEMENT_ID_: *(element_id *)(++stacktop) = node->op1.id; break; case VALID_ELEMENT_: id = get_full_id(node->op1.eltype,*(element_id*)stacktop); *stacktop = valid_element(id) ? 1.0 : 0.0; break; case VALID_CONSTRAINT_: { int connum = (int)*stacktop; if ( (connum<0) || (connum>=web.maxcon) || !(get_constraint(connum)->attr & IN_USE)) *stacktop = 0.0; else *stacktop = 1.0; break; } case VALID_BOUNDARY_: { int bnum = (int)*stacktop; if ( (bnum<0) || (bnum>=web.bdrymax) || !(web.boundaries[bnum].attr & IN_USE)) *stacktop = 0.0; else *stacktop = 1.0; break; } case LOAD_: if ( subshell_depth ) { kb_error(3634,"Can't reload in a subcommand.\n",WARNING); stacktop--; break; } strncpy(loadfilename,*(char**)(stacktop--),sizeof(loadfilename)); #ifdef __cplusplus loadstub(); /* indirect throw of exception */ #else longjmp(loadjumpbuf,1); #endif break; case ADDLOAD_: { FILE *newfd; int old_read_depth; char *name = *(char**)(stacktop--); newfd = path_open(name,NOTDATAFILENAME); if (newfd == NULL) { if ( name[0] ) { sprintf(errmsg,"Cannot open datafile %s.\n",name); kb_error(5432,errmsg,RECOVERABLE); } break; /* continue with old */ } ENTER_GRAPH_MUTEX; push_commandfd(newfd,name); /* start #include stack */ datafile_flag = 1; /* so parser knows */ addload_flag = 1; datafile_input_flag = 1; /* so lex input knows */ cmdptr = 0; old_read_depth = read_depth; initialize(); if ( read_depth >= old_read_depth ) pop_commandfd(); datafile_flag = 0; addload_flag = 0; if ( fabs(view[0][0])+fabs(view[1][1])+fabs(view[2][2]) < 1e-25 ) resize(); LEAVE_GRAPH_MUTEX; recalc(); break; } case PERMLOAD_: /* keep going with same command */ /* with contortions to preserve current list */ { struct expnode keeplist; struct eval_frame *fr; size_t spot; if ( subshell_depth ) { kb_error(3635,"Can't reload in a subcommand.\n",WARNING); stacktop--; break; } /* make sure all parent frames permanent (except very first frame corresponding to original user command */ for ( fr = this_frame; fr != NULL ; fr = (struct eval_frame*)(newstack + this_frame->parent_frame_spot) ) { if ( !(fr->flags & BASE_OF_WHOLE_STACK) && !(fr->base_ex->start->flags & PERMNODE) ) { strcpy(errmsg, "Calling permload in non-permanent command.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3364,errmsg, RECOVERABLE); } if ( fr->flags & BASE_OF_WHOLE_STACK ) break; } if ( !(fr->base_ex->start->flags & PERMNODE) ) { /* have to kludge to preserve current command list */ memset(&keeplist,0,sizeof(keeplist)); perm_tree_copy(&keeplist,fr->base_ex->root); spot = (*(fr->basenode) - (fr->base_ex->start)); *(fr->basenode) = keeplist.start + spot; memset(fr->base_ex,0,sizeof(struct expnode)); /* so doesn't do free_expr()later */ fr->base_ex = &keeplist; } strncpy(loadfilename,*(char**)(stacktop--),sizeof(loadfilename)); startup(loadfilename); exec_commands(commandfd,"Enter command: "); /* from end of datafile */ /* keeplist permanently allocated, but we'll live with memory leak as price for not doing shenanigans every eval() just to avoid leak. */ } break; case FUNCTION_CALL_: { struct global *g = globals(node->op1.name_id); if ( g->value.proc.root == NULL ) { sprintf(errmsg, "Function \"%s\" definition has not been executed yet.\n",g->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2621,errmsg,RECOVERABLE); } PROF_EVAL_END(ex); ex = &g->value.proc; PROF_EVAL_START(ex); return_node = node; node = g->value.proc.start; break; } case FUNCTION_CALL_RETURN_: { stacktop -= node->op2.argcount; /* pop arguments */ *(++stacktop) = return_value; break; } case PROCEDURE_CALL_: /* with arguments */ { struct global *g = globals(node->op1.name_id); if ( g->value.proc.root == NULL ) { sprintf(errmsg, "Procedure \"%s\" definition has not been executed yet.\n",g->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2622,errmsg,RECOVERABLE); } PROF_EVAL_END(ex); ex = &g->value.proc; PROF_EVAL_START(ex); return_node = node; node = g->value.proc.start; break; } case PROCEDURE_CALL_RETURN_: stacktop -= node->op2.argcount; /* pop arguments */ break; case PROCEDURE_: PROF_EVAL_END(ex); eval(&globals(node->op1.name_id)->value.proc,NULL,NULLID,NULL); PROF_EVAL_START(ex); break; case PERM_PROCEDURE_: PROF_EVAL_END(ex); eval(&perm_globals(node->op1.name_id)->value.proc,NULL,NULLID,NULL); PROF_EVAL_START(ex); break; case CMDLIST_: case COMMAND_BLOCK_: /* no action, just holds tree together */ break; case DECLARE_LOCAL_: break; case LOCAL_LIST_START_: break; case SINGLE_REDEFD_: if ( single_redefine[node->op1.letter].start ) { PROF_EVAL_END(ex); eval(&single_redefine[node->op1.letter],NULL,NULLID,NULL); PROF_EVAL_START(ex); } else letter_command(node->op1.letter); break; case MATRIX_INVERSE_: { REAL *datastart2 = *(REAL**)(stacktop--); REAL *datastart1 = *(REAL**)(stacktop--); struct array *a = get_name_arrayptr(node->op1.name_id,localstack,localbase); struct array *b = get_name_arrayptr(node->op2.name_id,localstack,localbase); *(++stacktop) = (REAL)matrix_inverse_command(a, b,datastart1,datastart2); } break; case MATRIX_MULTIPLY_: { REAL *datastart3 = *(REAL**)(stacktop--); REAL *datastart2 = *(REAL**)(stacktop--); REAL *datastart1 = *(REAL**)(stacktop--); struct array *a = get_name_arrayptr(node->op1.name_id,localstack,localbase); struct array *b = get_name_arrayptr(node->op2.name_id,localstack,localbase); struct array *c = get_name_arrayptr(node->op3.name_id,localstack,localbase); matrix_multiply_command(a,b,c,datastart1,datastart2,datastart3); } break; case MATRIX_DETERMINANT_: { REAL *datastart1 = *(REAL**)(stacktop--); struct array *a = get_name_arrayptr(node->op1.name_id,localstack,localbase); *(++stacktop) = matrix_determinant_command(a,datastart1); } break; case BACKQUOTE_START_: { struct expnode bqnode = *ex; bqnode.start = node; bqnode.root = node+node->op1.skipsize; bqnode.locals = localbase; PROF_EVAL_END(ex); eval(&bqnode,params,self_id,this_frame); PROF_EVAL_START(ex); node += node->op1.skipsize-1; /* skip what was evaluated */ } break; case BACKQUOTE_END_ : break; /* just a placeholder */ case ACOMMANDEXPR_: /* backquoted command at start of expression */ break; case INDEXSET_: break; /* just accumulate index values */ case DIMENSIONSET_: break; /* just accumulate index values */ case DEFINE_FIXED_LOCAL_ARRAY_: break; /* was allocated on stack */ case SHOW_: case SHOW_EXPR_: { int etype; /* element type */ where = node + node->op1.skipsize; if ( where->type == WHERE_ ) /* condition */ { /* copy over expression */ enode = where + where->left; /* NEXT */ eroot = where + where->right; etype = enode[enode->left].op1.eltype; show_expr[etype] = show_expr_table + etype; tree_copy(show_expr[etype],eroot); sprintf(show_expr[etype]->name,"show expression for %s.", typenames[etype]); /* can use first slot to record type of element */ /* element location */ show_expr[etype]->start->op2.eltype = enode->op2.eltype; enode += enode->left; /* INIT */ show_expr[etype]->start->op1.eltype = enode->op1.eltype; show_expr[etype]->locals = (struct locallist_t*)mycalloc(1,sizeof(struct locallist_t)); *(show_expr[etype]->locals) = *(ex->locals); } else { etype = where[where->left].op1.eltype; tree_copy(show_expr[etype],NULL); show_expr[etype] = NULL; } /* save for dump */ tree_copy(show_command+etype,node+node->op1.skipsize+1); if ( (node->type == SHOW_) && !OOGL_flag && !go_display_flag) do_show(); else update_display(); node += node->op1.skipsize; /* skip over expression */ break; } case UNREDEFINE_SINGLE_: free_expr(&single_redefine[node->op1.letter]); break; case REDEFINE_SINGLE_: tree_copy(&single_redefine[node->op1.letter],node+node->op2.jumpsize); sprintf(single_redefine[node->op1.letter].name,"redefined command '%c'", node->op1.letter); single_redefine[node->op1.letter].flag = USERCOPY; locals_copy(&(single_redefine[node->op1.letter].locals), node->op5.locals); node += node->op2.jumpsize; /* skip over procedure */ break; case SET_PROCEDURE_: g = globals(node->op1.name_id); free_expr(&g->value.proc); tree_copy(&g->value.proc, node+node->op2.jumpsize); strcpy(g->value.proc.name, g->name); g->attr.procstuff.proc_timestamp = proc_timestamp++; locals_copy(&(g->value.proc.locals),node->op5.locals); node += node->op2.jumpsize; /* skip over procedure */ break; case SET_PERM_PROCEDURE_: g = perm_globals(node->op1.name_id); perm_free_expr(&g->value.proc); perm_tree_copy(&g->value.proc, node+node->op2.jumpsize); strcpy(g->value.proc.name, g->name); g->attr.procstuff.proc_timestamp = proc_timestamp++; locals_copy_perm(&(g->value.proc.locals),node->op5.locals); node += node->op2.jumpsize; /* skip over procedure */ break; case SET_FUNCTION_: break; case FUNCTION_HEAD_ : break; case ARGLIST_ : break; case FUNCTION_DEF_START_ : g = globals(node->op1.name_id); free_expr(&g->value.proc); tree_copy(&g->value.proc, node+node->op2.jumpsize); g->value.proc.start[2].type = FUNCTION_START_; strcpy(g->value.proc.name, g->name); g->attr.procstuff.proc_timestamp = proc_timestamp++; locals_copy(&(g->value.proc.locals),node->op5.locals); node += node->op2.jumpsize; /* skip over procedure */ break; case FUNCTION_PROTO_START_: node += node->op2.jumpsize; /* skip over stuff */ break; case FUNCTION_START_: /* function entry code */ /* copy arguments over to local variable space */ memcpy((char*)(stacktop-localcount+1), (char*)(((REAL*)this_frame)-node->op3.argcount), node->op3.argcount*sizeof(REAL)); break; case SET_ARGSPROC_: break; case PROCEDURE_HEAD_ : break; case PROCEDURE_DEF_START_ : g = globals(node->op1.name_id); free_expr(&g->value.proc); tree_copy(&g->value.proc, node+node->op2.jumpsize); g->value.proc.start[2].type = PROCEDURE_START_; strcpy(g->value.proc.name, g->name); g->attr.procstuff.proc_timestamp = proc_timestamp++; locals_copy(&(g->value.proc.locals),node->op5.locals); node += node->op2.jumpsize; /* skip over procedure */ break; case PROCEDURE_PROTO_START_: node += node->op2.jumpsize; /* skip over stuff */ break; case PROCEDURE_START_: /* function entry code */ /* copy arguments over to local variable space */ if ( node->op3.argcount ) memcpy((char*)(stacktop-localcount+1), (char*)(((REAL*)this_frame)-node->op3.argcount), node->op3.argcount*sizeof(REAL)); break; case RETURN_: if ( node->left ) { localstack[localbase->totalsize] = *stacktop; stacktop = localstack + localbase->totalsize; } else stacktop = localstack + localbase->totalsize - 1; node = ex->root; /* since next node should be FINISHED */ if ( node[1].type != FINISHED ) kb_error(4578,"Internal error: no FINISH node after RETURN\n", RECOVERABLE); break; /********************/ /* repeated command */ /********************/ case REPEAT_INIT_: /* target on stack, then iteration count */ gocount = (int)*stacktop; *(++stacktop) = 0; /* iterations done */ if ( gocount <= 0 ) { stacktop -= 2; /* pop counts */ node += node->op1.skipsize; /* skip loop */ gocount = 1; } break; case REPEAT_: { struct treenode *rnode = node + node->left; stacktop[0] += 1; gocount = (int)(stacktop[-1] - stacktop[0]); #if defined(MAC_APP) || defined(MAC_CW) break_check(); #endif if ( breakflag ) { stacktop -= 2; breakflag = 0; break; } if ( (gocount > 0) && !breakflag ) { node = rnode; /* do again */ /* loop increment will step over REPEAT_INIT_ node */ } else { stacktop -= 2; /* pop gocount and total count */ gocount = 1; /* so prints as 1 when no count given */ } } break; /*******************/ /* flow of control */ /*******************/ case IFTEST_: case COND_TEST_: if ( *(stacktop--) == 0. ) { /* jump */ node += node->op1.skipsize; } break; case IF_: case COND_EXPR_: /* did first command, so skip second */ node += node->op1.skipsize; break; case CONTINUE_: node += node->op1.skipsize; /* back to loop top */ stacktop = localstack + *(size_t*)(localstack+node->stackpos); node += node->op4.contjump; /* jump to expr */ break; case BREAK_: node += node->op1.skipsize; /* back to loop top */ stacktop = localstack + *(size_t*)(localstack+node->stackpos); node += node->op3.breakjump; /* jump to end */ break; case WHILE_TOP_: /* break and continue jumps */ *(size_t*)(localstack + node->stackpos) = stacktop - localstack - 1; /* jumptest if expr false */ if ( (*(stacktop--) == 0.) || breakflag ) node += node->op1.skipsize; break; case WHILE_END_: /* loop back */ node += node->op1.skipsize; break; case DO_TOP_: /* break and continue jumps */ *(size_t*)(localstack + node->stackpos) = stacktop - localstack; break; case DO_ENTRY_: /* break and continue jumps */ *(size_t*)(localstack + node->stackpos) = stacktop - localstack; break; case DO_END_: /* loop back if true */ if ( *(stacktop--) && !breakflag ) node += node->op1.skipsize; break; case FOR_ENTRY_: /* break and continue jumps */ *(size_t*)(localstack + node->stackpos) = stacktop - localstack; break; case FOR_HEAD_: if ( *(stacktop--) && !breakflag ) node += node->op1.skipsize; /* loop body */ else node += node->op2.jumpsize; /* break out of loop */ break; case FOR_TOP_: node += node->op1.skipsize; /* jump to test expr */ break; case FOR_END_: node += node->op1.skipsize; /* jump to increment command */ break; /*******************/ /* aggregate verbs */ /*******************/ case LIST_: oldquiet = quiet_flag; quiet_flag = 0; switch ( node->op2.eltype ) { case VERTEX: vertex_dump(positive_id(q_id),outfd); break; case EDGE: edge_dump(positive_id(q_id),outfd); break; case FACET: facet_dump(positive_id(q_id),outfd); break; case BODY: body_dump(positive_id(q_id),outfd); break; case FACETEDGE: facetedge_dump(positive_id(q_id),outfd); break; default: sprintf(errmsg,"Bad LIST element type in %s.\n",ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); quiet_flag = oldquiet; kb_error(1255,errmsg,RECOVERABLE); } quiet_flag = oldquiet; node += node->op1.skipsize - 1; /* back to start of loop */ break; /*********************** * commands with counts * ***********************/ case REFINE_: switch ( node->op2.eltype ) { case EDGE: n = valid_id(edge_refine(q_id)); if ( web.counts_reported & edge_refine_count_bit ) { web.edge_refine_count = 0; web.counts_reported &= ~edge_refine_count_bit; } if ( n ) { web.edge_refine_count += n; web.counts_changed |= edge_refine_count_bit; recalc_flag = 1; } break; case FACET: face_triangulate(q_id,FACET_EDGES); n = 1; if ( web.counts_reported & facet_refine_count_bit ) { web.facet_refine_count = 0; web.counts_reported &= ~facet_refine_count_bit; } if ( n ) { web.facet_refine_count += n; web.counts_changed |= facet_refine_count_bit; recalc_flag = 1; } break; default: sprintf(errmsg,"Bad refine element type in %s.\n",ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1256,errmsg, RECOVERABLE); } node += node->op1.skipsize - 1; /* back to start of loop */ break; case POP_: n = 0; if ( id_type(q_id) == EDGE ) { n = pop_one_edge(q_id); if ( web.counts_reported & edge_pop_count_bit ) { web.edge_pop_count = 0; web.counts_reported &= ~edge_pop_count_bit; } if ( n ) { web.edge_pop_count += n; web.counts_changed |= edge_pop_count_bit; } } else if ( id_type(q_id) == VERTEX ) { n = pop_given_vertex(q_id); if ( web.counts_reported & vertex_pop_count_bit ) { web.vertex_pop_count = 0; web.counts_reported &= ~vertex_pop_count_bit; } if ( n ) { web.vertex_pop_count += n; web.counts_changed |= vertex_pop_count_bit; } } else { sprintf(errmsg,"Only vertices and edges poppable.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3356,errmsg, RECOVERABLE); } node += node->op1.skipsize - 1; /* back to start of loop */ if ( n ) recalc_flag = 1; break; case POP_TRI_TO_EDGE_: n = pop_tri_to_edge(q_id); if ( web.counts_reported & pop_tri_to_edge_count_bit ) { web.pop_tri_to_edge_count = 0; web.counts_reported &= ~pop_tri_to_edge_count_bit; } if ( n ) { web.pop_tri_to_edge_count += n; web.counts_changed |= pop_tri_to_edge_count_bit; } node += node->op1.skipsize - 1; /* back to start of loop */ if ( n ) recalc_flag = 1; break; case POP_EDGE_TO_TRI_: n = pop_edge_to_tri(q_id); if ( web.counts_reported & pop_edge_to_tri_count_bit ) { web.pop_edge_to_tri_count = 0; web.counts_reported &= ~pop_edge_to_tri_count_bit; } if ( n ) { web.pop_edge_to_tri_count += n; web.counts_changed |= pop_edge_to_tri_count_bit; } node += node->op1.skipsize - 1; /* back to start of loop */ if ( n ) recalc_flag = 1; break; case POP_QUAD_TO_QUAD_: n = pop_quad_to_quad(q_id); if ( web.counts_reported & pop_quad_to_quad_count_bit ) { web.pop_quad_to_quad_count = 0; web.counts_reported &= ~pop_quad_to_quad_count_bit; } if ( n ) { web.pop_quad_to_quad_count += n; web.counts_changed |= pop_quad_to_quad_count_bit; } node += node->op1.skipsize - 1; /* back to start of loop */ if ( n ) recalc_flag = 1; break; case EDGESWAP_: n = edgeswap(q_id); if ( web.counts_reported & edgeswap_count_bit ) { web.edgeswap_count = 0; web.counts_reported &= ~edgeswap_count_bit; } if ( n ) { web.edgeswap_count += n; web.counts_changed |= edgeswap_count_bit; } node += node->op1.skipsize - 1; /* back to start of loop */ if ( n ) recalc_flag = 1; break; case T1_EDGESWAP_: n = t1_edgeswap(q_id); if ( web.counts_reported & t1_edgeswap_count_bit ) { web.t1_edgeswap_count = 0; web.counts_reported &= ~t1_edgeswap_count_bit; } if ( n ) { web.t1_edgeswap_count += n; web.counts_changed |= t1_edgeswap_count_bit; } node += node->op1.skipsize - 1; /* back to start of loop */ if ( n ) recalc_flag = 1; break; case EQUIANGULATE_: n = equiangulate_edge(q_id); if ( web.counts_reported & equi_count_bit ) { web.equi_count = 0; web.counts_reported &= ~equi_count_bit; } if ( n ) { web.equi_count += n; web.counts_changed |= equi_count_bit; } node += node->op1.skipsize - 1; /* back to start of loop */ if ( n ) recalc_flag = 1; break; case DISSOLVE_: switch ( node->op2.eltype ) { case VERTEX: n = dissolve_vertex(q_id); if ( web.counts_reported & vertex_dissolve_count_bit ) { web.vertex_dissolve_count = 0; web.counts_reported &= ~vertex_dissolve_count_bit; } if ( n ) { web.vertex_dissolve_count += n; web.counts_changed |= vertex_dissolve_count_bit; recalc_flag = 1; } break; case EDGE: n = dissolve_edge(q_id); if ( web.counts_reported & edge_dissolve_count_bit ) { web.edge_dissolve_count = 0; web.counts_reported &= ~edge_dissolve_count_bit; } if ( n ) { web.edge_dissolve_count += n; web.counts_changed |= edge_dissolve_count_bit; recalc_flag = 1; } break; case FACET: n = dissolve_facet(q_id); if ( web.counts_reported & facet_dissolve_count_bit ) { web.facet_dissolve_count = 0; web.counts_reported &= ~facet_dissolve_count_bit; } if ( n ) { web.facet_dissolve_count += n; web.counts_changed |= facet_dissolve_count_bit; recalc_flag = 1; } break; case BODY: n = dissolve_body(q_id); if ( web.counts_reported & body_dissolve_count_bit ) { web.body_dissolve_count = 0; web.counts_reported &= ~body_dissolve_count_bit; } if ( n ) { web.body_dissolve_count += n; web.counts_changed |= body_dissolve_count_bit; recalc_flag = 1; } break; } node += node->op1.skipsize - 1; /* back to start of loop */ break; case REVERSE_ORIENTATION_: switch ( node->op2.eltype ) { case EDGE: reverse_orientation_edge(q_id); if ( web.counts_reported & edge_reverse_count_bit ) { web.edge_reverse_count = 0; web.counts_reported &= ~edge_reverse_count_bit; } web.edge_reverse_count += 1; web.counts_changed |= edge_reverse_count_bit; recalc_flag = 1; break; case FACET: reverse_orientation_facet(q_id); if ( web.counts_reported & facet_reverse_count_bit ) { web.facet_reverse_count = 0; web.counts_reported &= ~facet_reverse_count_bit; } web.facet_reverse_count += 1; web.counts_changed |= facet_reverse_count_bit; recalc_flag = 1; break; } node += node->op1.skipsize - 1; /* back to start of loop */ break; case FIX_: if ( web.counts_reported & fix_count_bit ) { web.fix_count = 0; web.counts_reported &= ~fix_count_bit; } if ( !(get_attr(q_id)&FIXED) ) { set_attr(q_id,FIXED); web.fix_count += 1; web.counts_changed |= fix_count_bit; } node += node->op1.skipsize - 1; /* back to start of loop */ break; case UNFIX_: if ( web.counts_reported & unfix_count_bit ) { web.unfix_count = 0; web.counts_reported &= ~unfix_count_bit; } if ( get_attr(q_id) & FIXED ) { unset_attr(q_id,FIXED); web.unfix_count += 1; web.counts_changed |= unfix_count_bit; } node += node->op1.skipsize - 1; /* back to start of loop */ break; case VERTEX_AVERAGE_: if ( new_vertex_average(q_id,VOLKEEP) ) recalc_flag = 1; node += node->op1.skipsize - 1; /* back to start of loop */ break; case RAW_VERTEX_AVERAGE_: if ( new_vertex_average(q_id,NOVOLKEEP) ) recalc_flag = 1; node += node->op1.skipsize - 1; /* back to start of loop */ break; case RAWEST_VERTEX_AVERAGE_: if ( new_vertex_average(q_id,RAWEST) ) recalc_flag = 1; node += node->op1.skipsize - 1; /* back to start of loop */ break; case DELETE_: switch ( node->op2.eltype ) { case EDGE: n = eliminate_edge(q_id); if ( n ) free_element(q_id); if ( web.counts_reported & edge_delete_count_bit ) { web.edge_delete_count = 0; web.counts_reported &= ~edge_delete_count_bit; } if ( n ) { web.edge_delete_count += n; web.counts_changed |= edge_delete_count_bit; recalc_flag = 1; } break; case FACET: n = eliminate_facet(q_id); if ( web.counts_reported & facet_delete_count_bit ) { web.facet_delete_count = 0; web.counts_reported &= ~facet_delete_count_bit; } if ( n > 0 ) { web.facet_delete_count += n; web.counts_changed |= facet_delete_count_bit; recalc_flag = 1; } if ( n < 0 ) /* from string_eliminate_edge, partial elim */ { web.edge_delete_count += -n; web.counts_reported &= ~edge_delete_count_bit; recalc_flag = 1; } break; default: sprintf(errmsg,"Bad delete element type in %s.\n",ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1257,errmsg, RECOVERABLE); } node += node->op1.skipsize - 1; /* back to start of loop */ break; /***************/ /* expressions */ /***************/ case REPLACECONST: *stacktop = node->op1.real; break; case PUSHCONST: *++stacktop = node->op1.real; break; case PUSHDELTA_: { struct global *g = globals(node->op1.name_id); *++stacktop = g->attr.varstuff.delta; break; } case PUSH_PARAM_SCALE: { struct global *g = globals(node->op1.name_id); *++stacktop = g->attr.varstuff.pscale; break; } case PUSH_PARAM_FIXED: { struct global *g = globals(node->op1.name_id); *++stacktop = (g->flags & OPTIMIZING_PARAMETER)?0.0:1.0; break; } case PUSH_PARAM_EXTRA_: { int i; for ( i = 0 ; i < optparamcount ; i++ ) if ( optparam[i].pnum == node->op1.name_id ) { switch ( node->op2.extranum ) { case V_VELOCITY_ATTR: *++stacktop = optparam[i].velocity; break; case V_FORCE_ATTR: *++stacktop = optparam[i].grad; break; } break; } if ( i == optparamcount ) *++stacktop = 0.0; break; } case PUSHGLOBAL_: case STRINGGLOBAL_: case PUSH_PERM_GLOBAL_: case PERM_STRINGGLOBAL_: { struct global *g = globals(node->op1.name_id); if ( g->flags & GLOB_LOCALVAR ) *++stacktop = localstack[g->value.offset]; else if ( g->flags & FILE_VALUES ) *++stacktop = g->value.file.values[int_val]; else if ( g->flags & STRINGVAL ) { int pp = (sizeof(REAL)+sizeof(char*)-1)/sizeof(char*); int nn; stacktop++; for ( nn = 0 ; nn < pp ; nn++ ) ((char **)stacktop)[nn] = g->value.string; } else if ( g->flags & INTERNAL_NAME ) { if ( g->flags & INTVAL ) *++stacktop = *(int*)(g->value.dataptr); else if ( g->flags & REALVAL ) *++stacktop = *(REAL*)(g->value.dataptr); else { sprintf(errmsg, "Internal error: Internal variable %s type not set.\n",g->name); kb_error(2851,errmsg,RECOVERABLE); } } else *++stacktop = g->value.real; } break; case PUSHPI: *++stacktop = M_PI; break; case PUSHE: *++stacktop = M_E; break; case PUSHG: *++stacktop = web.gravflag ? web.grav_const : 0.0; break; case PUSHPARAM: *++stacktop = params[node->op1.coordnum]; break; case USERFUNC: *++stacktop = (*userfunc[node->op1.userfunc])(params); break; case DYNAMIC_LOAD_FUNC_: if ( ! params ) { sprintf(errmsg, "Must use dynamic load function %s in context with parameters.\n", globals(node->op2.name_id)->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2059,errmsg,RECOVERABLE); } else (*node->op1.funcptr)(FUNC_VALUE,params,(struct dstack*)(++stacktop)); break; case GT_: stacktop--; stacktop[0] = (REAL)(stacktop[0] > stacktop[1]); break; case LT_: stacktop--; stacktop[0] = (REAL)(stacktop[0] < stacktop[1]); break; case LE_: stacktop--; stacktop[0] = (REAL)(stacktop[0] <= stacktop[1]); break; case GE_: stacktop--; stacktop[0] = (REAL)(stacktop[0] >= stacktop[1]); break; case NE_: stacktop--; stacktop[0] = (REAL)(stacktop[0] != stacktop[1]); break; case EQ_: stacktop--; stacktop[0] = (REAL)(stacktop[0] == stacktop[1]); break; case AND_: /* short-circuit */ if ( *stacktop == 0.0 ) { *(++stacktop) = 0.0; node += node->op1.skipsize; /* leave 0 as result */ } break; case CONJUNCTION_END: /* short-circuiting results in second arg being answer */ /* get proper 1 for true */ stacktop--; *stacktop = stacktop[1]; if ( *stacktop ) *stacktop = 1.0; break; case OR_: /* short-circuit */ if ( *stacktop != 0.0 ) { *(++stacktop) = 1.0; node += node->op1.skipsize; /* leave as result */ } break; case NOT_: stacktop[0] = (REAL)(!stacktop[0]); break; case PLUS: stacktop--; stacktop[0] += stacktop[1]; break; case MINUS: case EQUATE: stacktop--; stacktop[0] -= stacktop[1]; break; case TIMES: stacktop--; stacktop[0] *= stacktop[1]; break; case DIVIDE: stacktop--; if ( stacktop[1] == 0.0 ) { if ( valid_id(self_id) ) sprintf(errmsg,"Division by zero in %s, %s %s.\n",ex->name, typenames[id_type(self_id)],ELNAME(self_id)); else sprintf(errmsg,"Division by zero in %s.\n",ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1258,errmsg,RECOVERABLE); } stacktop[0] /= stacktop[1]; break; case REALMOD: stacktop--; if ( stacktop[1] == 0.0 ) { if ( valid_id(self_id) ) sprintf(errmsg,"Modulus base zero in %s, %s %s.\n",ex->name, typenames[id_type(self_id)],ELNAME(self_id)); else sprintf(errmsg,"Modulus base zero in %s.\n",ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1259,errmsg,RECOVERABLE); } stacktop[0] = stacktop[0] - floor(stacktop[0]/stacktop[1]) *stacktop[1]; break; case IMOD_: stacktop--; if ( stacktop[1] == 0.0 ) { if ( valid_id(self_id) ) sprintf(errmsg,"Modulus base zero in %s, %s %s.\n",ex->name, typenames[id_type(self_id)],ELNAME(self_id)); else sprintf(errmsg,"Modulus base zero in %s.\n",ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1260,errmsg,RECOVERABLE); } stacktop[0] = floor(stacktop[0]) - floor(floor(stacktop[0])/floor(stacktop[1])) *floor(stacktop[1]); break; case IDIV_: stacktop--; if ( (int)stacktop[1] == 0 ) { if ( valid_id(self_id) ) sprintf(errmsg,"Division by zero in %s, %s %s.\n",ex->name, typenames[id_type(self_id)],ELNAME(self_id)); else sprintf(errmsg,"Division by zero in %s.\n",ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1261,errmsg,RECOVERABLE); } stacktop[0] = (REAL)((int)(stacktop[0])/(int)(stacktop[1])); break; case INTPOW: /* cases n = 0,1,2 taken care of in parsing */ x = *stacktop; k = node->op1.intpow < 0 ? -node->op1.intpow : node->op1.intpow; for ( n = 1 ; n < k ; n++ ) *stacktop *= x; if ( node->op1.intpow < 0 ) { if ( *stacktop == 0.0 ) { if ( valid_id(self_id) ) sprintf(errmsg,"Negative power (%d) of zero in %s, %s %s.\n", node->op1.intpow,ex->name, typenames[id_type(self_id)],ELNAME(self_id)); else sprintf(errmsg,"Negative power (%d) of zero in %s.\n", node->op1.intpow,ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1262,errmsg,RECOVERABLE); } else *stacktop = 1/(*stacktop); } break; case POW: stacktop--; if ( (stacktop[0] < 0.0) && (floor(stacktop[1]) != stacktop[1]) ) { if ( valid_id(self_id) ) sprintf(errmsg, "Non-integer power of a negative number in %s, %s %s.\n", ex->name, typenames[id_type(self_id)],ELNAME(self_id)); else sprintf(errmsg,"Non-integer power of a negative number in %s.\n", ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2060,errmsg,RECOVERABLE); } if ( stacktop[0] == 0.0 ) { if ( stacktop[1] >= 0.0 ) *stacktop = 0.0; else *stacktop = 1e30; } else *stacktop = pow(stacktop[0],stacktop[1]); /* Note: pow recognizes integer powers, so OK for neg x */ break; case MAXIMUM_: stacktop--; *stacktop = (stacktop[0] > stacktop[1]) ? stacktop[0] : stacktop[1]; break; case MINIMUM_: stacktop--; *stacktop = (stacktop[0] < stacktop[1]) ? stacktop[0] : stacktop[1]; break; case WRAP_COMPOSE_: stacktop--; if ( sym_compose ) *stacktop = (REAL)(*sym_compose)((unsigned int)stacktop[0],(unsigned int)stacktop[1]); break; case WRAP_INVERSE_: if ( sym_compose ) *stacktop = (REAL)(*sym_inverse)((unsigned int)stacktop[0]); break; case ATAN2_: stacktop--; *stacktop = atan2(stacktop[0],stacktop[1]); break; case SQR: *stacktop *= *stacktop; break; case SQRT: if ( *stacktop < 0.0 ) { if ( *stacktop > -100*machine_eps ) *stacktop = 0.0; else { if ( valid_id(self_id) ) sprintf(errmsg,"Square root of negative number in %s, %s %s.\n", ex->name,typenames[id_type(self_id)],ELNAME(self_id)); else sprintf(errmsg,"Square root of negative number in %s.\n",ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1263,errmsg,RECOVERABLE); } } else *stacktop = sqrt(*stacktop); break; case CEIL_: *stacktop = ceil(*stacktop); break; case FLOOR_: *stacktop = floor(*stacktop); break; case ABS: *stacktop = fabs(*stacktop); break; case SIN: *stacktop = sin(*stacktop); break; case COS: *stacktop = cos(*stacktop); break; case TAN: *stacktop = tan(*stacktop); break; case EXP: *stacktop = exp(*stacktop); break; case SINH: *stacktop = (exp(*stacktop)-exp(-*stacktop))/2; break; case COSH: *stacktop = (exp(*stacktop)+exp(-*stacktop))/2; break; case TANH: y = exp(*stacktop); *stacktop = (y*y-1)/(y*y+1) ; break; case ASINH: *stacktop = log(*stacktop + sqrt(*stacktop*(*stacktop) + 1)); break; case ACOSH: if ( *stacktop < 1.0 ) { if ( valid_id(self_id) ) sprintf(errmsg,"Acosh argument less than 1 in %s, %s %s.\n", ex->name,typenames[id_type(self_id)],ELNAME(self_id)); else sprintf(errmsg,"Acosh argument less than 1 in %s.\n",ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2557,errmsg,RECOVERABLE); } *stacktop = 2*log(sqrt(*stacktop+1) + sqrt(*stacktop - 1)) - log(2.0); break; case ATANH: if ( fabs(*stacktop) >= 1.0 ) { if ( valid_id(self_id) ) sprintf(errmsg, "Atanh argument magnitude not less than 1 in %s, %s %s.\n", ex->name,typenames[id_type(self_id)],ELNAME(self_id)); else sprintf(errmsg,"Atanh argument magnitude not less than 1 in %s.\n", ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2559,errmsg, RECOVERABLE); } *stacktop = log(*stacktop+1)/2 - log(1-*stacktop)/2; break; case LOG: if ( *stacktop <= 0.0 ) { if ( valid_id(self_id) ) sprintf(errmsg,"Log of zero or negative number in %s, %s %s.\n", ex->name,typenames[id_type(self_id)],ELNAME(self_id)); else sprintf(errmsg,"Log of zero or negative number in %s.\n", ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1264,errmsg, RECOVERABLE ); } *stacktop = log(*stacktop); break; case ASIN: if ( *stacktop > 1.0 ) *stacktop = asin(1.0); else if ( *stacktop < -1.0 ) *stacktop = asin(-1.0); else *stacktop = asin(*stacktop); break; case ACOS: if ( *stacktop > 1.0 ) *stacktop = 0.0; else if ( *stacktop < -1.0 ) *stacktop = M_PI; else *stacktop = acos(*stacktop); break; case ATAN: *stacktop = atan(*stacktop); break; case ELLIPTICK: *stacktop = ellipticK(*stacktop); break; case ELLIPTICE: *stacktop = ellipticE(*stacktop); break; case INCOMPLETE_ELLIPTICF: stacktop--; *stacktop = incompleteEllipticF(stacktop[0],stacktop[1]); break; case INCOMPLETE_ELLIPTICE: stacktop--; *stacktop = incompleteEllipticE(stacktop[0],stacktop[1]); break; case CHS: *stacktop = -*stacktop; break; case INV: if ( *stacktop == 0.0 ) { if ( valid_id(self_id) ) sprintf(errmsg,"Division by zero in %s, %s %s.\n",ex->name, typenames[id_type(self_id)],ELNAME(self_id)); else sprintf(errmsg,"Division by zero in %s.\n",ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2560,errmsg,RECOVERABLE); } *stacktop = 1/(*stacktop); break; /* here are attributes for queries */ case COORD_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; switch ( id_type(id) ) { case VERTEX: *++stacktop = get_coord(id)[node->op2.coordnum]; break; case EDGE: get_edge_side(id,vect); *++stacktop = vect[node->op2.coordnum]; break; case FACET: get_facet_normal(id,vect); *++stacktop = vect[node->op2.coordnum]; break; } break; case INDEXED_COORD_: { int k = (int)*stacktop - 1; /* 1 based indexing */ if ( k < 0 || k >= SDIM ) { sprintf(errmsg, "Invalid index %d for x in %s; must be between 1 and %d, inclusive.\n", k+1,ex->name,SDIM); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2061,errmsg,RECOVERABLE ); } if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; switch( id_type(id) ) { case VERTEX: *stacktop = get_coord(id)[k]; break; case EDGE: get_edge_side(id,vect); *stacktop = vect[k]; break; case FACET: get_facet_normal(id,vect); *stacktop = vect[k]; break; default: sprintf(errmsg,"Can't have indexed x on %s, in %s.\n", typenames[id_type(id)], ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2062,errmsg,RECOVERABLE); } } break; case PRINT_VERTEXNORMAL_: { MAT2D(normal,MAXCOORD,MAXCOORD); REAL mag; int i; int normcount; if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; normcount = new_calc_vertex_normal(id,normal); project_vertex_normals(id,normal,normcount); mag = sqrt(SDIM_dot(normal[0],normal[0])); if ( mag == 0.0 ) { mag = 1; memset(normal[0],0,SDIM*sizeof(REAL));} sprintf(msg,"{"); for ( i = 0 ; i < SDIM ; i++ ) { if ( i > 0 ) strcat(msg,","); #ifdef LONGDOUBLE sprintf(msg+strlen(msg),"%#*.*L",DWIDTH,DPREC,normal[0][i]/mag); #else sprintf(msg+strlen(msg),"%17.15g",normal[0][i]/mag); #endif } strcat(msg,"}\n"); outstring(msg); break; } case GET_VERTEXNORMAL_: { MAT2D(normal,MAXCOORD,MAXCOORD); int k = (int)*stacktop - 1; /* 1 based indexing */ REAL mag; int normcount; if ( k < 0 || k >= SDIM ) { sprintf(errmsg, "Invalid index %d for vertexnormal in %s; must be between 1 and %d.\n", k+1,ex->name,SDIM); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2063,errmsg,RECOVERABLE ); } if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; normcount = new_calc_vertex_normal(id,normal); project_vertex_normals(id,normal,normcount); mag = sqrt(SDIM_dot(normal[0],normal[0])); *stacktop = mag == 0.0 ? 0.0 : normal[0][k]/mag; } break; case PARAM_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = get_param(id)[node->op2.coordnum]; break; case GET_SQ_MEAN_CURV_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = vertex_sq_mean_curvature(id); break; case GET_FIXEDVOL_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = get_battr(id)&FIXEDVOL ? 1.0 : 0.0; break; case GET_MEANCURV_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = vertex_mean_curvature(id); break; case GET_LENGTH_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; if ( web.representation != STRING ) calc_edge(id); *++stacktop = get_edge_length(id); break; case GET_DIHEDRAL_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; if ( id_type(id) == EDGE ) *++stacktop = dihedral(id); else if ( id_type(id) == VERTEX ) *++stacktop = vertex_angle(id); else *++stacktop = 0.0; break; case GET_ORIENTATION_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = (get_attr(id) & NEGBOUNDARY) ? -1.0 : 1.0; break; case VALENCE_: case GET_VALENCE_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; switch ( id_type(id) ) { case VERTEX: if ( web.representation == SIMPLEX ) *++stacktop = (REAL)get_vertex_fvalence(id); else *++stacktop = (REAL)get_vertex_evalence(id); break; case EDGE: *++stacktop = (REAL)get_edge_valence(id); break; case FACET: *++stacktop = (REAL)get_facet_valence(id); break; case BODY: *++stacktop = (REAL)get_body_valence(id); break; } break; case GET_EDGE_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = (REAL)(ordinal(get_fe_edge(id))+1); break; case GET_FACET_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = (REAL)(ordinal(get_fe_facet(id))+1); break; case AREA_: case GET_AREA_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = get_facet_area(id); break; case GET_MID_EDGE_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = get_vattr(id) & (Q_MIDEDGE|Q_MIDPOINT) ? 1.0 : 0.0; break; case GET_MID_FACET_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = get_vattr(id) & Q_MIDFACET ? 1.0 : 0.0; break; case GET_WRAP_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = web.symmetry_flag ? (REAL)get_edge_wrap(id) : 0; break; case GET_PRESSURE_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; switch ( id_type(id) ) { case BODY: *++stacktop = get_body_pressure(id); break; default: sprintf(errmsg,"Pressure only for bodies, in %s.\n",ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1267,errmsg,RECOVERABLE); } break; case GET_USERATTR_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = user_attribute(id); break; case GET_QUANTITY_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; if ( !valid_id(id) ) { sprintf(errmsg, "Quantity name '%s' needs attribute like .value (in %s)\n", GEN_QUANT(node->op2.quant_id)->name,ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2064,errmsg, RECOVERABLE); } *++stacktop = quantity_attribute(id,node->op2.quant_id); break; case GET_INSTANCE_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; if ( !valid_id(id) ) { sprintf(errmsg, "Instance name '%s' needs attribute like .value (in %s)\n", METH_INSTANCE(node->op2.meth_id)->name,ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2065,errmsg, RECOVERABLE); } *++stacktop = instance_attribute(id,node->op2.meth_id); break; case GET_PHASE_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; switch ( id_type(id) ) { case FACET: *++stacktop = (REAL)get_f_phase(id); break; case BODY: *++stacktop = (REAL)get_b_phase(id); break; default: sprintf(errmsg,"Phase of wrong type element in %s.\n",ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1268,errmsg,RECOVERABLE); } break; case DENSITY_: case GET_DENSITY_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; switch ( id_type(id) ) { case EDGE: *++stacktop = get_edge_density(id); break; case FACET: *++stacktop = get_facet_density(id); break; case BODY: *++stacktop = get_body_density(id); break; default: sprintf(errmsg,"Density of wrong type element in %s.\n",ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1269,errmsg,RECOVERABLE); } break; case GET_STAR_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; switch ( id_type(id) ) { case VERTEX: *++stacktop = get_vertex_star(id); break; case EDGE: *++stacktop = get_edge_star(id); break; default: *++stacktop = 0.0; break; } break; case VOLUME_: case GET_VOLUME_: { int attr; if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; attr = get_battr(id); if ( attr & FIXEDVOL ) { if (fixed_volume_timestamp < global_timestamp) calc_content(Q_FIXED); } else if ( (info_volume_timestamp < global_timestamp) ) calc_content(Q_INFO|Q_ENERGY); *++stacktop = get_body_volume(id); break; } case GET_VOLCONST_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = get_body_volconst(id); break; case GET_TARGET_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = get_body_fixvol(id); break; case ID_: case GET_ID_: case GET_OID_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; if ( (node->type == GET_OID_) && inverted(id) ) *++stacktop = -(REAL)(ordinal(id)+1); else *++stacktop = (REAL)(ordinal(id)+1); break; case ORIGINAL_: case GET_ORIGINAL_: /* as user's element id number */ if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = valid_id(id) ? (REAL)ordinal(get_original(id))+1 : 0; break; case GET_MPI_TASK_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = id_task(id); break; case GET_COLOR_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; switch ( id_type(id) ) { case EDGE: *++stacktop = (REAL)get_edge_color(id); break; case FACET: *++stacktop = (REAL)get_facet_color(id); break; default: *++stacktop = 0.0; } break; case GET_FRONTCOLOR_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; switch ( id_type(id) ) { case FACET: *++stacktop = (REAL)get_facet_frontcolor(id); break; default: *++stacktop = 0.0; } break; case GET_BACKCOLOR_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; switch ( id_type(id) ) { case FACET: *++stacktop = (REAL)get_facet_backcolor(id); break; default: *++stacktop = 0.0; } break; case GET_FRONTBODY_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; switch ( id_type(id) ) { case EDGE: fe = get_edge_fe(id); if ( !valid_id(fe) ) {*++stacktop = 0.0; break;} f_id = get_fe_facet(fe); if ( inverted(f_id) ) f_id = get_fe_facet(get_next_facet(fe)); if ( inverted(f_id) ) { *++stacktop = 0.0; break;} *++stacktop = (REAL)ordinal(get_facet_body(f_id))+1; break; case FACET: *++stacktop = (REAL)ordinal(get_facet_body(id))+1; break; default: *++stacktop = 0.0; } break; case GET_BACKBODY_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; switch ( id_type(id) ) { case FACET: *++stacktop = (REAL)ordinal(get_facet_body(inverse_id(id)))+1; break; case EDGE: fe = get_edge_fe(id); if ( !valid_id(fe) ) {*++stacktop = 0.0; break;} f_id = get_fe_facet(fe); if ( !inverted(f_id) ) f_id = get_fe_facet(get_next_facet(fe)); if ( !inverted(f_id) ) { *++stacktop = 0.0; break;} *++stacktop = (REAL)ordinal(get_facet_body(inverse_id(f_id)))+1; break; default: *++stacktop = 0.0; } break; case TAG_: case GET_TAG_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = (REAL)get_tag(id); break; case GET_BARE_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = (get_attr(id) & BARE_NAKED) ? 1.0 : 0.0; break; case GET_MIDV_: if ( web.modeltype != QUADRATIC ) { sprintf(errmsg,"Cannot do MIDV except in QUADRATIC model (in %s).\n", ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2066,errmsg,RECOVERABLE); } if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = ordinal(get_edge_midv(id)) + 1.0; break; case FIXED_: case GET_FIXED_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; if ( id_type(id) == BODY ) *++stacktop = get_battr(id)&FIXEDVOL ? 1.0 : 0.0; else *++stacktop = (get_attr(id) & FIXED) ? 1.0 : 0.0; break; case GET_NO_DISPLAY_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = (get_attr(id) & NODISPLAY) ? 1.0 : 0.0; break; case GET_NONCONTENT_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = (get_attr(id) & NONCONTENT) ? 1.0 : 0.0; break; case GET_HIT_PARTNER_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = (get_attr(id) & HIT_PARTNER) ? 1.0 : 0.0; break; case GET_NO_REFINE_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = (get_attr(id) & NO_REFINE) ? 1.0 : 0.0; break; case GET_TRIPLE_PT_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = (get_attr(id) & TRIPLE_PT) ? 1.0 : 0.0; break; case GET_TETRA_PT_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = (get_attr(id) & TETRA_PT) ? 1.0 : 0.0; break; case GET_AXIAL_POINT_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *++stacktop = (get_attr(id) & AXIAL_POINT) ? 1.0 : 0.0; break; case GET_SHOW_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; if ( id_type(id) == EDGE ) { int eattr = get_eattr(id); int showflag = 0; facetedge_id fe_id = get_edge_fe(id); if ( eattr & BOUNDARY ) showflag = 1; if ( equal_id(get_next_facet(fe_id),fe_id) ) /* valence 1 */ showflag = 1; else if ( !equal_id(get_next_facet(fe_id),get_prev_facet(fe_id)) ) showflag = 1; if ( eattr & HIT_WALL ) showflag = 1; if ( eattr & FIXED ) showflag = 1; if ( show_expr[EDGE] && show_expr[EDGE]->start ) { PROF_EVAL_END(ex); showflag = eval(show_expr[EDGE],NULL,id,NULL) ? 1 : 0; PROF_EVAL_START(ex); } if ( get_edge_color(id) == CLEAR ) showflag = 0; *++stacktop = showflag; } else if ( id_type(id) == FACET ) { int fattr = get_fattr(id); int showflag = 1; if ( (fattr & (BOUNDARY|CONSTRAINT)) && !bdry_showflag ) showflag = 0; if ( fattr & NODISPLAY ) showflag = 0; if ( no_wall_flag ) { /* skip facets with all three vertices on walls */ fe = get_facet_fe(id); if ( get_vattr(get_fe_headv(fe)) & (HIT_WALL|CONSTRAINT) ) if ( get_vattr(get_fe_tailv(fe)) & (HIT_WALL|CONSTRAINT) ) { fe = get_next_edge(fe); if ( get_vattr(get_fe_headv(fe)) & (HIT_WALL|CONSTRAINT) ) showflag = 0; } } if ( show_expr[FACET] && show_expr[FACET]->start ) { PROF_EVAL_END(ex); if ( !eval(show_expr[FACET],NULL,id,NULL) ) showflag = 0; PROF_EVAL_START(ex); } *++stacktop = showflag; } else { sprintf(errmsg, "\"show\" attribute applied to wrong type of element in %s.\n", ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2067,errmsg,RECOVERABLE); } break; case ATTR_FUNCTION_: node += node->op1.skipsize - 1; break; case ATTR_FUNCTION_END_: { struct extra *ext = EXTRAS(node->op2.eltype) + node->op1.extranum; ext->flags |= FUNCTION_ATTR; tree_copy(&ext->code,node + node->right); break; } case GET_EXTRA_ATTR_: { struct extra *ext; int spot; n = node->op3.extranum; /* attribute number */ if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ext = EXTRAS(node->op2.eltype) + n; /* get index */ spot = 0; for ( k = 0 ; k < ext->array_spec.dim ; k++ ) { int j = (int)(stacktop[-ext->array_spec.dim+k+1]); spot *= ext->array_spec.sizes[k]; if ( (j < 1) || (j > ext->array_spec.sizes[k]) ) { sprintf(errmsg, "Attribute %s index %d is %d; maximum is %d (in %s).\n", ext->name,k+1,j,ext->array_spec.sizes[k],ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1270,errmsg,RECOVERABLE); } spot += (int)(stacktop[-ext->array_spec.dim+k+1]) - 1; } stacktop -= ext->array_spec.dim; if ( id_type(id) != node->op2.eltype ) { if ( (id_type(id)==EDGE) && (node->op2.eltype==VERTEX) && params ) { ext = EXTRAS(VERTEX) + n; *++stacktop = interp_edge_attribute(id,ext,spot,(int)params[2*SDIM]); break; } else if ( (id_type(id)==FACET) && (node->op2.eltype==VERTEX) && params ) { ext = EXTRAS(VERTEX) + n; *++stacktop = interp_facet_attribute(id,ext,spot,(int)params[2*SDIM]); break; } else { sprintf(errmsg, "Attribute %s is %s attribute, not %s attribute (in %s).\n", EXTRAS(node->op2.eltype)[n].name, typenames[node->op2.eltype], typenames[id_type(id)],ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2068,errmsg,RECOVERABLE); } } if ( ext->code.start ) { int oldflag = autorecalc_flag; autorecalc_flag = 0; PROF_EVAL_END(ex); eval(&ext->code,NULL,id,NULL); /* side-effect fills in values */ PROF_EVAL_START(ex); autorecalc_flag = oldflag; } switch ( ext->type ) { case REAL_TYPE: *++stacktop = ((REAL*)get_extra(id,n))[spot]; break; case INTEGER_TYPE: case CONSTRAINT_TYPE: case BOUNDARY_TYPE: case QUANTITY_TYPE: case INSTANCE_TYPE: case PROCEDURE_TYPE: *++stacktop = (REAL)((int*)get_extra(id,n))[spot]; break; case UINT_TYPE: *++stacktop = (REAL)((unsigned int*)get_extra(id,n))[spot]; break; case ULONG_TYPE: *++stacktop = (REAL)((unsigned long*)get_extra(id,n))[spot]; break; case LONG_TYPE: *++stacktop = (REAL)((long*)get_extra(id,n))[spot]; break; case UCHAR_TYPE: *++stacktop = (REAL)((unsigned char*)get_extra(id,n))[spot]; break; case CHAR_TYPE: *++stacktop = (REAL)((char*)get_extra(id,n))[spot]; break; case SHORT_TYPE: *++stacktop = (REAL)((short int*)get_extra(id,n))[spot]; break; case USHORT_TYPE: *++stacktop = (REAL)((unsigned short int*)get_extra(id,n))[spot]; break; case ELEMENTID_TYPE: case VERTEX_TYPE: case EDGE_TYPE: case FACET_TYPE: case BODY_TYPE: case FACETEDGE_TYPE: *(element_id*)(++stacktop) = ((element_id*)get_extra(id,n))[spot]; break; case PTR_TYPE: *((char**)++stacktop) = (((char**)get_extra(id,n))[spot]); break; } } break; case ON_CONSTRAINT_: case ON_CONSTRAINT_NAME: { int testcon = (node->type == ON_CONSTRAINT_) ? (int)*(stacktop--) : node->op3.connum; if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; switch(id_type(id)) { case VERTEX: *++stacktop = (REAL)v_on_constraint(id,testcon); break; case EDGE : *++stacktop = (REAL)e_on_constraint(id,testcon); break; case FACET : *++stacktop = (REAL)f_on_constraint(id,testcon); break; default: sprintf(errmsg, "Can't do constraints on this type element (in %s).\n", ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1272,errmsg,RECOVERABLE); } } break; case HIT_CONSTRAINT_: case HIT_CONSTRAINT_NAME: { int testcon = (node->type == HIT_CONSTRAINT_) ? (int)*(stacktop--) : node->op3.connum; if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; switch(id_type(id)) { case VERTEX: *++stacktop = (REAL)get_v_constraint_status(id,testcon); break; default: sprintf(errmsg, "Can do hit_constraints only on vertices (in %s).\n",ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1273,errmsg,RECOVERABLE); } } break; case ON_BOUNDARY_: case ON_BOUNDARY_NAME: { struct boundary *b=NULL; int testb = (node->type == ON_BOUNDARY_) ? (int)*(stacktop--) : node->op3.bdrynum; if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; switch(id_type(id)) { case VERTEX: b = get_boundary(id); break; case EDGE : b = get_edge_boundary(id); break; case FACET : b = get_facet_boundary(id); break; default: sprintf(errmsg, "Can't do boundary on a %s (in %s).\n", typenames[id_type(id)],ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1274,errmsg,RECOVERABLE); } *++stacktop = (b == web.boundaries+testb) ? 1.0 : 0.0; } break; case ON_METHOD_INSTANCE_: { struct element *eptr; int *mptr; if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; eptr = elptr(id); mptr = (int*)((char*)eptr+get_meth_offset(id_type(id))); for ( i = 0 ; i < eptr->method_count ; i++ ) if ( abs(node->op2.meth_id) == abs(mptr[i]) ) { *++stacktop = 1.0; break; } if ( i == eptr->method_count ) *++stacktop = 0.0; break; } case ON_QUANTITY_: { struct element *eptr; int *mptr; struct method_instance *mi; if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; eptr = elptr(id); mptr = (int*)((char*)eptr+get_meth_offset(id_type(id))); for ( i = 0 ; i < eptr->method_count ; i++ ) { mi = METH_INSTANCE(abs(mptr[i])); if ( mi->quant == node->op2.quant_id ) { *++stacktop = 1.0; break; } } if ( i == eptr->method_count ) *++stacktop = 0.0; break; } case SELF_ELEMENT_: if ( !valid_id(self_id) ) { sprintf(errmsg,"No element for SELF to refer to in %s.\n",ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2069,errmsg,RECOVERABLE); } *(element_id*)get_localp(node->op2.localnum) = self_id; break; case SYMBOL_ELEMENT_: break; case SINGLE_ELEMENT_EXPR_: *(element_id*)(++stacktop) = *(element_id*)get_localp(node[node->left].op2.localnum); break; case ELEMENT_IDENT_: *(element_id*)get_localp(node->op2.localnum) = globals(node->op3.name_id)->value.id; break; case INDEXED_SUBTYPE_: /* like ee.vertex[1] */ { element_id next_id = NULLID; int ord = (int)*(stacktop--) - 1; /* which one */ element_id parent = node[node->left].op2.localnum ? *(element_id*)get_localp(node[node->left].op2.localnum) : q_id; int ptype = id_type(parent); element_id first; /* sentinel for looping */ if ( ord < 0 ) { sprintf(errmsg,"Element index must be positive in %s.\n",ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1275,errmsg,RECOVERABLE); } id=NULLID; switch ( ptype ) { case VERTEX: switch ( node->op1.eltype )/* subtype */ { case EDGE: /* indexed edge of vertex */ id = first = get_vertex_edge(parent); if ( !valid_element(id) ) { sprintf(errmsg,"Vertex %s has no edges.\n",ELNAME(parent)); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1276,errmsg,RECOVERABLE); } for ( n = 1 ; n <= ord ; n++ ) { id = get_next_tail_edge(id); if ( equal_id(id,first) ) { sprintf(errmsg,"Edge index %d exceeds valence of vertex %s.\n", ord+1,ELNAME(id)); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1277,errmsg,RECOVERABLE); } } next_id = id; break; case FACET: /* indexed facet of vertex */ id = first = get_vertex_first_facet(parent); if ( !valid_element(id) ) { sprintf(errmsg,"Vertex %s has no facets.\n",ELNAME(parent)); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1278,errmsg,RECOVERABLE); } for ( n = 1 ; n <= ord ; n++ ) { id = get_next_vertex_facet(parent,id); if ( equal_id(id,first) ) { sprintf(errmsg, "Facet index %d exceeds facet valence of vertex %s.\n", ord+1,ELNAME(parent)); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1279,errmsg,RECOVERABLE); } } if ( id_type(id) == FACETEDGE ) id = get_fe_facet(id); next_id = positive_id(id); break; } break; case EDGE: switch ( node->op1.eltype /* subtype */ ) { case VERTEX: if ( ord >= web.skel[EDGE].ctrlpts ) { sprintf(errmsg, "Index, %d, exceeds the number of vertices on an edge, %d.\n", ord+1,web.skel[EDGE].ctrlpts); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1280,errmsg, RECOVERABLE); } if ( web.modeltype == LAGRANGE ) { next_id = get_edge_vertices(parent) [inverted(parent) ? web.skel[EDGE].ctrlpts-ord-1 : ord]; } else switch ( ord ) { case 0: next_id = get_edge_tailv(parent); break; case 1: next_id = get_edge_headv(parent); break; case 2: next_id = get_edge_midv(parent); break; } break; case FACET: id = first = get_edge_fe(parent); if ( !valid_element(id) ) { sprintf(errmsg,"Edge %s has no facets.\n",ELNAME(parent)); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1176,errmsg,RECOVERABLE); } for ( n = 1 ; n <= ord ; n++ ) { id = get_next_facet(id); if ( equal_id(id,first) ) { sprintf(errmsg, "Facet index, %d, exceeds number of facets on edge %s.\n", ord+1,ELNAME(parent)); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1186,errmsg,RECOVERABLE); } } next_id = get_fe_facet(id); break; case FACETEDGE: id = first = get_edge_fe(parent); if ( !valid_element(id) ) { sprintf(errmsg,"Edge %s has no facets.\n",ELNAME(parent)); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1281,errmsg,RECOVERABLE); } for ( n = 1 ; n <= ord ; n++ ) { id = get_next_facet(id); if ( equal_id(id,first) ) { sprintf(errmsg, "Facetedge index, %d, exceeds valence on edge %s.\n", ord+1,ELNAME(parent)); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1282,errmsg,RECOVERABLE); } } next_id = id; break; } break; case FACET: switch ( node->op1.eltype /* subtype */ ) { case VERTEX: if ( (web.representation == SIMPLEX) || (web.modeltype == LAGRANGE) ) { if ( ord >= web.skel[FACET].ctrlpts ) { sprintf(errmsg, "Vertex index, %d, exceeds number of vertices on facet %s.\n", ord+1,ELNAME(parent)); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3503,errmsg,RECOVERABLE); } next_id = get_facet_vertices(parent)[ord]; break; } if ( (web.modeltype == QUADRATIC) && (web.representation == SOAPFILM) ) { if ( ord >= web.skel[FACET].ctrlpts ) { sprintf(errmsg, "Vertex index, %d, exceeds number of vertices on facet %s.\n", ord+1,ELNAME(parent)); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3504,errmsg,RECOVERABLE); } fe = get_facet_fe(parent); if ( ord < 3 ) { for ( i = 0 ; i < ord ; i++ ) fe = get_next_edge(fe); next_id = get_fe_tailv(fe); } else { for ( i = 3 ; i < ord ; i++ ) fe = get_next_edge(fe); next_id = get_fe_midv(fe); } break; } /* now string */ id = first = get_facet_fe(parent); if ( !valid_element(id) ) { sprintf(errmsg,"Facet has no edges.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1283,errmsg,RECOVERABLE); } if ( inverted(parent) ) { facetedge_id nextfe=id,startfe=id; /* go back round to find start */ do { id = nextfe; nextfe = get_prev_edge(id); } while ( valid_id(nextfe) && !equal_id(nextfe,startfe) ); first = id; } for ( n = 1 ; n <= ord ; n++ ) { edge_id next_edge = get_next_edge(id); if ( !valid_id(next_edge) && n == ord ) { next_id = get_fe_headv(id); id = NULLID; break; } id = next_edge; if ( !valid_id(id) || equal_id(id,first) ) { sprintf(errmsg, "Vertex index, %d, exceeds number of vertices on facet %s.\n", ord+1,ELNAME(parent)); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1284,errmsg,RECOVERABLE); } } if ( valid_id(id) ) next_id = get_fe_tailv(id); break; case EDGE: id = first = get_facet_fe(parent); if ( !valid_element(id) ) { sprintf(errmsg,"Facet has no edges.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1285,errmsg,RECOVERABLE); } if ( inverted(parent) ) { facetedge_id nextfe=id,startfe=id; /* go back round to find start */ do { id = nextfe; nextfe = get_prev_edge(id); } while ( valid_id(nextfe) && !equal_id(nextfe,startfe) ); first = id; } for ( n = 1 ; n <= ord ; n++ ) { id = get_next_edge(id); if ( !valid_id(id) || equal_id(id,first) ) { sprintf(errmsg, "Edge index, %d, exceeds number of vertices on facet %s.\n", ord+1,ELNAME(parent)); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1286,errmsg,RECOVERABLE); } } next_id = get_fe_edge(id); break; case BODY: switch ( ord ) { case 0: next_id = get_facet_body(parent); break; case 1: next_id = get_facet_body(inverse_id(parent)); break; default: sprintf(errmsg, "Illegal facet body index %d; must be 1 or 2.\n",ord+1); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1287,errmsg, RECOVERABLE); break; } if ( !valid_id(next_id) ) { sprintf(errmsg,"Facet %d does not have body of index %d.\n", oid(parent),ord+1); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2070,errmsg,RECOVERABLE); } break; } break; case BODY: switch ( node->op1.eltype /* subtype */ ) { case FACET: /* if ( bfacet_timestamp < top_timestamp ) make_bfacet_lists(); */ id = first = get_body_facet(parent); for ( n = 1 ; n <= ord ; n++ ) { id = get_next_body_facet(id); if ( equal_id(id,first) ) { id = NULLID; break; } } if ( !valid_id(id) ) { sprintf(errmsg, "Facet index, %d, exceeds number of facets on body %s.\n", ord+1, ELNAME(parent)); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1288,errmsg,RECOVERABLE); } next_id = id; break; } break; case FACETEDGE: switch ( node->op1.eltype /* subtype */ ) { case EDGE: next_id = get_fe_edge(parent); break; case FACET: next_id = get_fe_facet(parent); break; } break; } *(element_id*)get_localp(node->op2.localnum) = next_id; } break; /* end INDEXED_SUBTYPE */ case INDEXED_ELEMENT_: { element_id partid = *(element_id*)(stacktop--); element_id id; id = get_full_id(node->op1.eltype,partid); if ( !valid_id(id) ) { sprintf(errmsg,"%s index %d is not valid.\n", typenames[node->op1.eltype], valid_id(partid) ? (int)(partid & OFFSETMASK)+1 : 0); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1200,errmsg,RECOVERABLE); } *(element_id *)get_localp(node->op2.localnum) = id; break; } case QUALIFIED_ATTRIBUTE: break; /* just a no-op in execution */ /****************************/ /* aggregate initialization */ /****************************/ case SET_INIT_: web.where_count = 0; /* break and continue jumps */ *(size_t*)(localstack + node->stackpos) = stacktop - localstack + 3; #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) { mpi_subtask_command_flag = 1; mpi_aggregate(node,ex->locals->count); } #endif break; case AGGREGATE_INIT_: aggregate_depth++; web.where_count = 0; /* break and continue jumps */ *(size_t*)(localstack + node->stackpos) = stacktop - localstack + 3; switch ( node->op1.aggrtype ) { case FOREACH_: case SET_ATTRIBUTE_LOOP_: break; case MAX_: *++stacktop = -MAXDOUBLE; /* max of empty set */ break; case MIN_: *++stacktop = MAXDOUBLE; /* min of empty set */ break; case SUM_: *++stacktop = 0.0; /* sum */ break; case AVG_: *++stacktop = 0.0; /* count */ *++stacktop = 0.0; /* sum */ break; case COUNT_: *++stacktop = 0.0; /* count */ break; case HISTOGRAM_: case LOGHISTOGRAM_: *++stacktop = 0.0; /* first phase counter */ *++stacktop = MAXDOUBLE; /* min */ *++stacktop = -MAXDOUBLE; /* max */ for ( n = 0 ; n < HISTBINS+1 ; n++ ) *++stacktop = 0.0; histo_max = web.skel[node->op2.eltype].count + 5; histo_data = (REAL*)temp_calloc(histo_max,sizeof(REAL)); histo_count = 0; break; case LIST_: /* print column headers */ #ifdef MPI_EVOLVER if ( (this_task == MASTER_TASK) || !mpi_subtask_command_flag ) #endif switch ( node->op2.eltype ) { case VERTEX: if ( SDIM == 2 ) outstring("// Id X Y\n"); else outstring("// Id X Y Z\n"); break; case EDGE: outstring("// Id endpoints\n"); break; case FACET: if ( web.representation == SIMPLEX ) outstring("// Id vertices\n"); else outstring("// Id edges\n"); break; case BODY: outstring("// Id facets\n"); break; case FACETEDGE: outstring("// id edge facet prevedge nextedge prevfacet nextfacet\n"); break; } break; case REFINE_: #ifdef MPI_EVOLVER if ( (this_task != MASTER_TASK) && mpi_subtask_command_flag && node->op2.eltype == EDGE ) mpi_edge_refine_init(); #endif break; case DISSOLVE_: break; case DELETE_: #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) mpi_set_corona(THIN_CORONA); if ( (this_task != MASTER_TASK) && mpi_subtask_command_flag ) mpi_delete_init(); #endif break; case FIX_: break; case UNFIX_: break; case REVERSE_ORIENTATION_: break; case EDGESWAP_: break; case T1_EDGESWAP_: break; case EQUIANGULATE_: break; case POP_: case POP_TRI_TO_EDGE_: case POP_EDGE_TO_TRI_: case POP_QUAD_TO_QUAD_: break; case VERTEX_AVERAGE_: break; case RAW_VERTEX_AVERAGE_: break; case RAWEST_VERTEX_AVERAGE_: break; default: sprintf(errmsg,"Internal error: Bad aggregate type %d.\n", node->op1.aggrtype); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1290,errmsg, RECOVERABLE); break; } #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) { mpi_subtask_command_flag = 1; mpi_aggregate(node,ex->locals->count); } #endif break; /* end case AGGREGATE_INIT */ /************************/ /* next element in loop */ /************************/ case SINGLE_ELEMENT_: q_id = *(element_id*)get_localp(node[node->left].op2.localnum); if ( stacktop[-1] > 0.0 ) /* done */ node += node->op1.skipsize; else stacktop[-1] = 1.0; break; case NEXT_VERTEX_: /* all vertices */ { vertex_id *vp; vp = (element_id *)(stacktop - 0); if ( !valid_id(*vp) || breakflag ) { /* done */ node += node->op1.skipsize - 1; break; } q_id = *vp; *(element_id*)get_localp(node->op2.localnum) = q_id; /* check sentinel; set for next time around */ if ( equal_id(*vp,*(vertex_id*)(stacktop-1)) ) *vp = NULLID; else *vp = vptr(*vp)->forechain; if ( !valid_element(q_id) ) node--; /* skip body of loop */ #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (id_task(q_id) != this_task) ) node--; /* don't count foreign vertices in global aggregate */ #endif } break; case NEXT_EDGE_VERTEX_: /* edge vertices */ { int *numptr; vertex_id *vp; numptr = (int *)(stacktop - 1); if ( *numptr== web.skel[EDGE].ctrlpts ) { /* done */ node += node->op1.skipsize - 1; break; } vp = *(element_id **)(stacktop - 0); q_id = vp[*numptr]; *(element_id*)get_localp(node->op2.localnum) = q_id; (*numptr)++; if ( !valid_element(q_id) ) node--; /* skip body of loop */ #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (id_task(q_id) != this_task) ) node--; /* don't count foreign elements in global aggregate */ #endif break; } case NEXT_FACET_VERTEX_: /* facet vertices */ { facetedge_id *fe_ptr; facet_id *f_ptr; if ( (web.representation == SIMPLEX) || (web.modeltype==LAGRANGE) ) { int *numptr = (int *)(stacktop - 1); if ( *numptr > web.skel[FACET].ctrlpts ) { node += node->op1.skipsize - 1; break; } f_ptr = (facet_id *)(stacktop); q_id = get_facet_vertices(*f_ptr)[*numptr]; (*numptr)++; } else if ( (web.modeltype == QUADRATIC) && (web.representation == SOAPFILM) ) { int *numptr = (int *)(stacktop - 1); if ( *numptr >= web.skel[FACET].ctrlpts ) /* see if done */ { node += node->op1.skipsize - 1; break; } f_id = *(facet_id *)(stacktop); fe = get_facet_fe(f_id); if ( *numptr < 3 ) { for ( i = 0 ; i < *numptr ; i++ ) fe = get_next_edge(fe); q_id = get_fe_tailv(fe); } else { for ( i = 3 ; i < *numptr ; i++ ) fe = get_next_edge(fe); q_id = get_fe_midv(fe); } (*numptr)++; } else { fe_ptr = (element_id *)(stacktop - 0); if ( !valid_id(*fe_ptr) ) /* see if done */ { node += node->op1.skipsize - 1; break; } if ( id_type(*fe_ptr) == VERTEX ) { /* last vertex in string unclosed facet */ q_id = *fe_ptr; *fe_ptr = NULLID; } else { edge_id e_id = get_fe_edge(*fe_ptr); q_id = get_edge_tailv(e_id); *fe_ptr = get_next_edge(*fe_ptr); if ( !valid_id(*fe_ptr) ) *fe_ptr = get_edge_headv(e_id); /* end of string facet */ else if ( !valid_element(*fe_ptr) || equal_id(*fe_ptr,*(element_id *)(stacktop-1))) *fe_ptr = NULLID; /* last one */ } } *(element_id*)get_localp(node->op2.localnum) = q_id; if ( !valid_element(q_id) ) node--; /* skip body of loop */ #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (id_task(q_id) != this_task) ) node--; /* don't count foreign elements in global aggregate */ #endif break; } case NEXT_BODY_VERTEX_: /* body vertices */ break; case NEXT_EDGE_: /* all edges */ { edge_id *ep; ep = (element_id *)(stacktop - 0); if ( !valid_id(*ep) || breakflag ) { /* done */ node += node->op1.skipsize - 1; break; } q_id = *ep; *(element_id*)get_localp(node->op2.localnum) = q_id; /* check sentinel; set for next time around */ if ( equal_id(*ep,*(edge_id*)(stacktop-1)) ) *ep = NULLID; else *ep = eptr(*ep)->forechain; if ( !valid_element(q_id) ) node--; /* skip body of loop */ #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (id_task(q_id) != this_task) ) node--; /* don't count foreign elements in global aggregate */ #endif } break; case NEXT_VERTEX_EDGE_: /* vertex edges */ { edge_id *e_ptr; e_ptr = (element_id *)(stacktop - 0); if ( !valid_id(*e_ptr) ) /* see if done */ { node += node->op1.skipsize - 1; break; } q_id = *e_ptr; *(element_id*)get_localp(node->op2.localnum) = q_id; if ( get_attr(*(element_id *)(stacktop-2)) & (Q_MIDPOINT|Q_MIDEDGE) ) *e_ptr = NULLID; /* last one */ else { *e_ptr = get_next_tail_edge(*e_ptr); if ( !valid_element(*e_ptr) || equal_id(*e_ptr,get_vertex_edge(get_edge_tailv(*e_ptr)))) *e_ptr = NULLID; /* last one */ } if ( !valid_element(q_id) ) node--; /* skip body of loop */ #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (id_task(q_id) != this_task) ) node--; /* don't count foreign elements in global aggregate */ #endif break; } case NEXT_FACET_EDGE_: /* facet edges */ { facetedge_id *fe_ptr; fe_ptr = (element_id *)(stacktop - 0); if ( !valid_id(*fe_ptr) ) /* see if done */ { node += node->op1.skipsize - 1; break; } q_id = get_fe_edge(*fe_ptr); *(element_id*)get_localp(node->op2.localnum) = q_id; *fe_ptr = get_next_edge(*fe_ptr); if ( web.representation == STRING ) { if ( !valid_element(*fe_ptr) || equal_id(*fe_ptr,*(element_id *)(stacktop-1))) *fe_ptr = NULLID; /* last one */ } else { if ( ++(*(int*)(stacktop-1)) == FACET_VERTS ) *fe_ptr = NULLID; /* last one */ } if ( !valid_element(q_id) ) node--; /* skip body of loop */ #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (id_task(q_id) != this_task) ) node--; /* don't count foreign elements in global aggregate */ #endif break; } case NEXT_BODY_EDGE_: /* body edges */ break; case NEXT_FACET_: /* all facets */ { facet_id *fp; fp = (element_id *)(stacktop - 0); if ( !valid_id(*fp)|| breakflag ) { node += node->op1.skipsize - 1; /* skip to end */ break; } q_id = *fp; *(element_id*)get_localp(node->op2.localnum) = q_id; /* check sentinel; set for next time around */ if ( equal_id(*fp,*(facet_id*)(stacktop-1)) ) *fp = NULLID; else *fp = fptr(*fp)->forechain; if ( !valid_element(q_id) ) node--; /* skip body of loop */ #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (id_task(q_id) != this_task) ) node--; /* don't count foreign elements in global aggregate */ #endif } break; case NEXT_EDGE_FACET_: /* all facets on edge */ { facetedge_id *fe_ptr; fe_ptr = (element_id *)(stacktop - 0); if ( !valid_id(*fe_ptr) ) /* see if done */ { node += node->op1.skipsize - 1; break; } q_id = get_fe_facet(*fe_ptr); *(element_id*)get_localp(node->op2.localnum) = q_id; *fe_ptr = get_next_facet(*fe_ptr); if ( !valid_element(*fe_ptr) || equal_element(*fe_ptr,get_edge_fe(get_fe_edge(*fe_ptr)))) *fe_ptr = NULLID; /* last one */ if ( !valid_element(q_id) ) node--; /* skip body of loop */ #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (id_task(q_id) != this_task) ) node--; /* don't count foreign elements in global aggregate */ #endif break; } case NEXT_EDGE_FACETEDGE_: /* all facetedges on edge */ { facetedge_id *fe_ptr; fe_ptr = (element_id *)(stacktop - 0); if ( !valid_id(*fe_ptr) ) /* see if done */ { node += node->op1.skipsize - 1; break; } q_id = *fe_ptr; *(element_id*)get_localp(node->op2.localnum) = q_id; *fe_ptr = get_next_facet(*fe_ptr); if ( !valid_element(*fe_ptr) || equal_element(*fe_ptr,get_edge_fe(get_fe_edge(*fe_ptr)))) *fe_ptr = NULLID; /* last one */ if ( !valid_element(q_id) ) node--; /* skip body of loop */ #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (id_task(q_id) != this_task) ) node--; /* don't count foreign elements in global aggregate */ #endif break; } case NEXT_VERTEX_FACET_: /* facets adjacent to vertex */ { facet_id *f_ptr; f_ptr = (element_id *)(stacktop - 0); if ( !valid_id(*f_ptr) ) /* see if done */ { node += node->op1.skipsize - 1; break; } q_id = *f_ptr; if ( id_type(q_id) == FACETEDGE ) q_id = get_fe_facet(q_id); q_id = positive_id(q_id); *(element_id*)get_localp(node->op2.localnum) = q_id; v_id = *(vertex_id*)(stacktop-2); *f_ptr=get_next_vertex_facet(v_id,*f_ptr); if ( equal_id(*f_ptr,*(element_id*)(stacktop-1)) ) *f_ptr = NULLID; if ( !valid_element(q_id) ) node--; /* skip body of loop */ #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (id_task(q_id) != this_task) ) node--; /* don't count foreign elements in global aggregate */ #endif } break; case NEXT_BODY_FACET_: /* facets on body */ { facet_id *f_ptr; f_ptr = (element_id *)(stacktop - 0); if ( !valid_id(*f_ptr) ) /* see if done */ { node += node->op1.skipsize - 1; break; } q_id = *f_ptr; *(element_id*)get_localp(node->op2.localnum) = q_id; *f_ptr = get_next_body_facet(*f_ptr); if ( (get_fattr(*f_ptr)& (inverted(*f_ptr)?DID_BODYBACKFACET:DID_BODYFRONTFACET)) || equal_id(*f_ptr,q_id) || equal_id(*f_ptr,*(element_id *)(stacktop-1))) *f_ptr = NULLID; /* last one */ else set_attr(*f_ptr, (inverted(*f_ptr) ? DID_BODYBACKFACET : DID_BODYFRONTFACET) ); if ( !valid_element(q_id) ) node--; /* skip body of loop */ #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (id_task(q_id) != this_task) ) node--; /* don't count foreign elements in global aggregate */ #endif } break; case NEXT_BODY_: /* all bodies */ { body_id *bp; bp = (element_id *)(stacktop - 0); if ( !valid_id(*bp)|| breakflag ) { node += node->op1.skipsize - 1; break; } q_id = *bp; *(element_id*)get_localp(node->op2.localnum) = q_id; /* check sentinel; set for next time around */ if ( equal_id(*bp,*(body_id*)(stacktop-1)) ) *bp = NULLID; else *bp = bptr(*bp)->forechain; if ( !valid_element(q_id) ) node--; /* skip body of loop */ #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (id_task(q_id) != this_task) ) node--; /* don't count foreign elements in global aggregate */ #endif } break; case NEXT_FACET_BODY_: /* both bodies on facet */ { int *numptr = (int *)(stacktop - 1); f_id = *(facet_id *)(stacktop - 2); /* facet */ (*numptr)++; if ( *numptr == 3 ) /* see if done */ { /* done */ node += node->op1.skipsize - 1; break; } if ( *numptr == 1 ) /* first */ { q_id = get_facet_body(f_id); *(element_id*)get_localp(node->op2.localnum) = q_id; if ( !valid_id(q_id) ) node--; /* try again */ } else /* *numptr == 2 */ { q_id = get_facet_body(inverse_id(f_id)); *(element_id*)get_localp(node->op2.localnum) = q_id; if ( !valid_id(q_id) ) node--; /* try again */ } break; } case NEXT_VERTEX_BODY_: /* bodies adjacent to vertex */ break; case NEXT_EDGE_BODY_: /* bodies adjacent to edge */ break; case NEXT_FACETEDGE_: /* all facetedges */ { facetedge_id *fep; fep = (element_id *)(stacktop - 0); if ( !valid_id(*fep)|| breakflag ) { /* done */ node += node->op1.skipsize - 1; break; } q_id = *fep; *(element_id*)get_localp(node->op2.localnum) = q_id; /* check sentinel; set for next time around */ if ( equal_id(*fep,*(facetedge_id*)(stacktop-1)) ) *fep = NULLID; else *fep = feptr(*fep)->forechain; if ( !valid_element(q_id) ) node--; /* skip body of loop */ #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (id_task(q_id) != this_task) ) node--; /* don't count foreign elements in global aggregate */ #endif } break; case NEXT_FACETEDGE_EDGE_: /* edge on facetedge */ { edge_id *e_ptr; e_ptr = (element_id *)(stacktop - 0); if ( !valid_id(*e_ptr) ) /* see if done */ { node += node->op1.skipsize - 1; break; } q_id = *e_ptr; *(element_id*)get_localp(node->op2.localnum) = q_id; *e_ptr = NULLID; /* last one */ if ( !valid_element(q_id) ) node--; /* skip body of loop */ #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (id_task(q_id) != this_task) ) node--; /* don't count foreign elements in global aggregate */ #endif } break; case NEXT_FACETEDGE_FACET_: /* facet on facetedge */ { facet_id *f_ptr; f_ptr = (element_id *)(stacktop - 0); if ( !valid_id(*f_ptr) ) /* see if done */ { node += node->op1.skipsize - 1; break; } q_id = *f_ptr; *(element_id*)get_localp(node->op2.localnum) = q_id; *f_ptr = NULLID; /* last one */ if ( !valid_element(q_id) ) node--; /* skip body of loop */ #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (id_task(q_id) != this_task) ) node--; /* don't count foreign elements in global aggregate */ #endif } break; /***********************/ /* aggregate condition */ /***********************/ case WHERE_: /* see if element qualifies */ if ( *(stacktop--) == 0.0 ) node += node->left-1; /* no, go back to generator */ else node->op1.wherecount++; /* where count NONREENTRANT */ break; /* else continue with current element */ case SINGLE_ASSIGN_: /* restore old q_id */ q_id = *(element_id *)(stacktop - 2); stacktop -= 3; /* erase locals */ break; /*****************/ /* aggregate end */ /*****************/ case AGGREGATE_END_: aggregate_depth--; /* restore old q_id */ q_id = *(element_id *)(stacktop - 2); where = node+node->right; where = where+where->left; if ( where->type == WHERE_ ) { web.where_count = where->op1.wherecount; where->op1.wherecount = 0; /* reset where count */ } switch ( node->op1.aggrtype ) { case FOREACH_: case SET_ATTRIBUTE_LOOP_: stacktop -= 3; /* erase locals */ break; case MAX_: stacktop -= 3; /* erase locals */ #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (aggregate_depth==0)) { MPI_Reduce(stacktop,stacktop+1,1,MPI_REAL,MPI_MAX, MASTER_TASK,MPI_COMM_WORLD); stacktop[0] = stacktop[1]; } #endif break; case MIN_: stacktop -= 3; /* erase locals */ #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (aggregate_depth==0)) { MPI_Reduce(stacktop,stacktop+1,1,MPI_REAL,MPI_MIN, MASTER_TASK,MPI_COMM_WORLD); stacktop[0] = stacktop[1]; } #endif break; case SUM_: stacktop -= 3; /* erase locals */ #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (aggregate_depth==0)) { MPI_Reduce(stacktop,stacktop+1,1,MPI_REAL,MPI_SUM, MASTER_TASK,MPI_COMM_WORLD); stacktop[0] = stacktop[1]; } #endif break; case COUNT_: stacktop -= 3; /* erase locals */ #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (aggregate_depth==0)) { MPI_Reduce(stacktop,stacktop+1,1,MPI_REAL,MPI_SUM, MASTER_TASK,MPI_COMM_WORLD); stacktop[0] = stacktop[1]; } #endif break; case AVG_: stacktop -= 4; /* erase locals */ #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (aggregate_depth==0) ) { MPI_Reduce(stacktop,stacktop+2,2,MPI_REAL,MPI_SUM, MASTER_TASK,MPI_COMM_WORLD); stacktop[0] = stacktop[2]; stacktop[1] = stacktop[3]; } #endif if ( stacktop[0] > 0.0 ) stacktop[0] = stacktop[1]/stacktop[0]; /* else leave avg as 0 */ break; case LOGHISTOGRAM_: case HISTOGRAM_: { int i; REAL binsize; int one_bin_flag = 0; bins = stacktop - (HISTBINS+1) + 1 - 3; /* find hi and lo */ hi = -MAXDOUBLE; lo = MAXDOUBLE; for ( i = 0 ; i < histo_count ; i++ ) { if ( histo_data[i] < lo ) if ( (node->op1.aggrtype == HISTOGRAM_) || ( histo_data[i] > 0.0) ) lo = histo_data[i]; if ( histo_data[i] > hi ) hi = histo_data[i]; } #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (aggregate_depth==0)) { /* synchronize max-min values */ DOUBLE localhi = hi,locallo = lo; DOUBLE globalhi,globallo; MPI_Allreduce(&localhi,&globalhi,1,MPI_REAL,MPI_MAX, MPI_COMM_WORLD); hi = globalhi; MPI_Allreduce(&locallo,&globallo,1,MPI_REAL,MPI_MIN, MPI_COMM_WORLD); lo = globallo; } #endif if (node->op1.aggrtype == HISTOGRAM_ ) { if ( hi == lo ) { binsize = 1.0; /* will put everything in first bin */ one_bin_flag = 1; } else binsize = (hi-lo)/HISTBINS;/* binsize */ } else /* first bin for 0 values for log histogram */ { if ( hi <= 0.0 ) binsize = 0.0; else { hi = log(hi); if ( lo <= 0.0 ) { binsize = 1.0; lo = hi-(HISTBINS-1); } else { lo = log(lo); if ( hi == lo ) { binsize = 1.0; one_bin_flag = 1; } else binsize = (hi-lo)/(HISTBINS-1); } } } binsize *= 1+10*machine_eps; /* for rounding margin */ if ( lo != 0.0 ) lo -= 10*machine_eps*binsize; /* construct histogram */ for ( i = 0 ; i < histo_count ; i++ ) { val = histo_data[i]; if ( !is_finite(val) ) k = HISTBINS; /* NaN */ else if ( node->op1.aggrtype == HISTOGRAM_ ) k = (int)((val-lo)/binsize); else if ( val <= 0.0 ) k = 0; else { val = log(val); k = (int)((val-lo)/binsize)+1; } bins[k] += 1.0; } #ifdef MPI_EVOLVER if ( mpi_subtask_command_flag && (aggregate_depth==0)) { /* accumulate histogram data on master */ REAL recvbuf[HISTBINS+1]; MPI_Reduce(bins,recvbuf,HISTBINS+1,MPI_REAL,MPI_SUM, MASTER_TASK, MPI_COMM_WORLD); if ( this_task == MASTER_TASK ) for ( k = 0, histo_count = 0 ; k <= HISTBINS ; k++ ) { bins[k] = recvbuf[k]; histo_count += (int)bins[k]; } } #endif /* print histogram */ #ifdef MPI_EVOLVER if ( (this_task == MASTER_TASK) || !mpi_subtask_command_flag ) #endif { if ( histo_count == 0 ) { outstring("No qualifying values.\n"); stacktop -= HISTBINS+1 + 3 + 3; break; } if ( one_bin_flag ) { if (node->op1.aggrtype == HISTOGRAM_ ) sprintf(msg,"%10.5g - %10.5g %d\n", (DOUBLE)hi,(DOUBLE)hi,histo_count); else sprintf(msg,"%10.5g - %10.5g %d\n", (DOUBLE)exp(hi),(DOUBLE)exp(hi),histo_count); outstring(msg); stacktop -= HISTBINS+1 + 3 + 3; break; } if (node->op1.aggrtype == HISTOGRAM_ ) for ( n = 0 ; n < HISTBINS ; n++ ) { sprintf(msg,"%10.5g - %10.5g %d\n", (DOUBLE)(lo + n*binsize),(DOUBLE)(lo + (n+1)*binsize), (int)bins[n]); outstring(msg); } else /* log histogram */ { int zeroes = (int)bins[0]; if ( zeroes ) { sprintf(msg," <= 0.0 %d\n",zeroes); outstring(msg); } if ( hi > 0 ) for ( n = 1 ; n < HISTBINS ; n++ ) { sprintf(msg,"%10.5g - %10.5g %d\n", (DOUBLE)(exp(lo + (n-1)*binsize)), (DOUBLE)(exp(lo + n*binsize)), (int)bins[n]); outstring(msg); } } if ( bins[HISTBINS] > 0.0 ) { sprintf(msg,"NaN %d\n",(int)bins[HISTBINS]); outstring(msg); } } temp_free((char*)histo_data); stacktop -= HISTBINS+1 + 3 + 3; /* erase locals */ mpi_subtask_command_flag = 0; break; } case LIST_: stacktop -= 3; /* erase locals */ break; case SET_COLOR_: case SET_FRONTCOLOR_: case SET_BACKCOLOR_: case SET_OPACITY_: stacktop -= 3; /* erase locals */ update_display_flag = 1; break; case SET_FIXED_: case SET_NO_REFINE_: case SET_NO_DISPLAY_: case SET_DENSITY_: case UNSET_DENSITY_: case SET_BARE_: case SET_CONSTRAINT_: case UNSET_CONSTRAINT_: case SET_CONSTRAINT_NAME: case UNSET_CONSTRAINT_NAME: case SET_VOLUME_: case SET_PRESSURE_: case SET_NONCONTENT_: case UNSET_BOUNDARY_: case UNSET_BOUNDARY_NAME: case UNSET_FIXED_: case UNSET_PRESSURE_: case UNSET_VOLUME_: case SET_NAMED_QUANTITY_: case UNSET_NO_REFINE_: case UNSET_BARE_: case UNSET_NAMED_QUANTITY_: case UNSET_NO_DISPLAY_: case SET_METHOD_INSTANCE_: case UNSET_METHOD_INSTANCE_: case UNSET_TRIPLE_PT_: case UNSET_TETRA_PT_: case UNSET_NONCONTENT_: case SET_BOUNDARY_: case SET_HIT_PARTNER_: case UNSET_HIT_PARTNER_: stacktop -= 3; /* erase locals */ recalc_flag = 1; break; case SET_FRONTBODY_: case SET_BACKBODY_: case UNSET_FRONTBODY_: case UNSET_BACKBODY_: case UNSET_FACET_BODY_: /*make_bfacet_lists(); */ /* so lists legal */ stacktop -= 3; /* erase locals */ recalc_flag = 1; break; case REFINE_: stacktop -= 3; /* erase locals */ #ifdef MPI_EVOLVER if ( (this_task != MASTER_TASK) && mpi_subtask_command_flag ) mpi_task_edge_refine_wrapup(); #endif break; case DISSOLVE_: stacktop -= 3; /* erase locals */ break; case DELETE_: stacktop -= 3; /* erase locals */ #ifdef MPI_EVOLVER if ( (this_task != MASTER_TASK) && mpi_subtask_command_flag ) mpi_task_delete_wrapup(); #endif break; case FIX_: stacktop -= 3; /* erase locals */ break; case UNFIX_: stacktop -= 3; /* erase locals */ break; default: stacktop -= 3; /* erase locals */ break; } break; /* end AGGREGATE_END */ /*********************/ /* attribute setting */ /* aggregate verbs */ /*********************/ case SET_FIXED_: set_attr(q_id,FIXED); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case UNSET_FIXED_: unset_attr(q_id,FIXED); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case SET_BARE_: set_attr(q_id,BARE_NAKED); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case UNSET_BARE_: unset_attr(q_id,BARE_NAKED); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case SET_NO_DISPLAY_: set_attr(q_id,NODISPLAY); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case UNSET_NO_DISPLAY_: unset_attr(q_id,NODISPLAY); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case SET_NONCONTENT_: if ( everything_quantities_flag ) kb_error(2484, "Changing NONCONTENT not implemented for everything_quantities mode yet.\n", RECOVERABLE); set_attr(q_id,NONCONTENT); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case UNSET_NONCONTENT_: if ( everything_quantities_flag ) kb_error(2595, "Changing NONCONTENT not implemented for everything_quantities mode yet.\n", RECOVERABLE); unset_attr(q_id,NONCONTENT); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case SET_HIT_PARTNER_: set_attr(q_id,HIT_PARTNER); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case UNSET_HIT_PARTNER_: unset_attr(q_id,HIT_PARTNER); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case SET_NO_REFINE_: set_attr(q_id,NO_REFINE); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case UNSET_NO_REFINE_: unset_attr(q_id,NO_REFINE); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case UNSET_PRESSURE_: unset_attr(q_id,PRESSURE); if ( everything_quantities_flag ) { struct gen_quant *q = GEN_QUANT(get_body_volquant(q_id)); q->modulus = 1; q->flags &= ~(Q_ENERGY|Q_FIXED|Q_CONSERVED); q->flags |= Q_INFO; } /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case UNSET_CONSTRAINT_: case UNSET_CONSTRAINT_NAME: if (node->type == UNSET_CONSTRAINT_) k = (int)*(stacktop--); else k = node->op3.connum; k &= CONMASK; /* so will ignore hit bit */ if ( k < web.maxcon ) switch ( id_type(q_id) ) { case VERTEX: unset_v_constraint_map(q_id,k); break; case EDGE: unset_e_constraint_map(q_id,k); break; case FACET: unset_f_constraint_map(q_id,k); break; default: sprintf(errmsg,"Bad element type for constraint.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1292,errmsg, RECOVERABLE); } /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case SET_BOUNDARY_: k = (int)*(stacktop--); /* boundary number */ bdry = web.boundaries+abs(k); if ( (abs(k) >= web.bdrymax) || !(bdry->attr&IN_USE) ) { sprintf(errmsg,"Boundary %d is not valid.\n",k); kb_error(2998,errmsg,RECOVERABLE); } if ( get_attr(q_id) & BOUNDARY ) { struct boundary *qbdry; switch ( id_type(q_id) ) { case VERTEX: qbdry = get_boundary(q_id); break; case EDGE: qbdry = get_edge_boundary(q_id); break; case FACET: qbdry = get_facet_boundary(q_id); break; default: qbdry = NULL; /* error message below */ } if ( qbdry == bdry ) break; sprintf(errmsg,"%s %s already on a different boundary.\n", typenames[id_type(q_id)],ELNAME(q_id)); kb_error(2999,errmsg,RECOVERABLE); } set_attr(q_id,BOUNDARY); if ( k < 0 ) set_attr(q_id,NEGBOUNDARY); switch(id_type(q_id)) { case VERTEX: { REAL *x = get_coord(q_id); int n; set_boundary_num(q_id,abs(k)); for ( n = 0 ; n < SDIM ; n++ ) if ( bdry->coordf[n]->root != NULL ) { PROF_EVAL_END(ex); x[n] = eval(bdry->coordf[n],get_param(q_id),q_id,NULL); PROF_EVAL_START(ex); } break; } case EDGE: set_edge_boundary_num(q_id,abs(k)); break; case FACET: set_facet_boundary_num(q_id,abs(k)); break; default: sprintf(errmsg,"Bad element type for boundary.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3000,errmsg, RECOVERABLE); } if ( bdry->attr & CON_ENERGY ) apply_method_num(q_id,bdry->energy_method); if ( bdry->attr & CON_CONTENT ) { if ( (web.representation == STRING) && (id_type(q_id) == VERTEX) ) fixup_vertex_content_meths(q_id); else if ( (web.representation == SOAPFILM) && (id_type(q_id) == EDGE) ) fixup_edge_content_meths(q_id); } /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case UNSET_BOUNDARY_: case UNSET_BOUNDARY_NAME: k = (node->type == UNSET_BOUNDARY_) ? (int)*(stacktop--) : node->op3.bdrynum; if ( k >= web.bdrymax ) break; bdry = web.boundaries+k; if ( get_attr(q_id) & BOUNDARY ) { switch ( id_type(q_id) ) { case VERTEX: if ( get_boundary(q_id) == bdry ) { set_boundary_num(q_id,0); unset_attr(q_id,BOUNDARY|HIT_PARTNER); } break; case EDGE: if ( get_edge_boundary(q_id) == bdry ) { set_edge_boundary_num(q_id,0); unset_attr(q_id,BOUNDARY); } break; case FACET: if ( get_facet_boundary(q_id) == bdry ) { set_facet_boundary_num(q_id,0); unset_attr(q_id,BOUNDARY); } break; default: sprintf(errmsg,"Bad element type for boundary.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1293,errmsg, RECOVERABLE); } if ( bdry->attr & CON_ENERGY ) unapply_method(q_id,bdry->energy_method); if ( bdry->attr & CON_CONTENT ) { if ( (web.representation == STRING) && (id_type(q_id) == VERTEX) ) fixup_vertex_content_meths(q_id); else if ( (web.representation == SOAPFILM) && (id_type(q_id) == EDGE) ) fixup_edge_content_meths(q_id); } } /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case UNSET_VOLUME_: case UNSET_TARGET_: if ( get_attr(q_id) & FIXEDVOL) { unset_attr(q_id,FIXEDVOL); if (everything_quantities_flag ) { struct gen_quant *q = GEN_QUANT(get_body_volquant(q_id)); q->target = 0.0; if ( !(q->flags & Q_INFO) ) { q->flags &= ~(Q_FIXED|Q_ENERGY|Q_CONSERVED); q->flags |= Q_INFO; } if ( web.pressure_flag ) { q = GEN_QUANT(get_body_ambquant(q_id)); q->flags &= ~(Q_FIXED|Q_ENERGY|Q_CONSERVED); q->flags |= Q_INFO; } } } /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case UNSET_FACET_BODY_: if ( id_type(q_id) != FACET ) { sprintf(errmsg,"Trying to unset body of non-facet.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1294,errmsg, RECOVERABLE); } set_facet_body(q_id,NULLID); set_facet_body(inverse_id(q_id),NULLID); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case UNSET_FRONTBODY_: case UNSET_BACKBODY_: set_body(node->type==UNSET_FRONTBODY_?q_id:inverse_id(q_id),NULLID); recalc_flag = 1; node += node->op1.skipsize - 1; /* back to start of loop */ break; case UNSET_DENSITY_: unset_attr(q_id,DENSITY); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case UNSET_TRIPLE_PT_: unset_attr(q_id,TRIPLE_PT); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case UNSET_TETRA_PT_: unset_attr(q_id,TETRA_PT); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case UNSET_AXIAL_POINT_: unset_attr(q_id,AXIAL_POINT); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case SET_DENSITY_: { REAL density = *(stacktop--); if ( density != 1.0 ) set_attr(q_id,DENSITY); switch ( node->op2.eltype ) { case EDGE: set_edge_density(q_id,density); break; case FACET: set_facet_density(q_id,density); break; case BODY: set_body_density(q_id,density); break; } /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ } break; case SET_VOLUME_: { REAL v = *(stacktop--); if ( get_attr(q_id) & PRESSURE ) { sprintf(errmsg,"Must unset body pressure before fixing target.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2071,errmsg, RECOVERABLE); } set_attr(q_id,FIXEDVOL); set_body_fixvol(q_id,v); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; } case SET_PRESSURE_: { REAL p = *(stacktop--); if ( get_attr(q_id) & FIXEDVOL ) { sprintf(errmsg,"Must unset body target before fixing pressure.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2521,errmsg, RECOVERABLE); } set_attr(q_id,PRESSURE); set_body_pressure(q_id,p); if ( everything_quantities_flag ) { struct gen_quant *q = GEN_QUANT(get_body_volquant(q_id)); q->modulus = -p; if ( !(q->flags & Q_ENERGY) ) { q->flags &= ~(Q_INFO|Q_FIXED|Q_CONSERVED); q->flags |= Q_ENERGY; } } /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; } case SET_OPACITY_: facet_alpha = *(stacktop)--; /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case SET_CONSTRAINT_: case SET_CONSTRAINT_NAME: { int con = (node->type==SET_CONSTRAINT_) ? (int)*(stacktop--) : node->op3.connum; if ( (con<0) || (con>=web.maxcon) || !(get_constraint(con)->attr & IN_USE)) { sprintf(errmsg,"Illegal constraint number: %d.\n",con); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1295,errmsg,RECOVERABLE); } if ( get_attr(q_id) & BOUNDARY ) { sprintf(errmsg, "Cannot set %s %s on a constraint since it is on a boundary.\n", typenames[node->op2.eltype],ELNAME(q_id)); kb_error(4002,errmsg,RECOVERABLE); } switch ( node->op2.eltype ) { case VERTEX: set_v_constraint_map(q_id,con); project_v_constr(q_id,ACTUAL_MOVE,RESET_ONESIDEDNESS); break; case EDGE: set_e_constraint_map(q_id,con); break; case FACET: set_f_constraint_map(q_id,con); break; } /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ } break; case SET_NAMED_QUANTITY_: { int qnum = (int)*(stacktop--); apply_quantity(q_id,qnum); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ } break; case UNSET_NAMED_QUANTITY_: { int qnum = (int)*(stacktop--); unapply_quantity(q_id,qnum); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ } break; case SET_METHOD_INSTANCE_: { int qnum = (int)*(stacktop--); apply_method_num(q_id,qnum); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ } break; case UNSET_METHOD_INSTANCE_: { int qnum = (int)*(stacktop--); unapply_method(q_id,qnum); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ } break; case SET_COLOR_: { int color = (int)*(stacktop--); switch ( node->op2.eltype ) { case EDGE: set_edge_color(q_id,color); break; case FACET: set_facet_color(q_id,color); break; } /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ update_display_flag = 1; } break; case SET_FRONTCOLOR_: { int color = (int)*(stacktop--); set_facet_frontcolor(q_id,color); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ update_display_flag = 1; } break; case SET_BACKCOLOR_: { int color = (int)*(stacktop--); set_facet_backcolor(q_id,color); /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ update_display_flag = 1; } break; case SET_TAG_: { int tag = (int)*(stacktop--); switch ( node->op2.eltype ) { case FACET: set_tag(q_id,tag); break; } /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ } break; case SET_COORD_: get_coord(q_id)[node->op2.coordnum] = *(stacktop--); if ( node->op2.coordnum <= SDIM ) recalc_flag = 1; /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case SET_PARAM_: if ( get_vattr(q_id) & BOUNDARY ) { struct boundary *boundary = get_boundary(q_id); REAL *param = get_param(q_id); REAL *xx = get_coord(q_id); if ( node->op2.coordnum > boundary->pcount ) { sprintf(errmsg,"Parameter number is %d; maximum is %d.\n", node->op2.coordnum,boundary->pcount); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1296,errmsg,RECOVERABLE); } param[node->op2.coordnum] = *(stacktop--); PROF_EVAL_END(ex); for ( k = 0 ; k < SDIM ; k++ ) xx[k] = eval(boundary->coordf[k],param,q_id,NULL); PROF_EVAL_START(ex); recalc_flag = 1; } else { /* probably just handy for storage */ REAL *param = get_param(q_id); param[node->op2.coordnum] = *(stacktop--); } /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; /************************/ /* numerical aggregates */ /************************/ case MAX_: { REAL *aggrptr; val = *(stacktop--); aggrptr = (REAL *)(stacktop - 3); if ( val > *aggrptr ) *aggrptr = val; /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ } break; case MIN_: { REAL *aggrptr; val = *(stacktop--); aggrptr = (REAL *)(stacktop - 3); if ( val < *aggrptr ) *aggrptr = val; /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ } break; case SUM_: stacktop--; stacktop[-3] += stacktop[1]; /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case AVG_: stacktop--; stacktop[-3] += stacktop[1]; /* sum */ stacktop[-4] += 1.0; /* count */ /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case COUNT_: stacktop--; stacktop[-3] += 1.0; /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; case HISTOGRAM_: case LOGHISTOGRAM_: if ( histo_count >= histo_max ) { histo_data = (REAL*)temp_realloc((char*)histo_data, 2*histo_max*sizeof(REAL)); histo_max *= 2; } histo_data[histo_count++] = *(stacktop--); node += node->op1.skipsize - 1; /* back to start of loop */ break; case FOREACH_: case SET_ATTRIBUTE_LOOP_: node += node->op1.skipsize - 1; /* back to start of loop */ break; case ARRAY_HEAD_: break; /* let indices accumulate */ case ARRAYASSIGN: { struct array *a; REAL value=0.0,rhs; int i,offset; void *lvalue; struct global *g = globals(node->op2.name_id); a = get_name_arrayptr(node->op2.name_id,localstack,localbase); rhs = *(stacktop--); for ( i = 0 ; i < a->dim ; i++ ) { int k = (int)stacktop[i+1-a->dim]; if ( k < 1 ) { sprintf(errmsg, "Array index %d of array %s is %d. Indexes start at 1.\n", i+1,g->name,k); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3011,errmsg,RECOVERABLE); } if ( k > a->sizes[i] && (!(node->flags & IS_VIRTUAL_ATTR) ||(k > SDIM)) ) { sprintf(errmsg,"Array index %d of array %s is %d; exceeds bound of %d.\n", i+1,g->name,k,a->sizes[i]); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3012,errmsg,RECOVERABLE); } } for ( i = 1, offset = (int)stacktop[1-a->dim]-1 ; i < a->dim ; i++ ) { offset *= a->sizes[i]; offset += (int)stacktop[i+1-a->dim]-1; /* 1-based indexing */ } stacktop -= a->dim; lvalue = ((char *)a) + a->datastart + offset*a->itemsize; switch ( a->datatype ) { case REAL_TYPE: value = *(REAL*)(lvalue); break; case INTEGER_TYPE: value = *(int*)(lvalue); break; case UINT_TYPE: value = *(unsigned int*)(lvalue); break; case SHORT_TYPE: value = *(short int*)(lvalue); break; case USHORT_TYPE: value = *(unsigned short int*)(lvalue); break; case LONG_TYPE: value = *(long int*)(lvalue); break; case ULONG_TYPE: value = *(unsigned long int*)(lvalue); break; case CHAR_TYPE: value = *(char*)(lvalue); break; case UCHAR_TYPE: value = *(unsigned char*)(lvalue); break; case PTR_TYPE: value = (unsigned long int)*(char**)(lvalue); break; case VERTEX_TYPE: case EDGE_TYPE: case FACET_TYPE: case BODY_TYPE: case FACETEDGE_TYPE: case ELEMENTID_TYPE: break; default: value = *(int*)(lvalue); break; } switch ( node->op1.assigntype ) { case ASSIGN_: value = rhs; break; case PLUSASSIGN_: value += rhs; break; case SUBASSIGN_: value -= rhs; break; case MULTASSIGN_: value *= rhs; break; case DIVASSIGN_: if( rhs == 0.0 ) { sprintf(errmsg,"Division by zero.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3015,errmsg,RECOVERABLE); } value /= rhs; break; } switch ( a->datatype ) { case REAL_TYPE: *(REAL*)(lvalue) = value; break; case INTEGER_TYPE: *(int*)(lvalue) = (int)value; break; case UINT_TYPE: *(unsigned int*)(lvalue) = (unsigned int)value; break; case CHAR_TYPE: *(char*)(lvalue) = (char)value; break; case UCHAR_TYPE: *(unsigned char*)(lvalue) = (unsigned char)value; break; case SHORT_TYPE: *(short*)(lvalue) = (short)value; break; case USHORT_TYPE: *(unsigned short*)(lvalue) = (unsigned short)value; break; case LONG_TYPE: *(long*)(lvalue) = (long)value; break; case ULONG_TYPE: *(unsigned long*)(lvalue) = (unsigned long)value; break; case VERTEX_TYPE: case EDGE_TYPE: case FACET_TYPE: case BODY_TYPE: case FACETEDGE_TYPE: case ELEMENTID_TYPE: break; default: *(int*)(lvalue) = (int)value; break; } if ( g->flags & RECALC_PARAMETER ) recalc_flag = 1; } break; case ARRAYEVAL: { struct global *g = globals(node->op2.name_id); struct array *a; REAL value=0.0; int i,offset; void *lvalue; a = get_name_arrayptr(node->op2.name_id,localstack,localbase); for ( i = 0 ; i < a->dim ; i++ ) { int k = (int)stacktop[i+1-a->dim]; if ( k < 1 ) { sprintf(errmsg, "Array index %d of array %s is %d. Indexes start at 1.\n", i+1,g->name,k); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2047,errmsg,RECOVERABLE); } if ( k > a->sizes[i] ) { sprintf(errmsg, "Array index %d of array %s is %d; exceeds bound of %d.\n", i+1,g->name,k,a->sizes[i]); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2048,errmsg,RECOVERABLE); } } for ( i = 1, offset = (int)stacktop[1-a->dim]-1 ; i < a->dim ; i++ ) { offset *= a->sizes[i]; offset += (int)stacktop[i+1-a->dim]-1; /* 1-based indexing */ } stacktop -= a->dim; lvalue = ((char *)a) + a->datastart + offset*a->itemsize; switch ( a->datatype ) { case REAL_TYPE: value = *(REAL*)(lvalue); break; case INTEGER_TYPE: value = *(int*)(lvalue); break; case UINT_TYPE: value = *(unsigned int*)(lvalue); break; case CHAR_TYPE: value = *(char*)(lvalue); break; case UCHAR_TYPE: value = *(unsigned char*)(lvalue); break; case SHORT_TYPE: value = *(short int*)(lvalue); break; case USHORT_TYPE: value = *(unsigned short int*)(lvalue); break; case LONG_TYPE: value = *(long*)(lvalue); break; case ULONG_TYPE: value = *(unsigned long*)(lvalue); break; case PTR_TYPE: value = (unsigned long)*(char**)(lvalue); break; case VERTEX_TYPE: case EDGE_TYPE: case FACET_TYPE: case BODY_TYPE: case FACETEDGE_TYPE: case ELEMENTID_TYPE: value = (int)*(element_id*)(lvalue); break; default: value = *(int*)(lvalue); break; } *(++stacktop) = value; } break; /* whole array syntax */ case ARRAYIDENT_: /* push datastart for array */ { struct global *glvalue = globals(node->op2.name_id); struct array *alvalue; alvalue = get_name_arrayptr(node->op2.name_id,localstack,localbase); if ( glvalue->flags & FIXED_SIZE_ARRAY ) *(REAL**)(++stacktop) = localstack + glvalue->attr.arrayptr->datastart; else *(char**)(++stacktop) = (char*)alvalue + alvalue->datastart; break; } case ATTRIB_LVALUE_: /* push datastart for attribute array */ { element_id id; n = node->op2.name_id & GLOBMASK; /* attribute number */ if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *(char**)(++stacktop) = (char*)get_extra(id,n); } break; case ARRAY_VERTEX_NORMAL_: case ARRAY_EDGE_VECTOR_: case ARRAY_FACET_NORMAL_: { element_id id; REAL *datastart = get_localp(node->op3.localnum); *(REAL**)(++stacktop) = datastart; if ( node->flags & IS_RVALUE ) { if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; switch ( node->type ) { case ARRAY_VERTEX_NORMAL_: { MAT2D(normal,MAXCOORD,MAXCOORD); REAL mag; int normcount; normcount = new_calc_vertex_normal(id,normal); project_vertex_normals(id,normal,normcount); mag = sqrt(SDIM_dot(normal[0],normal[0])); if ( mag == 0.0 ) mag = 1; for ( i = 0 ; i < SDIM ; i++ ) datastart[i] = normal[0][i]/mag; break; } case ARRAY_EDGE_VECTOR_: get_edge_side(id,datastart); break; case ARRAY_FACET_NORMAL_: get_facet_normal(id,datastart); break; } } } break; case ARRAY_LVALUE_INDEXED_: break; case ARRAY_RVALUE_ : break; case DOT_: /* dot product */ { struct array *a,*b; int name1 = node->op2.name_id; int name2 = node->op3.name_id; REAL *datastart1,*datastart2; REAL sum; int counta,countb,count; if ( node[node->left].flags & IS_VIRTUAL_ATTR ) counta = SDIM; else { a = get_name_arrayptr(name1,NULL,localbase); counta = a->datacount; } if ( node[node->right].flags & IS_VIRTUAL_ATTR ) countb = SDIM; else { b = get_name_arrayptr(name2,NULL,localbase); countb = b->datacount; } count = (counta < countb) ? counta : countb; datastart1 = *(REAL**)(stacktop--); datastart2 = *(REAL**)(stacktop--); for ( sum = 0.0, i = 0 ; i < count ; i++ ) sum += datastart1[i]*datastart2[i]; *(++stacktop) = sum; break; } case ARRAY_EVAL_: /* rexpr: arraylvalue indexset */ { /* use info on stack to push value of array element. stack: datastart index-values -> rexpr */ struct array *a; REAL value=0.0; int i,offset; void *lvalue; char *datastart; a = get_name_arrayptr(node->op2.name_id,localstack,localbase); for ( i = 0 ; i < a->dim ; i++ ) { int k = (int)stacktop[i+1-a->dim]; if ( k < 1 ) { sprintf(errmsg, "Array index %d of array %s is %d. Indexes start at 1.\n", i+1,get_name_name(node->op2.name_id,localbase),k); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3022,errmsg,RECOVERABLE); } if ( k > a->sizes[i] && (!(node->flags & IS_VIRTUAL_ATTR) ||(k > SDIM)) ) {sprintf(errmsg,"Array index %d of array %s is %d; exceeds bound of %d.\n", i+1,get_name_name(node->op2.name_id,localbase),k,a->sizes[i]); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3023,errmsg,RECOVERABLE); } } for ( i = 1, offset = (int)stacktop[1-a->dim]-1 ; i < a->dim ; i++ ) { offset *= a->sizes[i]; offset += (int)stacktop[i+1-a->dim]-1; /* 1-based indexing */ } stacktop -= a->dim; datastart = *(char**)(stacktop--); lvalue = datastart + offset*a->itemsize; switch ( a->datatype ) { case REAL_TYPE: value = *(REAL*)(lvalue); break; case INTEGER_TYPE: value = *(int*)(lvalue); break; case UINT_TYPE: value = *(unsigned int*)(lvalue); break; case SHORT_TYPE: value = *(short int*)(lvalue); break; case USHORT_TYPE: value = *(unsigned short int*)(lvalue); break; case LONG_TYPE: value = *(long int*)(lvalue); break; case ULONG_TYPE: value = *(unsigned long int*)(lvalue); break; case CHAR_TYPE: value = *(char*)(lvalue); break; case UCHAR_TYPE: value = *(unsigned char*)(lvalue); break; case PTR_TYPE: value = (unsigned long int)*(char**)(lvalue); break; case VERTEX_TYPE: case EDGE_TYPE: case FACET_TYPE: case BODY_TYPE: case FACETEDGE_TYPE: case ELEMENTID_TYPE: break; default: value = *(int*)(lvalue); break; } *(++stacktop) = value; break; } case ARRAY_ASSIGNOP_SINGLE_: { /* use info on stack to assign value to array element. stack: datastart index-values rexpr */ struct array *a; REAL value=0.0,rhs; int i,offset; void *lvalue; char *datastart; a = get_name_arrayptr(node->op2.name_id,localstack,localbase); rhs = *(stacktop--); for ( i = 0 ; i < a->dim ; i++ ) { int k = (int)stacktop[i+1-a->dim]; if ( k < 1 ) { sprintf(errmsg, "Array index %d of array %s is %d. Indexes start at 1.\n", i+1,get_name_name(node->op2.name_id,localbase),k); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2045,errmsg,RECOVERABLE); } if ( k > a->sizes[i] && (!(node->flags & IS_VIRTUAL_ATTR) ||(k > SDIM)) ) { sprintf(errmsg,"Array index %d of array %s is %d; exceeds bound of %d.\n", i+1,get_name_name(node->op2.name_id,localbase),k,a->sizes[i]); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2046,errmsg,RECOVERABLE); } } for ( i = 1, offset = (int)stacktop[1-a->dim]-1 ; i < a->dim ; i++ ) { offset *= a->sizes[i]; offset += (int)stacktop[i+1-a->dim]-1; /* 1-based indexing */ } stacktop -= a->dim; datastart = *(char**)(stacktop--); lvalue = datastart + offset*a->itemsize; switch ( a->datatype ) { case REAL_TYPE: value = *(REAL*)(lvalue); break; case INTEGER_TYPE: value = *(int*)(lvalue); break; case UINT_TYPE: value = *(unsigned int*)(lvalue); break; case SHORT_TYPE: value = *(short int*)(lvalue); break; case USHORT_TYPE: value = *(unsigned short int*)(lvalue); break; case LONG_TYPE: value = *(long int*)(lvalue); break; case ULONG_TYPE: value = *(unsigned long int*)(lvalue); break; case CHAR_TYPE: value = *(char*)(lvalue); break; case UCHAR_TYPE: value = *(unsigned char*)(lvalue); break; case PTR_TYPE: value = (unsigned long int)*(char**)(lvalue); break; case VERTEX_TYPE: case EDGE_TYPE: case FACET_TYPE: case BODY_TYPE: case FACETEDGE_TYPE: case ELEMENTID_TYPE: break; default: value = *(int*)(lvalue); break; } switch ( node->op1.assigntype ) { case ASSIGN_: value = rhs; break; case PLUSASSIGN_: value += rhs; break; case SUBASSIGN_: value -= rhs; break; case MULTASSIGN_: value *= rhs; break; case DIVASSIGN_: if( rhs == 0.0 ) { sprintf(errmsg,"Division by zero.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2564,errmsg,RECOVERABLE); } value /= rhs; break; } switch ( a->datatype ) { case REAL_TYPE: *(REAL*)(lvalue) = value; break; case INTEGER_TYPE: *(int*)(lvalue) = (int)value; break; case UINT_TYPE: *(unsigned int*)(lvalue) = (unsigned int)value; break; case CHAR_TYPE: *(char*)(lvalue) = (char)value; break; case UCHAR_TYPE: *(unsigned char*)(lvalue) = (unsigned char)value; break; case SHORT_TYPE: *(short*)(lvalue) = (short)value; break; case USHORT_TYPE: *(unsigned short*)(lvalue) = (unsigned short)value; break; case LONG_TYPE: *(long*)(lvalue) = (long)value; break; case ULONG_TYPE: *(unsigned long*)(lvalue) = (unsigned long)value; break; case VERTEX_TYPE: case EDGE_TYPE: case FACET_TYPE: case BODY_TYPE: case FACETEDGE_TYPE: case ELEMENTID_TYPE: break; default: *(int*)(lvalue) = (int)value; break; } if ( node->flags & RECALC_NODE ) recalc_flag = 1; break; } case ARRAY_ASSIGNOP_VERTEX_NORMAL_: { MAT2D(normal,MAXCOORD,MAXCOORD); REAL mag; int normcount; element_id id; /* pop datastarts off stack */ REAL *q = *(REAL**)(stacktop--); /* not actually used, just pops stack */ REAL *p = *(REAL**)(stacktop--); struct array *alvalue = get_name_arrayptr(node->op2.name_id,localstack,localbase); int count = ( alvalue->datacount < SDIM ) ? alvalue->datacount : SDIM; if ( node[node->right].op1.localnum ) id = *(element_id*)get_localp(node[node->right].op1.localnum); else id = q_id; normcount = new_calc_vertex_normal(id,normal); project_vertex_normals(id,normal,normcount); mag = sqrt(SDIM_dot(normal[0],normal[0])); if ( mag == 0.0 ) mag = 1; switch ( node->op1.assigntype ) { case ASSIGN_: for ( i = 0 ; i < count ; i++ ) p[i] = normal[0][i]/mag; break; case PLUSASSIGN_: for ( i = 0 ; i < count ; i++ ) p[i] += normal[0][i]/mag; break; case SUBASSIGN_: for ( i = 0 ; i < count ; i++ ) p[i] -= normal[0][i]/mag; break; case MULTASSIGN_: for ( i = 0 ; i < count ; i++ ) p[i] *= normal[0][i]/mag; break; case DIVASSIGN_: for ( i = 0 ; i < count ; i++ ) p[i] /= normal[0][i]/mag; break; } break; } case ARRAY_ASSIGNOP_EDGE_VECTOR_: { /* pop datastarts off stack */ element_id id; REAL *q = *(REAL**)(stacktop--); /* not actually used, just pops stack */ REAL *p = *(REAL**)(stacktop--); REAL v[MAXCOORD]; struct array *alvalue = get_name_arrayptr(node->op2.name_id,localstack,localbase); int count = ( alvalue->datacount < SDIM ) ? alvalue->datacount : SDIM; if ( node[node->right].op1.localnum ) id = *(element_id*)get_localp(node[node->right].op1.localnum); else id = q_id; get_edge_side(id,v); switch ( node->op1.assigntype ) { case ASSIGN_: for ( i = 0 ; i < count ; i++ ) p[i] = v[i]; break; case PLUSASSIGN_: for ( i = 0 ; i < count ; i++ ) p[i] += v[i]; break; case SUBASSIGN_: for ( i = 0 ; i < count ; i++ ) p[i] -= v[i]; break; case MULTASSIGN_: for ( i = 0 ; i < count ; i++ ) p[i] *= v[i]; break; case DIVASSIGN_: for ( i = 0 ; i < count ; i++ ) p[i] /= v[i]; break; } break; } case ARRAY_ASSIGNOP_FACET_NORMAL_: { /* pop datastarts off stack */ element_id id; REAL *q = *((REAL**)stacktop--); /* not actually used, just pops stack */ REAL *p = *(REAL**)(stacktop--); struct array *alvalue = get_name_arrayptr(node->op2.name_id,localstack,localbase); int count = ( alvalue->datacount < SDIM ) ? alvalue->datacount : SDIM; REAL v[MAXCOORD]; if ( node[node->right].op1.localnum ) id = *(element_id*)get_localp(node[node->right].op1.localnum); else id = q_id; get_facet_normal(id,v); switch ( node->op1.assigntype ) { case ASSIGN_: for ( i = 0 ; i < count ; i++ ) p[i] = v[i]; break; case PLUSASSIGN_: for ( i = 0 ; i < count ; i++ ) p[i] += v[i]; break; case SUBASSIGN_: for ( i = 0 ; i < count ; i++ ) p[i] -= v[i]; break; case MULTASSIGN_: for ( i = 0 ; i < count ; i++ ) p[i] *= v[i]; break; case DIVASSIGN_: for ( i = 0 ; i < count ; i++ ) p[i] /= v[i]; break; } break; } case ARRAY_ASSIGNOP_ARRAY_: { struct array *alvalue= get_name_arrayptr(node->op2.name_id,localstack,localbase); struct array *arvalue = get_name_arrayptr(node->op3.name_id,localstack,localbase); int inx[100]; /* keep track of indices */ char *p,*q; int j,k; char *pspots[100],*qspots[100]; int pstride[100],qstride[100]; int minsize[100],lastsize; /* Was checked in parser that the arrays have the same number of indices, but might be different sizes. So do intersection. */ /* pop datastarts off stack */ q = *(char**)(stacktop--); p = *(char**)(stacktop--); for ( i = 0 ; i < arvalue->dim ; i++ ) { inx[i] = 0; minsize[i] = arvalue->sizes[i] < alvalue->sizes[i] ? arvalue->sizes[i] : alvalue->sizes[i] ; pspots[i] = p; qspots[i] = q; } if ( alvalue->dim >= 2 ) { pstride[alvalue->dim-2] = alvalue->sizes[alvalue->dim-1]*alvalue->itemsize; qstride[arvalue->dim-2] = arvalue->sizes[arvalue->dim-1]*arvalue->itemsize; for ( i = arvalue->dim - 3 ; i >= 0 ; i-- ) { pstride[i] = pstride[i+1]*alvalue->sizes[i+1]; qstride[i] = qstride[i+1]*arvalue->sizes[i+1]; } } lastsize = minsize[arvalue->dim-1]; do { /* do a row */ switch ( arvalue->datatype ) { case REAL_TYPE: { REAL *pp = (REAL*)p; REAL *qq = (REAL*)q; switch ( node->op1.assigntype ) { case ASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) = *(qq++); break; case PLUSASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) += *(qq++); break; case SUBASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) -= *(qq++); break; case MULTASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) *= *(qq++); break; case DIVASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) /= *(qq++); break; } } break; /* end REAL_TYPE */ case INTEGER_TYPE: { int *pp = (int*)p; int *qq = (int*)q; switch ( node->op1.assigntype ) { case ASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) = *(qq++); break; case PLUSASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) += *(qq++); break; case SUBASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) -= *(qq++); break; case MULTASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) *= *(qq++); break; case DIVASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) /= *(qq++); break; } break; /* end INTEGER_TYPE */ } } /* increment pointers */ for ( j = arvalue->dim-2 ; j >= 0 ; j-- ) { pspots[j] += pstride[j]; qspots[j] += qstride[j]; if ( ++inx[j] < minsize[j] ) { p = pspots[j]; q = qspots[j]; for ( k = j+1 ; k < alvalue->dim-1 ; k++ ) { pspots[k] = p; qspots[k] = q; } break; } inx[j] = 0 ; } } while ( j >= 0 ); if ( node->flags & RECALC_NODE ) recalc_flag = 1; break; } /* end ARRAY_ASSIGNOP_ARRAY_ */ case ARRAY_ASSIGNOP_SCALAR_: { struct array *alvalue= get_name_arrayptr(node->op2.name_id,localstack,localbase); REAL scalar = *(stacktop--); char *p = *(char**)(stacktop--); int lastsize = alvalue->datacount; switch ( alvalue->datatype ) { case REAL_TYPE: { REAL *pp = (REAL*)p; switch ( node->op1.assigntype ) { case ASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) = scalar; break; case PLUSASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) += scalar; break; case SUBASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) -= scalar; break; case MULTASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) *= scalar; break; case DIVASSIGN_: if ( scalar == 0.0 ) { sprintf(errmsg,"Division by zero.\n"); sprintf(errmsg+strlen(errmsg), "(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(4677,errmsg,RECOVERABLE); } for ( i = 0 ; i < lastsize ; i++ ) *(pp++) /= scalar; break; } break; /* end REAL_TYPE */ } case INTEGER_TYPE: { int intscalar = (int)floor(scalar); int *pp = (int*)p; switch ( node->op1.assigntype ) { case ASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) = intscalar; break; case PLUSASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) += intscalar; break; case SUBASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) -= intscalar; break; case MULTASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) = (int)(floor(*(int*)p) * scalar); break; case DIVASSIGN_: if ( scalar == 0.0 ) kb_error(4678,"Division by zero.\n",RECOVERABLE); for ( i = 0 ; i < lastsize ; i++ ) *(pp++) /= (int)floor((*(int*)p) / scalar); break; } } break; /* end INTEGER_TYPE */ } if ( node->flags & RECALC_NODE ) recalc_flag = 1; } /* end ARRAY_ASSIGNOP_SCALAR */ break; case ARRAY_ASSIGNOP_S_X_A_: { struct array *alvalue= get_name_arrayptr(node->op2.name_id,localstack,localbase); struct array *arvalue= get_name_arrayptr(node->op3.name_id,localstack,localbase); int inx[100]; /* keep track of indices */ char *p,*q; int j,k; char *pspots[100],*qspots[100]; int pstride[100],qstride[100]; int minsize[100],lastsize; REAL scalar; /* Was checked in parser that the arrays have the same number of indices, but might be different sizes. So do intersection. */ /* pop datastarts off stack */ q = *(char**)(stacktop--); scalar = *(stacktop--); p = *(char**)(stacktop--); for ( i = 0 ; i < arvalue->dim ; i++ ) { inx[i] = 0; minsize[i] = arvalue->sizes[i] < alvalue->sizes[i] ? arvalue->sizes[i] : alvalue->sizes[i] ; pspots[i] = p; qspots[i] = q; } if ( alvalue->dim >= 2 ) { pstride[alvalue->dim-2] = alvalue->sizes[alvalue->dim-1]*alvalue->itemsize; qstride[arvalue->dim-2] = arvalue->sizes[arvalue->dim-1]*arvalue->itemsize; for ( i = arvalue->dim - 3 ; i >= 0 ; i-- ) { pstride[i] = pstride[i+1]*alvalue->sizes[i+1]; qstride[i] = qstride[i+1]*arvalue->sizes[i+1]; } } lastsize = minsize[arvalue->dim-1]; p = pspots[0]; q = qspots[0]; do { /* do a row */ switch ( arvalue->datatype ) { case REAL_TYPE: { REAL *pp = (REAL*)p; REAL *qq = (REAL*)q; switch ( node->op1.assigntype ) { case ASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) = *(qq++)*scalar; break; case PLUSASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) += *(qq++)*scalar; break; case SUBASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) -= *(qq++)*scalar; break; case MULTASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) *= *(qq++)*scalar; break; case DIVASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) /= *(qq++)*scalar; break; } break; /* end REAL_TYPE */ } case INTEGER_TYPE: { int *pp = (int*)p; int *qq = (int*)q; switch ( node->op1.assigntype ) { case ASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) = (int)floor(*(qq++)*scalar); break; case PLUSASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) += (int)floor(*(qq++)*scalar); break; case SUBASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) -= (int)floor(*(qq++)*scalar); break; case MULTASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) *= (int)floor(*(qq++)*scalar); break; case DIVASSIGN_: if ( scalar == 0.0 ) kb_error(4679,"Division by zero.\n",RECOVERABLE); for ( i = 0 ; i < lastsize ; i++ ) *(pp++) /= (int)floor(*(qq++)*scalar); break; } break; /* end INTEGER_TYPE */ } } /* increment pointers */ for ( j = arvalue->dim-2 ; j >= 0 ; j-- ) { pspots[j] += pstride[j]; qspots[j] += qstride[j]; if ( ++inx[j] < minsize[j] ) { p = pspots[j]; q = qspots[j]; for ( k = j+1 ; k < alvalue->dim-1 ; k++ ) { pspots[k] = p; qspots[k] = q; } break; } inx[j] = 0 ; } } while ( j >= 0 ); if ( node->flags & RECALC_NODE ) recalc_flag = 1; break; } /* end ARRAY_ASSIGNOP_S_X_A_ */ case ARRAY_ASSIGNOP_A_P_A_: { struct array *alvalue= get_name_arrayptr(node->op2.name_id,localstack,localbase); struct array *arvalue= get_name_arrayptr(node->op3.name_id,localstack,localbase); struct array *asvalue= get_name_arrayptr(node->op4.name_id,localstack,localbase); int inx[100]; /* keep track of indices */ char *p,*q,*s; int j,k; char *pspots[100],*qspots[100],*sspots[100]; int pstride[100],qstride[100],sstride[100]; int minsize[100],lastsize; /* Was checked in parser that the arrays have the same number of indices, but might be different sizes. So do intersection. */ /* pop datastarts off stack */ s = *(char**)(stacktop--); q = *(char**)(stacktop--); p = *(char**)(stacktop--); for ( i = 0 ; i < arvalue->dim ; i++ ) { inx[i] = 0; minsize[i] = arvalue->sizes[i] < alvalue->sizes[i] ? arvalue->sizes[i] : alvalue->sizes[i] ; if ( minsize[i] > asvalue->sizes[i] ) minsize[i] = asvalue->sizes[i]; pspots[i] = p; qspots[i] = q; sspots[i] = s; } if ( alvalue->dim >= 2 ) { pstride[alvalue->dim-2] = alvalue->sizes[alvalue->dim-1]*alvalue->itemsize; qstride[arvalue->dim-2] = arvalue->sizes[arvalue->dim-1]*arvalue->itemsize; sstride[asvalue->dim-2] = asvalue->sizes[asvalue->dim-1]*asvalue->itemsize; for ( i = arvalue->dim - 3 ; i >= 0 ; i-- ) { pstride[i] = pstride[i+1]*alvalue->sizes[i+1]; qstride[i] = qstride[i+1]*arvalue->sizes[i+1]; sstride[i] = sstride[i+1]*asvalue->sizes[i+1]; } } lastsize = minsize[arvalue->dim-1]; p = pspots[0]; q = qspots[0]; s = sspots[0]; do { /* do a row */ switch ( arvalue->datatype ) { case REAL_TYPE: { REAL *pp = (REAL*)p; REAL *qq = (REAL*)q; REAL *ss = (REAL*)s; switch ( node->op1.assigntype ) { case ASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) = *(qq++) + *(ss++); break; case PLUSASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) += *(qq++) + *(ss++); break; case SUBASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) -= *(qq++) + *(ss++); break; case MULTASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) *= *(qq++) + *(ss++); break; case DIVASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) /= *(qq++) + *(ss++); break; } break; /* end REAL_TYPE */ } case INTEGER_TYPE: { int *pp = (int*)p; int *qq = (int*)q; int *ss = (int*)s; switch ( node->op1.assigntype ) { case ASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) = *(qq++) + *(ss++); break; case PLUSASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) += *(qq++) + *(ss++); break; case SUBASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) -= *(qq++) + *(ss++); break; case MULTASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) *= *(qq++) + *(ss++); break; case DIVASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) /= *(qq++) + *(ss++); break; } break; /* end INTEGER_TYPE */ } } /* increment pointers */ for ( j = arvalue->dim-2 ; j >= 0 ; j-- ) { pspots[j] += pstride[j]; qspots[j] += qstride[j]; sspots[j] += sstride[j]; if ( ++inx[j] < minsize[j] ) { p = pspots[j]; q = qspots[j]; s = sspots[j]; for ( k = j+1 ; k < alvalue->dim-1 ; k++ ) { pspots[k] = p; qspots[k] = q; sspots[k] = s; } break; } inx[j] = 0 ; } } while ( j >= 0 ); if ( node->flags & RECALC_NODE ) recalc_flag = 1; } /* end ARRAY_ASSIGNOP_A_P_A_ */ break; case ARRAY_ASSIGNOP_A_S_A_: { struct array *alvalue= get_name_arrayptr(node->op2.name_id,localstack,localbase); struct array *arvalue= get_name_arrayptr(node->op3.name_id,localstack,localbase); struct array *asvalue= get_name_arrayptr(node->op4.name_id,localstack,localbase); int inx[100]; /* keep track of indices */ char *p,*q,*s; int j,k; char *pspots[100],*qspots[100],*sspots[100]; int pstride[100],qstride[100],sstride[100]; int minsize[100],lastsize; /* Was checked in parser that the arrays have the same number of indices, but might be different sizes. So do intersection. */ /* pop datastarts off stack */ s = *(char**)(stacktop--); q = *(char**)(stacktop--); p = *(char**)(stacktop--); for ( i = 0 ; i < arvalue->dim ; i++ ) { inx[i] = 0; minsize[i] = arvalue->sizes[i] < alvalue->sizes[i] ? arvalue->sizes[i] : alvalue->sizes[i] ; if ( minsize[i] > asvalue->sizes[i] ) minsize[i] = asvalue->sizes[i]; pspots[i] = p; qspots[i] = q; sspots[i] = s; } if ( alvalue->dim >= 2 ) { pstride[alvalue->dim-2] = alvalue->sizes[alvalue->dim-1]*alvalue->itemsize; qstride[arvalue->dim-2] = arvalue->sizes[arvalue->dim-1]*arvalue->itemsize; sstride[asvalue->dim-2] = asvalue->sizes[asvalue->dim-1]*asvalue->itemsize; for ( i = arvalue->dim - 3 ; i >= 0 ; i-- ) { pstride[i] = pstride[i+1]*alvalue->sizes[i+1]; qstride[i] = qstride[i+1]*arvalue->sizes[i+1]; sstride[i] = sstride[i+1]*asvalue->sizes[i+1]; } } lastsize = minsize[arvalue->dim-1]; p = pspots[0]; q = qspots[0]; s = sspots[0]; do { /* do a row */ switch ( arvalue->datatype ) { case REAL_TYPE: { REAL *pp = (REAL*)p; REAL *qq = (REAL*)q; REAL *ss = (REAL*)s; switch ( node->op1.assigntype ) { case ASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) = *(qq++) - *(ss++); break; case PLUSASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) += *(qq++) - *(ss++); break; case SUBASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) -= *(qq++) - *(ss++); break; case MULTASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) *= *(qq++) - *(ss++); break; case DIVASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) /= *(qq++) - *(ss++); break; } break; /* end REAL_TYPE */ } case INTEGER_TYPE: { int *pp = (int*)p; int *qq = (int*)q; int *ss = (int*)s; switch ( node->op1.assigntype ) { case ASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) = *(qq++) - *(ss++); break; case PLUSASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) += *(qq++) - *(ss++); break; case SUBASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) -= *(qq++) - *(ss++); break; case MULTASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) *= *(qq++) - *(ss++); break; case DIVASSIGN_: for ( i = 0 ; i < lastsize ; i++ ) *(pp++) /= *(qq++) - *(ss++); break; } break; /* end INTEGER_TYPE */ } } /* increment pointers */ for ( j = arvalue->dim-2 ; j >= 0 ; j-- ) { pspots[j] += pstride[j]; qspots[j] += qstride[j]; sspots[j] += sstride[j]; if ( ++inx[j] < minsize[j] ) { p = pspots[j]; q = qspots[j]; s = sspots[j]; for ( k = j+1 ; k < alvalue->dim-1 ; k++ ) { pspots[k] = p; qspots[k] = q; sspots[k] = s; } break; } inx[j] = 0 ; } } while ( j >= 0 ); if ( node->flags & RECALC_NODE ) recalc_flag = 1; } break; /* end ARRAY_ASSIGNOP_A_S_A_ */ case PLUSASSIGN_: case SUBASSIGN_: case MULTASSIGN_: case DIVASSIGN_: case SET_GLOBAL_: { struct global *g = globals(node->op1.name_id); REAL *x; if ( g->flags & GLOB_LOCALVAR ) x = localstack + g->value.offset; else if ( g->flags & ORDINARY_PARAM ) x = &g->value.real; else { sprintf(errmsg, "Variable %s is read-only.\n",g->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3455,errmsg,WARNING); break; } if ( g->flags & STRINGVAL ) { if ( g->flags & GLOB_LOCALVAR ) myfree(*(char**)x); else myfree(g->value.string); g->flags &= ~STRINGVAL; } switch(node->type) { case SET_GLOBAL_: *x = *(stacktop--); break; case PLUSASSIGN_: *x += *(stacktop--); break; case SUBASSIGN_: *x -= *(stacktop--); break; case MULTASSIGN_: *x *= *(stacktop--); break; case DIVASSIGN_: if ( *stacktop == 0.0 ) { sprintf(errmsg,"Division by zero.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2562,errmsg,RECOVERABLE); } *x /= *(stacktop--); break; } if ( g->flags & RECALC_PARAMETER ) recalc_flag = 1; } break; case SET_PERM_GLOBAL_: { struct global *g = perm_globals(node->op1.name_id); REAL *x; if ( g->flags & STRINGVAL ) { free(g->value.string); g->flags &= ~STRINGVAL; } x = &g->value.real; *x = *(stacktop--); if ( g->flags & RECALC_PARAMETER ) recalc_flag = 1; } break; /* end whole-array syntax */ case FINISHED: if ( this_frame->flags & BASE_OF_EVAL ) { /* want to exit from eval() */ goto the_exit; } else /* want to return within eval() */ { if ( localbase && (localbase->flags & LL_HAS_ARRAY) ) { /* de-allocate local array storage */ int n; for ( n = 0 ; n < localbase->count ; n++ ) { struct global *g = &(localbase->list[n].g); if ( g->flags & ARRAY_PARAM ) { struct array *a = *(struct array**)(newstack + g->value.offset); if ( a ) temp_free((char*)a); a = NULL; } } } return_value = *stacktop; stacktop = (REAL*)this_frame; stacktop--; /* since points to occupied spot */ node = this_frame->return_node; td->frame_spot = this_frame->parent_frame_spot; localstack = (REAL*)(this_frame+1); PROF_EVAL_END(ex); ex = this_frame->base_ex; PROF_EVAL_START(ex); localbase = ex->locals; if ( ex->locals ) localcount = ex->locals->totalsize; else localcount = 0; } break; default: other_stuff(node,&recalc_flag, &update_display_flag,q_id,localstack,localbase); break; } if ( breakflag ) { switch ( breakflag ) { case BREAKFULL: { sprintf(errmsg,"Command aborted due to user interrupt.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2073,errmsg, RECOVERABLE); } break; case BREAKABORT: { sprintf(errmsg,"Command aborted.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3234,errmsg, RECOVERABLE_ABORT); } break; case BREAKAFTERWARNING: { sprintf(errmsg,"Command aborted due to break_after_warning.\n"); kb_error(3109,errmsg, RECOVERABLE); } } } #ifdef _XXDEBUG { static int didwarn; /* Check predicted stack alignment. There are circumstances when alignment is not equal (e.g. skipping procedure bodies when assigning them, so just check there is room. */ if ( (node[1].type != SETUP_FRAME_) && (stacktop > (REAL*)this_frame + FRAME_SPACE + localcount + node->stack_spot) ) { sprintf(errmsg,"Intermediate stack misalignment after node type %d.\n", node->type); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); if ( ! didwarn ) /* to prevent error cascade */ kb_error(2671,errmsg,WARNING); didwarn = 1; } else didwarn = 0; } #endif if ( td->stack_top < td->eval_stack ) kb_error(5243,"BAD EVAL STACK\n",RECOVERABLE); if ( newstack[stackmax-1] != STACKMAGIC ) /* nearing overflow, so extend */ { kb_error(3488,"Evaluation stack overflow.\n",RECOVERABLE); } } the_exit: if ( !params ) { if ( web.counts_changed ) flush_counts(); if ( recalc_flag && autorecalc_flag ) recalc(); else if ( update_display_flag ) update_display(); } PROF_EVAL_END(ex); iterate_flag = old_flag; if ( stacktop == (REAL*)this_frame + FRAME_SPACE + localcount ) { /* Have return value on stack */ REAL retval = *(stacktop--); stacktop = (REAL*)this_frame; stacktop--; /* since points to occupied spot */ td->frame_spot = this_frame->parent_frame_spot; return retval; } else if ( stacktop != (REAL*)this_frame + FRAME_SPACE + localcount - 1) { sprintf(errmsg,"Internal error: Stack misalignment by %d in eval()\n", stacktop-(REAL*)this_frame-localcount-FRAME_SPACE+1); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1298,errmsg, RECOVERABLE); } stacktop = (REAL*)this_frame; stacktop--; /* since points to occupied spot */ td->frame_spot = this_frame->parent_frame_spot; return 0.0; } /* end eval() */ /************************************************************************ * * function: flush_counts() * * purpose: print pending counts */ void flush_counts() { if ( web.counts_changed & fix_count_bit ) { sprintf(msg,"Fixed: %d\n",web.fix_count); outstring(msg); } if ( web.counts_changed & unfix_count_bit ) { sprintf(msg,"Unfixed: %d\n",web.unfix_count); outstring(msg); } if ( web.counts_changed & equi_count_bit ) { sprintf(msg,"Edges equiangulated: %d\n",web.equi_count); outstring(msg); } if ( web.counts_changed & edge_delete_count_bit ) { sprintf(msg,"Edges deleted: %d\n",web.edge_delete_count); outstring(msg); } if ( web.counts_changed & facet_delete_count_bit ) { sprintf(msg,"Facets deleted: %d\n",web.facet_delete_count); outstring(msg); } if ( web.counts_changed & edge_refine_count_bit ) { sprintf(msg,"Edges refined: %d\n",web.edge_refine_count); outstring(msg); } if ( web.counts_changed & facet_refine_count_bit ) { sprintf(msg,"Facets refined: %d\n",web.facet_refine_count); outstring(msg); } if ( web.counts_changed & vertex_dissolve_count_bit ) { sprintf(msg,"Vertices dissolved: %d\n",web.vertex_dissolve_count); outstring(msg); } if ( web.counts_changed & edge_dissolve_count_bit ) { sprintf(msg, "Edges dissolved: %d\n",web.edge_dissolve_count); outstring(msg); } if ( web.counts_changed & facet_dissolve_count_bit ) { sprintf(msg,"Facets dissolved: %d\n",web.facet_dissolve_count); outstring(msg); } if ( web.counts_changed & body_dissolve_count_bit ) { sprintf(msg,"Bodies dissolved: %d\n",web.body_dissolve_count); outstring(msg); } if ( web.counts_changed & edge_reverse_count_bit ) { sprintf(msg, "Edges reversed: %d\n",web.edge_reverse_count); outstring(msg); } if ( web.counts_changed & facet_reverse_count_bit ) { sprintf(msg, "Facets reversed: %d\n",web.facet_reverse_count); outstring(msg); } if ( web.counts_changed & vertex_pop_count_bit ) { sprintf(msg,"Vertices popped: %d\n",web.vertex_pop_count); outstring(msg); } if ( web.counts_changed & edge_pop_count_bit ) { sprintf(msg,"Edges popped: %d\n",web.edge_pop_count); outstring(msg); } if ( web.counts_changed & pop_tri_to_edge_count_bit ) { sprintf(msg,"pop_tri_to_edge count: %d\n",web.pop_tri_to_edge_count); outstring(msg); } if ( web.counts_changed & pop_edge_to_tri_count_bit ) { sprintf(msg,"pop_edge_to_tri count: %d\n",web.pop_edge_to_tri_count); outstring(msg); } if ( web.counts_changed & pop_quad_to_quad_count_bit ) { sprintf(msg,"pop_quad_to_quad count: %d\n",web.pop_quad_to_quad_count); outstring(msg); } if ( web.counts_changed & edgeswap_count_bit ) { sprintf(msg,"Edges swapped: %d\n",web.edgeswap_count); outstring(msg); } if ( web.counts_changed & t1_edgeswap_count_bit ) { sprintf(msg,"T1 swaps: %d\n",web.t1_edgeswap_count); outstring(msg); } web.counts_reported = ~0; web.counts_changed = 0; } /************************************************************************ * * function: reset_counts() * * purpose: set counts to 0 for event counts and profiling counts. */ void reset_counts() { web.equi_count = 0; web.edge_delete_count = 0; web.facet_delete_count = 0; web.edge_refine_count = 0; web.facet_refine_count = 0; web.notch_count = 0; web.vertex_dissolve_count = 0; web.edge_dissolve_count = 0; web.facet_dissolve_count = 0; web.body_dissolve_count = 0; web.edge_reverse_count = 0; web.facet_reverse_count = 0; web.vertex_pop_count = 0; web.edge_pop_count = 0; web.pop_tri_to_edge_count = 0; web.pop_edge_to_tri_count = 0; web.pop_quad_to_quad_count = 0; web.where_count = 0; web.edgeswap_count = 0; web.fix_count = 0; web.unfix_count = 0; web.t1_edgeswap_count = 0; web.notch_count = 0; web.counts_reported = 0; web.counts_changed = 0; #ifdef PROFILING_ENABLED { int i; for ( i = LOW_INST ; i < meth_inst_count ; i++ ) { struct method_instance *mi = METH_INSTANCE(i); mi->value_call_count = mi->grad_call_count = mi->hess_call_count = 0; mi->value_elapsed_time = mi->grad_elapsed_time = mi->hess_elapsed_time = 0.0; } for ( i = 0 ; i < 2 ; i++ ) { element_setup_elapsed_time[i] = 0; calc_quants_elapsed_time[i] = 0; calc_quant_grads_elapsed_time[i] = 0; calc_quant_hess_elapsed_time[i] = 0; exparse_elapsed_time[i] = 0; yyparse_elapsed_time[i] = 0; yylex_elapsed_time[i] = 0; kblex_elapsed_time[i] = 0; hessian_solve_elapsed_time[i] = 0; hessian_mul_elapsed_time[i] = 0; hessian_AIJ_setup_elapsed_time[i] = 0; hessian_constraint_setup_elapsed_time[i] = 0; hessian_project_setup_elapsed_time[i] = 0; hessian_factor_elapsed_time[i] = 0; hessian_CHinvC_elapsed_time[i] = 0; } find_cpu_speed(); } #endif } evolver-2.30c.dfsg/src/exprint.c0000644000175300017530000027367111410765113017072 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /***************************************************************** * * File: exprint.c * * Purpose: To print user commands and functions in algebraic form * */ #include "include.h" #include "lex.h" #include "ytab.h" void check_room_left ARGS((size_t)); void print_quote ARGS((char*)); void linebreak ARGS((void)); void newline ARGS((void)); struct locallist_t *current_proc_locals[100]; int current_proc_depth; char * assign_symbol ARGS((int)); char * assign_symbol (sym) int sym; { switch ( sym ) { case ASSIGN_: return ":="; case PLUSASSIGN_: return "+="; case SUBASSIGN_: return "-="; case MULTASSIGN_: return "*="; case DIVASSIGN_: return "/="; } return ""; } /***************************************************************** * * Function print_express() * * Purpose: print expression in algebraic format * Uses private working space, expanding as needed. * Returns pointer to working space. * */ static char *pos; /* current position in string */ static char vch; static size_t strsize; static char *strstart; static char *linestart; static int bracket_depth; /* precedence levels for knowing how to parenthesize */ #define PREC_POW 50 #define PREC_UMINUS 45 #define PREC_MUL 40 #define PREC_DIV 40 #define PREC_ADD 35 #define PREC_SUB 35 #define PREC_COMP 30 #define PREC_NOT 25 #define PREC_AND 20 #define PREC_OR 15 #define PREC_COND 13 #define PREC_ASSIGN 10 #define PREC_ARG 0 /************************************************************************** * * Function: print_express() * * Purpose: Convert parse tree for expression to ASCII string in * character array pos. */ char *print_express(node,c) struct expnode *node; /* expression tree */ int c; /* character for parameters */ { if ( !strstart ) /* for first time thru */ { strsize = 200; strstart = my_list_calloc(1,strsize,ETERNAL_BLOCK); } else strstart[0] = '\0'; linestart = pos = strstart; vch = (char)c; if ( !node || !node->root ) { strcpy(strstart,"{}"); return strstart;} bracket_depth = 0; current_proc_locals[0] = node->locals; current_proc_depth = 0; exprint_recur(node->root,0); return strstart; } /********************************************************************** * * Function: linebreak() * * Purpose: Insert a linebreak when the line gets too long. */ #define INDENT 2 #define GOODLEN 75 #define MINLEN 30 void linebreak() { int i; char *c,*cc; char *minline = linestart + MINLEN; int extra_indent = 0; if ( pos - linestart < GOODLEN ) return; cc = NULL; /* search for end of quote, starting at end so don't break quote */ for ( c = pos-1 ; c != linestart ; c-- ) if ( *c == '"' ) { cc = (c[1] == ';') ? c+1 : c; break; } if ( cc == NULL ) /* search for end bracket */ for ( c = linestart + GOODLEN ; c != minline ; c-- ) if ( *c == '}' ) { cc = (c[1] == ';') ? c+1 : c; break; } if ( cc == NULL ) /* scan back for handy ';' */ for ( c = linestart + GOODLEN ; c != minline ; c-- ) if ( *c == ';' ) { cc = c; break; } if ( cc == NULL ) /* just look for { */ for ( c = linestart + GOODLEN ; c != minline ; c-- ) if ( *c == '{' ) { cc = c; break; } if ( cc == NULL ) extra_indent = 2; if ( cc == NULL ) /* just look for space */ for ( c = linestart + GOODLEN ; c != minline ; c-- ) if ( *c == ' ' ) { cc = c; break; } if ( cc == NULL ) /* just look for comma */ for ( c = linestart + GOODLEN ; c != minline ; c-- ) if ( *c == ',' ) { cc = c; break; } if ( cc == NULL ) /* just look for = */ for ( c = linestart + GOODLEN ; c != minline ; c-- ) if ( *c == '=' ) { cc = c; break; } if ( cc == NULL ) /* just look for logic signs */ for ( c = linestart + GOODLEN ; c != minline ; c-- ) if ( (*c == '&') || (*c == '%') ) { cc = c; break; } if ( cc == NULL ) /* just look for arithmetic signs */ for ( c = linestart + GOODLEN ; c != minline ; c-- ) if ( ((*c == '+') || (*c == '-')) && !(isalpha(c[-1]) && !isalpha(c[-2]))) { cc = c; break; } /* don't split scientific notation */ if ( (cc == NULL) || (cc - linestart < GOODLEN/2) ) for ( c = linestart + GOODLEN ; c != minline ; c-- ) if ( ((*c == '*') || (*c == '/')) && (c[1] != '=') && (c[1] != '*') ) { cc = c; break; } if ( cc == NULL ) /* just look for closing parenthesis */ for ( c = linestart + GOODLEN ; c != minline ; c-- ) if ( *c == ')' ) { cc = c; break; } if ( cc == NULL ) /* just look for opening parenthesis */ for ( c = linestart + GOODLEN ; c != minline ; c-- ) if ( *c == '(' ) { cc = c; break; } /* Have break, so do split, with *cc as last character on old line */ if ( cc ) { char *ch; int bd = bracket_depth; linestart = cc+3; c = cc+1 ; while (*c == ' ' ) c++ ; /* skip spaces */ for ( ch = c ; ch < pos ; ch++ ) /* unwind bracket depth */ if ( *ch == '{' ) bd--; else if ( *ch == '}' ) bd++; pos = cc + 3 + INDENT*bd + extra_indent + strlen(c); kb_memmove(cc+3+INDENT*bd+extra_indent,c,strlen(c)); cc[1] = (char)(bd ? ' ' : '\\'); cc[2] = '\n'; for ( i = 0 ; i < INDENT*bd + extra_indent ; i++ ) cc[i+3] = ' '; *pos = 0; } else { if ( bracket_depth <= 0 ) *(pos++) = '\\'; *(pos++) = '\n'; linestart = pos; for ( i = 0 ; i < INDENT*bracket_depth ; i++ ) *(pos++) = ' '; *pos = 0; } } /*************************************************************************** * * function: newline() * * purpose: Begin a new line, suitably indented. */ void newline() { int i; *(pos++) = '\n'; for ( i = 0 ; i < INDENT*bracket_depth ; i++ ) *(pos++) = ' '; *pos = 0; linestart = pos; } /********************************************************************** * * Function: check_room_left() * * Purpose: Keep print string from overflowing. */ void check_room_left(n) size_t n; /* room needed */ { /* check room remaining */ if ( (pos + n - strstart) > (int)strsize ) { size_t len = pos - strstart; size_t linespot = linestart - strstart; strstart = my_list_realloc(strstart,strsize + 1000 + n,ETERNAL_BLOCK); strsize += 1000 + n; linestart = strstart + linespot; pos = strstart + len; } } void print_quote(c) char *c; { check_room_left(strlen(c)+30); convert_string(c,pos); pos += strlen(pos); } /******************************************************************** * * function: convert_string() * * purpose: convert string from internal to printable representation. * Converts to C escapes, encloses in quotes. */ void convert_string(c,p) char *c; /* source */ char *p; /* destination */ { /* convert to C escape sequences */ *(p++) = '"'; if ( c ) for ( ; *c ; c++ ) switch ( *c ) { case '\n': *(p++) = '\\'; *(p++) = 'n'; break; case '\r': *(p++) = '\\'; *(p++) = 'r'; break; case '\t': *(p++) = '\\'; *(p++) = 't'; break; case '\b': *(p++) = '\\'; *(p++) = 'b'; break; case '"': *(p++) = '\\'; *(p++) = 'q'; break; case '\\': *(p++) = '\\'; *(p++) = '\\'; break; default: *(p++) = *c; } *(p++) = '"'; *p = 0; return; } /************************************************************************* * * function: exprint_recur() * * purpose: Convert node of parse tree to ASCII, recursively doing sons. */ void exprint_recur(node,prec_parent) struct treenode *node; int prec_parent; /* precedence level of parent, for parenthesizing */ { struct treenode *nn; struct extra *ex; struct locallist_t *localbase = current_proc_locals[current_proc_depth]; check_room_left(1000); /* insert some handy line breaks */ if ( (pos - linestart > GOODLEN) /* && (pos[-2] != ';') */ ) linebreak(); switch ( node->type ) { case NOP_: return; case NULLBLOCK_: sprintf(pos,"{}");pos+=2; return; case NULLCMD_: return; case BREAKPOINT_: sprintf(pos,"breakpoint %s ",globals(node->op1.name_id)->name); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); return; case UNSET_BREAKPOINT_: if ( node->left ) { sprintf(pos,"unset breakpoint %s ",globals(node->op1.name_id)->name); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); } else { sprintf(pos,"unset breakpoints"); pos += strlen(pos); } return; case SUPPRESS_WARNING_: sprintf(pos,"suppress_warning "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); return; case UNSUPPRESS_WARNING_: sprintf(pos,"unsuppress_warning "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); return; case KEYLOGFILE_: sprintf(pos,"keylogfile "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); return; case LOGFILE_: sprintf(pos,"logfile "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); return; case CMDLIST_: exprint_recur(node+node->left,prec_parent); if ( node->right ) { sprintf(pos,"; "); pos += 2; newline(); exprint_recur(node+node->right,prec_parent); } return; case BACKQUOTE_START_: return; case BACKQUOTE_END_: exprint_recur(node+node->right,prec_parent); /* left was dummy */ return; case TEXT_SPOT_: exprint_recur(node+node->left,prec_parent); *(pos++) = ','; exprint_recur(node+node->right,prec_parent); return; case DISPLAY_TEXT_: sprintf(pos,"display_text("); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); *(pos++) = ','; exprint_recur(node+node->right,prec_parent); *(pos++) = ')'; return; case DELETE_TEXT_: sprintf(pos,"delete_text("); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); *(pos++) = ')'; return; case ACOMMANDEXPR_: *(pos++) = '`'; /* surround with backquotes */ exprint_recur(node+node->left,prec_parent); sprintf(pos,";`"); pos+=2; /* surround with backquotes; make sure of ; */ if ( node->right ) { sprintf(pos,", "); pos += 2; exprint_recur(node+node->right,prec_parent); } return; case ATTR_FUNCTION_END_: exprint_recur(node+node->left,prec_parent); /* define part */ strcat(pos," function {"); pos += strlen(pos); exprint_recur(node+node->right,prec_parent); /* code part */ strcat(pos," } "); pos += strlen(pos); return; case ATTR_FUNCTION_: exprint_recur(node+node->left,prec_parent); /* define part */ return; case WRAP_VERTEX_: strcat(pos,"wrap_vertex("); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); sprintf(pos,","); pos++; exprint_recur(node+node->right,prec_parent); sprintf(pos,")"); pos++; return; case CREATE_VERTEX_: sprintf(pos,"new_vertex("); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); sprintf(pos,")"); pos++; return; case CREATE_EDGE_: sprintf(pos,"new_edge("); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); sprintf(pos,","); pos++; exprint_recur(node+node->right,prec_parent); sprintf(pos,")"); pos++; return; case CREATE_FACET_: sprintf(pos,"new_facet("); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); sprintf(pos,")"); pos++; return; case CREATE_BODY_: sprintf(pos,"new_body"); pos += strlen(pos); return; case ELINDEX_: exprint_recur(node+node->left,prec_parent); if ( node->right ) { *pos = '@'; pos++; *pos = 0; exprint_recur(node+node->right,prec_parent); } return; case PUSH_ELEMENT_ID_: sprintf(pos,"%%sd@%d\n",(inverted(node->op1.id)?"-":""), node->op1.id & OFFSETMASK, id_task(node->op1.id)); pos += strlen(pos); return; case VALID_ELEMENT_: sprintf(pos,"valid_element(%s[",typenames[node->op1.eltype]); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); sprintf(pos,"])"); pos+=2; return; case VALID_CONSTRAINT_: sprintf(pos,"valid_constraint("); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); sprintf(pos,")"); pos+=1; return; case VALID_BOUNDARY_: sprintf(pos,"valid_boundary("); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); sprintf(pos,")"); pos+=1; return; case MATRIX_INVERSE_: sprintf(pos,"matrix_inverse(%s,%s)", globals(node->op1.name_id)->name,globals(node->op2.name_id)->name); pos += strlen(pos); return; case MATRIX_MULTIPLY_: sprintf(pos,"matrix_multiply(%s,%s,%s)", globals(node->op1.name_id)->name,globals(node->op2.name_id)->name, globals(node->op3.name_id)->name); pos += strlen(pos); return; case MATRIX_DETERMINANT_: sprintf(pos,"matrix_determinant(%s)", globals(node->op1.name_id)->name); pos += strlen(pos); return; case MERGE_VERTEX_: sprintf(pos,"vertex_merge("); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); sprintf(pos,","); pos++; exprint_recur(node+node->right,prec_parent); sprintf(pos,")"); pos++; return; case MERGE_EDGE_: sprintf(pos,"edge_merge("); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); sprintf(pos,","); pos++; exprint_recur(node+node->right,prec_parent); sprintf(pos,")"); pos++; return; case MERGE_FACET_: sprintf(pos,"facet_merge("); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); sprintf(pos,","); pos++; exprint_recur(node+node->right,prec_parent); sprintf(pos,")"); pos++; return; case COMMAND_BLOCK_: sprintf(pos,"{ "); pos+=2; bracket_depth++; exprint_recur(node+node->left,prec_parent); bracket_depth--; newline(); sprintf(pos,"}"); pos++; return; case LOCAL_LIST_START_: sprintf (pos,"local "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case DECLARE_LOCAL_: { if ( node->left ) { exprint_recur(node+node->left,0); *pos = ','; pos++; } sprintf (pos,"%s",globals(node->op1.name_id)->name); pos += strlen(pos); return; } case DEFINE_QUANTITY_: { struct gen_quant *g = GEN_QUANT(node->op1.quant_id); sprintf(pos,"/* Definition of quantity %s was originally here.*/", g->name); pos += strlen(pos); return; } case DEFINE_METHOD_INSTANCE_: { struct method_instance *mi = METH_INSTANCE(node->op1.meth_id); sprintf(pos, "/* Definition of method instance %s was originally here.*/", mi->name); pos += strlen(pos); return; } case DEFINE_CONSTRAINT_: { struct constraint *con = get_constraint(node->op1.con_id); if ( con->attr & NAMED_THING ) sprintf(pos, "/* Definition of constraint %s was originally here.*/", con->name); else sprintf(pos, "/* Definition of constraint %d was originally here.*/", node->op1.con_id); pos += strlen(pos); return; } case DEFINE_BOUNDARY_: { struct boundary *bdry = web.boundaries+node->op1.bdry_id; if ( bdry->attr & NAMED_THING ) sprintf(pos, "/* Definition of boundary %s was originally here.*/", bdry->name); else sprintf(pos, "/* Definition of boundary %d was originally here.*/", node->op1.con_id); pos += strlen(pos); return; } case DEFINE_EXTRA_: ex = EXTRAS(node->op2.eltype)+node->op1.extranum; sprintf(pos,"define %s attribute %s %s", typenames[node->op2.eltype],ex->name, datatype_name[ex->type]); pos += strlen(pos); if ( ex->code.start ) { strcat(pos," function "); pos += strlen(pos); /*current_proc_locals[++current_proc_depth] = ex->locals;*/ exprint_recur(ex->code.root,prec_parent); /*current_proc_depth--;*/ } break; case DEFINE_EXTRA_INDEX_: exprint_recur(node+node->left,prec_parent); exprint_recur(node+node->right,prec_parent); break; case DEFINE_ARRAY_: sprintf(pos,"define %s %s",globals(node->op1.name_id)->name, datatype_name[node->op2.valtype]); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case DEFINE_FIXED_LOCAL_ARRAY_: { struct global *g = globals(node->op1.name_id); struct array *a = g->attr.arrayptr; int i; sprintf(pos,"define %s %s",g->name, datatype_name[node->op2.valtype]); pos += strlen(pos); for ( i = 0 ; i < a->dim ; i++ ) { sprintf(pos,"[%d]",a->sizes[i]); pos += strlen(pos); } } break; case ARRAY_EVAL_: exprint_recur(node+node->left,prec_parent); exprint_recur(node+node->right,prec_parent); break; case ARRAY_HEAD_: exprint_recur(node+node->left,prec_parent); break; case ARRAYASSIGN: sprintf(pos,"%s",globals(node->op2.name_id)->name); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); switch ( node->op1.assigntype ) { case ASSIGN_: sprintf(pos," := "); break; case PLUSASSIGN_: sprintf(pos," += "); break; case SUBASSIGN_: sprintf(pos," -= "); break; case MULTASSIGN_: sprintf(pos," *= "); break; case DIVASSIGN_: sprintf(pos," /= "); break; } pos += strlen(pos); exprint_recur(node+node->right,prec_parent); break; case ARRAYEVAL: sprintf(pos,"%s",globals(node->op2.name_id)->name); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; /* whole-array syntax */ case ARRAYIDENT_: sprintf(pos,"%s",get_name_name(node->op2.name_id,localbase)); pos += strlen(pos); break; case ARRAY_ASSIGNOP_FACET_NORMAL_: case ARRAY_ASSIGNOP_EDGE_VECTOR_ : case ARRAY_ASSIGNOP_VERTEX_NORMAL_: case ARRAY_ASSIGNOP_ARRAY_ : case ARRAY_ASSIGNOP_SCALAR_ : case ARRAY_ASSIGNOP_S_X_A_: case ARRAY_ASSIGNOP_A_P_A_: case ARRAY_ASSIGNOP_A_S_A_: exprint_recur(node+node->left,prec_parent); if ( !(node->flags & SET_ASSIGNOP) ) { switch ( node->op1.assigntype ) { case ASSIGN_: sprintf(pos," := "); break; case PLUSASSIGN_: sprintf(pos," += "); break; case SUBASSIGN_: sprintf(pos," -= "); break; case MULTASSIGN_: sprintf(pos," *= "); break; case DIVASSIGN_: sprintf(pos," /= "); break; } pos += strlen(pos); } exprint_recur(node+node->right,prec_parent); break; case ARRAY_ASSIGNOP_SINGLE_: exprint_recur(node+node->left,prec_parent); if ( !(node->flags & SET_ASSIGNOP) ) { switch ( node->op1.assigntype ) { case ASSIGN_: sprintf(pos," := "); break; case PLUSASSIGN_: sprintf(pos," += "); break; case SUBASSIGN_: sprintf(pos," -= "); break; case MULTASSIGN_: sprintf(pos," *= "); break; case DIVASSIGN_: sprintf(pos," /= "); break; } pos += strlen(pos); } else { *pos = '('; *(++pos) = 0; /* avoid - after ] */} exprint_recur(node+node->right,prec_parent); if ( node->flags & SET_ASSIGNOP ) { *pos = ')'; *(++pos) = 0; } break; case DOT_: exprint_recur(node+node->left,prec_parent); sprintf(pos," dot_product "); pos += strlen(pos); exprint_recur(node+node->right,prec_parent); break; case ARRAY_LVALUE_INDEXED_: exprint_recur(node+node->left,prec_parent); exprint_recur(node+node->right,prec_parent); *pos = ' '; *(++pos) = 0; break; case PRINT_ARRAY_LVALUE_: sprintf(pos,"print "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case ATTRIB_LVALUE_: case ARRAY_VERTEX_NORMAL_: case ARRAY_EDGE_VECTOR_: case ARRAY_FACET_NORMAL_: if ( node->left ) { exprint_recur(node+node->left,prec_parent); /* sprintf(pos," %s",get_name_name(node->op2.name_id,localbase)); */ } if ( node->op1.localnum == 0) sprintf(pos," %s",get_name_name(node->op2.name_id,localbase)); else sprintf(pos,".%s",get_name_name(node->op2.name_id,localbase)); pos += strlen(pos); break; case ARRAY_RVALUE_: exprint_recur(node+node->left,PREC_MUL); sprintf(pos," %c ",node->op1.intval); pos += strlen(pos); exprint_recur(node+node->right,prec_parent); break; /* end whole-array syntax */ case SET_CONSTRAINT_GLOBAL: strcat(pos,"set constraint "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); strcat(pos," global "); pos += strlen(pos); break; case UNSET_CONSTRAINT_GLOBAL: strcat(pos,"unset constraint "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); strcat(pos," global "); pos += strlen(pos); break; case SET_CONSTRAINT_NAME_GLOBAL: sprintf(pos,"set constraint %s global", get_constraint(node->op3.connum)->name); pos += strlen(pos); break; case UNSET_CONSTRAINT_NAME_GLOBAL: sprintf(pos,"unset constraint %s global", get_constraint(node->op3.connum)->name); pos += strlen(pos); break; case RESET_COUNTS_: strcat(pos,"reset_counts"); pos += strlen(pos); return; case FLUSH_COUNTS_: strcat(pos,"flush_counts"); pos += strlen(pos); return; case PRINT_PROFILING_: strcat(pos,"print profiling"); pos += strlen(pos); return; case RESET_PROFILING_: strcat(pos,"reset_profiling"); pos += strlen(pos); return; case PAUSE_: strcat(pos,"pause"); pos += strlen(pos); return; case RETURN_: strcat(pos,"return "); pos += strlen(pos); if ( node->left ) exprint_recur(node+node->left,prec_parent); return; case DATE_AND_TIME_: strcat(pos,"date_and_time"); pos += strlen(pos); return; case BREAK_: if ( node->op2.breakdepth > 1 ) sprintf(pos,"break %d",node->op2.breakdepth); else strcat(pos,"break "); pos += strlen(pos); return; case CONTINUE_: if ( node->op2.breakdepth > 1 ) sprintf(pos,"continue %d",node->op2.breakdepth); else strcat(pos,"continue "); pos += strlen(pos); return; case HISTORY_: strcat(pos,"history "); pos += strlen(pos); return; case GET_TRANSFORM_EXPR_: strcat(pos,"transform_expr "); pos += strlen(pos); return; case WARNING_MESSAGES_: strcat(pos,"warning_messages "); pos += strlen(pos); return; case DATAFILENAME_: strcat(pos,"datafilename "); pos += strlen(pos); return; case REPEAT_: exprint_recur(node+node->right,prec_parent); nn = node + node->left; exprint_recur(nn+nn->left,prec_parent); return; case EXPRLIST_: exprint_recur(node+node->left,prec_parent); if ( node->right ) { sprintf(pos,", "); pos += 2; exprint_recur(node+node->right,prec_parent); } return; case QUOTATION_: print_quote(node->op1.string); return; case SPRINTFHEAD_: case PRESPRINTF_: sprintf(pos,"sprintf "); pos += strlen(pos); if ( node->op1.string ) print_quote(node->op1.string); else exprint_recur(node+node->left,prec_parent); return; case PREPRINTF_: case PRINTFHEAD_: sprintf(pos,"printf "); pos += strlen(pos); if ( node->op1.string ) print_quote(node->op1.string); else exprint_recur(node+node->left,prec_parent); return; case ERRPRINTFHEAD_: sprintf(pos,"errprintf "); pos += strlen(pos); if ( node->op1.string ) print_quote(node->op1.string); else exprint_recur(node+node->left,prec_parent); return; case BINARY_PRINTFHEAD_: sprintf(pos,"binary_printf "); pos += strlen(pos); if ( node->op1.string ) print_quote(node->op1.string); else exprint_recur(node+node->left,prec_parent); return; case PRINTF_: case BINARY_PRINTF_: case ERRPRINTF_: case SPRINTF_: exprint_recur(node+node->left,prec_parent); strcat(pos++,","); exprint_recur(node+node->right,prec_parent); return; case STRPRINT_: case PRINT_: case EPRINT_: sprintf(pos,"print "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); return; case PRINT_LETTER_: sprintf(pos,"print %c ",node->op1.name_id); pos += strlen(pos); return; case PRINT_PROCEDURE_: case PRINT_ARRAY_: sprintf(pos,"print %s ",globals(node->op1.name_id)->name); pos += strlen(pos); return; case PRINT_ARRAYPART_: sprintf(pos,"print %s",globals(node[node->left].op1.name_id)->name); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); return; case PRINT_VERTEXNORMAL_: sprintf(pos,"print "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); strcat(pos,".vertexnormal"); pos += strlen(pos); return; case PRINT_ATTR_ARRAY_: sprintf(pos,"print "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); sprintf(pos,".%s",EXTRAS(node->op2.eltype)[node->op3.extranum].name); pos += strlen(pos); if ( node->right ) exprint_recur(node+node->right,prec_parent); return; case PRINT_PERM_PROCEDURE_: sprintf(pos,"print %s ",perm_globals(node->op1.name_id)->name); pos += strlen(pos); return; case EXPRINT_PROCEDURE_: sprintf(pos,"exprint %s ",globals(node->op1.name_id)->name); pos += strlen(pos); return; case ELSE_: /* root of IF */ exprint_recur(node+node->left,prec_parent); /* IF part */ if ( node->right ) { sprintf(pos," else "); pos += strlen(pos); newline(); exprint_recur(node+node->right,prec_parent); /* command */ } bracket_depth--; return; case IF_: exprint_recur(node+node->left,prec_parent); /* IF part */ exprint_recur(node+node->right,prec_parent); /* command */ return; case IFTEST_: sprintf(pos,"if ( "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); /* expr */ sprintf(pos," ) then "); pos += strlen(pos); bracket_depth++; newline(); return; case WHILE_END_: exprint_recur(node+node->left,prec_parent); /* test part */ sprintf(pos," do "); pos += strlen(pos); bracket_depth++; if ( node->right ) { newline(); exprint_recur(node+node->right,prec_parent); /* command */ } else { strcat(pos," ;"); pos += strlen(pos); } bracket_depth--; return; case WHILE_TOP_: sprintf(pos,"while ("); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); /* expr */ strcat(pos,") "); pos += strlen(pos); return; case DO_TOP_: exprint_recur(node+node->right,prec_parent); /* command */ return; case DO_END_: sprintf(pos,"do "); pos += strlen(pos); bracket_depth++; newline(); exprint_recur(node+node->left,prec_parent); /* command */ bracket_depth--; newline(); sprintf(pos," while ("); pos += strlen(pos); exprint_recur(node+node->right,prec_parent); /* expr */ strcat(pos,") "); pos += strlen(pos); return; case FOR_END_: sprintf(pos,"for ( "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); /* FOR_TOP_ */ sprintf(pos," ) "); pos += strlen(pos); bracket_depth++; newline(); if ( node->right ) exprint_recur(node+node->right,prec_parent); /* command3 */ else { strcat(pos," ;") ; pos += strlen(pos); } /* empty command3 */ bracket_depth--; newline(); return; case FOR_TOP_: exprint_recur(node+node->left,prec_parent); /* FOR_HEAD_ */ sprintf(pos," ; "); pos += strlen(pos); exprint_recur(node+node->right,prec_parent); /* command2 */ return; case FOR_HEAD_: exprint_recur(node+node->left,prec_parent); /* FOR_ENTRY */ sprintf(pos," ; "); pos += strlen(pos); exprint_recur(node+node->right,prec_parent); /* expr */ return; case FOR_ENTRY_: exprint_recur(node+node->left,prec_parent); /* command1 */ return; case REDIRECT_: if ( node->left ) { strcat(pos,">> "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); /* command */ } else sprintf(pos,">> \"%s\" ",node->op1.string); pos += strlen(pos); return; case REDIRECTOVER_: if ( node->left ) { strcat(pos,">>> "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); /* command */ } else sprintf(pos,">>> \"%s\" ",node->op1.string); pos += strlen(pos); return; case PIPE_: if ( node->left ) { strcat(pos,"| "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); /* command */ } else sprintf(pos,"| \"%s\" ",node->op1.string); pos += strlen(pos); return; case PIPE_END_: case REDIRECT_END_: exprint_recur(node+node->right,prec_parent); /* command */ exprint_recur(node+node->left,prec_parent); /* pipe */ return; case SINGLE_REDEFD_: sprintf(pos,"%c ",node->op1.letter); pos += strlen(pos); return; case SINGLE_LETTER_: if ( single_redefine[node->op1.letter].start ) sprintf(pos,"'%c' ",node->op1.letter); else sprintf(pos,"%c ",node->op1.letter); pos += strlen(pos); return; /* toggles */ case QUIETGO_: case QUIETLOAD_: case SLICE_VIEW_: case CLIP_VIEW_: case STAR_FINAGLING_: case FORCE_DELETION_: case KUSNER_: case ESTIMATE_: case DETURCK_: case HOMOTHETY_: case SQGAUSS_: case AUTOPOP_: case AUTOCHOP_: case QUIET_: case OLD_AREA_: case APPROX_CURV_: case RUNGE_KUTTA_: case CHECK_INCREASE_: case DEBUG_: case MEAN_CURV_: case RIBIERE_CG_: case DIFFUSION_: case GRAVITY_: case CONJ_GRAD_: case TRANSFORMS_: case CONF_EDGE_SQCURV_: case EFFECTIVE_AREA_: case AREA_FIXED_: case RAW_CELLS_: case CONNECTED_CELLS_: case CLIPPED_CELLS_: case THICKEN_: case SHOW_INNER_: case SHOW_OUTER_: case COLORMAP_: case HESSIAN_DIFF_: case POST_PROJECT_: case MEAN_CURV_INT_: case OPTIMIZE_: case NORMAL_CURVATURE_: case DIV_NORMAL_CURVATURE_: case SHADING_: case FACET_COLORS_: case BOUNDARY_CURVATURE_: case NORMAL_MOTION_: case PINNING_: case VIEW_4D_: case MEMDEBUG_: case METRIC_CONVERSION_: case AUTORECALC_: case GV_BINARY_: case SELF_SIMILAR_: case AUTODISPLAY_: case FORCE_POS_DEF_: case ASSUME_ORIENTED_: case HESSIAN_QUIET_: case JIGGLE_TOGGLE_: case HESSIAN_NORMAL_: case YSMP_: case BUNCH_KAUFMAN_: case QUANTITIES_ONLY_: case LINEAR_METRIC_: case SQUARED_GRADIENT_: case H_INVERSE_METRIC_: case HESSIAN_DOUBLE_NORMAL_: case INTERP_BDRY_PARAM_: case HESSIAN_NORMAL_ONE_: case PSCOLORFLAG_: case GRIDFLAG_: case CROSSINGFLAG_: case LABELFLAG_: case SHOW_ALL_QUANTITIES_: case HESSIAN_NORMAL_PERP_: case HESSIAN_SPECIAL_NORMAL_: case ITDEBUG_: case METIS_FACTOR_: case VOLGRADS_EVERY_: case ZENER_DRAG_: case BACKCULL_: case INTERP_NORMALS_: case TORUS_FILLED_: case VERBOSE_: case AMBIENT_PRESSURE_: case DIRICHLET_MODE_: case SOBOLEV_MODE_: case KRAYNIKPOPVERTEX_FLAG_: case FUNCTION_QUANTITY_SPARSE_: case KRAYNIKPOPEDGE_FLAG_: case VISIBILITY_TEST_: case SPARSE_CONSTRAINTS_: case BLAS_FLAG_: case AUGMENTED_HESSIAN_: case BREAK_AFTER_WARNING_: case RGB_COLORS_FLAG_: case CIRCULAR_ARC_DRAW_: case BEZIER_BASIS_: case SMOOTH_GRAPH_: case MPI_DEBUG_: case POP_DISJOIN_: case POP_TO_EDGE_: case POP_TO_FACE_: case POP_ENJOIN_: case FULL_BOUNDING_BOX_: case BIG_ENDIAN_: case LITTLE_ENDIAN_: case AUTOPOP_QUARTIC_: case IMMEDIATE_AUTOPOP_: sprintf(pos,"%s %s ",keywordname(node->type), node->op1.toggle_state==ON_?"ON":"OFF"); pos += strlen(pos); break; case LOGFILE_TOGGLE_: sprintf(pos,"logfile %s ", node->op1.toggle_state==ON_?"ON":"OFF"); pos += strlen(pos); break; case KEYLOGFILE_TOGGLE_: sprintf(pos,"keylogfile %s ", node->op1.toggle_state==ON_?"ON":"OFF"); pos += strlen(pos); break; case GEOMVIEW_TOGGLE_: sprintf(pos,"geomview %s ", node->op1.toggle_state==ON_?"ON":"OFF"); pos += strlen(pos); break; case GEOMPIPE_TOGGLE_: sprintf(pos,"geompipe %s ", node->op1.toggle_state==ON_?"ON":"OFF"); pos += strlen(pos); break; case GEOMPIPE_: sprintf(pos,"geompipe "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case POSTSCRIPT_: sprintf(pos,"postscript "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case BINARY_OFF_FILE_: sprintf(pos,"binary_off_file "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case OOGLFILE_: sprintf(pos,"ooglfile "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case TOGGLEVALUE: sprintf(pos,"(%s) ",keywordname(node->op1.toggle_id)); pos += strlen(pos); break; case GET_INTERNAL_: sprintf(pos,"%s",keywordname(node->op1.name_id)); pos += strlen(pos); break; case PROCEDURE_: sprintf(pos,"%s ",globals(node->op1.name_id)->name); pos += strlen(pos); break; case PERM_PROCEDURE_: sprintf(pos,"%s ",perm_globals(node->op1.name_id)->name); pos += strlen(pos); break; case FIX_PARAMETER_: sprintf(pos,"fix %s",globals(node->op1.name_id)->name); pos += strlen(pos); break; case UNFIX_PARAMETER_: sprintf(pos,"unfix %s",globals(node->op1.name_id)->name); pos += strlen(pos); break; case FIX_QUANTITY_: sprintf(pos,"fix %s",GEN_QUANT(node->op1.quant_id)->name); pos += strlen(pos); break; case UNFIX_QUANTITY_: sprintf(pos,"unfix %s",GEN_QUANT(node->op1.quant_id)->name); pos += strlen(pos); break; case SET_Q_FIXED_: sprintf(pos,"set %s fixed",GEN_QUANT(node->op1.quant_id)->name); pos += strlen(pos); break; case SET_Q_ENERGY_: sprintf(pos,"set %s energy",GEN_QUANT(node->op1.quant_id)->name); pos += strlen(pos); break; case SET_Q_INFO_: sprintf(pos,"set %s info_only",GEN_QUANT(node->op1.quant_id)->name); pos += strlen(pos); break; case SET_Q_CONSERVED_: sprintf(pos,"set %s conserved",GEN_QUANT(node->op1.quant_id)->name); pos += strlen(pos); break; case SET_INTERNAL_: sprintf(pos,"%s %s ",keywordname(node->op1.name_id), assign_symbol(node->op2.assigntype)); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case VIEW_MATRIX_LVALUE_: sprintf(pos,"view_matrix["); pos += strlen("view_matrix["); exprint_recur(node+node->left,prec_parent); sprintf(pos,"]["); pos += 2; exprint_recur(node+node->right,prec_parent); sprintf(pos,"]"); pos += 1; break; case SET_VIEW_MATRIX_: exprint_recur(node+node->left,prec_parent); sprintf(pos," := "); pos += 4; exprint_recur(node+node->right,prec_parent); break; case SET_SCALE_: sprintf(pos,"m "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case SET_OPTIMIZE_: sprintf(pos,"optimize "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case SET_FIXED_AREA_: sprintf(pos,"area_fixed := "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case SET_GAP_CONSTANT_: sprintf(pos,"gap_constant := "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case SKINNY_: sprintf(pos,"K "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case TORDUP_: sprintf(pos,"y "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case SET_MODEL_: sprintf(pos,"M "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case INVOKE_P_MENU_: sprintf(pos,"P "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case SET_GRAVITY_: switch ( node->op1.assigntype ) { case ASSIGN_: sprintf(pos,"G "); break; case PLUSASSIGN_: sprintf(pos,"gravity += "); break; case SUBASSIGN_: sprintf(pos,"gravity -= "); break; case MULTASSIGN_: sprintf(pos,"gravity *= "); break; case DIVASSIGN_: sprintf(pos,"gravity /= "); break; } pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case SET_DIFFUSION_: sprintf(pos,"diffusion := "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case SET_THICKEN_: sprintf(pos,"thicken := "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case SET_AUTOCHOP_: sprintf(pos,"autochop := "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case SET_AMBIENT_PRESSURE_: sprintf(pos,"p "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case ZOOM_: sprintf(pos,"zoom "); pos+=strlen(pos); if ( node->left ) { exprint_recur(node+node->left,prec_parent); exprint_recur(node+node->right,prec_parent); } break; case CHDIR_: case SYSTEM_: case EXEC_: case PARALLEL_EXEC_: case READ_: case LOAD_: case PERMLOAD_: case ADDLOAD_: case SHOW_TRANS_: case TRANSFORM_EXPR_: case GEOMVIEW_: sprintf(pos,"%s ",keywordname(node->type)); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case TASK_EXEC_: sprintf(pos,"%s ",keywordname(node->type)); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); *pos = ','; pos++; exprint_recur(node+node->right,prec_parent); break; case VIEW_TRANSFORM_PARITY_: strcat(pos,"view_transform_parity["); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); strcat(pos,"]"); pos++; break; case VIEW_TRANSFORM_SWAP_COLORS_: strcat(pos,"view_transform_swap_colors["); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); strcat(pos,"]"); pos++; break; case VIEW_TRANSFORMS_NOP_: strcat(pos,"view_transforms["); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); strcat(pos,"]["); pos += 2; exprint_recur(node+node->right,prec_parent); break; case VIEW_TRANSFORMS_ELEMENT_: exprint_recur(node+node->left,prec_parent); strcat(pos,"]["); pos += 2; exprint_recur(node+node->right,prec_parent); *(pos++) = ']'; break; case IS_DEFINED_: sprintf(pos,"is_defined("); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); *(pos++) = ')'; break; case DUMP_: sprintf(pos,"dump "); pos += strlen(pos); if ( node->left ) exprint_recur(node+node->left,prec_parent); break; case SET_COLORMAP_: sprintf(pos,"colormap := "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case SHOW_VOL_: case CHECK_: case LONG_JIGGLE_: case RAW_VERAVG_: case STABILITY_TEST_: case UTEST_: case GO_: case SHELL_: case ALICE_: case RECALC_: case COUNTS_: case RAWEST_VERAVG_: case EXTRAPOLATE_: case LINEAR_: case QUADRATIC_: case REBODY_: case HESSIAN_: case SHOWQ_: case CLOSE_SHOW_: case HESSIAN_MENU_: case DIRICHLET_: case SOBOLEV_: case REORDER_STORAGE_: case DIRICHLET_SEEK_: case SOBOLEV_SEEK_: case CONVERT_TO_QUANTS_: case RENUMBER_ALL_: case DUMP_MEMLIST_: case FREE_DISCARDS_: case REPARTITION_: case SUBCOMMAND_: case ABORT_: case SIMPLEX_TO_FE_: sprintf(pos,"%s ",keywordname(node->type)); pos+=strlen(pos); break; case BURCHARD_: sprintf(pos,"%s %d ",keywordname(node->type),node->op1.maxsteps); pos+=strlen(pos); break; case SHOW_END_: if ( node[node->left].type == SHOW_ ) sprintf(pos,"show_expr "); /* prevent extraneous shows from dump */ else sprintf(pos,"show_expr "); pos += strlen(pos); exprint_recur(node+node->right,prec_parent); break; case SET_PROC_END_: { struct global *g = globals(node[node->left].op1.name_id); if ( node[node->left].type == REDEFINE_SINGLE_ ) sprintf(pos,"%c :::= ",node[node->left].op1.letter); else if ( g->flags & PERMANENT ) sprintf(pos,"%s ::= ",g->name); else sprintf(pos,"%s := ",g->name); pos += strlen(pos); if ( node[node->right].type != COMMAND_BLOCK_ ) { strcat(pos,"{"); pos++; } current_proc_locals[++current_proc_depth] = g->value.proc.locals; exprint_recur(node+node->right,prec_parent); current_proc_depth--; if ( node[node->right].type != COMMAND_BLOCK_ ) { strcat(pos,"}"); pos++; } break; } case SET_PERM_PROC_END_: sprintf(pos,"%s ::= ",perm_globals(node[node->left].op1.name_id)->name); pos += strlen(pos); if ( node[node->right].type != COMMAND_BLOCK_ ) { strcat(pos,"{"); pos++; } exprint_recur(node+node->right,prec_parent); if ( node[node->right].type != COMMAND_BLOCK_ ) { strcat(pos,"}"); pos++; } break; case ARGLIST_: { struct global *g; if ( node->op1.name_id == 0 ) break; /* empty list */ g = globals(node->op1.name_id); if ( node->left ) { exprint_recur(node+node->left,prec_parent); /* arglist */ strcat(pos,","); pos++; } sprintf(pos,"%s %s",datatype_name[node->op3.argtype],g->name); pos += strlen(pos); break; } case FUNCTION_CALL_: { struct global *g = globals(node->op1.name_id); sprintf(pos,"%s(",g->name); pos += strlen(pos); if ( node->left ) exprint_recur(node+node->left,prec_parent); /* arglist */ strcat(pos,")"); pos++; break; } case FUNCTION_CALL_RETURN_: exprint_recur(node+node->left,prec_parent); /* FUNCTION_CALL_ */ break; case FUNCTION_START_: break; case FUNCTION_DEF_START_: break; case FUNCTION_PROTO_START_: break; case FUNCTION_HEAD_: exprint_recur(node+node->right,prec_parent); /* arglist */ break; case SET_FUNCTION_: case FUNCTION_PROTO_: { struct global *g = globals(node[node->left].op1.name_id); sprintf(pos,"function %s %s (",datatype_name[node->op4.ret_type], g->name); pos += strlen(pos); current_proc_locals[++current_proc_depth] = g->value.proc.locals; exprint_recur(node+node->left,prec_parent); /* arglist */ if ( node->type == SET_FUNCTION_ ) { strcat(pos,")\n"); pos += strlen(pos); exprint_recur(node+node->right,prec_parent); /* body */ } else { strcat(pos,");\n"); pos += strlen(pos); } current_proc_depth--; break; } case PROCEDURE_CALL_: sprintf(pos,"%s(",globals(node->op1.name_id)->name); pos += strlen(pos); if ( node->left ) exprint_recur(node+node->left,prec_parent); /* arglist */ strcat(pos,")"); pos++; break; case PROCEDURE_CALL_RETURN_: exprint_recur(node+node->left,prec_parent); /* PROCEDURE_CALL_ */ break; case PROCEDURE_START_: break; case PROCEDURE_DEF_START_: break; case PROCEDURE_PROTO_START_: break; case PROCEDURE_HEAD_: exprint_recur(node+node->right,prec_parent); /* arglist */ break; case SET_ARGSPROC_: case PROCEDURE_PROTO_: { struct global *g = globals(node[node->left].op1.name_id); sprintf(pos,"procedure %s (",g->name); pos += strlen(pos); current_proc_locals[++current_proc_depth] = g->value.proc.locals; exprint_recur(node+node->left,prec_parent); /* arglist */ if ( node->type == SET_ARGSPROC_ ) { strcat(pos,")\n"); pos += strlen(pos); exprint_recur(node+node->right,prec_parent); /* body */ } else { strcat(pos,");\n"); pos += strlen(pos); } current_proc_depth--; break; } case DEFINE_IDENT_: sprintf(pos,"define %s %s",globals(node->op1.name_id)->name, datatype_name[node->op2.valtype]); pos += strlen(pos); break; case SET_DELTA_: sprintf(pos,"%s.pdelta",globals(node->op1.name_id)->name); pos += strlen(pos); switch ( node->op2.assigntype ) { case ASSIGN_: sprintf(pos," := "); break; case PLUSASSIGN_: sprintf(pos," += "); break; case SUBASSIGN_: sprintf(pos," -= "); break; case MULTASSIGN_: sprintf(pos," *= "); break; case DIVASSIGN_: sprintf(pos," /= "); break; } pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case SET_PARAM_SCALE: sprintf(pos,"%s.pscale",globals(node->op1.name_id)->name); pos += strlen(pos); switch ( node->op2.assigntype ) { case ASSIGN_: sprintf(pos," := "); break; case PLUSASSIGN_: sprintf(pos," += "); break; case SUBASSIGN_: sprintf(pos," -= "); break; case MULTASSIGN_: sprintf(pos," *= "); break; case DIVASSIGN_: sprintf(pos," /= "); break; } pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case SET_GLOBAL_: case SET_SGLOBAL_: { struct global *g = globals(node->op1.name_id); if ( g->flags & PERMANENT ) sprintf(pos,"%s ::= ",g->name); else sprintf(pos,"%s := ",g->name); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; } case SET_PERM_GLOBAL_: case SET_PERM_SGLOBAL_: sprintf(pos,"%s ::= ",globals(node->op1.name_id)->name); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case PLUSASSIGN_: sprintf(pos,"%s += ",globals(node->op1.name_id)->name); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case SUBASSIGN_: sprintf(pos,"%s -= ",globals(node->op1.name_id)->name); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case MULTASSIGN_: sprintf(pos,"%s *= ",globals(node->op1.name_id)->name); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case DIVASSIGN_: sprintf(pos,"%s /= ",globals(node->op1.name_id)->name); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case SIZEOF_ATTR_: sprintf(pos,"sizeof(%s)", EXTRAS(node->op2.eltype)[node->op1.extranum].name); pos += strlen(pos); break; case SIZEOF_ARRAY_: sprintf(pos,"sizeof(%s)",globals(node->op1.name_id)->name); pos += strlen(pos); break; case SIZEOF_STRING_: sprintf(pos,"sizeof("); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); strcat(pos,")"); pos++; break; case LAGRANGE_: sprintf(pos,"%s ",keywordname(node->type)); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); return; case LANCZOS_: case EIGENPROBE_: sprintf(pos,"%s ",keywordname(node->type)); pos += strlen(pos); if ( node->right ) { strcat(pos,"("); pos++; exprint_recur(node+node->left,prec_parent); strcat(pos,","); pos++; exprint_recur(node+node->right,prec_parent); strcat(pos,")"); pos++; } else exprint_recur(node+node->left,prec_parent); return; case RITZ_: sprintf(pos,"%s(",keywordname(node->type)); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); strcat(pos,","); pos++; exprint_recur(node+node->right,prec_parent); strcat(pos,")"); pos++; return; case GET_TORUS_PERIODS_: sprintf(pos,"torus_periods"); pos += strlen(pos); strcat(pos++,"["); exprint_recur(node+node->left,prec_parent); strcat(pos,"]["); pos+=2; exprint_recur(node+node->right,prec_parent); strcat(pos++,"]"); return; case GET_INVERSE_PERIODS_: sprintf(pos,"inverse_periods"); pos += strlen(pos); strcat(pos++,"["); exprint_recur(node+node->left,prec_parent); strcat(pos,"]["); pos+=2; exprint_recur(node+node->right,prec_parent); strcat(pos++,"]"); return; case HESSIAN_SADDLE_: case HESSIAN_SEEK_: sprintf(pos,"%s ",keywordname(node->type)); pos += strlen(pos); if ( node->left ) exprint_recur(node+node->left,prec_parent); return; case MOVE_: case AREAWEED_: case EDGEWEED_: case METIS_: case METIS_READJUST_: case KMETIS_: case BODY_METIS_: case NOTCH_: case EDGEDIVIDE_: sprintf(pos,"%s ",keywordname(node->type)); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); return; case OMETIS_: sprintf(pos,"%s ",keywordname(node->type)); pos += strlen(pos); if ( node->left ) exprint_recur(node+node->left,prec_parent); return; case JIGGLE_: strcat(pos,"j "); pos += 2; exprint_recur(node+node->left,prec_parent); return; case LIST_PROCS_: sprintf(pos,"list procedures "); pos+=strlen(pos); break; case LIST_ATTRIBUTES_: sprintf(pos,"list attributes "); pos+=strlen(pos); break; case LIST_QUANTITY_: sprintf(pos,"list quantity %s",GEN_QUANT(node->op1.quant_id)->name); pos += strlen(pos); break; case LIST_METHOD_INSTANCE_: sprintf(pos,"list method_instance %s", METH_INSTANCE(node->op1.meth_id)->name); pos += strlen(pos); break; case LIST_CONSTRAINT_: { if ( node->op1.con_id > 0 ) { sprintf(pos,"list constraint %s", get_constraint(node->op1.con_id)->name); pos += strlen(pos); } else { sprintf(pos,"list constraint "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); } } break; case LIST_BOUNDARY_: { if ( node->op1.bdry_id > 0 ) { sprintf(pos,"list constraint %s", web.boundaries[node->op1.bdry_id].name); pos += strlen(pos); } else { sprintf(pos,"list boundary "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); } } break; case TOPINFO_: sprintf(pos,"list topinfo "); pos+=strlen(pos); break; case BOTTOMINFO_: sprintf(pos,"list bottominfo "); pos+=strlen(pos); break; case AGGREGATE_END_: exprint_recur(node+node->right,prec_parent); return; case LIST_: case DELETE_: case REFINE_: case DISSOLVE_: case POP_: case FIX_: case UNFIX_: case EDGESWAP_: case VERTEX_AVERAGE_: case RAW_VERTEX_AVERAGE_: case RAWEST_VERTEX_AVERAGE_: case EQUIANGULATE_: case POP_EDGE_TO_TRI_: case POP_TRI_TO_EDGE_: case POP_QUAD_TO_QUAD_: case T1_EDGESWAP_: case REVERSE_ORIENTATION_: case WHEREAMI_COMMAND_: sprintf(pos,"%s ",keywordname(node->type)); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); return; case SINGLE_ELEMENT_INIT_: return; case SINGLE_ELEMENT_: exprint_recur(node+node->left,prec_parent); if ( node[node->left].type == INDEXED_SUBTYPE_ || node[node->left].type == INDEXED_ELEMENT_ ) if ( node[node->left].op5.string ) { sprintf(pos," %s ",node[node->left].op5.string); pos += strlen(pos); } return; case GET_VERTEXNORMAL_: strcat(pos,"vertexnormal"); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); return; case INDEXED_COORD_: strcat(pos,"x"); pos += 1; exprint_recur(node+node->left,prec_parent); return; case INDEXED_ELEMENT_: switch ( node->op1.eltype ) { case VERTEX: strcat(pos,"vertex["); break; case EDGE: strcat(pos,"edge["); break; case FACET: strcat(pos,"facet["); break; case BODY: strcat(pos,"body["); break; case FACETEDGE: strcat(pos,"facetedge["); break; } pos += strlen(pos); exprint_recur(node+node->left,prec_parent); strcat(pos++,"]"); return; case INDEXED_SUBTYPE_: exprint_recur(node+node->left,prec_parent); sprintf(pos,".%s[",typenames[node->op1.eltype]); pos += strlen(pos); exprint_recur(node+node->right,prec_parent); strcat(pos,"]"); pos += strlen(pos); return; case SELF_ELEMENT_: strcat(pos,"self"); pos+=4; break; case SINGLE_ELEMENT_EXPR_: exprint_recur(node+node->left,prec_parent); break; case SYMBOL_: case SYMBOL_ELEMENT_: sprintf(pos,"%s", node->op5.string); pos += strlen(pos); break; case SET_PHASE_: set_print(node,"set","phase",prec_parent); break; case SET_DENSITY_: set_print(node,"set","density",prec_parent); break; case SET_VOLCONST_: set_print(node,"set","volconst",prec_parent); break; case SET_TARGET_: set_print(node,"set","target",prec_parent); break; case SET_PRESSURE_: set_print(node,"set","pressure",prec_parent); break; case SET_OPACITY_: set_print(node,"set","opacity",prec_parent); break; case SET_CONSTRAINT_: set_print(node,"set","constraint",prec_parent); break; case SET_CONSTRAINT_NAME: set_print(node,"set","constraint ",prec_parent); strcat(pos,get_constraint((int)(node[node->left].op1.real))->name); pos += strlen(pos); break; case SET_NAMED_QUANTITY_: set_print(node,"set","quantity",prec_parent); break; case UNSET_NAMED_QUANTITY_: set_print(node,"unset","quantity",prec_parent); break; case SET_METHOD_INSTANCE_: set_print(node,"set","method_instance",prec_parent); break; case UNSET_METHOD_INSTANCE_: set_print(node,"unset","method_instance",prec_parent); break; case SET_EXTRA_ATTR_: { ex = EXTRAS(node->op3.extra_info >> ESHIFT) +(node->op3.extra_info & 0xFF); set_print(node,"set",ex->name,prec_parent); if ( node->right ) exprint_recur(node+node->right,prec_parent); } break; case SET_COLOR_: set_print(node,"set","color",prec_parent); break; case SET_FRONTCOLOR_: set_print(node,"set","frontcolor",prec_parent); break; case SET_BACKCOLOR_: set_print(node,"set","backcolor",prec_parent); break; case SET_TAG_: set_print(node,"set","tag",prec_parent); break; case SET_BACKGROUND_: set_print(node,"set","background",prec_parent); break; case SET_COORD_: { char cname[10]; if ( (vch == 'X') && (node->op2.coordnum+1 <= 3) ) sprintf(cname,"%c",'x'+node->op2.coordnum); else sprintf(cname,"%c%d",vch,node->op2.coordnum+1); set_print(node,"set",cname,prec_parent); } break; case SET_PARAM_: { char cname[10]; sprintf(cname,"P%d",node->op2.coordnum+1); set_print(node,"set",cname,prec_parent); } break; case UNSET_FIXED_: set_print(node,"unset","fixed",prec_parent); break; case SET_FIXED_: set_print(node,"set","fixed",prec_parent); break; case UNSET_BARE_: set_print(node,"unset","bare",prec_parent); break; case SET_BARE_: set_print(node,"set","bare",prec_parent); break; case UNSET_NO_DISPLAY_: set_print(node,"unset","no_display",prec_parent); break; case SET_NO_DISPLAY_: set_print(node,"set","no_display",prec_parent); break; case UNSET_NONCONTENT_: set_print(node,"unset","noncontent",prec_parent); break; case SET_NONCONTENT_: set_print(node,"set","noncontent",prec_parent); break; case UNSET_HIT_PARTNER_: set_print(node,"unset","hit_partner",prec_parent); break; case SET_HIT_PARTNER_: set_print(node,"set","hit_partner",prec_parent); break; case UNSET_NO_REFINE_: set_print(node,"unset","no_refine",prec_parent); break; case SET_NO_REFINE_: set_print(node,"set","no_refine",prec_parent); break; case UNSET_TRIPLE_PT_: set_print(node,"unset","triple_point",prec_parent); break; case SET_ORIENTATION_: set_print(node,"set","orientation",prec_parent); break; case UNSET_TETRA_PT_: set_print(node,"unset","tetra_point",prec_parent); break; case UNSET_AXIAL_POINT_: set_print(node,"unset","axial_point",prec_parent); break; case UNSET_FACET_BODY_: set_print(node,"unset","body",prec_parent); break; case SET_FRONTBODY_: set_print(node,"set","frontbody",prec_parent); break; case UNSET_FRONTBODY_: set_print(node,"unset","frontbody",prec_parent); break; case SET_BACKBODY_: set_print(node,"set","backbody",prec_parent); break; case UNSET_BACKBODY_: set_print(node,"unset","backbody",prec_parent); break; case UNSET_DENSITY_: set_print(node,"unset","density",prec_parent); break; case UNSET_PRESSURE_: set_print(node,"unset","pressure",prec_parent); break; case UNSET_VOLUME_: case UNSET_TARGET_: set_print(node,"unset","target",prec_parent); break; case UNSET_CONSTRAINT_: set_print(node,"unset","constraint",prec_parent); break; case UNSET_CONSTRAINT_NAME: { char temp[100]; sprintf(temp,"constraint %s",get_constraint(node->op3.connum)->name); set_print(node,"unset",temp,prec_parent); pos += strlen(pos); break; } case UNSET_BOUNDARY_: set_print(node,"unset","boundary",prec_parent); break; case UNSET_BOUNDARY_NAME: { char temp[100]; sprintf(temp,"boundary %s",web.boundaries[node->op3.bdrynum].name); set_print(node,"unset",temp,prec_parent); pos += strlen(pos); break; } case SET_INIT_ : break; case SET_ATTRIBUTE_LOOP_: { struct treenode *nnode; sprintf(pos," set "); pos += strlen(pos); nnode = node + node->left; if ( nnode->type == WHERE_ ) nnode += nnode->left; /* get NEXT_ */ exprint_recur(nnode,prec_parent); if ( node->right ) exprint_recur(node+node->right,prec_parent); if ( node[node->left].type == WHERE_ ) { node+= node->left; sprintf(pos," where "); pos += strlen(pos); exprint_recur(node+node->right,prec_parent); } } return; case FOREACH_: sprintf(pos,"foreach "); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); sprintf(pos," do "); pos += strlen(pos); bracket_depth++; newline(); exprint_recur(node+node->right,prec_parent); bracket_depth--; return; case MAX_: case MIN_: case SUM_: case AVG_: case COUNT_: case HISTOGRAM_: case LOGHISTOGRAM_: sprintf(pos,"%s(",keywordname(node->type)); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); sprintf(pos,","); pos++; exprint_recur(node+node->right,prec_parent); sprintf(pos,")"); pos++; return; case WHERE_: exprint_recur(node+node->left,prec_parent); sprintf(pos," where "); pos += strlen(pos); exprint_recur(node+node->right,prec_parent); return; case NEXT_VERTEX_: case NEXT_EDGE_VERTEX_: case NEXT_FACET_VERTEX_: case NEXT_BODY_VERTEX_: case NEXT_FACETEDGE_: case NEXT_EDGE_: case NEXT_VERTEX_EDGE_: case NEXT_FACET_EDGE_: case NEXT_FACET_: case NEXT_VERTEX_FACET_: case NEXT_EDGE_FACET_: case NEXT_BODY_: case NEXT_VERTEX_BODY_: case NEXT_EDGE_BODY_: case NEXT_FACET_BODY_: case NEXT_BODY_FACET_: case NEXT_BODY_EDGE_: exprint_recur(node+node->left,prec_parent); if ( strcmp(node->op5.string,default_name) != 0 ) { sprintf(pos,"%s ",node->op5.string); pos += strlen(pos); } return; case INIT_FACETEDGE_: sprintf(pos,"facetedges "); pos += strlen(pos); return; case INIT_VERTEX_: sprintf(pos,"vertices "); pos += strlen(pos); return; case INIT_EDGE_VERTEX_: case INIT_FACET_VERTEX_: case INIT_BODY_VERTEX_: exprint_recur(node+node->left,prec_parent); sprintf(pos,".vertices "); pos += strlen(pos); return; case INIT_EDGE_: sprintf(pos,"edges "); pos += strlen(pos); return; case INIT_VERTEX_EDGE_: case INIT_FACET_EDGE_: case INIT_BODY_EDGE_: exprint_recur(node+node->left,prec_parent); sprintf(pos,".edges "); pos += strlen(pos); return; case INIT_FACET_: sprintf(pos,"facets "); pos += strlen(pos); return; case INIT_VERTEX_FACET_: case INIT_EDGE_FACET_: case INIT_BODY_FACET_: exprint_recur(node+node->left,prec_parent); sprintf(pos,".facets "); pos += strlen(pos); return; case INIT_BODY_: sprintf(pos,"bodies "); pos += strlen(pos); break; case INIT_VERTEX_BODY_: case INIT_EDGE_BODY_: case INIT_FACET_BODY_: exprint_recur(node+node->left,prec_parent); sprintf(pos,".bodies "); pos += strlen(pos); return; case PUSH_NAMED_QUANTITY: sprintf(pos,"%s ",GEN_QUANT(node->op1.quant_id)->name); pos += strlen(pos); return; case PUSH_METHOD_INSTANCE_: sprintf(pos,"%s ",METH_INSTANCE(node->op1.meth_id)->name); pos += strlen(pos); return; case PUSHCONST: #ifdef LONGDOUBLE sprintf(pos,"%1.*Lg",DPREC,node->op1.real); #else sprintf(pos,"%1.15g",node->op1.real); #endif pos += strlen(pos); return; case PUSHPI: sprintf(pos,"pi"); pos += strlen(pos); return; case PUSHE: sprintf(pos,"e"); pos += strlen(pos); return; case PUSHG: sprintf(pos,"G"); pos += strlen(pos); return; case COORD_: if ( node->left ) { exprint_recur(node+node->left,PREC_COND); sprintf(pos,"."); pos += strlen(pos); } if ( (vch == 'X') && (node->op2.coordnum+1 <= 3) ) sprintf(msg,"%c",'x'+node->op2.coordnum); else sprintf(msg,"%c%d",vch,node->op2.coordnum+1); print_attr(node,msg); return; case PARAM_: sprintf(msg,"P%d",node->op2.coordnum+1); print_attr(node,msg); return; case PUSHPARAM: if ( (vch == 'X') && (node->op1.coordnum+1 <= 3) ) sprintf(msg,"%c",'x'+node->op1.coordnum); else sprintf(msg,"%c%d",vch,node->op1.coordnum+1); print_attr(node,msg); return; case SET_MMODULUS_: sprintf(pos,"%s.modulus %s ",METH_INSTANCE(node->op1.meth_id)->name, assign_symbol(node->op2.assigntype)); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case SET_QMODULUS_: sprintf(pos,"%s.modulus %s ",GEN_QUANT(node->op1.quant_id)->name, assign_symbol(node->op2.assigntype)); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case SET_QTARGET_: sprintf(pos,"%s.target %s ",GEN_QUANT(node->op1.quant_id)->name, assign_symbol(node->op2.assigntype)); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case SET_QVOLCONST_: sprintf(pos,"%s.volconst %s ",GEN_QUANT(node->op1.quant_id)->name, assign_symbol(node->op2.assigntype)); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case SET_QTOLERANCE_: sprintf(pos,"%s.tolerance %s ",GEN_QUANT(node->op1.quant_id)->name, assign_symbol(node->op2.assigntype)); pos += strlen(pos); exprint_recur(node+node->left,prec_parent); break; case PUSHQFIXED_: sprintf(pos,"%s.fixed",GEN_QUANT(node->op1.quant_id)->name); pos += strlen(pos); return; case PUSHQENERGY_: sprintf(pos,"%s.energy",GEN_QUANT(node->op1.quant_id)->name); pos += strlen(pos); return; case PUSHQINFO_ONLY_: sprintf(pos,"%s.info_only",GEN_QUANT(node->op1.quant_id)->name); pos += strlen(pos); return; case PUSHQCONSERVED_: sprintf(pos,"%s.conserved",GEN_QUANT(node->op1.quant_id)->name); pos += strlen(pos); return; case PUSHQPRESSURE_: sprintf(pos,"%s.pressure",GEN_QUANT(node->op1.quant_id)->name); pos += strlen(pos); return; case PUSHQTARGET_: sprintf(pos,"%s.target",GEN_QUANT(node->op1.quant_id)->name); pos += strlen(pos); return; case PUSHMVALUE_: sprintf(pos,"%s.value",METH_INSTANCE(node->op1.meth_id)->name); pos += strlen(pos); return; case PUSHQVALUE_: sprintf(pos,"%s.value",GEN_QUANT(node->op1.quant_id)->name); pos += strlen(pos); return; case PUSHMMODULUS_: sprintf(pos,"%s.modulus",GEN_QUANT(node->op1.meth_id)->name); pos += strlen(pos); return; case PUSHQMODULUS_: sprintf(pos,"%s.modulus",GEN_QUANT(node->op1.quant_id)->name); pos += strlen(pos); return; case PUSHQVOLCONST_: sprintf(pos,"%s.volconst",globals(node->op1.quant_id)->name); pos += strlen(pos); return; case PUSHDELTA_: sprintf(pos,"%s.pdelta",globals(node->op1.name_id)->name); pos += strlen(pos); return; case PUSH_PARAM_SCALE: sprintf(pos,"%s.pscale",globals(node->op1.name_id)->name); pos += strlen(pos); return; case PUSH_PARAM_FIXED: sprintf(pos,"%s.fixed",globals(node->op1.name_id)->name); pos += strlen(pos); return; case PUSH_PARAM_EXTRA_: sprintf(pos,"%s.%s",globals(node->op1.name_id)->name, EXTRAS(0)[node->op2.extranum].name); pos += strlen(pos); return; case ELEMENT_IDENT_: { struct global *g = globals(node->op3.name_id); sprintf(pos,"%s",g->name); pos += strlen(pos); return; } case PUSHGLOBAL_: case STRINGGLOBAL_: { struct global *g = globals(node->op1.name_id); if ( g->flags & QUANTITY_NAME ) sprintf(pos,"total %s",g->name); else sprintf(pos,"%s",g->name); pos += strlen(pos); return; } case PUSH_PERM_GLOBAL_: case PERM_STRINGGLOBAL_: sprintf(pos,"%s",perm_globals(node->op1.name_id)->name); pos += strlen(pos); return; case USERFUNC: sprintf(pos,"usr%d",node->op1.userfunc+1); pos += strlen(pos); return; case DYNAMIC_LOAD_FUNC_: sprintf(pos,globals(node->op2.name_id)->name); pos += strlen(pos); return; case PLUS: binary_print(node,prec_parent,PREC_ADD," + ",PREC_ADD); return; case MINUS: binary_print(node,prec_parent,PREC_SUB," - ",PREC_SUB+1); return; case EQUATE: binary_print(node,prec_parent,PREC_ASSIGN," = ",PREC_ASSIGN+1); return; case TIMES: binary_print(node,prec_parent,PREC_MUL,"*",PREC_MUL); return; case DIVIDE: binary_print(node,prec_parent,PREC_DIV,"/",PREC_DIV+1); return; case REALMOD: binary_print(node,prec_parent,PREC_DIV,"%%",PREC_DIV+1); return; case IMOD_: binary_print(node,prec_parent,PREC_DIV," imod ",PREC_DIV+1); return; case IDIV_: binary_print(node,prec_parent,PREC_DIV," idiv ",PREC_DIV+1); return; case COND_ELSE_: binary_print(node,prec_parent,PREC_COND,"):(",PREC_COND); strcat(pos++,")"); return; case COND_EXPR_: strcat(pos++,"("); binary_print(node,prec_parent,PREC_COND,")?(",PREC_COND); return; case COND_TEST_: exprint_recur(node+node->left,PREC_COND); return; case INV: exprint_recur(node+node->left,PREC_POW); sprintf(pos,"^(-1)"); pos += strlen(pos); return; case INTPOW: exprint_recur(node+node->left,PREC_POW); sprintf(pos,"^%1d",node->op1.intpow); pos += strlen(pos); return; case POW: binary_print(node,prec_parent,PREC_POW,"**",PREC_POW); return; case MAXIMUM_: strcat(pos,"maximum("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); strcat(pos++,","); exprint_recur(node+node->right,PREC_ARG); strcat(pos++,")"); return; case MINIMUM_: strcat(pos,"minimum("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); strcat(pos++,","); exprint_recur(node+node->right,PREC_ARG); strcat(pos++,")"); return; case INCOMPLETE_ELLIPTICF: sprintf(pos,"incompleteEllipticF("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); strcat(pos++,","); exprint_recur(node+node->right,PREC_ARG); strcat(pos++,")"); return; case INCOMPLETE_ELLIPTICE: sprintf(pos,"incompleteEllipticE("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); strcat(pos++,","); exprint_recur(node+node->right,PREC_ARG); strcat(pos++,")"); return; case ATAN2_: sprintf(pos,"atan2("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); strcat(pos++,","); exprint_recur(node+node->right,PREC_ARG); strcat(pos++,")"); return; case WRAP_COMPOSE_: sprintf(pos,"wrap_compose("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); strcat(pos++,","); exprint_recur(node+node->right,PREC_ARG); strcat(pos++,")"); return; case WRAP_INVERSE_: sprintf(pos,"wrap_inverse("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case SQR: sprintf(pos,"("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")^2"); pos += strlen(pos); return; case SQRT: sprintf(pos,"sqrt("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case FLOOR_: sprintf(pos,"floor("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case CEIL_: sprintf(pos,"ceil("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case ABS: sprintf(pos,"abs("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case SINH: sprintf(pos,"sinh("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case COSH: sprintf(pos,"cosh("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case TANH: sprintf(pos,"tanh("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case ACOSH: sprintf(pos,"acosh("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case ASINH: sprintf(pos,"asinh("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case ATANH: sprintf(pos,"atanh("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case SIN: sprintf(pos,"sin("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case COS: sprintf(pos,"cos("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case TAN: sprintf(pos,"tan("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case EXP: sprintf(pos,"exp("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case LOG: sprintf(pos,"log("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case ASIN: sprintf(pos,"asin("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case ACOS: sprintf(pos,"acos("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case ATAN: sprintf(pos,"atan("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case ELLIPTICK: sprintf(pos,"ellipticK("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case ELLIPTICE: sprintf(pos,"ellipticE("); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); sprintf(pos,")"); pos += strlen(pos); return; case CHS: sprintf(pos,"-"); pos += strlen(pos); exprint_recur(node+node->left,PREC_UMINUS); return; case NOT_: sprintf(pos," not "); pos += strlen(pos); exprint_recur(node+node->left,PREC_NOT); return; case VIEW_MATRIX_: sprintf(pos,"view_matrix["); pos += strlen(pos); exprint_recur(node+node->left,PREC_ARG); strcat(pos,"]["); pos += 2; exprint_recur(node+node->right,PREC_ARG); strcat(pos++,"]"); return; case LENGTH_: case VALENCE_: case AREA_: case VOLUME_: case DENSITY_: case PHASE_: case ID_: case STAR_: case OID_: case TAG_: case ORIGINAL_: case FIXED_: case NO_REFINE_: case HIT_PARTNER_: case NONCONTENT_: case NODISPLAY_: case BARE_: case SQ_MEAN_CURV_: sprintf(pos," %s ",keywordname(node->type)); pos += strlen(pos); return; case GET_EXTRA_ATTR_: ex = EXTRAS(node->op2.eltype) + node->op3.extranum; print_attr(node,ex->name); if ( node->left ) exprint_recur(node+node->left,prec_parent); break; case ON_QUANTITY_: print_attr(node,"on_quantity "); strcat(pos,GEN_QUANT(node->op2.quant_id)->name); pos += strlen(pos); return; case ON_METHOD_INSTANCE_: print_attr(node,"on_method_instance "); strcat(pos,METH_INSTANCE(node->op2.meth_id)->name); pos += strlen(pos); return; case ON_CONSTRAINT_: print_attr(node,"on_constraint "); exprint_recur(node+node->left,prec_parent); return; case ON_CONSTRAINT_NAME: print_attr(node,"on_constraint "); strcat(pos,get_constraint(node->op3.connum)->name); pos += strlen(pos); return; case HIT_CONSTRAINT_: print_attr(node,"hit_constraint "); exprint_recur(node+node->left,prec_parent); return; case HIT_CONSTRAINT_NAME: print_attr(node,"hit_constraint "); strcat(pos,get_constraint(node->op3.connum)->name); pos += strlen(pos); return; case ON_BOUNDARY_: print_attr(node,"on_boundary "); exprint_recur(node+node->left,prec_parent); return; case ON_BOUNDARY_NAME: print_attr(node,"on_boundary "); strcat(pos,web.boundaries[node->op3.bdrynum].name); pos += strlen(pos); return; case QUALIFIED_ATTRIBUTE: exprint_recur(node+node->left,prec_parent); strcat(pos,"."); pos++; exprint_recur(node+node->right,prec_parent); return; case GET_MIDV_: print_attr(node,"midv"); return; case GET_TRIPLE_PT_: print_attr(node,"triple_point"); return; case GET_TETRA_PT_: print_attr(node,"tetra_point"); return; case GET_AXIAL_POINT_: print_attr(node,"axial_point"); return; case GET_FIXED_: print_attr(node,"fixed"); return; case GET_BARE_: print_attr(node,"bare"); return; case GET_NO_DISPLAY_: print_attr(node,"no_display"); return; case GET_NONCONTENT_: print_attr(node,"noncontent"); return; case GET_HIT_PARTNER_: print_attr(node,"hit_partner"); return; case GET_NO_REFINE_: print_attr(node,"no_refine"); return; case GET_ORIGINAL_: print_attr(node,"original"); return; case GET_ID_: print_attr(node,"id"); return; case GET_STAR_: print_attr(node,"star"); return; case GET_OID_: print_attr(node,"oid"); return; case GET_VALENCE_: print_attr(node,"valence"); return; case GET_COLOR_: print_attr(node,"color"); return; case GET_FRONTCOLOR_: print_attr(node,"frontcolor"); return; case GET_BACKCOLOR_: print_attr(node,"backcolor"); return; case GET_FRONTBODY_: print_attr(node,"frontbody"); return; case GET_BACKBODY_: print_attr(node,"backbody"); return; case GET_TAG_: print_attr(node,"tag"); return; case GET_ORIENTATION_: print_attr(node,"orientation"); return; case GET_SHOW_: print_attr(node,"show"); return; case GET_LENGTH_: print_attr(node,"length"); return; case GET_MEANCURV_: print_attr(node,"mean_curvature"); return; case GET_FIXEDVOL_: print_attr(node,"volfixed"); return; case GET_MID_EDGE_: print_attr(node,"mid_edge"); return; case GET_MID_FACET_: print_attr(node,"mid_facet"); return; case GET_WRAP_: print_attr(node,"wrap"); return; case GET_SQ_MEAN_CURV_: print_attr(node,"sqcurve"); return; case GET_DIHEDRAL_: print_attr(node,"dihedral"); return; case GET_AREA_: print_attr(node,"area"); return; case GET_VOLUME_: print_attr(node,"volume"); return; case GET_VOLCONST_: print_attr(node,"volconst"); return; case GET_TARGET_: print_attr(node,"target"); return; case GET_MPI_TASK_: print_attr(node,"mpi_task"); return; case GET_PRESSURE_: print_attr(node,"pressure"); return; case GET_USERATTR_: print_attr(node,"user_attr"); return; case GET_DENSITY_: print_attr(node,"density"); return; case GET_PHASE_: print_attr(node,"phase"); return; case GET_QUANTITY_: print_attr(node,GEN_QUANT(node->op2.quant_id)->name); return; case GET_INSTANCE_: print_attr(node,METH_INSTANCE(node->op2.meth_id)->name); return; case INDEXSET_: case DIMENSIONSET_: if ( node->right ) { exprint_recur(node+node->left,prec_parent); strcat(pos++,"["); exprint_recur(node+node->right,prec_parent); strcat(pos++,"]"); } else { strcat(pos++,"["); exprint_recur(node+node->left,prec_parent); strcat(pos++,"]"); } break; case SET_ATTRIBUTE_: strcat(pos,"set "); pos += 4; case SET_ATTRIBUTE_A: /* single element assign */ case SET_ATTRIBUTE_L: /* skip printing set when in set loop */ if ( pos[-1] != '.' ) strcat(pos++," "); /* just to be sure */ switch ( node->op2.attr_kind ) { case SET_DENSITY_: print_set_attr(node,"density"); break; case SET_EXTRA_ATTR_: { ex = EXTRAS(node->op3.extra_info>>ESHIFT) +(node->op3.extra_info&0xFF); print_attr(node,ex->name); if ( node->right ) exprint_recur(node+node->right,prec_parent); } break; case SET_PHASE_ : print_set_attr(node,"phase"); break; case SET_WRAP_ : print_set_attr(node,"wrap"); break; case SET_ORIENTATION_ : print_set_attr(node,"orientation"); break; case SET_TARGET_: print_set_attr(node,"target"); break; case SET_VOLCONST_: print_set_attr(node,"volconst"); break; case SET_PRESSURE_: print_set_attr(node,"pressure"); break; case SET_OPACITY_: print_set_attr(node,"opacity"); break; case SET_COLOR_: print_set_attr(node,"color"); break; case SET_ORIGINAL_: print_set_attr(node,"original"); break; case SET_FRONTBODY_: print_set_attr(node,"frontbody"); break; case SET_BACKBODY_: print_set_attr(node,"backbody"); break; case SET_FRONTCOLOR_: print_attr(node,"frontcolor"); break; case SET_BACKCOLOR_: print_set_attr(node,"backcolor"); break; case SET_CONSTRAINT_: { struct constraint *con; print_set_attr(node,"constraint "); if ( node[node->left].type == PUSHCONST ) { int cnum = (int)(node[node->left].op1.real); con = get_constraint(cnum); if ( (cnum <= web.maxcon) && (con->attr & NAMED_THING) ) { strcat(pos,con->name); pos += strlen(pos); return; } /* else recursion takes care of expression number constant */ } pos += strlen(pos); break; } case SET_BOUNDARY_: { struct boundary *bdry; print_set_attr(node,"boundary "); if ( node[node->left].type == PUSHCONST ) { int bnum = (int)(node[node->left].op1.real); bdry = web.boundaries+bnum; if ( (bnum <= web.bdrymax) && (bdry->attr & NAMED_THING) ) { strcat(pos,bdry->name); pos += strlen(pos); return; } /* else recursion takes care of expression number constant */ } pos += strlen(pos); break; } case SET_TAG_: print_set_attr(node,"tag"); break; case SET_FIXED_: print_set_attr(node,"fixed"); break; case SET_BARE_: print_set_attr(node,"bare"); break; case SET_NO_REFINE_: print_set_attr(node,"no_refine"); break; case SET_HIT_PARTNER_: print_set_attr(node,"hit_partner"); break; case SET_NONCONTENT_: print_set_attr(node,"noncontent"); break; case SET_NO_DISPLAY_: print_set_attr(node,"no_display"); break; case SET_TETRA_PT_: print_set_attr(node,"tetra_pt"); break; case SET_AXIAL_POINT_: print_set_attr(node,"axial_point"); break; case SET_TRIPLE_PT_: print_set_attr(node,"triple_point"); break; case SET_COORD_1: if ( node->right ) { print_set_attr(node,"x"); exprint_recur(node+node->right,prec_parent); break; } else print_set_attr(node,"X1"); break; case SET_COORD_2: print_set_attr(node,"X2"); break; case SET_COORD_3: print_set_attr(node,"X3"); break; case SET_COORD_4: print_set_attr(node,"X4"); break; case SET_COORD_5: print_set_attr(node,"X5"); break; case SET_COORD_6: print_set_attr(node,"X6"); break; case SET_COORD_7: print_set_attr(node,"X7"); break; case SET_COORD_8: print_set_attr(node,"X8"); break; case SET_PARAM_1: if ( node->right ) { print_set_attr(node,"p1["); pos += 3; exprint_recur(node+node->right,prec_parent); strcat(pos,"]"); pos += strlen(pos); break; } else print_set_attr(node,"P1"); break; case SET_PARAM_2: print_set_attr(node,"P2"); break; case SET_PARAM_3: print_set_attr(node,"P3"); break; case SET_PARAM_4: print_set_attr(node,"P4"); break; default: sprintf(errmsg,"Internal error: bad SET_ATTRIBUTE type %d.\n", node->op2.attr_kind); kb_error(1655,errmsg,RECOVERABLE); } if ( node->type == SET_ATTRIBUTE_A ) { switch ( node[1].op1.assigntype ) { case ASSIGN_: strcat(pos," := "); break; case PLUSASSIGN_: strcat(pos," += "); break; case SUBASSIGN_: strcat(pos," -= "); break; case MULTASSIGN_: strcat(pos," *= "); break; case DIVASSIGN_: strcat(pos," /= "); break; } pos += 4; } strcat(pos," ("); pos += 2; if ( node->left ) exprint_recur(node+node->left,prec_parent); strcat(pos++,")"); return; case SINGLE_ASSIGN_ : exprint_recur(node+node->left,prec_parent); *(pos++) = '.'; *pos = 0; exprint_recur(node+node->right,prec_parent); return; case EQ_: binary_print(node,prec_parent,PREC_COMP," == ",PREC_COMP); return; case NE_: binary_print(node,prec_parent,PREC_COMP," != ",PREC_COMP); return; case GE_: binary_print(node,prec_parent,PREC_COMP," >= ",PREC_COMP); return; case LE_: binary_print(node,prec_parent,PREC_COMP," <= ",PREC_COMP); return; case GT_: binary_print(node,prec_parent,PREC_COMP," > ",PREC_COMP); return; case LT_: binary_print(node,prec_parent,PREC_COMP," < ",PREC_COMP); return; case AND_: binary_print(node,prec_parent,PREC_AND," && ",PREC_AND); return; case OR_: binary_print(node,prec_parent,PREC_OR," || ",PREC_OR); return; case CONJUNCTION_END: exprint_recur(node+node->left,prec_parent); return; default: sprintf(pos,"(unknown)"); pos += strlen(pos); sprintf(errmsg,"Printing of expression node type %s (%d) unimplemented.\n", tokname(node->type),node->type); kb_error(1656,errmsg,WARNING); return; } return ; /* shouldn't happen */ } /* end exprint_recur */ /************************************************************************** * * function: binary_print() * * purpose: print binary operation with parentheses if needed. * */ void binary_print(node,prec_parent,prec1,op,prec2) struct treenode *node; int prec_parent; int prec1; char *op; int prec2; { if ( prec_parent > prec1 ) { sprintf(pos,"("); pos += strlen(pos); } exprint_recur(node+node->left,prec1); sprintf(pos,op); pos += strlen(pos); exprint_recur(node+node->right,prec2); if ( prec_parent > prec1 ) { sprintf(pos,")"); pos += strlen(pos); } return; } /************************************************************************** * * function: set_print() * * purpose: print SET type command, with possible WHERE clause. * */ void set_print(node,keyw,attrw,prec_parent) struct treenode *node; char *keyw,*attrw; int prec_parent; { struct treenode *nnode; sprintf(pos,"%s ",keyw); pos += strlen(pos); nnode = node + node->left; if ( nnode->type == WHERE_ ) nnode += nnode->left; /* get NEXT_ */ exprint_recur(nnode,prec_parent); sprintf(pos," %s ",attrw); pos += strlen(pos); if ( node->right ) exprint_recur(node+node->right,prec_parent); if ( node[node->left].type == WHERE_ ) { node+= node->left; sprintf(pos," where "); pos += strlen(pos); exprint_recur(node+node->right,prec_parent); } return; } void print_attr(node,word) struct treenode *node; char *word; { sprintf(pos,"%s",word); pos += strlen(pos); return; } void print_set_attr(node,word) struct treenode *node; char *word; { sprintf(pos,"%s",word); pos += strlen(pos); return; } evolver-2.30c.dfsg/src/grapher.c0000644000175300017530000006050611410765113017020 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************* * * file: grapher.c * * contents: Functions for control of interactive * graphics display. */ #include "include.h" #ifndef TRUE #define TRUE 1 #define FALSE 0 #endif #define dang (M_PI/30) /* rotation increment */ static REAL zoomfactor = 1.2; /* scale factor */ static REAL low[MAXCOORD],high[MAXCOORD]; /* extreme coordiates */ static REAL mid[MAXCOORD]; /* midpoint of extremes */ /* matrices */ /* image display motion done via homogeneous coordinates */ static REAL **spinl, **tipup; /* rotation increment matrices */ static REAL **spinr, **tipdown; /* rotation increment matrices */ static REAL **clockwise, **counterclock; /* rotation increment matrices */ static REAL **transleft,**transright; /* translation increment matrices */ static REAL **transup,**transdown; /* translation increment matrices */ static REAL **zoom, **shrink; /* scaling matrices */ static int showflag; /******************************************************************** * * function: update_display() * * purpose: wrapper for local_update_display() */ void update_display() { #ifdef MPI_EVOLVER if ( this_task == 0 ) mpi_update_display(); #endif local_update_display(); } /******************************************************************** * * function: local_update_display() * * purpose: Check for necessity of re-displaying surface. * */ void local_update_display() { graph_timestamp = ++global_timestamp; /* new surface */ if ( go_display_flag ) { if ( OOGL_flag ) UpdateOOGL(); else display(); } } /******************************************************************** * * function: do_show() * * purpose: Handles 's' command, displays and switches to * graphics command mode. * */ void do_show() { char line[100]; /* for reading user commands */ int old_flag = iterate_flag; #ifdef MAC_OS_X do_show_flag++; /* kludge so mac_exec_commands() does do_show() again */ #endif #ifndef OPENGL /* to prevent unnecessary recalculation of display */ graph_timestamp = ++global_timestamp; /* new surface */ #endif if ( torus_display_mode == TORUS_DEFAULT_MODE ) ask_wrap_display(); /* main loop */ showflag = 1; do { iterate_flag = 2; if ( showflag ) display(); showflag = 1; /* default to show next time around, unless option below decides otherwise */ #ifdef MOTIF return; #endif if ( prompt("Graphics command: ",line,sizeof(line)) == EOF ) break; } while ( view_transform(line) ); iterate_flag = old_flag; } /******************************************************************** * * function: ask_wrap_display() * * purpose: Dialog asking for symmetry group display mode. * */ void ask_wrap_display() { if ( commandfd != stdin ) return; if ( web.torus_flag ) { char response[100]; if ( web.skel[BODY].count == 0 ) prompt("Display raw facets or clipped cell? (0,2): ",response,sizeof(response)); else prompt("Display raw facets, connected bodies or clipped cell? (0,1,2): ", response,sizeof(response)); switch ( response[0] ) { case '0' : torus_display_mode = TORUS_RAW_MODE; web.torus_body_flag = 0; web.torus_clip_flag = 0; break; case '1' : if ( web.skel[BODY].count == 0 ) kb_error(1042,"There are no bodies to display connectedly.\n", WARNING); else { web.torus_body_flag = 1; web.torus_clip_flag = 0; torus_display_mode = TORUS_CONNECTED_MODE;} break; case '2' : web.torus_body_flag = 0; web.torus_clip_flag = 1; torus_display_mode = TORUS_CLIPPED_MODE; break; } } else if ( web.symmetry_flag ) { char response[100]; if ( web.skel[BODY].count == 0 ) { torus_display_mode = TORUS_RAW_MODE; return; } prompt("Display raw cell or connected bodies? (0,1): ",response,sizeof(response)); switch ( response[0] ) { case '0' : torus_display_mode = TORUS_RAW_MODE; web.torus_body_flag = 0; web.torus_clip_flag = 0; break; case '1' : if ( web.skel[BODY].count == 0 ) kb_error(1043,"There are no bodies to display connectedly.\n", WARNING); else { web.torus_body_flag = 1; web.torus_clip_flag = 0; torus_display_mode = TORUS_CONNECTED_MODE;} break; } } } /******************************************************************** * * function: view_transform() * * purpose: parse and execute "graphics command" input. * */ int view_transform(string) char *string; { char *c; size_t legal; /* number of legal characters at start of string */ /* test for illegal characters */ legal = strspn(string, "0123456789.+-udrlcCRmzsABDxqtvwbeETH?h\034\035\036\037\033\133\n\r"); if ( legal != strlen(string) ) { sprintf(msg,"Illegal character in graphics command: %c",string[legal]); kb_error(1044,msg,WARNING); showflag = 0; /* don't reshow */ return 1; } for ( c = string ; *c ; c++ ) { int reps = 1; /* repetition count */ REAL val = 0.0; /* for arbitrary rotations */ int decflag = 0; /* whether have real number for angle or other */ char *cc = c; if ( isdigit(*c) ) reps = atoi(c); if ( isdigit(*c) || (*c=='.') || (*c=='-')) { val = atof(c); if ( *cc == '.' ) decflag = 1; cc = c+1; while ( isdigit(*cc) || (*cc=='.')) { if ( *cc == '.' ) decflag = 1; cc++; } } if ( decflag ) { reps = 1; c = cc; } else if ( *c == '-' ) { /* '-' for color decrement */ } else { c = cc; val = 6.0; /* default angle */ } while ( reps-- > 0 ) switch ( *c ) { case 0: return 1; case 'u': if ( !decflag ) val = 6.0; set_tipup(val*M_PI/180); mat_mult(tipup,view,view,HOMDIM,HOMDIM,HOMDIM); break; case 'd': if ( !decflag ) val = 6.0; set_tipdown(val*M_PI/180); mat_mult(tipdown,view,view,HOMDIM,HOMDIM,HOMDIM); break; case 'r': if ( !decflag ) val = 6.0; set_spinr(val*M_PI/180); mat_mult(spinr,view,view,HOMDIM,HOMDIM,HOMDIM); break; case 'l': if ( !decflag ) val = 6.0; set_spinl(val*M_PI/180); mat_mult(spinl,view,view,HOMDIM,HOMDIM,HOMDIM); break; case 'c': if ( !decflag ) val = 6.0; set_clockwise(val*M_PI/180); mat_mult(clockwise,view,view,HOMDIM,HOMDIM,HOMDIM); break; case 'C': if ( !decflag ) val = 6.0; set_counterclockwise(val*M_PI/180); mat_mult(counterclock,view,view,HOMDIM,HOMDIM,HOMDIM); break; case 'z': if ( !decflag ) val = 1.2; set_zoom(val); mat_mult(zoom ,view,view,HOMDIM,HOMDIM,HOMDIM); break; case 's': if ( !decflag ) val = 1.2; set_zoom(1/val); mat_mult(zoom,view,view,HOMDIM,HOMDIM,HOMDIM); break; /* MS-DOS arrow keys for translation */ case 30: transup[SDIM>2?2:1][HOMDIM-1] = decflag ? val : 0.25; mat_mult(transup, view,view,HOMDIM,HOMDIM,HOMDIM); break; case 31: transdown[SDIM>2?2:1][HOMDIM-1] = decflag ? -val : -0.25; mat_mult(transdown,view,view,HOMDIM,HOMDIM,HOMDIM); break; case 28: transright[SDIM>2?1:0][HOMDIM-1] = decflag ? val : 0.25; mat_mult(transright,view,view,HOMDIM,HOMDIM,HOMDIM); break; case 29: transleft[SDIM>2?1:0][HOMDIM-1] = decflag ? -val : -0.25; mat_mult(transleft,view,view,HOMDIM,HOMDIM,HOMDIM); break; case 0x1b : /* ANSI arrow keys for translation */ if ( *(++c) != 0x5B ) { if ( isprint(*c) ) sprintf(msg,"Unrecognized character: %c\n",*c); else sprintf(msg,"Unrecognized character: 0x%04X\n",*c); outstring(msg); break; } switch ( *(++c) ) { case 0x41: transup[SDIM>2?2:1][HOMDIM-1] = decflag ? val : 0.25; mat_mult(transup, view,view,HOMDIM,HOMDIM,HOMDIM); break; case 0x42: transdown[SDIM>2?2:1][HOMDIM-1] = decflag ? -val : -0.25; mat_mult(transdown,view,view,HOMDIM,HOMDIM,HOMDIM); break; case 0x43: transright[SDIM>2?1:0][HOMDIM-1] = decflag ? val : 0.25; mat_mult(transright,view,view,HOMDIM,HOMDIM,HOMDIM); break; case 0x44: transleft[SDIM>2?1:0][HOMDIM-1] = decflag ? -val : -0.25; mat_mult(transleft,view,view,HOMDIM,HOMDIM,HOMDIM); break; default: if ( isprint(*c) ) sprintf(msg,"Unrecognized character: %c\n",*c); else sprintf(msg,"Unrecognized character: 0x%04X \n",*c); outstring(msg); break; } break; case 'R': if ( decflag ) /* particular scaling */ { int i; matcopy(view,identmat,HOMDIM,HOMDIM); for ( i = 0 ; i < HOMDIM-1 ; i++ ) { view[i][i] = val; } } else resize(); reps = 0; graph_timestamp = ++global_timestamp; break; case 'm': /* middle, for centering */ { do_gfile(0,NULL); /* get bounding box */ if ( SDIM == 2 ) { view[0][HOMDIM-1] -= (bbox_maxx+bbox_minx)/2; view[1][HOMDIM-1] -= (bbox_maxy+bbox_miny)/2; } else { view[1][HOMDIM-1] -= (bbox_maxx+bbox_minx)/2; view[2][HOMDIM-1] -= (bbox_maxy+bbox_miny)/2; } break; } case 'x': case 'q': return 0; case 't': if ( !web.symmetry_flag ) break; ask_wrap_display(); graph_timestamp = ++global_timestamp; reps = 0; break; case 'B': bdry_showflag = !bdry_showflag; graph_timestamp = ++global_timestamp; reps = 0; break; case 'v': ridge_color_flag = !ridge_color_flag; reps = 0; graph_timestamp = ++global_timestamp;break; case 'w': no_wall_flag = !no_wall_flag; reps = 0; graph_timestamp = ++global_timestamp;break; case 'b': box_flag = !box_flag; reps = 0; graph_timestamp = ++global_timestamp; break; case 'e': edgeshow_flag = !edgeshow_flag; graph_timestamp = ++global_timestamp; break; case 'E': triple_edgeshow_flag = !triple_edgeshow_flag; graph_timestamp = ++global_timestamp; break; case 'T': transforms_flag = !transforms_flag; graph_timestamp = ++global_timestamp; break; case '+': fillcolor++; sprintf(msg,"fillcolor %d\n",fillcolor); outstring(msg); graph_timestamp = ++global_timestamp; reps = 0; break; case '-': fillcolor--; sprintf(msg,"fillcolor %d\n",fillcolor); outstring(msg); graph_timestamp = ++global_timestamp; reps = 0; break; case 'H': web.hide_flag = !web.hide_flag; reps = 0; graph_timestamp = ++global_timestamp; break; case '?': case 'h': graph_help(); showflag = 0; reps = 0; break; case '\n': case '\r': break; default: if ( isprint(*c) ) sprintf(msg,"Unrecognized letter: %c\n",*c); else sprintf(msg,"Unrecognized character: 0x%04x \n",*c); outstring(msg); reps = 0; showflag = 0; break; } } return 1; } /* end view_transform() */ /******************************************************************** * * function: init_view() * * purpose: Initialize viewing and transform matrices. * */ void init_view() { int i; view = dmatrix(0,SDIM,0,SDIM); if ( identmat == NULL ) { /* first time set-up */ /* set up identity matrix */ identmat = perm_matrix2(MAXCOORD+1,MAXCOORD+1); for ( i = 0 ; i <= MAXCOORD ; i++ ) identmat[i][i] = 1.0; to_focus = perm_matrix2(MAXCOORD+1,MAXCOORD+1); from_focus = perm_matrix2(MAXCOORD+1,MAXCOORD+1); /* set rotation matrices */ spinr = perm_matrix2(MAXCOORD+1,MAXCOORD+1); matcopy(spinr,identmat,SDIM+1,SDIM+1); spinr[0][0] = spinr[1][1] = cos(dang); spinr[0][1] = -(spinr[1][0] = sin(dang)); spinl = perm_matrix2(MAXCOORD+1,MAXCOORD+1); matcopy(spinl,identmat,SDIM+1,SDIM+1); spinl[0][0] = spinl[1][1] = cos(dang); spinl[0][1] = -(spinl[1][0] = -sin(dang)); tipup = perm_matrix2(MAXCOORD+1,MAXCOORD+1); matcopy(tipup,identmat,SDIM+1,SDIM+1); tipup[0][0] = tipup[2][2] = cos(dang); tipup[0][2] = -(tipup[2][0] = sin(dang)); tipdown = perm_matrix2(MAXCOORD+1,MAXCOORD+1); matcopy(tipdown,identmat,SDIM+1,SDIM+1); tipdown[0][0] = tipdown[2][2] = cos(dang); tipdown[0][2] = -(tipdown[2][0] = -sin(dang)); clockwise = perm_matrix2(MAXCOORD+1,MAXCOORD+1); matcopy(clockwise,identmat,SDIM+1,SDIM+1); clockwise[1][1] = clockwise[2][2] = cos(dang); clockwise[1][2] = -(clockwise[2][1] = -sin(dang)); counterclock = perm_matrix2(MAXCOORD+1,MAXCOORD+1); matcopy(counterclock,identmat,SDIM+1,SDIM+1); counterclock[1][1] = counterclock[2][2] = cos(dang); counterclock[1][2] = -(counterclock[2][1] = sin(dang)); /* set magnifying matrix */ zoom = perm_matrix2(MAXCOORD+1,MAXCOORD+1); matcopy(zoom,identmat,SDIM+1,SDIM+1); zoom[0][0] = zoom[1][1] = zoom[2][2] = zoomfactor; /* set shrink matrix */ shrink = perm_matrix2(MAXCOORD+1,MAXCOORD+1); matcopy(shrink,identmat,SDIM+1,SDIM+1); shrink[0][0] = shrink[1][1] = shrink[2][2] = 1/zoomfactor; /* set translation matrices */ transleft = perm_matrix2(MAXCOORD+1,MAXCOORD+1); transup = perm_matrix2(MAXCOORD+1,MAXCOORD+1); transright = perm_matrix2(MAXCOORD+1,MAXCOORD+1); transdown = perm_matrix2(MAXCOORD+1,MAXCOORD+1); } set_view_matrix_global(); matcopy(to_focus,identmat,SDIM+1,SDIM+1); matcopy(from_focus,identmat,SDIM+1,SDIM+1); } /******************************************************************** * * functions: set_*() * * purpose: set key entries of transform matrices. * */ void set_spinr(val) REAL val; { spinr[0][0] = spinr[1][1] = cos(val); spinr[0][1] = -(spinr[1][0] = sin(val)); } void set_spinl(val) REAL val; { spinl[0][0] = spinl[1][1] = cos(val); spinl[0][1] = -(spinl[1][0] = -sin(val)); } void set_tipup(val) REAL val; { tipup[0][0] = tipup[2][2] = cos(val); tipup[0][2] = -(tipup[2][0] = sin(val)); } void set_tipdown(val) REAL val; { tipdown[0][0] = tipdown[2][2] = cos(val); tipdown[0][2] = -(tipdown[2][0] = -sin(val)); } void set_clockwise(val) REAL val; { clockwise[1][1] = clockwise[2][2] = cos(val); clockwise[1][2] = -(clockwise[2][1] = -sin(val)); } void set_counterclockwise(val) REAL val; { counterclock[1][1] = counterclock[2][2] = cos(val); counterclock[1][2] = -(counterclock[2][1] = sin(val)); } void set_zoom(val) REAL val; { zoom[0][0] = zoom[1][1] = val; if ( web.sdim >= 3 ) zoom[2][2] = val; } /******************************************************************** * * function: reset_view() * * purpose: re-initialize view matrix. * */ void reset_view() { HOMDIM = web.sdim + 1; matcopy(transleft,identmat,HOMDIM,HOMDIM); matcopy(transdown,identmat,HOMDIM,HOMDIM); matcopy(transright,identmat,HOMDIM,HOMDIM); matcopy(transup,identmat,HOMDIM,HOMDIM); if ( SDIM > 2 ) { transright[1][HOMDIM-1] = .25; transleft[1][HOMDIM-1] = -.25; transup[2][HOMDIM-1] = .25; transdown[2][HOMDIM-1] = -.25; } else /* show x-y plane */ { transright[0][HOMDIM-1] = .25; transleft[0][HOMDIM-1] = -.25; transup[1][HOMDIM-1] = .25; transdown[1][HOMDIM-1] = -.25; shrink[2][2] = 1.0; zoom[2][2] = 1.0; } } /******************************************************************** * * function: resize() * * purpose: Recalculate bounding box of surface. * Also initializes clip_view and slice_view if empty. * */ void resize() { int i,j,k; vertex_id v_id; REAL size; /* if domain is torus, get torus fundamental cell in view */ if ( web.torus_flag ) { for ( i = 0 ; i < SDIM ; i++ ) /* coordinate loop */ { low[i] = high[i] = 0.0; for ( j = 0 ; j < SDIM ; j++ ) /* axis loop */ if ( web.torus_period[j][i] < 0.0 ) low[i] += web.torus_period[j][i]; else high[i] += web.torus_period[j][i]; } if ( transforms_flag ) { REAL x[MAXCOORD+1]; for ( j = 0 ; j < SDIM ; j++ ) { for ( i = 0 ; i < SDIM ; i++ ) x[i] = web.torus_period[j][i]; x[SDIM] = 1.0; for ( k = 0 ; k < transform_count ; k++ ) { REAL xx,newx[MAXCOORD+1]; matvec_mul(view_transforms[k],x,newx,SDIM+1,SDIM+1); for ( i = 0 ; i < SDIM ; i++ ) { xx = newx[i]/newx[SDIM]; /* project */ if ( xx < low[i] ) low[i] = xx; if ( xx > high[i] ) high[i] = xx; } } } for ( i = 0 ; i < SDIM ; i++ ) x[i] = 0; x[SDIM] = 1.0; for ( k = 0 ; k < transform_count ; k++ ) { REAL xx,newx[MAXCOORD+1]; matvec_mul(view_transforms[k],x,newx,SDIM+1,SDIM+1); for ( i = 0 ; i < SDIM ; i++ ) { xx = newx[i]/newx[SDIM]; /* project */ if ( xx < low[i] ) low[i] = xx; if ( xx > high[i] ) high[i] = xx; } } } } else if ( web.symmetry_flag ) { edge_id e_id; for ( i = 0 ; i < SDIM ; i++ ) /* initialize */ { low[i] = 1e30; high[i] = -1e30; } /* figure out how big window should be */ FOR_ALL_EDGES(e_id) { REAL *t; REAL x[MAXCOORD+1],y[MAXCOORD+1]; t = get_coord(get_edge_tailv(e_id)); for ( i = 0 ; i < SDIM ; i++ ) x[i] = t[i]; (*sym_wrap)(get_coord(get_edge_headv(e_id)),y,get_edge_wrap(e_id)); x[SDIM] = y[SDIM] = 1.0; /* homogeneous coord */ for ( i = 0 ; i < SDIM ; i++ ) { if ( x[i] < low[i] ) low[i] = x[i]; if ( x[i] > high[i] ) high[i] = x[i]; if ( y[i] < low[i] ) low[i] = y[i]; if ( y[i] > high[i] ) high[i] = y[i]; } if ( transforms_flag ) for ( j = 0 ; j < transform_count ; j++ ) { REAL xx,newx[MAXCOORD+1]; matvec_mul(view_transforms[j],x,newx,SDIM+1,SDIM+1); for ( i = 0 ; i < SDIM ; i++ ) { xx = newx[i]/newx[SDIM]; /* project */ if ( xx < low[i] ) low[i] = xx; if ( xx > high[i] ) high[i] = xx; } matvec_mul(view_transforms[j],y,newx,SDIM+1,SDIM+1); for ( i = 0 ; i < SDIM ; i++ ) { xx = newx[i]/newx[SDIM]; /* project */ if ( xx < low[i] ) low[i] = xx; if ( xx > high[i] ) high[i] = xx; } } } } else { for ( i = 0 ; i < SDIM ; i++ ) /* initialize */ { low[i] = 1e30; high[i] = -1e30; } /* figure out how big window should be */ FOR_ALL_VERTICES(v_id) { REAL *x = get_coord(v_id); if ( transform_count && transforms_flag ) { REAL y[MAXCOORD+1]; for ( i = 0 ; i < SDIM ; i++ ) y[i] = x[i]; y[SDIM] = 1.0; /* homogeneous coord */ for ( j = 0 ; j < transform_count ; j++ ) { REAL xx,newx[MAXCOORD+1]; matvec_mul(view_transforms[j],y,newx,SDIM+1,SDIM+1); if ( fabs(newx[SDIM]) < 1e-12 ) { sprintf(errmsg,"View transform matrix %d is singular.\n",j+1); kb_error(1045,errmsg,WARNING); } else for ( i = 0 ; i < SDIM ; i++ ) { xx = newx[i]/newx[SDIM];; if ( xx < low[i] ) low[i] = xx; if ( xx > high[i] ) high[i] = xx; } } } /* end transforms */ else /* just plain vertices */ for ( i = 0 ; i < SDIM ; i++ ) { if ( x[i] < low[i] ) low[i] = x[i]; if ( x[i] > high[i] ) high[i] = x[i]; } } } for ( i = 0 ; i < SDIM ; i++ ) mid[i] = (low[i] + high[i])/2; size = high[2] - low[2]; if ( high[1] - low[1] > size ) size = high[1] - low[1]; if ( high[0] - low[0] > size ) size = high[0] - low[0]; /* transformation matrix will be set up to scale object into [-1,1]^3 cube */ matcopy(view,identmat,HOMDIM,HOMDIM); if ( size != 0.0 ) for ( i = 0 ; i < HOMDIM-1 ; i++ ) { view[i][i] = 2/size; view[i][HOMDIM-1] = -mid[i]*2/size; } if ( to_focus ) /* for oglgraph.c focus reset */ { matcopy(to_focus,identmat,HOMDIM,HOMDIM); matcopy(from_focus,identmat,HOMDIM,HOMDIM); } /* see if clip_view and slice_view need defaults */ if ( (clip_coeff[0][0] == 0.0) && (clip_coeff[0][1] == 0.0) && (clip_coeff[0][2] == 0.0)) { clip_coeff[0][0] = 1.0; clip_coeff[0][3] = mid[0]; } if ( (slice_coeff[0] == 0.0) && (slice_coeff[1] == 0.0) && (slice_coeff[2] == 0.0)) { slice_coeff[0] = 1.0; slice_coeff[3] = mid[0]; } overall_size = size; /* for anybody who wants to know how big */ if ( !user_thickness_flag ) thickness = 0.001*size; } /******************************************************************** * * function: fix_ctm() * * Rotates coordinate transformation matrix according to how mouse * dragged. */ void fix_ctm(viewmat,dx,dy) REAL **viewmat; /* matrix to modify */ REAL dx,dy; /* pixels mouse dragged */ { MAT2D(rot,MAXCOORD+1,MAXCOORD+1); REAL alpha; /* angle around axis */ REAL theta; /* tilt of rotation axis */ int i,j; for ( i = 0 ; i < HOMDIM ; i++ ) { for ( j = 0 ; j < HOMDIM ; j++ ) rot[i][j] = 0.0; rot[i][i] = 1.0; } alpha = sqrt(dx*dx + dy*dy)/300; /* one radian per 300 pixels */ if ( dx == 0.0 ) { if ( dy > 0.0 ) theta = M_PI/2; else if ( dy < 0.0 ) theta = -M_PI/2; else goto ctm_exit; /* no change */ } else { theta = atan(dy/dx); if ( dx < 0.0 ) alpha = - alpha; } if ( SDIM == 2 ) { /* tilt axis */ rot[2][2] = 1.0; rot[0][0] = rot[1][1] = cos(theta); rot[0][1] = sin(theta); rot[1][0] = -sin(theta); mat_mult(rot,viewmat,viewmat,HOMDIM,HOMDIM,HOMDIM); /* rotate */ rot[2][2] = rot[0][0] = cos(alpha); rot[2][0] = -sin(alpha); rot[0][2] = sin(alpha); rot[1][1] = 1.0; rot[0][1] = rot[1][0] = 0.0; mat_mult(rot,viewmat,viewmat,HOMDIM,HOMDIM,HOMDIM); /* untilt axis */ rot[2][2] = 1.0; rot[0][0] = rot[1][1] = cos(theta); rot[0][1] = -sin(theta); rot[1][0] = sin(theta); rot[2][0] = rot[0][2] = 0.0; } else { /* tilt axis */ rot[0][0] = 1.0; rot[1][1] = rot[2][2] = cos(theta); rot[1][2] = sin(theta); rot[2][1] = -sin(theta); mat_mult(rot,viewmat,viewmat,HOMDIM,HOMDIM,HOMDIM); /* rotate */ rot[0][0] = rot[1][1] = cos(alpha); rot[0][1] = -sin(alpha); rot[1][0] = sin(alpha); rot[2][2] = 1.0; rot[1][2] = rot[2][1] = 0.0; mat_mult(rot,viewmat,viewmat,HOMDIM,HOMDIM,HOMDIM); /* untilt axis */ rot[0][0] = 1.0; rot[1][1] = rot[2][2] = cos(theta); rot[1][2] = -sin(theta); rot[2][1] = sin(theta); rot[0][1] = rot[1][0] = 0.0; } mat_mult(rot,viewmat,viewmat,HOMDIM,HOMDIM,HOMDIM); ctm_exit: ; } /* end fix_ctm() */ evolver-2.30c.dfsg/src/method5.c0000644000175300017530000015362511410765113016742 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * * * * Code for SVK and Neo_hookean submitted by * * Dr. Rabah Bouzidi * * Institut de Recherche en Gnie Civil et Mcanique * * (Civil Engineering and Mechanical Research Institut) * * Nantes University * * France * * Tl : +33 2 51 12 55 23 * * rabah.bouzidi@physique.univ-nantes.fr * *************************************************************/ #include "include.h" REAL LambertW ARGS((REAL)); /************************************************************************ Named method: SVK (Saint-Venant - Kirchhoff) potential Psi = lambda/2*(tr(E))^2+mu*(E:E) - (3 lambda + 2 mu) * alpha*(theta)*tr(E) with E=(C-I)/2 the Green-Lagrange Strain tensor theta = T-T0 : temperture variation alpha : thermal dilation coefficient Written by Dr. Rabah Bouzidi ************************************************************************/ #define LAMBDA_NAME "SVK_lambda" #define MU_NAME "SVK_mu" #define ALPHA_NAME "SVK_alpha" #define THETA_NAME "SVK_theta" #define FORM_FACTORS_NAME "form_factors" static int lambda_attr; /* number of lambda extra attribute */ static int mu_attr; /* number of mu extra attribute */ static int alpha_attr ; /* number of alpha extra attribute */ static int theta_attr ; /* number of theta extra attribute */ extern int form_factors_attr; /* number of form_factors extra attribute */ /*************************************************************** * * function: SVK_init() * * purpose: Make sure needed extra attributes are present. * */ void SVK_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { if ( web.modeltype != LINEAR ) kb_error(2811,"Saint-Veant - Kirchhoff method only for LINEAR model.\n",RECOVERABLE); if ( web.dimension != 2 ) kb_error(2812,"Saint-Veant - Kirchhoff method only for SOAPFILM model.\n", RECOVERABLE); /* extra edge atribute */ lambda_attr = find_attribute(FACET,LAMBDA_NAME); if ( lambda_attr < 0 ) /* not found */ kb_error(2813,"Facet extra attribute SVK_lambda missing. Needed by SVK_elastic method.\n",RECOVERABLE); mu_attr = find_attribute(FACET,MU_NAME); if ( mu_attr < 0 ) /* not found */ kb_error(2814,"Facet extra attribute SVK_mu missing. Needed by SVK_elastic method.\n",RECOVERABLE); alpha_attr = find_attribute(FACET,ALPHA_NAME); if ( alpha_attr < 0 ) /* not found */ kb_error(2815,"Facet extra attribute alpha missing. Needed by Saint-Veant - Kirchhoff.\n",RECOVERABLE); theta_attr = find_attribute(FACET,THETA_NAME); if ( theta_attr < 0 ) /* not found */ kb_error(2816,"Facet extra attribute theta missing. Needed by Saint-Veant - Kirchhoff.\n",RECOVERABLE); form_factors_attr = find_attribute(FACET,FORM_FACTORS_NAME); if ( form_factors_attr < 0 ) /* not found */ kb_error(2817,"Facet extra attribute form_factors real[3] missing. Needed by Saint-Veant - Kirchhoff.\n",RECOVERABLE); if ( EXTRAS(FACET)[form_factors_attr].array_spec.datacount != 3 ) kb_error(2818,"Facet extra attribute form_factors must have size 3.\n", RECOVERABLE); } /************************************************************************ * * function: SVK_all() * * purpose: energy, gradient, and hessian for linear_elastic method. */ REAL SVK_all ARGS((struct qinfo *,int)); REAL SVK_all(f_info,mode) struct qinfo *f_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ { REAL *s; /* pointer to extra attributes */ REAL **side; REAL q11,q12,q22; /* Q entries */ REAL det; /* det S */ REAL lambda; /* lambda coefficient */ REAL area; /* reference area of facet (area of facet in initial configuration - undeformed facet)*/ REAL mu; /* mu coefficient */ REAL alpha; /* Thermic dilatation coefficient */ REAL theta ; /* Relative variation of temperture to the the reference state's temperature*/ REAL f11,f12,f22; REAL c11,c12,c21,c22; REAL energy; REAL dc11dv[FACET_VERTS][MAXCOORD]; REAL dc12dv[FACET_VERTS][MAXCOORD]; REAL dc21dv[FACET_VERTS][MAXCOORD]; REAL dc22dv[FACET_VERTS][MAXCOORD]; REAL ddc11dv[FACET_VERTS][MAXCOORD][FACET_VERTS]; REAL ddc12dv[FACET_VERTS][MAXCOORD][FACET_VERTS]; REAL ddc21dv[FACET_VERTS][MAXCOORD][FACET_VERTS]; REAL ddc22dv[FACET_VERTS][MAXCOORD][FACET_VERTS]; /* Potential derivatives */ REAL dpsirdc11,dpsirdc12,dpsirdc21,dpsirdc22; REAL ddpsirdc11dc11, ddpsirdc11dc12, ddpsirdc11dc21, ddpsirdc11dc22; REAL ddpsirdc12dc11, ddpsirdc12dc12, ddpsirdc12dc21, ddpsirdc12dc22; REAL ddpsirdc21dc11, ddpsirdc21dc12, ddpsirdc21dc21, ddpsirdc21dc22; REAL ddpsirdc22dc11, ddpsirdc22dc12, ddpsirdc22dc21, ddpsirdc22dc22; int i,j,ii,jj; lambda = *(REAL*)get_extra(f_info->id,lambda_attr); mu = *(REAL*)get_extra(f_info->id,mu_attr); alpha = *(REAL*)get_extra(f_info->id,alpha_attr); theta = *(REAL*)get_extra(f_info->id,theta_attr); s = (REAL*)get_extra(f_info->id,form_factors_attr); det = s[0]*s[2] - s[1]*s[1]; if ( det <= 0.0 ) { if ( mode == METHOD_VALUE ) return 0.0; sprintf(errmsg," SVK_elastic: Facet %d has unstrained area 0.\n", ordinal(f_info->id)+1); kb_error(2895,errmsg,RECOVERABLE); } area = sqrt(det)/2.0; q11 = s[2]/det; q12 = -s[1]/det; q22 = s[0]/det; side = f_info->sides[0]; f11 = SDIM_dot(side[0],side[0]); f12 = SDIM_dot(side[0],side[1]); f22 = SDIM_dot(side[1],side[1]); c11 = f11*q11 + f12*q12; c12 = f11*q12 + f12*q22; c21 = f12*q11 + f22*q12; c22 = f12*q12 + f22*q22; /* This exression of the potential takes into account the plane stress condition : Sig33 = 0, This condition leads to the expression of c33, which can be written as a function of c11 and c22 : c33 = -(lambda*(c11+c22-3)-2*mu)/(lambda+2*mu) */ energy =area*((2.0*mu+3.0*lambda)*alpha*theta*(-(2.0*mu+3.0*lambda)*alpha*theta-2.0*mu*(c11+c22-2.0))+((c22*c22+c11*c11+2.0*c12*c21+2.0-2.0*c11-2.0*c22)*mu*mu+(c11*c22+c12*c21+c22*c22+c11*c11+3.0-3.0*c11-3.0*c22)*lambda*mu))/(2.0*lambda+4.0*mu); if ( mode == METHOD_VALUE ) return energy; /* gradient */ for ( i = 0 ; i < SDIM ; i++ ) { dc11dv[1][i] = 2*side[0][i]*q11 + side[1][i]*q12; dc11dv[2][i] = side[0][i]*q12; dc12dv[1][i] = 2*side[0][i]*q12 + side[1][i]*q22; dc12dv[2][i] = side[0][i]*q22; dc21dv[1][i] = side[1][i]*q11; dc21dv[2][i] = side[0][i]*q11 + 2*side[1][i]*q12; dc22dv[1][i] = side[1][i]*q12; dc22dv[2][i] = side[0][i]*q12 + 2*side[1][i]*q22; dc11dv[0][i] = -(dc11dv[1][i] + dc11dv[2][i]); dc12dv[0][i] = -(dc12dv[1][i] + dc12dv[2][i]); dc21dv[0][i] = -(dc21dv[1][i] + dc21dv[2][i]); dc22dv[0][i] = -(dc22dv[1][i] + dc22dv[2][i]); } dpsirdc11 = mu*((2.0*c11-2.0)*mu+2.0*lambda*c11-3.0*lambda+lambda*c22-4.0*alpha*theta*mu-6.0*alpha*theta*lambda)/(lambda+2.0*mu)/2.0; dpsirdc12 = mu*c21/2.0; dpsirdc21 = mu*c12/2.0; dpsirdc22 = mu*((2.0*c22-2.0)*mu+2.0*lambda*c22-3.0*lambda+lambda*c11-4.0*alpha*theta*mu-6.0*alpha*theta*lambda)/(lambda+2.0*mu)/2.0; for ( j = 0 ; j < FACET_VERTS ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) { f_info->grad[j][i] = area*(dpsirdc11*dc11dv[j][i] +dpsirdc12*dc12dv[j][i] +dpsirdc21*dc21dv[j][i] +dpsirdc22*dc22dv[j][i]); } if ( mode == METHOD_GRADIENT ) return energy; /* hessian */ for ( i = 0 ; i < SDIM ; i++ ) { ddc11dv[1][i][1] = 2*q11; ddc11dv[1][i][2] = q12; ddc11dv[2][i][1] = q12; ddc11dv[2][i][2] = 0.0; ddc12dv[1][i][1] = 2*q12; ddc12dv[1][i][2] = q22; ddc12dv[2][i][1] = q22; ddc12dv[2][i][2] = 0.0; ddc21dv[1][i][1] = 0.0; ddc21dv[1][i][2] = q11; ddc21dv[2][i][1] = q11; ddc21dv[2][i][2] = 2*q12; ddc22dv[1][i][1] = 0.0; ddc22dv[1][i][2] = q12; ddc22dv[2][i][1] = q12; ddc22dv[2][i][2] = 2*q22; for ( j = 1 ; j < FACET_VERTS; j++ ) { ddc11dv[0][i][j] = -(ddc11dv[1][i][j] + ddc11dv[2][i][j]); ddc12dv[0][i][j] = -(ddc12dv[1][i][j] + ddc12dv[2][i][j]); ddc21dv[0][i][j] = -(ddc21dv[1][i][j] + ddc21dv[2][i][j]); ddc22dv[0][i][j] = -(ddc22dv[1][i][j] + ddc22dv[2][i][j]); ddc11dv[j][i][0] = -(ddc11dv[j][i][1] + ddc11dv[j][i][2]); ddc12dv[j][i][0] = -(ddc12dv[j][i][1] + ddc12dv[j][i][2]); ddc21dv[j][i][0] = -(ddc21dv[j][i][1] + ddc21dv[j][i][2]); ddc22dv[j][i][0] = -(ddc22dv[j][i][1] + ddc22dv[j][i][2]); } ddc11dv[0][i][0] = -(ddc11dv[1][i][0] + ddc11dv[2][i][0]); ddc12dv[0][i][0] = -(ddc12dv[1][i][0] + ddc12dv[2][i][0]); ddc21dv[0][i][0] = -(ddc21dv[1][i][0] + ddc21dv[2][i][0]); ddc22dv[0][i][0] = -(ddc22dv[1][i][0] + ddc22dv[2][i][0]); } ddpsirdc11dc11 = mu*(mu+lambda)/(lambda+2.0*mu); ddpsirdc11dc12 = 0.0; ddpsirdc11dc21 = 0.0 ; ddpsirdc11dc22 = lambda*mu/(2.0*lambda+4.0*mu); ddpsirdc12dc11 = 0.0; ddpsirdc12dc12 = 0.0; ddpsirdc12dc21 = mu/2.0; ddpsirdc12dc22 = 0.0; ddpsirdc21dc11 = 0.0; ddpsirdc21dc12 = mu/2.0; ddpsirdc21dc21 = 0.0; ddpsirdc21dc22 = 0.0; ddpsirdc22dc11 = mu*lambda/(2.0*lambda+4.0*mu); ddpsirdc22dc12 = 0.0; ddpsirdc22dc21 = 0.0; ddpsirdc22dc22 = mu*(mu+lambda)/(lambda+2.0*mu) ; for ( j = 0 ; j < FACET_VERTS ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) for ( jj = 0 ; jj < FACET_VERTS ; jj++ ) { f_info->hess[j][jj][i][i] += area*(dpsirdc11*ddc11dv[j][i][jj] +dpsirdc12*ddc21dv[j][i][jj] +dpsirdc21*ddc12dv[j][i][jj] +dpsirdc22*ddc22dv[j][i][jj]); for ( ii = 0 ; ii < SDIM ; ii++ ) f_info->hess[j][jj][i][ii] += area*((ddpsirdc11dc11+ddpsirdc12dc11+ddpsirdc21dc11+ddpsirdc22dc11)*dc11dv[j][i] +(ddpsirdc11dc12+ddpsirdc12dc12+ddpsirdc21dc12+ddpsirdc22dc12)*dc12dv[j][i] +(ddpsirdc11dc21+ddpsirdc12dc21+ddpsirdc21dc21+ddpsirdc22dc21)*dc21dv[j][i] +(ddpsirdc11dc22+ddpsirdc12dc22+ddpsirdc21dc22+ddpsirdc22dc22)*dc22dv[j][i]); } return energy; } /************************************************************** * * function: SVK_energy() (Saint-Veant - Kirchhoff potential) * * purpose: calculates energy of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL SVK_energy(f_info) struct qinfo *f_info; { return SVK_all(f_info,METHOD_VALUE); } /************************************************************** * * function: SVK_gradient() * * purpose: calculates gradient of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL SVK_gradient(f_info) struct qinfo *f_info; { return SVK_all(f_info,METHOD_GRADIENT); } /************************************************************** * * function: SVK_hessian() * * purpose: calculates hessian of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL SVK_hessian(f_info) struct qinfo *f_info; { return SVK_all(f_info,METHOD_HESSIAN); } /***********************************************************************/ /************************************************************************ Named method: Neo_Hookean Written by Dr. Rabah Bouzidi ************************************************************************/ #define NEO_LAMBDA_NAME "neo_lambda" #define NEO_MU_NAME "neo_mu" #define FORM_FACTORS_NAME "form_factors" /*************************************************************** * * function: Neo_Hookean_init() * * purpose: Make sure needed extra attributes are present. * */ void Neo_Hookean_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { if ( web.modeltype != LINEAR ) kb_error(2819,"Neo_Hookean method only for LINEAR model.\n",RECOVERABLE); if ( web.dimension != 2 ) kb_error(2829,"Neo_Hookean method only for SOAPFILM model.\n", RECOVERABLE); /* extra edge atribute */ lambda_attr = find_attribute(FACET,NEO_LAMBDA_NAME); if ( lambda_attr < 0 ) /* not found */ kb_error(2837,"Facet extra attribute neo_lambda missing. Needed by Neo_Hookean.\n",RECOVERABLE); mu_attr = find_attribute(FACET,NEO_MU_NAME); if ( mu_attr < 0 ) /* not found */ kb_error(2838,"Facet extra attribute neo_mu missing. Needed by Neo_Hookean.\n",RECOVERABLE); form_factors_attr = find_attribute(FACET,FORM_FACTORS_NAME); if ( form_factors_attr < 0 ) /* not found */ kb_error(2839,"Facet extra attribute form_factors real[3] missing. Needed by Neo_Hookean.\n",RECOVERABLE); if ( EXTRAS(FACET)[form_factors_attr].array_spec.datacount != 3 ) kb_error(2850,"Facet extra attribute form_factors must have size 3.\n", RECOVERABLE); } /************************************************************************ * * function: neo_Hookean_all() * * purpose: energy, gradient, and hessian for neo_Hookean method. */ REAL Neo_Hookean_all ARGS((struct qinfo *,int)); REAL Neo_Hookean_all(f_info,mode) struct qinfo *f_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ { REAL *s; /* pointer to extra attributes */ REAL **side; REAL q11,q12,q22; /* Q entries */ REAL det; /* det S */ REAL lambda; /* coefficient lambda */ REAL area; /* reference area of facet */ REAL mu; /* coefficient mu */ REAL f11,f12,f22; REAL c11,c12,c21,c22, c33; REAL energy; REAL dc11dv[FACET_VERTS][MAXCOORD]; REAL dc12dv[FACET_VERTS][MAXCOORD]; REAL dc21dv[FACET_VERTS][MAXCOORD]; REAL dc22dv[FACET_VERTS][MAXCOORD]; REAL ddc11dv[FACET_VERTS][MAXCOORD][FACET_VERTS]; REAL ddc12dv[FACET_VERTS][MAXCOORD][FACET_VERTS]; REAL ddc21dv[FACET_VERTS][MAXCOORD][FACET_VERTS]; REAL ddc22dv[FACET_VERTS][MAXCOORD][FACET_VERTS]; /* potential derivatives declaration */ REAL dpsirdc11,dpsirdc12,dpsirdc21,dpsirdc22; REAL ddpsirdc11dc11, ddpsirdc11dc12, ddpsirdc11dc21, ddpsirdc11dc22; REAL ddpsirdc12dc11, ddpsirdc12dc12, ddpsirdc12dc21, ddpsirdc12dc22; REAL ddpsirdc21dc11, ddpsirdc21dc12, ddpsirdc21dc21, ddpsirdc21dc22; REAL ddpsirdc22dc11, ddpsirdc22dc12, ddpsirdc22dc21, ddpsirdc22dc22; REAL s1, s2, s3, s4, s5, s6, s7; REAL LW, tmp1, tmp2, logtmp2; int i,j,ii,jj; REAL detr; lambda = *(REAL*)get_extra(f_info->id,lambda_attr); mu = *(REAL*)get_extra(f_info->id,mu_attr); s = (REAL*)get_extra(f_info->id,form_factors_attr); det = s[0]*s[2] - s[1]*s[1]; if ( det <= 0.0 ) { if ( mode == METHOD_VALUE ) return 0.0; sprintf(errmsg," Neo_Hookean: Facet %d has unstrained area 0.\n", ordinal(f_info->id)+1); kb_error(2852,errmsg,RECOVERABLE); } area = sqrt(det)/2.0; q11 = s[2]/det; q12 = -s[1]/det; q22 = s[0]/det; side = f_info->sides[0]; f11 = SDIM_dot(side[0],side[0]); f12 = SDIM_dot(side[0],side[1]); f22 = SDIM_dot(side[1],side[1]); c11 = f11*q11 + f12*q12 ; c12 = f11*q12 + f12*q22; c21 = f12*q11 + f22*q12; c22 = f12*q12 + f22*q22; detr = c11*c22-c12*c21; /* plane stress condition */ LW = LambertW(2.0*mu/detr/lambda*exp(2.0*mu/lambda)); c33 = exp((-LW*lambda+2.0*mu)/lambda)/detr; tmp1 = log(exp(-LW+2.0*mu/lambda)); tmp2 = exp(-(LW*lambda-2.0*mu)/lambda); logtmp2 = log(tmp2); energy =area*(mu*c11/2.0+mu*c22/2.0+mu/exp(LW)*pow(exp(mu/lambda),2.0)/(detr)/2.0-3.0/2.0*mu-mu*tmp1/2.0+lambda*pow(tmp1,2.0)/8.0); if ( mode == METHOD_VALUE ) return energy; /* gradient */ for ( i = 0 ; i < SDIM ; i++ ) { dc11dv[1][i] = 2*side[0][i]*q11 + side[1][i]*q12; dc11dv[2][i] = side[0][i]*q12; dc12dv[1][i] = 2*side[0][i]*q12 + side[1][i]*q22; dc12dv[2][i] = side[0][i]*q22; dc21dv[1][i] = side[1][i]*q11; dc21dv[2][i] = side[0][i]*q11 + 2*side[1][i]*q12; dc22dv[1][i] = side[1][i]*q12; dc22dv[2][i] = side[0][i]*q12 + 2*side[1][i]*q22; dc11dv[0][i] = -(dc11dv[1][i] + dc11dv[2][i]); dc12dv[0][i] = -(dc12dv[1][i] + dc12dv[2][i]); dc21dv[0][i] = -(dc21dv[1][i] + dc21dv[2][i]); dc22dv[0][i] = -(dc22dv[1][i] + dc22dv[2][i]); } s1 = (1/(pow(detr,2.0))*LW/(1.0+LW)*c22*exp(-LW+2.0*mu/lambda)/2.0-1/(pow(detr,2.0))*c22*exp(-LW+2.0*mu/lambda)/2.0-LW/(1.0+LW)/(detr)*c22/2.0+1.0/2.0)*mu; s2 = lambda*tmp1*LW/(1.0+LW)/(detr)*c22/4.0; dpsirdc11 = s1+s2; s1 = c21/4.0; s4 = 2.0*mu*tmp2+2.0*mu*LW*c11*c22; s3 = s4-2.0*mu*LW*c12*c21-lambda*logtmp2*LW*c11*c22+lambda*logtmp2*LW*c12*c21; s4 = 1/(pow(detr,2.0))/(1.0+LW); s2 = s3*s4; dpsirdc12 = s1*s2; s1 = c12/4.0; s4 = 2.0*mu*tmp2+2.0*mu*LW*c11*c22; s3 = s4-2.0*mu*LW*c12*c21-lambda*logtmp2*LW*c11*c22+lambda*logtmp2*LW*c12*c21; s4 = 1/(pow(detr,2.0))/(1.0+LW); s2 = s3*s4; dpsirdc21 = s1*s2; s1 = (1/(pow(detr,2.0))*LW/(1.0+LW)*c11*exp(-LW+2.0*mu/lambda)/2.0-1/(pow(detr,2.0))*c11*exp(-LW+2.0*mu/lambda)/2.0-LW/(1.0+LW)/(detr)*c11/2.0+1.0/2.0)*mu; s2 = lambda*tmp1*LW/(1.0+LW)/(detr)*c11/4.0; dpsirdc22 = s1+s2; for ( j = 0 ; j < FACET_VERTS ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) { f_info->grad[j][i] = area*(dpsirdc11*dc11dv[j][i] +dpsirdc12*dc12dv[j][i] +dpsirdc21*dc21dv[j][i] +dpsirdc22*dc22dv[j][i]); } if ( mode == METHOD_GRADIENT ) return energy; /* hessian */ for ( i = 0 ; i < SDIM ; i++ ) { ddc11dv[1][i][1] = 2*q11; ddc11dv[1][i][2] = q12; ddc11dv[2][i][1] = q12; ddc11dv[2][i][2] = 0.0; ddc12dv[1][i][1] = 2*q12; ddc12dv[1][i][2] = q22; ddc12dv[2][i][1] = q22; ddc12dv[2][i][2] = 0.0; ddc21dv[1][i][1] = 0.0; ddc21dv[1][i][2] = q11; ddc21dv[2][i][1] = q11; ddc21dv[2][i][2] = 2*q12; ddc22dv[1][i][1] = 0.0; ddc22dv[1][i][2] = q12; ddc22dv[2][i][1] = q12; ddc22dv[2][i][2] = 2*q22; for ( j = 1 ; j < FACET_VERTS; j++ ) { ddc11dv[0][i][j] = -(ddc11dv[1][i][j] + ddc11dv[2][i][j]); ddc12dv[0][i][j] = -(ddc12dv[1][i][j] + ddc12dv[2][i][j]); ddc21dv[0][i][j] = -(ddc21dv[1][i][j] + ddc21dv[2][i][j]); ddc22dv[0][i][j] = -(ddc22dv[1][i][j] + ddc22dv[2][i][j]); ddc11dv[j][i][0] = -(ddc11dv[j][i][1] + ddc11dv[j][i][2]); ddc12dv[j][i][0] = -(ddc12dv[j][i][1] + ddc12dv[j][i][2]); ddc21dv[j][i][0] = -(ddc21dv[j][i][1] + ddc21dv[j][i][2]); ddc22dv[j][i][0] = -(ddc22dv[j][i][1] + ddc22dv[j][i][2]); } ddc11dv[0][i][0] = -(ddc11dv[1][i][0] + ddc11dv[2][i][0]); ddc12dv[0][i][0] = -(ddc12dv[1][i][0] + ddc12dv[2][i][0]); ddc21dv[0][i][0] = -(ddc21dv[1][i][0] + ddc21dv[2][i][0]); ddc22dv[0][i][0] = -(ddc22dv[1][i][0] + ddc22dv[2][i][0]); } s1 = c22*c22/4.0; s5 = 2.0*mu*pow(LW,2.0)*tmp2+4.0*mu*pow(LW,2.0)*c11*c22+4.0*mu*LW*c11*c22+4.0*mu*tmp2; s4 = s5+4.0*mu*LW*tmp2-4.0*mu*pow(LW,2.0)*c12*c21-4.0*mu*LW*c12*c21+2.0*mu*pow(LW,3.0)*c11*c22-2.0*mu*pow(LW,3.0)*c12*c21; s5 = s4-lambda*pow(LW,2.0)*c12*c21-2.0*lambda*logtmp2*LW*c11*c22+lambda*pow(LW,3.0)*c11*c22+lambda*pow(LW,2.0)*c11*c22; s6 = s5+lambda*logtmp2*pow(LW,3.0)*c12*c21-lambda*logtmp2*pow(LW,3.0)*c11*c22; s7 = s6-lambda*pow(LW,3.0)*c12*c21; s3 = s7+2.0*lambda*logtmp2*pow(LW,2.0)*c12*c21+2.0*lambda*logtmp2*LW*c12*c21-2.0*lambda*logtmp2*pow(LW,2.0)*c11*c22; s4 = 1/(pow(detr,3.0))/pow(1.0+LW,3.0); s2 = s3*s4; ddpsirdc11dc11 = s1*s2; s1 = -c22*c21/4.0; s5 = 2.0*mu*pow(LW,2.0)*tmp2+4.0*mu*pow(LW,2.0)*c11*c22+4.0*mu*LW*c11*c22+4.0*mu*tmp2; s4 = s5+4.0*mu*LW*tmp2-4.0*mu*pow(LW,2.0)*c12*c21-4.0*mu*LW*c12*c21+2.0*mu*pow(LW,3.0)*c11*c22-2.0*mu*pow(LW,3.0)*c12*c21; s5 = s4-lambda*pow(LW,2.0)*c12*c21-2.0*lambda*logtmp2*LW*c11*c22+lambda*pow(LW,3.0)*c11*c22+lambda*pow(LW,2.0)*c11*c22; s6 = s5+lambda*logtmp2*pow(LW,3.0)*c12*c21-lambda*logtmp2*pow(LW,3.0)*c11*c22; s7 = s6-lambda*pow(LW,3.0)*c12*c21; s3 = s7+2.0*lambda*logtmp2*pow(LW,2.0)*c12*c21+2.0*lambda*logtmp2*LW*c12*c21-2.0*lambda*logtmp2*pow(LW,2.0)*c11*c22; s4 = 1/(pow(detr,3.0))/pow(1.0+LW,3.0); s2 = s3*s4; ddpsirdc11dc12 = s1*s2; s1 = -c22*c12/4.0; s5 = 2.0*mu*pow(LW,2.0)*tmp2+4.0*mu*pow(LW,2.0)*c11*c22+4.0*mu*LW*c11*c22+4.0*mu*tmp2; s4 = s5+4.0*mu*LW*tmp2-4.0*mu*pow(LW,2.0)*c12*c21-4.0*mu*LW*c12*c21+2.0*mu*pow(LW,3.0)*c11*c22-2.0*mu*pow(LW,3.0)*c12*c21; s5 = s4-lambda*pow(LW,2.0)*c12*c21-2.0*lambda*logtmp2*LW*c11*c22+lambda*pow(LW,3.0)*c11*c22+lambda*pow(LW,2.0)*c11*c22; s6 = s5+lambda*logtmp2*pow(LW,3.0)*c12*c21-lambda*logtmp2*pow(LW,3.0)*c11*c22; s7 = s6-lambda*pow(LW,3.0)*c12*c21; s3 = s7+2.0*lambda*logtmp2*pow(LW,2.0)*c12*c21+2.0*lambda*logtmp2*LW*c12*c21-2.0*lambda*logtmp2*pow(LW,2.0)*c11*c22; s4 = 1/(pow(detr,3.0))/pow(1.0+LW,3.0); s2 = s3*s4; ddpsirdc11dc21 = s1*s2; s3 = mu*tmp2*c12*c21/2.0+mu*LW*c11*c11*c22*c22/2.0+lambda*pow(LW,3.0)*c11*c11*c22*c22/4.0+lambda*pow(LW,2.0)*c11*c11*c22*c22/4.0-mu*pow(LW,3.0)*c12*c12*c21*c21/2.0; s4 = s3-mu*pow(LW,2.0)*c12*c12*c21*c21-mu*LW*c12*c12*c21*c21/2.0; s2 = s4+mu*c22*c11*tmp2/2.0+mu*LW*tmp2*c12*c21+mu*pow(LW,2.0)*tmp2*c12*c21/2.0; s4 = s2-lambda*logtmp2*pow(LW,3.0)*c11*c22*c12*c21/4.0; s3 = s4-lambda*logtmp2*pow(LW,2.0)*c11*c22*c12*c21/2.0-lambda*logtmp2*LW*c11*c11*c22*c22/4.0-lambda*pow(LW,3.0)*c11*c22*c12*c21/4.0; s4 = s3-lambda*pow(LW,2.0)*c11*c22*c12*c21/4.0+mu*pow(LW,3.0)*c11*c22*c12*c21/2.0; s5 = s4+mu*pow(LW,2.0)*c11*c22*c12*c21; s1 = s5+lambda*logtmp2*pow(LW,3.0)*c12*c12*c21*c21/4.0+lambda*logtmp2*LW*c12*c12*c21*c21/4.0+lambda*logtmp2*pow(LW,2.0)*c12*c12*c21*c21/2.0; s2 = 1/(pow(detr,3.0))/pow(1.0+LW,3.0); ddpsirdc11dc22 = s1*s2; s1 = c21*c22/4.0; s6 = lambda*logtmp2*pow(LW,3.0)*c11*c22-lambda*logtmp2*pow(LW,3.0)*c12*c21; s5 = s6+2.0*lambda*logtmp2*LW*c11*c22+2.0*lambda*logtmp2*pow(LW,2.0)*c11*c22; s6 = s5-2.0*lambda*logtmp2*LW*c12*c21-2.0*lambda*logtmp2*pow(LW,2.0)*c12*c21; s4 = s6-4.0*mu*tmp2-4.0*mu*LW*tmp2-2.0*mu*pow(LW,2.0)*tmp2; s5 = s4+4.0*mu*pow(LW,2.0)*c12*c21+4.0*mu*LW*c12*c21-4.0*mu*pow(LW,2.0)*c11*c22-4.0*mu*LW*c11*c22; s3 = s5-2.0*mu*pow(LW,3.0)*c11*c22+lambda*pow(LW,3.0)*c12*c21+lambda*pow(LW,2.0)*c12*c21-lambda*pow(LW,3.0)*c11*c22-lambda*pow(LW,2.0)*c11*c22+2.0*mu*pow(LW,3.0)*c12*c21; s4 = 1/(pow(detr,3.0))/pow(1.0+LW,3.0); s2 = s3*s4; ddpsirdc12dc11 = s1*s2; s1 = -c21*c21/4.0; s6 = lambda*logtmp2*pow(LW,3.0)*c11*c22-lambda*logtmp2*pow(LW,3.0)*c12*c21; s5 = s6+2.0*lambda*logtmp2*LW*c11*c22+2.0*lambda*logtmp2*pow(LW,2.0)*c11*c22; s6 = s5-2.0*lambda*logtmp2*LW*c12*c21-2.0*lambda*logtmp2*pow(LW,2.0)*c12*c21; s4 = s6-4.0*mu*tmp2-4.0*mu*LW*tmp2-2.0*mu*pow(LW,2.0)*tmp2; s5 = s4+4.0*mu*pow(LW,2.0)*c12*c21+4.0*mu*LW*c12*c21-4.0*mu*pow(LW,2.0)*c11*c22-4.0*mu*LW*c11*c22; s3 = s5-2.0*mu*pow(LW,3.0)*c11*c22+lambda*pow(LW,3.0)*c12*c21+lambda*pow(LW,2.0)*c12*c21-lambda*pow(LW,3.0)*c11*c22-lambda*pow(LW,2.0)*c11*c22+2.0*mu*pow(LW,3.0)*c12*c21; s4 = 1/(pow(detr,3.0))/pow(1.0+LW,3.0); s2 = s3*s4; ddpsirdc12dc12 = s1*s2; s3 = -mu*LW*c12*c12*c21*c21/2.0-lambda*pow(LW,3.0)*c12*c12*c21*c21/4.0-lambda*pow(LW,2.0)*c12*c12*c21*c21/4.0+mu*pow(LW,3.0)*c11*c11*c22*c22/2.0+mu*pow(LW,2.0)*c11*c11*c22*c22; s4 = s3+mu*c21*c12*tmp2/2.0+mu*pow(LW,2.0)*tmp2*c11*c22/2.0; s2 = s4+mu*LW*tmp2*c11*c22-mu*pow(LW,2.0)*c12*c21*c11*c22+mu*tmp2*c11*c22/2.0; s3 = s2+lambda*logtmp2*LW*c12*c12*c21*c21/4.0+lambda*pow(LW,3.0)*c12*c21*c11*c22/4.0+lambda*pow(LW,2.0)*c12*c21*c11*c22/4.0+mu*LW*c11*c11*c22*c22/2.0; s4 = s3-mu*pow(LW,3.0)*c12*c21*c11*c22/2.0+lambda*logtmp2*pow(LW,2.0)*c12*c21*c11*c22/2.0; s5 = s4+lambda*logtmp2*pow(LW,3.0)*c12*c21*c11*c22/4.0; s1 = s5-lambda*logtmp2*pow(LW,3.0)*c11*c11*c22*c22/4.0-lambda*logtmp2*LW*c11*c11*c22*c22/4.0-lambda*logtmp2*pow(LW,2.0)*c11*c11*c22*c22/2.0; s2 = 1/(pow(detr,3.0))/pow(1.0+LW,3.0); ddpsirdc12dc21 = s1*s2; s1 = c21*c11/4.0; s6 = lambda*logtmp2*pow(LW,3.0)*c11*c22-lambda*logtmp2*pow(LW,3.0)*c12*c21; s5 = s6+2.0*lambda*logtmp2*LW*c11*c22+2.0*lambda*logtmp2*pow(LW,2.0)*c11*c22; s6 = s5-2.0*lambda*logtmp2*LW*c12*c21-2.0*lambda*logtmp2*pow(LW,2.0)*c12*c21; s4 = s6-4.0*mu*tmp2-4.0*mu*LW*tmp2-2.0*mu*pow(LW,2.0)*tmp2; s5 = s4+4.0*mu*pow(LW,2.0)*c12*c21+4.0*mu*LW*c12*c21-4.0*mu*pow(LW,2.0)*c11*c22-4.0*mu*LW*c11*c22; s3 = s5-2.0*mu*pow(LW,3.0)*c11*c22+lambda*pow(LW,3.0)*c12*c21+lambda*pow(LW,2.0)*c12*c21-lambda*pow(LW,3.0)*c11*c22-lambda*pow(LW,2.0)*c11*c22+2.0*mu*pow(LW,3.0)*c12*c21; s4 = 1/(pow(detr,3.0))/pow(1.0+LW,3.0); s2 = s3*s4; ddpsirdc12dc22 = s1*s2; s1 = c12*c22/4.0; s5 = 4.0*mu*pow(LW,2.0)*c12*c21+4.0*mu*LW*c12*c21-4.0*mu*pow(LW,2.0)*c11*c22-4.0*mu*LW*c11*c22; s4 = s5-2.0*mu*pow(LW,3.0)*c11*c22+2.0*mu*pow(LW,3.0)*c12*c21+lambda*pow(LW,2.0)*c12*c21-lambda*pow(LW,3.0)*c11*c22-lambda*pow(LW,2.0)*c11*c22; s5 = s4+lambda*pow(LW,3.0)*c12*c21-4.0*mu*tmp2-4.0*mu*LW*tmp2-2.0*mu*pow(LW,2.0)*tmp2; s6 = s5-2.0*lambda*logtmp2*pow(LW,2.0)*c12*c21-2.0*lambda*logtmp2*LW*c12*c21; s7 = s6+2.0*lambda*logtmp2*pow(LW,2.0)*c11*c22; s3 = s7+2.0*lambda*logtmp2*LW*c11*c22+lambda*logtmp2*pow(LW,3.0)*c11*c22-lambda*logtmp2*pow(LW,3.0)*c12*c21; s4 = 1/(pow(detr,3.0))/pow(1.0+LW,3.0); s2 = s3*s4; ddpsirdc21dc11 = s1*s2; s4 = -lambda*logtmp2*pow(LW,3.0)*c11*c11*c22*c22/4.0-lambda*logtmp2*LW*c11*c11*c22*c22/4.0; s3 = s4-lambda*logtmp2*pow(LW,2.0)*c11*c11*c22*c22/2.0+lambda*logtmp2*pow(LW,3.0)*c21*c12*c11*c22/4.0-mu*LW*c21*c21*c12*c12/2.0; s2 = s3+mu*tmp2*c11*c22/2.0+mu*LW*c11*c11*c22*c22/2.0-lambda*pow(LW,3.0)*c21*c21*c12*c12/4.0-lambda*pow(LW,2.0)*c21*c21*c12*c12/4.0+mu*pow(LW,3.0)*c11*c11*c22*c22/2.0; s3 = s2+mu*pow(LW,2.0)*c11*c11*c22*c22+mu*c12*c21*tmp2/2.0+mu*pow(LW,2.0)*tmp2*c11*c22/2.0+mu*LW*tmp2*c11*c22; s4 = s3-mu*pow(LW,2.0)*c21*c12*c11*c22-mu*pow(LW,3.0)*c21*c12*c11*c22/2.0; s1 = s4+lambda*logtmp2*LW*c21*c21*c12*c12/4.0+lambda*logtmp2*pow(LW,2.0)*c21*c12*c11*c22/2.0+lambda*pow(LW,3.0)*c21*c12*c11*c22/4.0+lambda*pow(LW,2.0)*c21*c12*c11*c22/4.0; s2 = 1/(pow(detr,3.0))/pow(1.0+LW,3.0); ddpsirdc21dc12 = s1*s2; s1 = -c12*c12/4.0; s5 = 4.0*mu*pow(LW,2.0)*c12*c21+4.0*mu*LW*c12*c21-4.0*mu*pow(LW,2.0)*c11*c22-4.0*mu*LW*c11*c22; s4 = s5-2.0*mu*pow(LW,3.0)*c11*c22+2.0*mu*pow(LW,3.0)*c12*c21+lambda*pow(LW,2.0)*c12*c21-lambda*pow(LW,3.0)*c11*c22-lambda*pow(LW,2.0)*c11*c22; s5 = s4+lambda*pow(LW,3.0)*c12*c21-4.0*mu*tmp2-4.0*mu*LW*tmp2-2.0*mu*pow(LW,2.0)*tmp2; s6 = s5-2.0*lambda*logtmp2*pow(LW,2.0)*c12*c21-2.0*lambda*logtmp2*LW*c12*c21; s7 = s6+2.0*lambda*logtmp2*pow(LW,2.0)*c11*c22; s3 = s7+2.0*lambda*logtmp2*LW*c11*c22+lambda*logtmp2*pow(LW,3.0)*c11*c22-lambda*logtmp2*pow(LW,3.0)*c12*c21; s4 = 1/(pow(detr,3.0))/pow(1.0+LW,3.0); s2 = s3*s4; ddpsirdc21dc21 = s1*s2; s1 = c12*c11/4.0; s5 = 4.0*mu*pow(LW,2.0)*c12*c21+4.0*mu*LW*c12*c21-4.0*mu*pow(LW,2.0)*c11*c22-4.0*mu*LW*c11*c22; s4 = s5-2.0*mu*pow(LW,3.0)*c11*c22+2.0*mu*pow(LW,3.0)*c12*c21+lambda*pow(LW,2.0)*c12*c21-lambda*pow(LW,3.0)*c11*c22-lambda*pow(LW,2.0)*c11*c22; s5 = s4+lambda*pow(LW,3.0)*c12*c21-4.0*mu*tmp2-4.0*mu*LW*tmp2-2.0*mu*pow(LW,2.0)*tmp2; s6 = s5-2.0*lambda*logtmp2*pow(LW,2.0)*c12*c21-2.0*lambda*logtmp2*LW*c12*c21; s7 = s6+2.0*lambda*logtmp2*pow(LW,2.0)*c11*c22; s3 = s7+2.0*lambda*logtmp2*LW*c11*c22+lambda*logtmp2*pow(LW,3.0)*c11*c22-lambda*logtmp2*pow(LW,3.0)*c12*c21; s4 = 1/(pow(detr,3.0))/pow(1.0+LW,3.0); s2 = s3*s4; ddpsirdc21dc22 = s1*s2; s3 = mu*LW*c22*c22*c11*c11/2.0-mu*pow(LW,3.0)*c12*c12*c21*c21/2.0-mu*pow(LW,2.0)*c12*c12*c21*c21-mu*LW*c12*c12*c21*c21/2.0+lambda*pow(LW,2.0)*c22*c22*c11*c11/4.0; s4 = s3+lambda*pow(LW,3.0)*c22*c22*c11*c11/4.0+mu*LW*tmp2*c12*c21; s2 = s4+mu*c11*c22*tmp2/2.0-lambda*logtmp2*pow(LW,3.0)*c22*c11*c12*c21/4.0-lambda*logtmp2*pow(LW,2.0)*c22*c11*c12*c21/2.0; s3 = s2-lambda*pow(LW,3.0)*c22*c11*c12*c21/4.0-lambda*pow(LW,2.0)*c22*c11*c12*c21/4.0-lambda*logtmp2*LW*c22*c22*c11*c11/4.0+mu*pow(LW,3.0)*c22*c11*c12*c21/2.0; s4 = s3+mu*pow(LW,2.0)*c22*c11*c12*c21+mu*tmp2*c12*c21/2.0; s5 = s4+mu*pow(LW,2.0)*tmp2*c12*c21/2.0; s1 = s5+lambda*logtmp2*pow(LW,2.0)*c12*c12*c21*c21/2.0+lambda*logtmp2*pow(LW,3.0)*c12*c12*c21*c21/4.0+lambda*logtmp2*LW*c12*c12*c21*c21/4.0; s2 = 1/(pow(detr,3.0))/pow(1.0+LW,3.0); ddpsirdc22dc11 = s1*s2; s1 = c11*c21/4.0; s6 = -4.0*mu*LW*tmp2-2.0*lambda*logtmp2*LW*c12*c21; s5 = s6-2.0*lambda*logtmp2*pow(LW,2.0)*c12*c21+2.0*lambda*logtmp2*LW*c11*c22; s6 = s5+2.0*lambda*logtmp2*pow(LW,2.0)*c11*c22-2.0*mu*pow(LW,2.0)*tmp2; s4 = s6-lambda*logtmp2*pow(LW,3.0)*c12*c21+lambda*logtmp2*pow(LW,3.0)*c11*c22-2.0*mu*pow(LW,3.0)*c11*c22; s5 = s4+4.0*mu*pow(LW,2.0)*c12*c21+4.0*mu*LW*c12*c21-4.0*mu*pow(LW,2.0)*c11*c22-4.0*mu*LW*c11*c22; s3 = s5+lambda*pow(LW,3.0)*c12*c21+lambda*pow(LW,2.0)*c12*c21-lambda*pow(LW,3.0)*c11*c22-lambda*pow(LW,2.0)*c11*c22+2.0*mu*pow(LW,3.0)*c12*c21-4.0*mu*tmp2; s4 = 1/(pow(detr,3.0))/pow(1.0+LW,3.0); ddpsirdc22dc12 = s1*s2; s1 = c11*c12/4.0; s6 = -4.0*mu*LW*tmp2-2.0*lambda*logtmp2*LW*c12*c21; s5 = s6-2.0*lambda*logtmp2*pow(LW,2.0)*c12*c21+2.0*lambda*logtmp2*LW*c11*c22; s6 = s5+2.0*lambda*logtmp2*pow(LW,2.0)*c11*c22-2.0*mu*pow(LW,2.0)*tmp2; s4 = s6-lambda*logtmp2*pow(LW,3.0)*c12*c21+lambda*logtmp2*pow(LW,3.0)*c11*c22-2.0*mu*pow(LW,3.0)*c11*c22; s5 = s4+4.0*mu*pow(LW,2.0)*c12*c21+4.0*mu*LW*c12*c21-4.0*mu*pow(LW,2.0)*c11*c22-4.0*mu*LW*c11*c22; s3 = s5+lambda*pow(LW,3.0)*c12*c21+lambda*pow(LW,2.0)*c12*c21-lambda*pow(LW,3.0)*c11*c22-lambda*pow(LW,2.0)*c11*c22+2.0*mu*pow(LW,3.0)*c12*c21-4.0*mu*tmp2; s4 = 1/(pow(detr,3.0))/pow(1.0+LW,3.0); s2 = s3*s4; ddpsirdc22dc21 = s1*s2; s1 = -c11*c11/4.0; s6 = -4.0*mu*LW*tmp2-2.0*lambda*logtmp2*LW*c12*c21; s5 = s6-2.0*lambda*logtmp2*pow(LW,2.0)*c12*c21+2.0*lambda*logtmp2*LW*c11*c22; s6 = s5+2.0*lambda*logtmp2*pow(LW,2.0)*c11*c22-2.0*mu*pow(LW,2.0)*tmp2; s4 = s6-lambda*logtmp2*pow(LW,3.0)*c12*c21+lambda*logtmp2*pow(LW,3.0)*c11*c22-2.0*mu*pow(LW,3.0)*c11*c22; s5 = s4+4.0*mu*pow(LW,2.0)*c12*c21+4.0*mu*LW*c12*c21-4.0*mu*pow(LW,2.0)*c11*c22-4.0*mu*LW*c11*c22; s3 = s5+lambda*pow(LW,3.0)*c12*c21+lambda*pow(LW,2.0)*c12*c21-lambda*pow(LW,3.0)*c11*c22-lambda*pow(LW,2.0)*c11*c22+2.0*mu*pow(LW,3.0)*c12*c21-4.0*mu*tmp2; s4 = 1/(pow(detr,3.0))/pow(1.0+LW,3.0); s2 = s3*s4; ddpsirdc22dc22 = s1*s2; for ( j = 0 ; j < FACET_VERTS ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) for ( jj = 0 ; jj < FACET_VERTS ; jj++ ) { f_info->hess[j][jj][i][i] += area*(dpsirdc11*ddc11dv[j][i][jj] +dpsirdc12*ddc21dv[j][i][jj] +dpsirdc21*ddc12dv[j][i][jj] +dpsirdc22*ddc22dv[j][i][jj]); for ( ii = 0 ; ii < SDIM ; ii++ ) f_info->hess[j][jj][i][ii] += area*((ddpsirdc11dc11+ddpsirdc12dc11+ddpsirdc21dc11+ddpsirdc22dc11)*dc11dv[j][i] +(ddpsirdc11dc12+ddpsirdc12dc12+ddpsirdc21dc12+ddpsirdc22dc12)*dc12dv[j][i] +(ddpsirdc11dc21+ddpsirdc12dc21+ddpsirdc21dc21+ddpsirdc22dc21)*dc21dv[j][i] +(ddpsirdc11dc22+ddpsirdc12dc22+ddpsirdc21dc22+ddpsirdc22dc22)*dc22dv[j][i]); } return energy; } /* Lambert W function. Was ~/C/LambertW.c written K M Briggs Keith dot Briggs at bt dot com 97 May 21. Revised KMB 97 Nov 20; 98 Feb 11, Nov 24, Dec 28; 99 Jan 13; 00 Feb 23; 01 Apr 09 Computes Lambert W function, principal branch. See LambertW1.c for -1 branch. Returned value W(z) satisfies W(z)*exp(W(z))=z test data... W(1)= 0.5671432904097838730 W(2)= 0.8526055020137254914 W(20)=2.2050032780240599705 To solve (a+b*R)*exp(-c*R)-d=0 for R, use R=-(b*W(-exp(-a*c/b)/b*d*c)+a*c)/b/c Test: gcc -DTESTW LambertW.c -o LambertW -lm && LambertW Library: gcc -O3 -c LambertW.c */ REAL LambertW(z) REAL z; { int i; const REAL eps=4.0e-16, em1=0.3678794411714423215955237701614608; REAL p,e,t,w; if (z<-em1 || !is_finite(z)) { fprintf(stderr,"LambertW: bad argument %g, exiting.\n",z); exit(1); } if (0.0==z) return 0.0; if (z<-em1+1e-4) { /* series near -em1 in sqrt(q) */ double q=z+em1,r=sqrt(q),q2=q*q,q3=q2*q; w = -1.0 +2.331643981597124203363536062168*r -1.812187885639363490240191647568*q +1.936631114492359755363277457668*r*q -2.353551201881614516821543561516*q2 +3.066858901050631912893148922704*r*q2 -4.175335600258177138854984177460*q3 +5.858023729874774148815053846119*r*q3 -8.401032217523977370984161688514*q3*q; /* error approx 1e-16 */ fprintf(stderr,"LambertW expression 1: W(%15.12g) = %15.12g \n",z,w); return w; } /* initial approx for iteration... */ if (z<1.0) { /* series near 0 */ p=sqrt(2.0*(2.7182818284590452353602874713526625*z+1.0)); w=-1.0+p*(1.0+p*(-0.333333333333333333333+p*0.152777777777777777777777)); } else w=log(z); /* asymptotic */ if (z>3.0) w-=log(w); /* useful? */ for (i=0; i<10; i++) { /* Halley iteration */ e=exp(w); t=w*e-z; p=w+1.0; t/=e*p-0.5*(p+1.0)*t/p; w-=t; if (fabs(t)array_spec.dim != 2 || ex->array_spec.sizes[0] != 2 || ex->array_spec.sizes[1] != 2 ) kb_error(3204, "Facet extra attribute elastic_basis must have dimension [2][2].\n", RECOVERABLE); ex = &EXTRAS(FACET)[coeff_attr]; if ( ex->array_spec.dim != 1 || ex->array_spec.sizes[0] != 6 ) kb_error(3205, "Facet extra attribute elastic_coeff must have 1 dimension and size 6.\n", RECOVERABLE); } /************************************************************************ * * function: general_linear_elastic_all() * * purpose: energy, gradient, and hessian for linear_elastic method. */ REAL general_linear_elastic_all ARGS((struct qinfo *,int)); REAL general_linear_elastic_all(f_info,mode) struct qinfo *f_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ { REAL s[2][2],*sptr; /* pointer to extra attributes */ REAL **side; REAL q11,q12,q21,q22; /* Q entries */ REAL det; /* det S */ REAL area; /* reference area of facet */ REAL * coeff; /* elastic coeff extra attribute */ REAL elastic_coeff[2][2][2][2]; REAL f11,f12,f21,f22; REAL c11,c12,c21,c22; REAL energy; REAL dc11dv[FACET_VERTS][MAXCOORD]; REAL dc12dv[FACET_VERTS][MAXCOORD]; REAL dc21dv[FACET_VERTS][MAXCOORD]; REAL dc22dv[FACET_VERTS][MAXCOORD]; REAL ddc11dv[FACET_VERTS][MAXCOORD][FACET_VERTS]; REAL ddc12dv[FACET_VERTS][MAXCOORD][FACET_VERTS]; REAL ddc21dv[FACET_VERTS][MAXCOORD][FACET_VERTS]; REAL ddc22dv[FACET_VERTS][MAXCOORD][FACET_VERTS]; int i,j,ii,jj; coeff = (REAL*)get_extra(f_info->id,coeff_attr); elastic_coeff[0][0][0][0] = coeff[0]; elastic_coeff[1][1][1][1] = coeff[1]; elastic_coeff[0][0][1][1] = coeff[2]; elastic_coeff[1][1][0][0] = coeff[2]; elastic_coeff[0][0][0][1] = coeff[3]/2; elastic_coeff[0][0][1][0] = coeff[3]/2; elastic_coeff[0][1][0][0] = coeff[3]/2; elastic_coeff[1][0][0][0] = coeff[3]/2; elastic_coeff[1][1][1][0] = coeff[4]/2; elastic_coeff[0][1][1][1] = coeff[4]/2; elastic_coeff[1][0][1][1] = coeff[4]/2; elastic_coeff[1][1][0][1] = coeff[4]/2; elastic_coeff[1][0][1][0] = coeff[5]/4; elastic_coeff[1][0][0][1] = coeff[5]/4; elastic_coeff[0][1][0][1] = coeff[5]/4; elastic_coeff[0][1][1][0] = coeff[5]/4; /* get unstrained sides in columns of S and get inverse */ sptr = (REAL*)get_extra(f_info->id,elastic_base_attr); s[0][0] = sptr[0]; s[1][0] = sptr[1]; s[0][1] = sptr[2]; s[1][1] = sptr[3]; det = s[0][0]*s[1][1] - s[0][1]*s[1][0]; if ( det == 0.0 ) { if ( mode == METHOD_VALUE ) return 0.0; sprintf(errmsg, "general_linear_elastic: Facet %s has unstrained area 0.\n", ELNAME(f_info->id)); kb_error(3206,errmsg,RECOVERABLE); } area = fabs(det)/2; q11 = s[1][1]/det; q12 = -s[0][1]/det; q21 = -s[1][0]/det; q22 = s[0][0]/det; /* Gram matrix of strained sides */ side = f_info->sides[0]; f11 = SDIM_dot(side[0],side[0]); f21 = f12 = SDIM_dot(side[0],side[1]); f22 = SDIM_dot(side[1],side[1]); /* Strain matrix */ c11 = (q11*f11*q11 + q11*f12*q21 + q21*f21*q11 + q21*f22*q21 - 1)/2; c12 = (q11*f11*q12 + q11*f12*q22 + q21*f21*q12 + q21*f22*q22)/2; c21 = c12; c22 = (q12*f11*q12 + q12*f12*q22 + q22*f21*q12 + q22*f22*q22 - 1)/2; energy = c11*c11*elastic_coeff[0][0][0][0] + c11*c12*elastic_coeff[0][0][0][1] + c11*c22*elastic_coeff[0][0][1][1] + c11*c21*elastic_coeff[0][0][1][0] + c12*c11*elastic_coeff[0][1][0][0] + c12*c12*elastic_coeff[0][1][0][1] + c12*c22*elastic_coeff[0][1][1][1] + c12*c21*elastic_coeff[0][1][1][0] + c21*c11*elastic_coeff[1][0][0][0] + c21*c12*elastic_coeff[1][0][0][1] + c21*c22*elastic_coeff[1][0][1][1] + c21*c21*elastic_coeff[1][0][1][0] + c22*c11*elastic_coeff[1][1][0][0] + c22*c12*elastic_coeff[1][1][0][1] + c22*c22*elastic_coeff[1][1][1][1] + c22*c21*elastic_coeff[1][1][1][0] ; energy *= area/2; if ( mode == METHOD_VALUE ) return energy; /* gradient */ for ( i = 0 ; i < SDIM ; i++ ) { dc11dv[1][i] = (q11*2*side[0][i]*q11 + q11*side[1][i]*q21 + q21*side[1][i]*q11 )/2; dc11dv[2][i] = (q11*side[0][i]*q21 + q21*side[0][i]*q11 + q21*2*side[1][i]*q21 )/2; dc12dv[1][i] = (q11*2*side[0][i]*q12 + q11*side[1][i]*q22 + q21*side[1][i]*q12 )/2; dc12dv[2][i] = (q11*side[0][i]*q22 + q21*side[0][i]*q12 + q21*2*side[1][i]*q22 )/2; dc21dv[1][i] = dc12dv[1][i]; dc21dv[2][i] = dc12dv[2][i]; dc22dv[1][i] = (q12*2*side[0][i]*q12 + q12*side[1][i]*q22 + q22*side[1][i]*q12 )/2; dc22dv[2][i] = (q12*side[0][i]*q22 + q22*side[0][i]*q12 + q22*2*side[1][i]*q22 )/2; dc11dv[0][i] = -(dc11dv[1][i] + dc11dv[2][i]); dc12dv[0][i] = -(dc12dv[1][i] + dc12dv[2][i]); dc21dv[0][i] = -(dc21dv[1][i] + dc21dv[2][i]); dc22dv[0][i] = -(dc22dv[1][i] + dc22dv[2][i]); } for ( j = 0 ; j < FACET_VERTS ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) { f_info->grad[j][i] = area*( c11*dc11dv[j][i]*elastic_coeff[0][0][0][0] + c11*dc12dv[j][i]*elastic_coeff[0][0][0][1] + c11*dc22dv[j][i]*elastic_coeff[0][0][1][1] + c11*dc21dv[j][i]*elastic_coeff[0][0][1][0] + c12*dc11dv[j][i]*elastic_coeff[0][1][0][0] + c12*dc12dv[j][i]*elastic_coeff[0][1][0][1] + c12*dc22dv[j][i]*elastic_coeff[0][1][1][1] + c12*dc21dv[j][i]*elastic_coeff[0][1][1][0] + c21*dc11dv[j][i]*elastic_coeff[1][0][0][0] + c21*dc12dv[j][i]*elastic_coeff[1][0][0][1] + c21*dc22dv[j][i]*elastic_coeff[1][0][1][1] + c21*dc21dv[j][i]*elastic_coeff[1][0][1][0] + c22*dc11dv[j][i]*elastic_coeff[1][1][0][0] + c22*dc12dv[j][i]*elastic_coeff[1][1][0][1] + c22*dc22dv[j][i]*elastic_coeff[1][1][1][1] + c22*dc21dv[j][i]*elastic_coeff[1][1][1][0]); /* don't need to divide by 2 here, since only doing derivative of second term and using symmetry */ } if ( mode == METHOD_GRADIENT ) return energy; /* hessian */ for ( i = 0 ; i < SDIM ; i++ ) { ddc11dv[1][i][1] = (q11*2*q11)/2; ddc11dv[1][i][2] = ( q11*q21 + q21*q11 )/2; ddc11dv[2][i][1] = (q11*q21 + q21*q11 )/2; ddc11dv[2][i][2] = (q21*2*q21)/2; ddc12dv[1][i][1] = (q11*2*q12 )/2; ddc12dv[1][i][2] = ( q11*q22 + q21*q12 )/2; ddc12dv[2][i][1] = (q11*q22 + q21*q12)/2; ddc12dv[2][i][2] = (q21*2*q22 )/2; ddc21dv[1][i][1] = ddc12dv[1][i][1]; ddc21dv[1][i][2] = ddc12dv[1][i][2]; ddc21dv[2][i][1] = ddc12dv[2][i][1]; ddc21dv[2][i][2] = ddc12dv[2][i][2]; ddc22dv[1][i][1] = (q12*2*q12)/2; ddc22dv[1][i][2] = (q12*q22 + q22*q12)/2; ddc22dv[2][i][1] = (q12*q22 + q22*q12)/2; ddc22dv[2][i][2] = (q22*2*q22)/2; for ( j = 1 ; j < FACET_VERTS; j++ ) { ddc11dv[0][i][j] = -(ddc11dv[1][i][j] + ddc11dv[2][i][j]); ddc12dv[0][i][j] = -(ddc12dv[1][i][j] + ddc12dv[2][i][j]); ddc21dv[0][i][j] = -(ddc21dv[1][i][j] + ddc21dv[2][i][j]); ddc22dv[0][i][j] = -(ddc22dv[1][i][j] + ddc22dv[2][i][j]); ddc11dv[j][i][0] = -(ddc11dv[j][i][1] + ddc11dv[j][i][2]); ddc12dv[j][i][0] = -(ddc12dv[j][i][1] + ddc12dv[j][i][2]); ddc21dv[j][i][0] = -(ddc21dv[j][i][1] + ddc21dv[j][i][2]); ddc22dv[j][i][0] = -(ddc22dv[j][i][1] + ddc22dv[j][i][2]); } ddc11dv[0][i][0] = -(ddc11dv[1][i][0] + ddc11dv[2][i][0]); ddc12dv[0][i][0] = -(ddc12dv[1][i][0] + ddc12dv[2][i][0]); ddc21dv[0][i][0] = -(ddc21dv[1][i][0] + ddc21dv[2][i][0]); ddc22dv[0][i][0] = -(ddc22dv[1][i][0] + ddc22dv[2][i][0]); } for ( j = 0 ; j < FACET_VERTS ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) for ( jj = 0 ; jj < FACET_VERTS ; jj++ ) { f_info->hess[j][jj][i][i] += area*( c11*ddc11dv[j][i][jj]*elastic_coeff[0][0][0][0] + c11*ddc12dv[j][i][jj]*elastic_coeff[0][0][0][1] + c11*ddc22dv[j][i][jj]*elastic_coeff[0][0][1][1] + c11*ddc21dv[j][i][jj]*elastic_coeff[0][0][1][0] + c12*ddc11dv[j][i][jj]*elastic_coeff[0][1][0][0] + c12*ddc12dv[j][i][jj]*elastic_coeff[0][1][0][1] + c12*ddc22dv[j][i][jj]*elastic_coeff[0][1][1][1] + c12*ddc21dv[j][i][jj]*elastic_coeff[0][1][1][0] + c21*ddc11dv[j][i][jj]*elastic_coeff[1][0][0][0] + c21*ddc12dv[j][i][jj]*elastic_coeff[1][0][0][1] + c21*ddc22dv[j][i][jj]*elastic_coeff[1][0][1][1] + c21*ddc21dv[j][i][jj]*elastic_coeff[1][0][1][0] + c22*ddc11dv[j][i][jj]*elastic_coeff[1][1][0][0] + c22*ddc12dv[j][i][jj]*elastic_coeff[1][1][0][1] + c22*ddc22dv[j][i][jj]*elastic_coeff[1][1][1][1] + c22*ddc21dv[j][i][jj]*elastic_coeff[1][1][1][0]); for ( ii = 0 ; ii < SDIM ; ii++ ) f_info->hess[j][jj][i][ii] += area*( dc11dv[jj][ii]*dc11dv[j][i]*elastic_coeff[0][0][0][0] + dc11dv[jj][ii]*dc12dv[j][i]*elastic_coeff[0][0][0][1] + dc11dv[jj][ii]*dc22dv[j][i]*elastic_coeff[0][0][1][1] + dc11dv[jj][ii]*dc21dv[j][i]*elastic_coeff[0][0][1][0] + dc12dv[jj][ii]*dc11dv[j][i]*elastic_coeff[0][1][0][0] + dc12dv[jj][ii]*dc12dv[j][i]*elastic_coeff[0][1][0][1] + dc12dv[jj][ii]*dc22dv[j][i]*elastic_coeff[0][1][1][1] + dc12dv[jj][ii]*dc21dv[j][i]*elastic_coeff[0][1][1][0] + dc21dv[jj][ii]*dc11dv[j][i]*elastic_coeff[1][0][0][0] + dc21dv[jj][ii]*dc12dv[j][i]*elastic_coeff[1][0][0][1] + dc21dv[jj][ii]*dc22dv[j][i]*elastic_coeff[1][0][1][1] + dc21dv[jj][ii]*dc21dv[j][i]*elastic_coeff[1][0][1][0] + dc22dv[jj][ii]*dc11dv[j][i]*elastic_coeff[1][1][0][0] + dc22dv[jj][ii]*dc12dv[j][i]*elastic_coeff[1][1][0][1] + dc22dv[jj][ii]*dc22dv[j][i]*elastic_coeff[1][1][1][1] + dc22dv[jj][ii]*dc21dv[j][i]*elastic_coeff[1][1][1][0]); } return energy; } /* end general_linear_elastic_all() */ /************************************************************** * * function: general_linear_elastic_energy() * * purpose: calculates energy of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL general_linear_elastic_energy(f_info) struct qinfo *f_info; { return general_linear_elastic_all(f_info,METHOD_VALUE); } /************************************************************** * * function: general_linear_elastic_gradient() * * purpose: calculates gradient of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL general_linear_elastic_gradient(f_info) struct qinfo *f_info; { return general_linear_elastic_all(f_info,METHOD_GRADIENT); } /************************************************************** * * function: general_linear_elastic_hessian() * * purpose: calculates hessian of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL general_linear_elastic_hessian(f_info) struct qinfo *f_info; { return general_linear_elastic_all(f_info,METHOD_HESSIAN); } /***********************************************************************/ /************************************************************************ Named method: dirichlet_elastic Dirichlet elastic strain energy on facets. For conformal mappings. Let S be Gram matrix of unstrained facet (dots of sides). Let Q be the inverse of S. Let F be Gram matrix of strained facet. Let C = FQ, the linear deformation matrix Then energy density is Tr(CC^T) Each facet extra attribute array form_factors[3] = {s11,s12,s22} where sij = dot(si,sj) and s1 = (v1-v0), s2 = (v2-v0). ************************************************************************/ #define FORM_FACTORS_NAME "form_factors" extern int form_factors_attr; /* number of form_factors extra attribute */ /*************************************************************** * * function: dirichlet_elastic_init() * * purpose: Make sure needed extra attributes are present. * * */ void dirichlet_elastic_init(mode,mi) int mode; /* energy or gradient or hessian */ struct method_instance *mi; { if ( web.modeltype != LINEAR ) kb_error(4529,"dirichlet_elastic method only for LINEAR model.\n",RECOVERABLE); if ( web.dimension != 2 ) kb_error(4150,"dirichlet_elastic method only for SOAPFILM model.\n", RECOVERABLE); form_factors_attr = find_attribute(FACET,FORM_FACTORS_NAME); if ( form_factors_attr < 0 ) /* not found */ kb_error(4152,"Facet extra attribute form_factors real[3] missing. Needed by dirichlet_elastic.\n",RECOVERABLE); if ( EXTRAS(FACET)[form_factors_attr].array_spec.datacount < 3 ) kb_error(4153,"Facet extra attribute form_factors must have size 3.\n", RECOVERABLE); } /************************************************************************ * * function: dirichlet_elastic_all() * * purpose: energy, gradient, and hessian for dirichlet_elastic method. */ REAL dirichlet_elastic_all ARGS((struct qinfo *,int)); REAL dirichlet_elastic_all(f_info,mode) struct qinfo *f_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ { REAL *s; /* pointer to extra attributes */ REAL **side; REAL q11,q12,q22; /* Q entries */ REAL det; /* det S */ REAL area; /* reference area of facet */ REAL f11,f12,f22; REAL c11,c22; REAL energy; REAL dc11dv[FACET_VERTS][MAXCOORD]; REAL dc22dv[FACET_VERTS][MAXCOORD]; REAL ddc11dv[FACET_VERTS][MAXCOORD][FACET_VERTS]; REAL ddc22dv[FACET_VERTS][MAXCOORD][FACET_VERTS]; int i,j,jj; s = (REAL*)get_extra(f_info->id,form_factors_attr); det = s[0]*s[2] - s[1]*s[1]; if ( det <= 0.0 ) { if ( mode == METHOD_VALUE ) return 0.0; sprintf(errmsg,"dirichlet_elastic: Facet %s has unstrained area <= 0.\n", ELNAME(f_info->id)); kb_error(4154,errmsg,RECOVERABLE); } area = sqrt(det)/2; q11 = s[2]/det; q12 = -s[1]/det; q22 = s[0]/det; side = f_info->sides[0]; f11 = SDIM_dot(side[0],side[0]); f12 = SDIM_dot(side[0],side[1]); f22 = SDIM_dot(side[1],side[1]); c11 = f11*q11 + f12*q12; c22 = f12*q12 + f22*q22; energy = (c11 + c22)*area; if ( mode == METHOD_VALUE ) return energy; /* gradient */ for ( i = 0 ; i < SDIM ; i++ ) { dc11dv[1][i] = 2*side[0][i]*q11 + side[1][i]*q12; dc11dv[2][i] = side[0][i]*q12; dc22dv[1][i] = side[1][i]*q12; dc22dv[2][i] = side[0][i]*q12 + 2*side[1][i]*q22; dc11dv[0][i] = -(dc11dv[1][i] + dc11dv[2][i]); dc22dv[0][i] = -(dc22dv[1][i] + dc22dv[2][i]); } for ( j = 0 ; j < FACET_VERTS ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) { f_info->grad[j][i] = (dc11dv[j][i] + dc22dv[j][i]) * area; } if ( mode == METHOD_GRADIENT ) return energy; /* hessian */ for ( i = 0 ; i < SDIM ; i++ ) { ddc11dv[1][i][1] = 2*q11; ddc11dv[1][i][2] = q12; ddc11dv[2][i][1] = q12; ddc11dv[2][i][2] = 0.0; ddc22dv[1][i][1] = 0.0; ddc22dv[1][i][2] = q12; ddc22dv[2][i][1] = q12; ddc22dv[2][i][2] = 2*q22; for ( j = 1 ; j < FACET_VERTS; j++ ) { ddc11dv[0][i][j] = -(ddc11dv[1][i][j] + ddc11dv[2][i][j]); ddc22dv[0][i][j] = -(ddc22dv[1][i][j] + ddc22dv[2][i][j]); ddc11dv[j][i][0] = -(ddc11dv[j][i][1] + ddc11dv[j][i][2]); ddc22dv[j][i][0] = -(ddc22dv[j][i][1] + ddc22dv[j][i][2]); } ddc11dv[0][i][0] = -(ddc11dv[1][i][0] + ddc11dv[2][i][0]); ddc22dv[0][i][0] = -(ddc22dv[1][i][0] + ddc22dv[2][i][0]); } for ( j = 0 ; j < FACET_VERTS ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) for ( jj = 0 ; jj < FACET_VERTS ; jj++ ) { f_info->hess[j][jj][i][i] += (ddc11dv[j][i][jj] + ddc22dv[j][i][jj]) * area; } return energy; } /************************************************************** * * function: dirichlet_elastic_energy() * * purpose: calculates energy of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL dirichlet_elastic_energy(f_info) struct qinfo *f_info; { return dirichlet_elastic_all(f_info,METHOD_VALUE); } /************************************************************** * * function: dirichlet_elastic_gradient() * * purpose: calculates gradient of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL dirichlet_elastic_gradient(f_info) struct qinfo *f_info; { return dirichlet_elastic_all(f_info,METHOD_GRADIENT); } /************************************************************** * * function: dirichlet_elastic_hessian() * * purpose: calculates hessian of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL dirichlet_elastic_hessian(f_info) struct qinfo *f_info; { return dirichlet_elastic_all(f_info,METHOD_HESSIAN); } evolver-2.30c.dfsg/src/Makefile0000644000175300017530000002334611410765113016665 0ustar hazelscthazelsct# Makefile for Surface Evolver SHELL= /bin/sh # INSTRUCTIONS: Customize this makefile for your system by choosing # your compiler (cc or gcc or whatever), and then uncomment the # appropriate CFLAGS, GRAPH, and GRAPHLIB definitions for your system. # If lines need to be modified for your system, please let me know # at brakke@susqu.edu. # The following systems have their own sections: # Generic Unix # Linux OpenGL # Linux Xwindows # cygwin # Mac OSX # Sun Xwindows # 32-bint Solaris OpenGL # 64-bint Solaris OpenGL # NOTE: -DOOGL should be specified in CFLAGS if you want Evolver to # be able to use geomview. And -DOOGL is harmless in any case. # NOTE: You can use the readline input editing system (if your system has it) # by adding -DUSE_READLINE to CFLAGS and -lreadline -lcurses to GRAPHLIB. # But beware; readline may be incompatible with using geomview. # Also, the readline code was contributed by a user for version 2.30, # and may be incompatible with readline libraries on some systems (Mac, # for instance). #----------------------------------------------------------------------------- # Pick your compiler, and add any options you want, such as optimization CC= cc #CC= gcc #---------------------Start of system choices--------------------------------- # Remove #'s from following 3 lines for generic Unix without builtin graphics. # Add -DOOGL to CFLAGS if you are using geomview. #CFLAGS= -DGENERIC #GRAPH= nulgraph.o #GRAPHLIB= #---- Generic Unix ----------------------------------------------------------- # Remove #'s from following 3 lines for generic Unix with X-windows. # Add -DOOGL to CFLAGS if you are using geomview. # You might have to add something like -I/usr/X11R6/include to CFLAGS if # there is a problem finding Xlib.h while compiling xgraph.c, and add # -L/usr/X11R6/lib to GRAPHLIB. #CFLAGS= -DGENERIC #GRAPH= xgraph.o #GRAPHLIB= -lX11 #some places might have -lX11-mit #----- Linux OpenGL ---------------------------------------------------------- # Remove #'s from following 3 lines for LINUX with OpenGL GLUT graphics. # The graphics are on a second thread, so pthreads are needed. # NOTE: -DPTRHEADS is necessary with glutgraph.o. #CFLAGS= -DLINUX -DOOGL -DPTHREADS #GRAPH= glutgraph.o #GRAPHLIB= -lGL -lGLU -lglut -lpthread # NOTE: It has been reported to me that RedHat 9 needs the following line # for GRAPHLIB, but earlier and later versions do okay with the GRAPHLIB # line just above. #GRAPHLIB= -lGL -lGLU -lglut -L/usr/X11R6/lib -lXi -lXmu -lpthread #------ Linux Xwindows ------------------------------------------------------ # Remove #'s from following 3 lines for LINUX with crummy Xwindows graphics. # You may have to modify the X11 lib path given here. #CFLAGS= -DLINUX -DOOGL #GRAPH= xgraph.o #GRAPHLIB= -L/usr/X11R6/lib -lX11 #-------- cygwin -------------------------------------------------------------- # Remove #'s from the following three lines for cygwin. #CFLAGS= -DLINUX -DOOGL -DPTHREADS #GRAPH= glutgraph.o #GRAPHLIB= -lglut -lglu32 -lopengl32 -lpthread #------- Mac OSX ------------------------------------------------------------- # Remove #'s from following 5 lines for MAC OSX with OpenGL GLUT graphics. #INC=/System/Library/Frameworks/GLUT.framework/Versions/A/Headers #CFLAGS= -DLINUX -DPTHREADS -DOOGL -DMAC_OS_X -I$(INC) #GRAPH= glutgraph.o #GLDIR=/System/Library/Frameworks/OpenGL.framework/Libraries #GRAPHLIB= -L$(GLDIR) -lGL -lGLU -lobjc -framework GLUT #------- Sun Xwindows -------------------------------------------------------- # Remove #'s from following 3 lines for SUNs or SPARCSTATIONs with X-windows # WARNING: If you use -O2 optimization, you may have to compile popfilm.c # without optimization, because sun optimization may hang. #CFLAGS= -DSUN -DOOGL -I/usr/openwin/include #GRAPH= xgraph.o #GRAPHLIB= -L/usr/openwin/lib -lX11 #------- 32-bint Solaris OpenGL ------------------------------------------------------ # 32-bit SOLARIS with GLUT. The GLUT graphics window runs in a separate # thread; -DPTHREADS enables threads in Evolver source code, and -mt # enables it in the Solaris cc compiler. For the gcc compiler, use -pthreads # instead of -mt. # You should check to see that the GLUTHOME directory listed below exists; # it may be some other place on your machine. If you can't find it, # download glut-3.7b.sparc_solaris.tar.gz from www.sun.com. #GLUTHOME= /usr/local/sparc_solaris/glut-3.7 #INCLUDE= -I/usr/openwin/include -I$(GLUTHOME)/include #CFLAGS= -DSUN -DOOGL -DGLUT -DPTHREADS -mt $(INCLUDE) #GRAPH= glutgraph.o #GRAPHLIB= -L/usr/openwin/lib -lX11 -L$(GLUTHOME)/lib/glut -lglut -lGL -lGLU -lXmu #------- 64-bint Solaris OpenGL ------------------------------------------------------ # 64-bit SOLARIS with GLUT. Also see 32 bit comments above. # You should check to see that the GLUTHOME directory listed below exists; # it may be some other place on your machine. If you can't find it, # download glut-3.7b.sparc_solaris_64.tar.gz from www.sun.com. #GLUTHOME= /usr/local/sparc_solaris_64bit/glut-3.7 #INCLUDE= -I/usr/openwin/include -I$(GLUTHOME)/include #CFLAGS= -DSUN -DOOGL -DGLUT -DPTHREADS -mt $(INCLUDE) -m64 #GRAPH= glutgraph.o #GRAPHLIB= -L/usr/openwin/lib -lX11 -L$(GLUTHOME)/lib/glut -lglut -lGL -lGLU -lXmu #Note on Solaris: #SOLARISFLAGS= -L/usr/ucblib -lucb -R/usr/ucblib -lnsl -lsocket #are needed for some programs on Solaris; but not this one -- they're poison #for Evolver! #----------------------------------------------------------------------------- # Remove #'s from following 3 lines for DEC Alpha with X-windows # Add -DOOGL to CFLAGS if you have the X version of geomview. #CFLAGS= -DDECALPHA -O -Olimit 2000 #GRAPH= xgraph.o #GRAPHLIB= -lX11 #----------------------------------------------------------------------------- # IRIX 4, with old gl graphics # Remove #'s from the following three lines for Iris workstations with # geomview. Those with R4000 CPUs or above may add -mips2 to CFLAGS. #CFLAGS = -DIRIS -DOOGL -O2 -Olimit 3000 #GRAPH= iriszgraph.o #GRAPHLIB= -lgl_s -lmalloc -lc_s #----------------------------------------------------------------------------- # IRIX 5 # Remove #'s from the following three lines for Iris workstations with # geomview. Those with R4000 CPUs or above may add -mips2 to CFLAGS. #CFLAGS = -DIRIS -DOOGL -O2 -Olimit 3000 #GRAPH= xgraph.o #GRAPHLIB= -lmalloc -lX11 #----------------------------------------------------------------------------- # IRIX 5, parallel # Remove #'s from the following three lines for Iris workstations with # multiple processors and geomview. OK for single processors, too. # Those with R4000 CPUs or above may add -mips2 to CFLAGS. #CFLAGS = -DIRIS -DOOGL -DSGI_MULTI -O2 -Olimit 3000 -mips2 #GRAPH= xgraph.o #GRAPHLIB= -lmalloc -lX11 #----------------------------------------------------------------------------- # IRIX 6 # Remove #'s from the following three lines for Iris workstations with # IRIX 6.x operating system. Add -DSGI_MULTI to CLFAGS if you # have multiple processors. #CFLAGS = -DIRIS -DOOGL -O2 -OPT:Olimit=10000 #GRAPH= xgraph.o #GRAPHLIB= -lmalloc -lX11 #----------------------------------------------------------------------------- # Remove #'s from following 3 lines for HP workstation # Omit -Aa if you're using gcc #CFLAGS= -Aa -D_HPUX_SOURCE -I/usr/include/X11R5 #GRAPH= xgraph.o #GRAPHLIB= -L/usr/lib/X11R5 -lX11 #----------------------------------------------------------------------------- # Remove #'s from following 3 lines for NeXTStep without screen graphics # (see separate ftp archive evolver.next.tar.Z for graphics version) #CFLAGS= -DNeXT #GRAPH= nulgraph.o #GRAPHLIB= #-------------------End of system-specific options---------------------------- OBJ= calcforc.o variable.o trirevis.o stringl.o stringq.o model.o\ fixvol.o query.o matrix.o grapher.o painter.o filml.o filmq.o\ torvol.o lexinit.o graphgen.o modify.o userio.o boundary.o\ curtest.o display.o yexparse.o lexyy.o ytab.o hessian.o\ evaltree.o cnstrnt.o verpopst.o popfilm.o machine.o veravg.o \ pixgraph.o tmain.o tordup.o wulff.o help.o psgraph.o check.o \ utility.o skeleton.o storage.o dump.o iterate.o filgraph.o zoom.o\ softimag.o mvgraph.o diffuse.o sqcurve.o klein.o\ command.o hidim.o simplex.o metric.o torus.o quotient.o alice.o\ sdrv.o odrv.o userfunc.o kusner.o simequi2.o\ geomgraph.o symtable.o exprint.o quantity.o meanint.o mindeg.o\ dodecGroup.o registry.o khyp.o gauss.o knot1.o eval_all.o\ lexinit2.o evalmore.o knot2.o knot3.o teix.o sqcurve2.o\ hessian2.o hessian3.o method1.o method2.o method3.o bk.o\ method4.o method5.o eval_sec.o sqcurve3.o metis.o lagrange.o evolver: makemark $(OBJ) $(GRAPH) $(CC) $(CFLAGS) $(OBJ) $(GRAPH) $(GRAPHLIB) -o evolver -lm # This is to get global dependencies on the main header files. makemark: skeleton.h storage.h model.h web.h if [[ -z "$(GRAPH)" ]] ; then (echo "ERROR: You need to uncomment your system's lines in Makefile.") ; fi rm *.o || true touch makemark .c.o: $(CC) $(CFLAGS) -c $< # lexyy.c and ytab.c should only be remade when interface # language is changed, which users shouldn't be touching. #lexyy.c: datafile.lex express.h ytab.c lex.h # lex datafile.lex # sed '/#ident/d' t.c # sed '/int \* p, int m/s/(int \* p, int m)/(p,m) int *p,m;/' lexyy.c # rm lex.yy.c #ytab.c: command.yac express.h # yacc -d command.yac # sed '/#ident/d' tmp.c # sed '/malloc()/d' ytab.c # rm tmp.c # cp y.tab.h ytab.h # rm y.tab.c y.tab.h ytab.h: ytab.c lexinit.o: lex.h lexinit.c express.h ytab.h query.o: lex.h ytab.h query.c evaltree.o: evaltree.c lex.h ytab.h express.h eval_all.o: eval_all.c lex.h ytab.h express.h eval_sec.o: eval_all.c lex.h ytab.h express.h yexparse.o: yexparse.c lex.h ytab.h express.h exprint.o: exprint.c lex.h ytab.h express.h tmain.o: tmain.c include.h evolver-2.30c.dfsg/src/filml.c0000644000175300017530000005350611410765113016475 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************* * * file: filml.c * * Contents: Functions calculating energy, volume, and their * gradients for the LINEAR SOAPFILM model. */ #include "include.h" REAL wee_area = 0.0; /********************************************************************* * * Function: facet_energy_l() * * Purpose: Calculates energy due to facet for LINEAR SOAPFILM. * Also does facet quantity integrands. */ void facet_energy_l(f_id,mode) facet_id f_id; int mode; /* AREA_ONLY or ALL_ENERGY */ { int i,j; REAL side[FACET_EDGES][MAXCOORD]; REAL normal[MAXCOORD]; REAL wulff[MAXCOORD]; /* energy covector to area normal vector */ REAL energy; body_id b_id; REAL *x[FACET_VERTS]; REAL unwrap_x[FACET_VERTS][MAXCOORD]; REAL z[FACET_VERTS]; /* for calculating average z*z */ REAL zz; /* for average z*z, for gravity */ REAL u; /* gravitational energy */ facetedge_id fe_id; vertex_id v_id[FACET_VERTS]; REAL ss,st,tt; REAL det; #ifdef THREADS struct thread_data *data = GET_THREAD_DATA; #endif int_val = ordinal(get_original(f_id))+1; /* for eval of file parameters */ /* get side vectors */ fe_id = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { v_id[i] = get_fe_tailv(fe_id); x[i] = unwrap_x[i]; fe_id = get_next_edge(fe_id); } get_facet_verts(f_id,x,NULL); /* in tail order */ for ( i = 0 ; i < FACET_EDGES ; i++ ) { int ii = (i+1)%FACET_EDGES; for ( j = 0 ; j < SDIM ; j++ ) side[i][j] = x[ii][j] - x[i][j]; z[i] = x[i][2]; } if ( web.metric_flag ) { if ( klein_metric_flag ) energy = klein_area(x); else energy = simplex_energy_metric(v_id,x); set_facet_area(f_id,energy); goto skip_from_metric; } /* calculate surface tension energy */ ss = SDIM_dot(side[0],side[0]); st = SDIM_dot(side[0],side[1]); tt = SDIM_dot(side[1],side[1]); det = ss*tt-st*st; if ( det > 0.0 ) energy = sqrt(det)/2; else energy = 0.0; set_facet_area(f_id,energy); if ( mode == AREA_ONLY ) return; if ( web.wulff_flag ) { /* calculate normal */ cross_prod(side[0],side[1],normal); (*get_wulff)(normal,wulff); energy = SDIM_dot(wulff,normal)/2; } /* do square curvature if wanted */ if ( ((square_curvature_flag | mean_curv_int_flag) & EVALUATE ) && !kusner_flag && !conf_edge_curv_flag ) sqcurve_energy(v_id,side); skip_from_metric: #ifdef THREADS if ( threadflag ) data->total_area = data->total_area + energy; /* Borland C bug */ else #endif binary_tree_add(web.total_area_addends,energy); if ( get_fattr(f_id) & DENSITY ) energy = energy * get_facet_density(f_id); /* Borland C bug */ /* add gravitational energy, vector potential z*z/2*k */ if ( web.gravflag && !(get_fattr(f_id) & NONCONTENT) ) { zz = (z[0]*z[0]+z[1]*z[1]+z[2]*z[2]+z[0]*z[1]+z[1]*z[2]+z[0]*z[2])/6; u = zz*(side[0][0]*side[1][1]-side[0][1]*side[1][0])/2/2; /* half for area, half from potential */ b_id = get_facet_body(f_id); if ( valid_id(b_id) ) energy += u*get_body_density(b_id)*web.grav_const; b_id = get_facet_body(facet_inverse(f_id)); if ( valid_id(b_id) ) energy -= u*get_body_density(b_id)*web.grav_const; } #ifdef THREADS if ( threadflag ) data->total_energy = data->total_energy + energy; /* Borland C bug */ else #endif binary_tree_add(web.total_energy_addends,energy); } /* end facet_energy_l() */ /************************************************************************ * * Function: facet_force_l() * * Purpose: Calculates all forces on control points due to facet and * accumulates them at each control point. */ void facet_force_l(f_id) facet_id f_id; { REAL side[FACET_EDGES][MAXCOORD]; REAL normal[MAXCOORD]; REAL wulff[MAXCOORD]; /* area vector covector for energy */ REAL temp[MAXCOORD]; int i,j; REAL area; facetedge_id fe_id; REAL *x[FACET_VERTS]; REAL z[FACET_VERTS]; /* for gravitational forces */ body_id b_id; REAL density = get_facet_density(f_id); vertex_id v_id[FACET_VERTS]; edge_id e_id[FACET_EDGES]; REAL unwrap_x[FACET_VERTS][MAXCOORD]; WRAPTYPE wraps[FACET_VERTS]; REAL forces[FACET_VERTS][MAXCOORD]; /* total forces from this facet */ REAL *forceptr[FACET_VERTS]; /* pointers to forces */ REAL ss,st,tt; memset((char*)forces,0,sizeof(forces)); /* set to 0 */ int_val = ordinal(get_original(f_id))+1; /* for eval of file parameters */ /* get side vectors */ fe_id = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { e_id[i] = get_fe_edge(fe_id); v_id[i] = get_edge_tailv(e_id[i]); x[i] = unwrap_x[i]; forceptr[i] = forces[i]; fe_id = get_next_edge(fe_id); } get_facet_verts(f_id,x,wraps); /* verts in tail order */ for ( i = 0 ; i < FACET_EDGES ; i++ ) { int ii = (i+1)%FACET_EDGES; for ( j = 0 ; j < SDIM ; j++ ) side[i][j] = x[ii][j] - x[i][j]; z[i] = x[i][2]; } if ( web.metric_flag ) { if ( klein_metric_flag ) { klein_area_grad(x,forceptr); for ( i = 0 ; i < FACET_VERTS ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) forceptr[i][j] *= density; area = klein_area(x); } else { simplex_force_metric(v_id,x,density,forceptr); area = simplex_energy_metric(v_id,x); } goto end_euclidean; } if ( ((square_curvature_flag | mean_curv_int_flag) & EVALUATE) && !kusner_flag && !conf_edge_curv_flag ) sqcurve_force(v_id,e_id,side); /* calculate area */ ss = SDIM_dot(side[0],side[0]); st = SDIM_dot(side[0],side[1]); tt = SDIM_dot(side[1],side[1]); area = ss*tt-st*st; if ( area < 0.0 ) area = 0.0; /* stupid inaccurate computers! */ area = sqrt(area)/2; set_facet_area(f_id,area); /* an error check, and accommodation for possibly deliberately degenerate triangles on boundary */ if ( area <= wee_area ) { facetedge_id ffe; ffe = fe_id; sprintf(errmsg,"WARNING! Zero area for facet %s.\n",ELNAME(f_id)); outstring(errmsg); if ( itdebug ) { outstring("Facet-edges and side vectors: \n"); for ( i = 0 ; i < FACET_EDGES ; i++, ffe = get_next_edge(ffe) ) { sprintf(msg," %8lX %18.15f %18.15f %18.15f\n",ffe, (DOUBLE)side[i][0],(DOUBLE)side[i][1],(DOUBLE)side[i][2]); outstring(msg); } prompt("Hit RETURN to continue.",msg,msgmax); } return; } /* get energy covector */ if ( web.wulff_flag ) { /* calculate normal */ cross_prod(side[0],side[1],normal); (*get_wulff)(normal,wulff); if ( get_fattr(f_id) & DENSITY ) for ( i = 0 ; i < SDIM ; i++ ) wulff[i] *= density; /* force on each vertex */ for ( i = 0 ; i < FACET_VERTS ; i++ ) /* vertex loop */ { int k; j = (i+1)%FACET_EDGES; /* opposite side */ cross_prod(side[j],wulff,temp); for ( k = 0 ; k < SDIM ; k++ ) forces[i][k] += temp[k]/2; } } else { REAL coeff = density/4/area; int k; for ( k = 0 ; k < SDIM ; k++ ) { forces[0][k] += coeff*(side[0][k]*tt - st*side[1][k]); forces[1][k] -= coeff*(side[0][k]*tt - st*side[1][k]); forces[1][k] += coeff*(ss*side[1][k] - st*side[0][k]); forces[2][k] -= coeff*(ss*side[1][k] - st*side[0][k]); } } /* gravity forces, negative of gravity energy gradient */ if ( web.gravflag && !(get_fattr(f_id) & NONCONTENT) ) { REAL zz; /* average z*z */ REAL gdensity; /* net density difference of bodies across facet */ REAL normz; /* facet normal component in z direction */ zz = (z[0]*z[0]+z[1]*z[1]+z[2]*z[2]+z[0]*z[1]+z[1]*z[2]+z[0]*z[2])/6; b_id = get_facet_body(f_id); gdensity = 0.0; if ( valid_id(b_id) ) gdensity += get_body_density(b_id); b_id = get_facet_body(facet_inverse(f_id)); if ( valid_id(b_id) ) gdensity -= get_body_density(b_id); normz = side[0][0]*side[1][1] - side[0][1]*side[1][0]; for ( i = 0 ; i < FACET_VERTS ; i++ ) /* vertex loop */ { j = (i+1)%FACET_EDGES; /* opposite side */ forces[i][0] += web.grav_const*gdensity*side[j][1]*zz/4; forces[i][1] -= web.grav_const*gdensity*side[j][0]*zz/4; forces[i][2] -= web.grav_const*gdensity*normz*(z[i]+z[0]+z[1]+z[2])/24; } } end_euclidean: /* accumulate star area around each vertex and edge */ fe_id = get_facet_fe(f_id); for ( i = 0 ; i < FACET_VERTS ; i++ ) /* vertex loop */ { edge_id ee_id; vertex_id vv_id; ee_id = get_fe_edge(fe_id); vv_id = get_edge_headv(ee_id); add_vertex_star(vv_id,area); add_edge_star(ee_id,area); fe_id = get_next_edge(fe_id); } /* add to totals, unwrapping if necessary */ for ( i = 0 ; i < FACET_VERTS ; i++ ) /* vertex loop */ { REAL *force; REAL wforce[MAXCOORD]; /* unwrapped forces */ force = get_force(v_id[i]); if ( web.symmetry_flag ) { (*sym_form_pullback)(get_coord(v_id[i]),wforce,forces[i],wraps[i]); for ( j = 0 ; j < SDIM ; j++ ) force[j] += wforce[j]; } else { for ( j = 0 ; j < SDIM ; j++ ) force[j] += forces[i][j]; } } } /* end facet_force_l() */ /********************************************************************** * * Function: facet_volume_l() * * Purpose: Find triangle's contribution to volumes of neighboring bodies. * Volumes with respect to origin are calculated for each * face, and then oriented contributions added for each body. */ void facet_volume_l(f_id) facet_id f_id; { body_id b_id0,b_id1; facetedge_id fe_id; REAL vol; if ( get_fattr(f_id) & NONCONTENT ) return; int_val = ordinal(get_original(f_id))+1; /* for eval of file parameters */ b_id0 = get_facet_body(f_id); b_id1 = get_facet_body(facet_inverse(f_id)); if ( !valid_id(b_id0) && !valid_id(b_id1) ) return; if ( web.symmetric_content ) { vertex_id v1,v2,v3; facetedge_id next_fe; fe_id = get_facet_fe(f_id); next_fe = get_next_edge(fe_id); v1 = get_fe_tailv(fe_id); v2 = get_fe_headv(fe_id); v3 = get_fe_headv(next_fe); vol = triple_prod(get_coord(v1), get_coord(v2), get_coord(v3))/6; } else { MAT2D(x,MAXCOORD,MAXCOORD); get_facet_verts(f_id,x,NULL); vol = (x[0][2]+x[1][2]+x[2][2])/6* ((x[1][0]-x[0][0])*(x[2][1]-x[0][1])-(x[1][1]-x[0][1])*(x[2][0]-x[0][0])); } /* add to body volumes */ if ( valid_id(b_id0) ) add_body_volume(b_id0,vol); if ( valid_id(b_id1) ) add_body_volume(b_id1,-vol); } /* end facet_volume_l() */ /****************************************************************** * * Function: film_grad_l() * * Purpose: Compute volume gradients for vertices on facets. * Also fixed quantity gradients. */ #define NEWTORVOL void film_grad_l() { body_id bi_id; /* identifier for body i */ body_id bj_id; /* identifier for body j */ facetedge_id fe; vertex_id v_id; facet_id f_id; facetedge_id fe_id; int i,k; volgrad *vgptr; REAL z; REAL side[FACET_EDGES][MAXCOORD]; REAL normal[MAXCOORD]; REAL *x[FACET_VERTS]; /* coordinates of vertices */ REAL unwrap_x[FACET_VERTS][MAXCOORD]; #ifdef NEWTORVOL struct qinfo f_info; /* for calling q_facet_torus_volume */ if ( web.torus_flag ) q_info_init(&f_info,METHOD_GRADIENT); #endif if ( sym_flags & NEED_FORM_UNWRAPPING ) kb_error(1035,"Have to do convert_to_quantities with this symmetry group.\n",RECOVERABLE); FOR_ALL_FACETS(f_id) { WRAPTYPE wraps[3]; int_val = ordinal(get_original(f_id))+1; /* for eval of file parameters */ bi_id = get_facet_body(f_id); bj_id = get_facet_body(facet_inverse(f_id)); if ( (!valid_id(bi_id) || !(get_battr(bi_id) & (FIXEDVOL|PRESSURE)) ) && (!valid_id(bj_id) || !(get_battr(bj_id) & (FIXEDVOL|PRESSURE)) ) ) continue; if ( web.torus_flag ) { #ifdef NEWTORVOL int n; vertex_id v_ids[FACET_EDGES]; /* get basic info */ fe = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i ++ ) { v_ids[i] = get_fe_tailv(fe); fe = get_next_edge(fe); } f_info.id = f_id; q_facet_setup(NULL,&f_info,NEED_SIDE|TORUS_MODULO_MUNGE|ORIENTABLE_METHOD); q_facet_torus_volume_grad(&f_info); if ( valid_id(bi_id) && (get_battr(bi_id) & (FIXEDVOL|PRESSURE)) ) for ( i = 0 ; i < FACET_VERTS ; i++ ) { vgptr = get_bv_new_vgrad(get_body_fixnum(bi_id),v_ids[i]); vgptr->bb_id = bi_id; for ( n = 0 ; n < SDIM ; n++ ) vgptr->grad[n] += f_info.grad[i][n]; } if ( valid_id(bj_id) && (get_battr(bj_id) & (FIXEDVOL|PRESSURE)) ) for ( i = 0 ; i < FACET_VERTS ; i++ ) { vgptr = get_bv_new_vgrad(get_body_fixnum(bj_id),v_ids[i]); vgptr->bb_id = bj_id; for ( n = 0 ; n < SDIM ; n++ ) vgptr->grad[n] -= f_info.grad[i][n]; } #else /* kludge copy from torvol_project, so could ditch torvol_project */ REAL *v[FACET_VERTS]; /* pointers to three vertex coordinates */ int j; REAL adj[FACET_EDGES][MAXCOORD]; /* torus wrap adjustments for edge */ vertex_id v_ids[FACET_VERTS]; int n; REAL g[MAXCOORD]; /* get basic info */ fe = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i ++ ) { v_ids[i] = get_fe_tailv(fe); v[i] = get_coord(get_fe_tailv(fe)); get_edge_adjust(get_fe_edge(fe),adj[i]); fe = get_next_edge(fe); } for ( i = 0 ; i < FACET_EDGES ; i++ ) { int m; REAL ga[MAXCOORD],gb[MAXCOORD],gc[MAXCOORD],gd[MAXCOORD], ge[MAXCOORD],gf[MAXCOORD]; /* gradient parts */ j = (i+1)%FACET_EDGES; k = (i+2)%FACET_EDGES; /* basic tetrahedron */ cross_prod(v[j],v[k],ga); /* torus wrap corrections */ /* two-vertex term */ cross_prod(adj[j],v[j],gb); /* - */ cross_prod(adj[i],v[k],gc); /* + */ cross_prod(adj[k],v[j],gd); /* + */ cross_prod(adj[j],v[k],ge); /* - */ /* one-vertex term */ cross_prod(adj[k],adj[i],gf); /* add parts to existing gradient */ for ( m = 0 ; m < SDIM ; m++ ) g[m] = (ga[m] + (-gb[m]+gc[m]+gd[m]-ge[m])/2 + gf[m])/6; if ( valid_id(bi_id) && (get_battr(bi_id) & (FIXEDVOL|PRESSURE)) ) { vgptr = get_bv_new_vgrad(get_body_fixnum(bi_id),v_ids[i]); vgptr->bb_id = bi_id; for ( n = 0 ; n < SDIM ; n++ ) vgptr->grad[n] += g[n]; } if ( valid_id(bj_id) && (get_battr(bj_id) & (FIXEDVOL|PRESSURE)) ) { vgptr = get_bv_new_vgrad(get_body_fixnum(bj_id),v_ids[i]); vgptr->bb_id = bj_id; for ( n = 0 ; n < SDIM ; n++ ) vgptr->grad[n] -= g[n]; } } #endif } else { vertex_id v[3]; /* get side vectors */ fe_id = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { get_fe_side(fe_id,side[i]); v[i] = get_fe_headv(fe_id); x[i] = get_coord(v[i]); fe_id = get_next_edge(fe_id); } if ( web.symmetry_flag ) { for ( i = 0 ; i < FACET_VERTS ; i++ ) x[i] = unwrap_x[i]; get_facet_verts(f_id,x,wraps); /* have to readjust indexing for agreement with nonsym way.. kludge, but don't want to risk massive changes */ for ( i = 0 ; i < FACET_VERTS ; i++ ) x[i] = unwrap_x[(i+1)%3]; for ( i = 0 ; i < SDIM ; i++ ) { side[0][i] = x[0][i] - x[2][i]; side[1][i] = x[1][i] - x[0][i]; side[2][i] = x[2][i] - x[1][i]; } } if ( web.symmetric_content ) { /* do each of the three vertices */ fe = get_facet_fe(f_id); for ( k = 0 ; k < FACET_VERTS ; k++, fe = get_next_edge(fe) ) { fe_id = get_next_edge(fe); v_id = get_fe_headv(fe_id); if ( get_vattr(v_id) & FIXED ) continue; cross_prod(get_coord(get_fe_tailv(fe)), get_coord(get_fe_headv(fe)),normal); if ( valid_id(bi_id) && (get_battr(bi_id) & (PRESSURE|FIXEDVOL)) ) { vgptr = get_bv_new_vgrad(get_body_fixnum(bi_id),v_id); vgptr->bb_id = bi_id; for ( i = 0 ; i < SDIM ; i++ ) vgptr->grad[i] += normal[i]/6.0; } if ( valid_id(bj_id) && (get_battr(bj_id) & (PRESSURE|FIXEDVOL)) ) { vgptr = get_bv_new_vgrad(get_body_fixnum(bj_id),v_id); vgptr->bb_id = bj_id; for ( i = 0 ; i < SDIM ; i++ ) vgptr->grad[i] -= normal[i]/6.0; } } } else { /* content from integrating z dx dy */ /* get centroid z (divide by 3 later) */ for ( i = 0,z = 0.0; i < FACET_EDGES ; i++ ) z += x[i][2]; /* calculate normal */ cross_prod(side[0],side[1],normal); /* now do each of the three vertices */ fe = get_facet_fe(f_id); for ( k = 0 ; k < FACET_VERTS ; k++, fe = get_next_edge(fe) ) { fe_id = get_next_edge(fe); v_id = get_fe_headv(fe_id); if ( get_vattr(v_id) & FIXED ) continue; if ( valid_id(bi_id) && (get_battr(bi_id) & (PRESSURE|FIXEDVOL)) ) { vgptr = get_bv_new_vgrad(get_body_fixnum(bi_id),v_id); vgptr->bb_id = bi_id; vgptr->grad[0] += -side[k][1]*z/6.0; vgptr->grad[1] += side[k][0]*z/6.0; vgptr->grad[2] += normal[2]/6.0; } if ( valid_id(bj_id) && (get_battr(bj_id) & (PRESSURE|FIXEDVOL)) ) { vgptr = get_bv_new_vgrad(get_body_fixnum(bj_id),v_id); vgptr->bb_id = bj_id; vgptr->grad[0] -= -side[k][1]*z/6.0; vgptr->grad[1] -= side[k][0]*z/6.0; vgptr->grad[2] -= normal[2]/6.0; } } } } /* end torus/nontorus */ } /* end facet loop */ #ifdef NEWTORVOL if ( web.torus_flag ) q_info_free(&f_info); #endif } /* end film_grad_l() */ /******************************************************************* * * Function: film_constr_grad() * * Purpose: Add cell volume gradients due to constraint integrals. * And quantity integrals. */ void film_constr_grad() { edge_id e_id; REAL side[MAXCOORD]; REAL tgrad[MAXCOORD]; REAL hgrad[MAXCOORD]; REAL grad; int i,j,k,m,sign,bodysign=0; REAL green[MAXCOORD]; REAL green_deriv[MAXCOORD][MAXCOORD]; REAL midpt[MAXCOORD]; REAL *tcoord,*hcoord; struct constraint *constr; vertex_id headv,tailv; facetedge_id fe,first_fe; FOR_ALL_EDGES(e_id) { struct volgrad *vgptri; facet_id f_id; ATTR attr = get_eattr(e_id); conmap_t *conmap = get_e_constraint_map(e_id); /* only hit constraints */ if ( attr & FIXED ) continue; if ( !(attr & CONSTRAINT) ) continue; headv = get_edge_headv(e_id); tailv = get_edge_tailv(e_id); tcoord = get_coord(tailv); hcoord = get_coord(headv); int_val = ordinal(get_original(e_id))+1; /* for eval of file parameters */ for ( j = 0 ; j < SDIM ; j++ ) side[j] = hcoord[j] - tcoord[j]; if ( !(attr & BDRY_CONTENT) ) continue; if ( web.modeltype == QUADRATIC ) { constr_vol_grad_q(e_id); continue; } else if ( web.modeltype == LAGRANGE ) kb_error(1036,"constr_vol_grad(): Cannot do LAGRANGE model.\n",RECOVERABLE); if ( attr & NEGBOUNDARY ) sign = -1; else sign = 1; if ( inverted(e_id) ) sign = -sign; for ( i = 0 ; i < SDIM ; i++ ) tgrad[i] = hgrad[i] = 0.0; for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { constr = get_constraint(conmap[j]); if ( constr->compcount != SDIM ) continue; if ( !(constr->attr & CON_CONTENT) ) continue; for ( m = 0 ; m < gauss1D_num ; m++ ) { for ( i = 0 ; i < SDIM ; i++ ) midpt[i] = gauss1Dpt[m]*hcoord[i] + (1 - gauss1Dpt[m])*tcoord[i]; for ( i = 0 ; i < SDIM ; i++ ) eval_all(constr->convect[i],midpt,SDIM,&green[i], green_deriv[i],e_id); for ( i = 0 ; i < SDIM ; i++ ) { for ( k = 0,grad = 0.0 ; k < SDIM ; k++ ) grad += side[k]*green_deriv[k][i]; tgrad[i] += sign*gauss1Dwt[m]*((1-gauss1Dpt[m])*grad - green[i]); hgrad[i] += sign*gauss1Dwt[m]*(gauss1Dpt[m]*grad + green[i]); } } } fe = first_fe = get_edge_fe(e_id); if ( valid_id(fe) ) do { f_id = get_fe_facet(fe); vgptri = get_vertex_vgrad(tailv); for ( ; vgptri ; vgptri = vgptri->chain ) { if ( !valid_id(vgptri->bb_id) ) continue; /* skip quantities */ if ( !equal_id(get_facet_body(f_id),vgptri->bb_id) ) { if ( !equal_id(get_facet_body(inverse_id(f_id)),vgptri->bb_id) ) continue; else bodysign = -sign; } else bodysign = sign; for ( i = 0 ; i < SDIM ; i++ ) vgptri->grad[i] += bodysign*tgrad[i]; } vgptri = get_vertex_vgrad(headv); for ( ; vgptri ; vgptri = vgptri->chain ) { if ( !valid_id(vgptri->bb_id) ) continue; /* skip quantities */ if ( !equal_id(get_facet_body(f_id),vgptri->bb_id) ) { if ( !equal_id(get_facet_body(inverse_id(f_id)),vgptri->bb_id) ) continue; else bodysign = -sign; } else bodysign = sign; for ( i = 0 ; i < SDIM ; i++ ) vgptri->grad[i] += bodysign*hgrad[i]; } fe = get_next_facet(fe); } while ( valid_id(fe) && !equal_id(fe,first_fe) ); } } /* end film_constr_grad() */ evolver-2.30c.dfsg/src/curtest.c0000644000175300017530000002015711410765113017057 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************* * * File: curtest.c * * Purpose: Test curvature of quadratic soapfilms to see if * the curvature is monotone. * */ #include "include.h" /********************************************************************* * * Function: curtest() * * Purpose: Overall control of curvature testing. * */ void curtest() { facet_id f_id; edge_id e_id; int retval; int edgetally[3],facettally[3]; int i; if ( web.modeltype != QUADRATIC ) { outstring("Cannot only do curvature sign test on QUADRATIC model.\n"); return; } for ( i = 0 ; i < 3; i++ ) edgetally[i] = facettally[i] = 0; FOR_ALL_FACETS(f_id) { retval = curtest_facet(f_id); if ( retval < 0 ) facettally[0]++; else if ( retval == 0 ) facettally[1]++; else facettally[2]++; } FOR_ALL_EDGES(e_id) { facetedge_id fe1,fe2; fe1 = get_edge_fe(e_id); if ( !valid_id(fe1) ) continue; /* ensure orientation lines up with original definition */ if ( inverted(get_fe_facet(fe1)) ) { invert(e_id); invert(fe1); } fe2 = get_next_facet(fe1); if ( equal_id(fe1,fe2) ) continue; /* only 1 facet on edge */ if ( !equal_id(fe1,get_next_facet(fe2)) ) continue; /* more than 2 */ retval = curtest_edge(e_id,fe1,fe2); if ( retval < 0 ) edgetally[0]++; else if ( retval == 0 ) edgetally[1]++; else edgetally[2]++; } /* report results */ sprintf(msg,"\nPositive curvature edges: %6d\n",edgetally[2]); outstring(msg); sprintf(msg,"Negative curvature edges: %6d\n",edgetally[0]); outstring(msg); sprintf(msg,"Mixed curvature edges: %6d\n",edgetally[1]); outstring(msg); sprintf(msg,"Positive curvature facets: %6d\n",facettally[2]); outstring(msg); sprintf(msg,"Negative curvature facets: %6d\n",facettally[0]); outstring(msg); sprintf(msg,"Mixed curvature facets: %6d\n\n",facettally[1]); outstring(msg); } /******************************************************************* * * Function: curtest_edge() * * Purpose: See if two facets meet along an edge with monotone * meeting angle. * * Return: Sign of (normal 1) x (normal 2) . (edge) * +1 for all positive * -1 for all negative * 0 otherwise */ /* evaluation points of cubic angle polynomial */ REAL uu[4] = {0.0, 0.7, 1.3, 2.0}; /* parameter along edge, 0 < u < 2 */ /* coefficients for finding polynomial coefficients */ REAL b1 = 0.3, b2 = 0.09, b3 = 0.027, denom = 1.0/(1 - 0.09); int curtest_edge(e_id,fe_1,fe_2) edge_id e_id; /* the edge */ facetedge_id fe_1,fe_2; /* link edge with facets */ { REAL *x[2][FACET_CTRL]; /* pointers to sets of vertices, starting along edge */ REAL h[4]; /* values of curvature polynomial */ REAL a[4]; /* polynomial coefficients */ int i,j,k; int sign; /* return value */ REAL tu[MAXCOORD]; /* common tangent along edge */ REAL tv[2][MAXCOORD]; /* the other tangent for each face */ REAL discr; /* quadratic formula discriminant */ REAL r[2]; /* quadratic roots */ REAL val; /* curvature values */ /* gather vertex coordinates */ x[0][0] = x[1][0] = get_coord(get_edge_tailv(e_id)); x[0][1] = x[1][1] = get_coord(get_edge_midv(e_id)); x[0][2] = x[1][2] = get_coord(get_edge_headv(e_id)); x[0][3] = get_coord(get_fe_midv(get_next_edge(fe_1))); x[1][3] = get_coord(get_fe_midv(get_next_edge(fe_2))); x[0][4] = get_coord(get_fe_headv(get_next_edge(fe_1))); x[1][4] = get_coord(get_fe_headv(get_next_edge(fe_2))); x[0][5] = get_coord(get_fe_midv(get_prev_edge(fe_1))); x[1][5] = get_coord(get_fe_midv(get_prev_edge(fe_2))); /* calculate test triple product at 4 points (it being cubic) */ for ( i = 0 ; i < 4 ; i++ ) { /* edge tangent */ for ( j = 0 ; j < SDIM ; j++ ) { tu[j] = x[0][0][j]*(uu[i] - 1.5) + 2*x[0][1][j]*(1 - uu[i]) + x[0][2][j]*(uu[i] - 0.5); for ( k = 0 ; k < 2 ; k++ ) tv[k][j] = x[k][0][j]*(uu[i] - 1.5) - x[k][1][j]*uu[i] + x[k][3][j]*uu[i] - 0.5*x[k][4][j] + x[k][5][j]*(2.0 - uu[i]); } h[i] = triple_prod(tv[0],tv[1],tu); } /* see if signs at endpoints same or opposite */ if ( (h[0] > 0.0) != (h[3] > 0.0) ) return 0; if ( h[0] > 0.0 ) sign = 1; else sign = -1; /* get polynomial coefficients */ /* (remapping to points -1, -0.3, 0.3, 1) */ a[0] = (h[1] + h[2] - b2*(h[0] + h[3]))/denom; a[1] = (h[2] - h[1] - b3*(h[3] - h[0]))/denom; a[2] = (h[0] - h[2] + h[3] - h[1])/denom; a[3] = (b1*(h[3] - h[0]) - (h[2] - h[1]))/denom; /* now decide on sign of cubic polynomial fitting h[] */ /* find max and min points */ discr = a[2]*a[2] - 3*a[1]*a[3]; if ( discr <= 0.0 ) return sign; /* no critical points */ discr = sqrt(discr); r[0] = (-a[2] + discr)/3/a[3]; r[1] = (-a[2] - discr)/3/a[3]; for ( i = 0 ; i < 2 ; i++ ) { if ( (r[i] < -1.0) || (r[i] > 1.0) ) continue; val = a[0] + r[i]*(a[1] + r[i]*(a[2] + r[i]*a[3])); if ( (val > 0.0) != (h[0] > 0.0) ) return 0; } return sign; } /******************************************************************* * * Function: curtest_facet() * * Purpose: See if a facet has constant sign mean curvature. * * Return: Sign of curvature with respect to oriented facet: * +1 for all positive * -1 for all negative * 0 otherwise */ int curtest_facet(f_id) facet_id f_id; { REAL *x[FACET_CTRL]; /* pointers to coordinates */ facetedge_id fe; REAL tt[2][2][MAXCOORD]; /* position second partials */ REAL tutu,tvtv,tutv,t[2][MAXCOORD]; int i,j,k,m; int sign = 0; /* return value 1 or -1 at end if all same curvature */ REAL u,v; REAL vv[MAXCOORD]; /* first variation vector, sort of */ REAL h; /* curvature at test point */ /* gather vertices */ fe = get_facet_fe(f_id); x[0] = get_coord(get_fe_tailv(fe)); x[1] = get_coord(get_fe_midv(fe)); fe = get_next_edge(fe); x[2] = get_coord(get_fe_tailv(fe)); x[3] = get_coord(get_fe_midv(fe)); fe = get_next_edge(fe); x[4] = get_coord(get_fe_tailv(fe)); x[5] = get_coord(get_fe_midv(fe)); /* calculate position second partials (independent of u,v) */ for ( i = 0 ; i < 2 ; i++ ) for ( j = 0 ; j < 2 ; j++ ) for ( k = 0 ; k < SDIM ; k++ ) { tt[i][j][k] = 0.0; for ( m = 0 ; m < FACET_CTRL ; m++ ) tt[i][j][k] += poly2partial[m][i][j]*x[m][k]; } /* loop for sampling curvature */ for ( u = 0.0 ; u < 2.00001 ; u += 0.4 ) for ( v = 0.0 ; u+v < 2.00001 ; v += 0.4 ) { /* inner loop does test at one point */ /* calculate tangents */ for ( i = 0 ; i < 2 ; i++ ) for ( k = 0 ; k < SDIM ; k++ ) { t[i][k] = 0.0; for ( m = 0 ; m < FACET_CTRL ; m++ ) t[i][k] += intpoly6part(m,i,u,v)*x[m][k]; } /* tangent dot products */ tutu = SDIM_dot(t[0],t[0]); tutv = SDIM_dot(t[0],t[1]); tvtv = SDIM_dot(t[1],t[1]); /* assemble variation vector */ for ( k = 0 ; k < SDIM ; k++ ) vv[k] = 2*tutv*tt[0][1][k] - tutu*tt[1][1][k] - tvtv*tt[0][0][k]; /* test normal component */ h = triple_prod(t[0],t[1],vv); if ( h > 0.0 ) { if ( sign == 0 ) sign = 1; else if ( sign < 0 ) return 0; } else if ( sign == 0 ) sign = -1; else if ( sign > 0 ) return 0; /* end inner loop */ } return sign; } evolver-2.30c.dfsg/src/oglgraph.c0000644000175300017530000016212311410765113017171 0ustar hazelscthazelsct/********************************************************************* * * File: oglgraph.c * * Contents: OpenGL display for Surface Evolver * For OpenGL Version 1.1 */ /* This runs in a separate thread from the main program, so it avoids calling kb_error, since that would longjump to the wrong place. Except for WARNINGS, which are OK since kb_error just returns. */ /* Some timings in various OpenGL drawing modes: (done with cat.fe and gtime.cmd)(list, arrays exclude setup time) (done in 'u' mode for 10 drawings; time for one reported here) (flat shading for no arrays; doesn't matter for others) facets edges no arrays display list arrays indexed arrays strips 6144 9132 0.2224 0.1500 0.1021 0.0691 0.0420 24576 37056 0.8552 0.5740 0.3846 0.2544 0.1292 98304 147840 3.4059 1.4932 1.0154 0.4888 98304 0 1.6513 0.6630 0.5208 0.5348 0.2223 393216 0 7.8273 3.2527 2.1140 2.1827 0.8720 To get a sense of the set-up times, here are recalc times, flat shading except for strips. facets edges no arrays display list arrays indexed arrays strips 6144 9132 0.32 1.2319 0.3796 0.5558 0.9143 24576 37056 0.9164 11.1961 1.0064 1.6804 2.9993 98304 147840 3.6503 4.2781 6.9260 12.5481 On transforms, using srol.8.fe Transforms facets edges arrays indexed arrays strips 24 196608 297216 3.3058 2.2122 1.2307 A little slower than one would expect, maybe due to normal flipping?? Or memory caching? But at least set-up is fast. */ #undef DOUBLE #if defined(WIN32) || defined(_WIN32) #undef FIXED #include HWND hwnd; #else #include #define CALLBACK #endif #include "include.h" static char opengl_version[20]; /* from glGetString */ int close_flag = 0; /* whether close_show has been done */ vertex_id focus_vertex_id; /* rotate and zoom focus */ static float rgba[16][4]; static long win_id; /* graphics window id */ #ifdef TC static void *th; /* draw_thread handle */ #else static unsigned long th; #endif /* screen corners */ static double scrx[4],scry[4]; static double aspect = 1; static double xscale=2.8/400,yscale=2.8/400; static GLsizei xsize,ysize; /* window size */ static int initz_flag = 0; /* z buffer */ static float projmat[16]; /* for saving projection matrix */ #define P_ORTHO 0 #define P_PERSP 1 static int projmode = P_ORTHO; /* kind of projection to do */ #define NO_STEREO 0 #define CROSS_STEREO 1 static int stereomode = NO_STEREO; #define NOLIST 0 #define NORMALLIST 1 #define RESETLIST 2 static int dlistflag = NOLIST; /* whether to use display list */ static int facetshow_flag = 1; /* whether to show facets */ static GLfloat linewidth = 1.0; static REAL edge_bias = 0.005; /* amount edges drawn in front */ static int arraysflag=0; /* whether to use OpenGL 1.1 arrays */ struct vercol { float c[4]; float n[3]; float x[3]; int inx; }; static struct vercol *fullarray; static int amax; /* allocated size of final array */ static int edgestart,edgecount,facetstart,facetcount; static int vertexcount; static struct vercol *edgearray,*facetarray; static int emax,fmax; /* allocated */ static long arrays_timestamp; /* for remembering surface version */ static int interleaved_flag = 1; /* whether to do arrays as interleaved */ static int indexing_flag = 1; /* whether to use indexed arrays (smaller,but random access) */ static int *indexarray; static int strips_flag; /* whether to do GL strips */ static int strip_color_flag; /* whether to color facets according to strip number */ static void make_strips(void); static void make_indexlists(void); struct stripstruct { int mode; /* GL_TRIANGLE_STRIP or GL_LINE_STRIP */ int start; /* starting offset in indexarray */ int count; /* number of vertices in strip */ }; static struct stripstruct *striparray; static int stripcount; /* size of striparray */ static int estripcount; /* number of edge strips */ static int fstripcount; /* number of facet strips */ static int *stripdata; static int doing_lazy; /* whether oglgraph should do transforms itself */ static int q_flag; /* whether to print drawing stats */ static REAL gleps = 1e-5; /* tolerance for identifying vertices */ // Note: using indexed arrays seems not to have any particular benefits. /******************************************************************** * * function: enlarge_edge_array() * * purpose: Expand the space for the edge list */ void enlarge_edge_array(void) { int more = emax + 10; edgearray = (struct vercol*)kb_realloc((char*)edgearray, (emax+more)*sizeof(struct vercol)); emax += more; } /******************************************************************** * * function: enlarge_facet_array() * * purpose: Expand the space for the facet list */ void enlarge_facet_array(void) { int more = web.skel[FACET].count + 10; facetarray = (struct vercol*)kb_realloc((char*)facetarray, (fmax+more)*sizeof(struct vercol)); fmax += more; } /*********************************************************************** * * function: kb_glNormal3fv() etc. * * purpose: Either save current normal in kb_norm[] for later list entry, * or pass it on to gl. */ static float kb_norm[3] = { 0.0, 0.0, 1.0 }; /* state of normal vector */ void kb_glNormal3fv(float *v) /* save for edges and facets */ { if ( !arraysflag ) glNormal3fv(v); else { kb_norm[0] = v[0]; kb_norm[1] = v[1]; kb_norm[2] = v[2]; } } void kb_glNormal3dv(REAL *v) /* save for edges and facets */ { kb_norm[0] = (float)v[0]; kb_norm[1] = (float)v[1]; kb_norm[2] = (float)v[2]; if ( !arraysflag ) glNormal3fv(kb_norm); } void kb_glAntiNormal3dv(REAL *v) /* save for edges and facets */ { kb_norm[0] = -(float)v[0]; kb_norm[1] = -(float)v[1]; kb_norm[2] = -(float)v[2]; if ( !arraysflag ) glNormal3fv(kb_norm); } /*********************************************************************** * * function: e_glColor() * * purpose: Either save current edge color in er,eg,eb for later list entry, * or pass it on to gl. */ static float er,eg,eb,ea; /* current edge color */ void e_glColor(int c) { if ( edge_rgb_color_attr > 0 ) { if ( !arraysflag ) glColor4ubv((const GLubyte*)&c); else { er=(float)(((c>>24)&0xFF)/255.); eg=(float)(((c>>16)&0xFF)/255.); eb=(float)(((c>>8)&0xFF)/255.); ea= (float)(((c)&0xFF)/255.); } } else { if ( !arraysflag ) glColor4fv(rgba[c]); else { er = rgba[c][0]; eg = rgba[c][1]; eb = rgba[c][2]; ea = rgba[c][3]; } } } /********************************************************************* * * function: e_glVertex3dv() * * purpose: Either save current edge data in edge list, or pass on to gl. */ void e_glVertex3dv(double *x) { if ( !arraysflag ) { glVertex3dv(x); return; } if ( edgecount > emax-5 ) enlarge_edge_array(); edgearray[edgecount].c[0] = er; edgearray[edgecount].c[1] = eg; edgearray[edgecount].c[2] = eb; edgearray[edgecount].c[3] = ea; edgearray[edgecount].x[0] = (float)x[0]; edgearray[edgecount].x[1] = (float)x[1]; edgearray[edgecount].x[2] = (float)x[2]; edgearray[edgecount].n[0] = kb_norm[0]; edgearray[edgecount].n[1] = kb_norm[1]; edgearray[edgecount].n[2] = kb_norm[2]; edgecount++; } /*********************************************************************** * * function: f_glColor() * * purpose: Either save current facet color in fr,fg,fb for later list entry, * or pass it on to gl. */ static float fr,fg,fb,fa; /* current facet color */ void f_glColor(int c) { if ( facet_rgb_color_attr > 0 ) { if ( !arraysflag ) glColor4ubv((const GLubyte*)&c); else { fr=(float)(((c>>24)&0xFF)/255.); fg=(float)(((c>>16)&0xFF)/255.); fb=(float)(((c>>8)&0xFF)/255.); fa=(float)(((c)&0xFF)/255.); } } else { if ( !arraysflag ) glColor4fv(rgba[c]); else { fr = rgba[c][0]; fg = rgba[c][1]; fb = rgba[c][2]; fa = rgba[c][3]; } } } /********************************************************************* * * function: f_glVertex3dv() * * purpose: Either save current facet data in facet list, or pass on to gl. */ void f_glVertex3dv(double *x) { if ( !arraysflag ) { glVertex3dv(x); return; } if ( facetcount > fmax-5 ) enlarge_facet_array(); facetarray[facetcount].c[0] = fr; facetarray[facetcount].c[1] = fg; facetarray[facetcount].c[2] = fb; facetarray[facetcount].c[3] = fa; facetarray[facetcount].x[0] = (float)x[0]; facetarray[facetcount].x[1] = (float)x[1]; facetarray[facetcount].x[2] = (float)x[2]; facetarray[facetcount].n[0] = kb_norm[0]; facetarray[facetcount].n[1] = kb_norm[1]; facetarray[facetcount].n[2] = kb_norm[2]; facetcount++; } /* gl matrices have vector on left! */ typedef double Matrix[4][4]; Matrix vt3 = /* gl transform matrix */ { {0.,0.,1.,0.}, {1.,0.,0.,0.}, {0.,-1.,0.,0.}, {0.,0.,0.,1.} }; Matrix vt3p = /* gl transform matrix, perspective version */ { {0.,0.,1.,0.}, {1.,0.,0.,0.}, {0.,1.,0.,0.}, {0.,0.,0.,1.} }; Matrix vt2 = /* gl transform matrix */ { {2.,0.,0.,0.}, /* can't figure why I need the 2's */ {0.,-2.,0.,0.}, {0.,0.,2.,0.}, {0.,0.,0.,1.} }; Matrix flip = /* gl transform matrix */ { {1.,0.,0.,0.}, {0.,1.,0.,0.}, {0.,0.,-1.,0.}, {0.,0.,0.,1.} }; Matrix flip2D = /* gl transform matrix */ { {1.,0.,0.,0.}, {0.,-1.,0.,0.}, {0.,0.,1.,0.}, {0.,0.,0.,1.} }; static long prev_timestamp; /* for remembering surface version */ #define MM_ROTATE 1 #define MM_TRANSLATE 2 #define MM_SCALE 3 #define MM_SPIN 4 #define MM_SLICE 5 static int mouse_mode = MM_ROTATE; static int newarraysflag = 0; /* to cause rebuild of arrays */ static int dindex = 1; /* display list object index */ void CALLBACK draw_screen ARGS((void)); void Ogl_init(void) { } void Ogl_finish(void) { } void graph_new_surface(void) { /* to account for global deallocation at start of new surface */ fullarray = NULL; edgearray = NULL; facetarray = NULL; indexarray = NULL; striparray = NULL; stripdata = NULL; } void init_Oglz ARGS((void)); int oldx,oldy,newx,newy; /* for tracking motion */ int basex=0,basey=0; /* translation to do on object */ /************************************************************************ * * function: pick_func() * * purpose: To handle mouse picking of element. Invoked by right click. */ #define PICKBUFLENGTH 500 GLuint pickbuf[PICKBUFLENGTH]; void CALLBACK pick_func(AUX_EVENTREC *rec) { GLint hits, viewport[4]; int i,n; unsigned int enearz = 0xFFFFFFFF; unsigned int fnearz = 0xFFFFFFFF; facet_id f_id = NULLID; edge_id e_id = NULLID; vertex_id v_id = NULLID; int count; glSelectBuffer(PICKBUFLENGTH,pickbuf); glGetIntegerv(GL_VIEWPORT,viewport); glMatrixMode(GL_PROJECTION); glPushMatrix(); glRenderMode(GL_SELECT); /* see what picked when drawn */ glLoadIdentity(); gluPickMatrix(rec->data[0],rec->data[1],4,4,viewport); glMultMatrixf(projmat); if ( SDIM >= 3 ) glMultMatrixd(flip[0]); /* don't know why, but need */ else glMultMatrixd(flip2D[0]); if ( arraysflag ) /* no picking during arrays */ { arraysflag = 0; graph_timestamp = ++global_timestamp; draw_screen(); arraysflag = 1; graph_timestamp = ++global_timestamp; } else draw_screen(); hits = glRenderMode(GL_RENDER); /* back to ordinary drawing */ if ( hits < 0 ) { hits = PICKBUFLENGTH/4; /* buffer overflow */ kb_error(2541, "Pick buffer overflow; selected element may not be foreground element.\n", WARNING); } for ( i=0, n=0 ; (i < hits) && (n < PICKBUFLENGTH-4) ; i++, n += count + 3 ) { count = pickbuf[n]; switch ( id_type(pickbuf[n+3]) ) { case FACET: if ( pickbuf[n+1] < fnearz ) { f_id = pickbuf[n+3]; fnearz = pickbuf[n+1]; } break; case EDGE: if ( pickbuf[n+1] < enearz ) if ( (pickbuf[n+3] != NULLEDGE) || !valid_id(e_id) ) { e_id = pickbuf[n+3]; enearz = pickbuf[n+1]; } break; } } printf("\n"); if ( valid_id(e_id) ) { /* check for vertex in common to picked edges */ for ( i = 0, n = 0 ; (i < hits) && (n < PICKBUFLENGTH-4) ; i++, n += count+3 ) { element_id id,ee_id; int ii,nn,ccount; count = pickbuf[n]; id = pickbuf[n+3]; if ( (id_type(id) == EDGE) && valid_id(id) ) { vertex_id v1 = get_edge_headv(id); vertex_id v2 = get_edge_tailv(id); for ( ii = i+1, nn = n+count+3 ; (ii < hits) && (n < PICKBUFLENGTH-4) ; ii++, nn += ccount + 3 ) { ccount = pickbuf[nn]; ee_id = pickbuf[nn+3]; if ( (id_type(ee_id) == EDGE) && valid_id(ee_id) && !equal_element(id,ee_id) ) { if ( v1 == get_edge_tailv(ee_id) ) { v_id = v1; break; } if ( v1 == get_edge_headv(ee_id) ) { v_id = v1; break; } if ( v2 == get_edge_tailv(ee_id) ) { v_id = v2; break; } if ( v2 == get_edge_headv(ee_id) ) { v_id = v2; break; } } } } } if ( valid_id(v_id) ) { pickvnum = loc_ordinal(v_id) + 1; sprintf(msg,"Picked vertex %d\n",pickvnum); outstring(msg); } pickenum = loc_ordinal(e_id) + 1; sprintf(msg,"Picked edge %d\n",pickenum); outstring(msg); } else if ( e_id == NULLEDGE ) outstring("Picked facet subdivision edge.\n"); if ( valid_id(f_id) ) { pickfnum = loc_ordinal(f_id) + 1; sprintf(msg,"Picked facet %d\n",pickfnum); outstring(msg); } outstring(current_prompt); glMatrixMode(GL_PROJECTION); glPopMatrix(); } /* end pick_func */ /*********************************************************** * * function: mouse_down_func() * * purpose: Called on left button down, records position. */ void CALLBACK mouse_down_func(AUX_EVENTREC *rec) { oldx = (short)rec->data[0]; oldy = (short)rec->data[1]; } /*********************************************************** * * function: mouse_up_func() * * purpose: Called on left button up, records position as base. */ void CALLBACK mouse_up_func(AUX_EVENTREC *rec) { basex = newx; basey = newy; } /******************************************************************* * * function: mouse_loc_func() * * purpose: Called as mouse moves with left button down, this * moves surface according to current mouse_mode. */ void CALLBACK mouse_loc_func(AUX_EVENTREC *rec) { int i,j; newx = (short)rec->data[0]; newy =(short)rec->data[1]; switch ( mouse_mode ) { case MM_ROTATE: mat_mult(to_focus,view,view,HOMDIM,HOMDIM,HOMDIM); fix_ctm(view,(double)( newx-oldx),-(double)(newy - oldy)); mat_mult(from_focus,view,view,HOMDIM,HOMDIM,HOMDIM); break; case MM_SCALE: mat_mult(to_focus,view,view,HOMDIM,HOMDIM,HOMDIM); for(i = 0 ; i < HOMDIM-1; i++ ) for ( j = 0 ; j < HOMDIM ; j++ ) view[i][j] *= 1.0 +0.002*(newx-oldx); mat_mult(from_focus,view,view,HOMDIM,HOMDIM,HOMDIM); break; case MM_SCALE: /* slicing altitude */ slice_coeff[SDIM] += (newx-oldx)*xscale; break; case MM_TRANSLATE: if ( SDIM == 2 ) { view[0][2] += (newx-oldx)*xscale; view[1][2] -= (newy-oldy)*yscale; to_focus[0][2] -= (newx-oldx)*xscale; to_focus[1][2] += (newy-oldy)*yscale; from_focus[0][2] += (newx-oldx)*xscale; from_focus[1][2] -= (newy-oldy)*yscale; } else { view[1][HOMDIM-1] += (newx-oldx)*xscale; view[2][HOMDIM-1] -= (newy-oldy)*yscale; to_focus[1][HOMDIM-1] -= (newx-oldx)*xscale; to_focus[2][HOMDIM-1] += (newy-oldy)*yscale; from_focus[1][HOMDIM-1] += (newx-oldx)*xscale; from_focus[2][HOMDIM-1] -= (newy-oldy)*yscale; }; break; case MM_SPIN: /* about z axis */ { MAT2D(rot,MAXCOORD+1,MAXCOORD+1); REAL dth; REAL dang; /* fourth dimension */ for ( i = 0 ; i < HOMDIM ; i++ ) { for ( j = 0 ; j < HOMDIM ; j++ ) rot[i][j] = 0.0; rot[i][i] = 1.0; } dth = (newx - oldx)/300.0*M_PI; dang = (newy - oldy)/300.0*M_PI; if ( SDIM == 2 ) { rot[0][0] = rot[1][1] = cos(dth); rot[0][1] = -(rot[1][0] = sin(dth)); } else { rot[1][1] = rot[2][2] = cos(dth); rot[1][2] = -(rot[2][1] = sin(dth)); } mat_mult(to_focus,view,view,HOMDIM,HOMDIM,HOMDIM); mat_mult(rot,view,view,HOMDIM,HOMDIM,HOMDIM); mat_mult(from_focus,view,view,HOMDIM,HOMDIM,HOMDIM); } break; } oldx = newx; oldy = newy; } /************************************************************************ * * function: reshape_func() * * purpose: handle window resize messages. */ void CALLBACK reshape_func ( GLsizei x, GLsizei y ) { xsize = x; ysize = y; aspect = (double)y/x; glViewport(0,0,x,y); if ( aspect > 1 ) { xscale = 2.8/aspect/x; yscale = 2.8/y; } else { xscale = 2.8/x; yscale = 2.8*aspect/y; } if ( x < y ) { minclipx = -1.5; maxclipx = 1.5; minclipy = -1.5*y/x; maxclipy = 1.5*y/x; } else { minclipx = -1.5*x/y; maxclipx = 1.5*x/y; minclipy = -1.5; maxclipy = 1.5; } glMatrixMode(GL_PROJECTION); glLoadIdentity(); if ( (projmode == P_PERSP) || stereomode ) { gluPerspective((float)(y/1600.*180/3.14159),1/aspect,1.0,20.0); if ( SDIM == 2 ) glMultMatrixd(vt2[0]); else glMultMatrixd(vt3p[0]); /* rotate axes */ } else { if ( aspect <= 1.0 ) { glOrtho(scrx[0],scrx[2],aspect*scry[0],aspect*scry[2],-20.0,20.0); minclipx = scrx[0]; maxclipx = scrx[2]; minclipy = aspect*scry[2]; maxclipy = aspect*scry[0]; } else { glOrtho(scrx[0]/aspect,scrx[2]/aspect,scry[0],scry[2],-20.0,20.0); minclipx = scrx[0]/aspect; maxclipx = scrx[2]/aspect; minclipy = scry[2]; maxclipy = scry[0]; } if ( SDIM == 2 ) glMultMatrixd(vt2[0]); /* upside down */ else glMultMatrixd(vt3[0]); /* upside down and rotate axes */ } glGetFloatv(GL_PROJECTION_MATRIX,projmat); // save } /*********************************************************************** * * Functions: key callbacks * * purpose: handle keystrokes in graphics window. */ void CALLBACK up_arrow (void) { view_transform("\036"); } void CALLBACK down_arrow (void) { view_transform("\037"); } void CALLBACK left_arrow (void) { view_transform("\035"); } void CALLBACK right_arrow (void) { view_transform("\034"); } void CALLBACK r_key (void) { mouse_mode = MM_ROTATE; } void CALLBACK t_key (void) { mouse_mode = MM_TRANSLATE; } void CALLBACK z_key (void) { mouse_mode = MM_SCALE; } void CALLBACK c_key (void) { mouse_mode = MM_SPIN;} void CALLBACK l_key (void) { if ( slice_view ) mouse_mode = MM_SLICE;} void CALLBACK b_key (void) { edge_bias -= 0.001; sprintf(msg,"\nEdge front bias now %f\n",edge_bias); outstring(msg); outstring(current_prompt); } void CALLBACK B_key (void) { edge_bias += 0.001; sprintf(msg,"\nEdge front bias now %f\n",edge_bias); outstring(msg); outstring(current_prompt); } void CALLBACK R_key (void) { resize(); } void CALLBACK minus_key (void) { if ( linewidth > 0.6 ) { linewidth -= 0.5; glLineWidth(linewidth);} } void CALLBACK plus_key (void) { if ( linewidth < 9.9 ) { linewidth += 0.5; glLineWidth(linewidth);}} void CALLBACK e_key (void) { edgeshow_flag = !edgeshow_flag; graph_timestamp = ++global_timestamp; newarraysflag = 1;} void CALLBACK f_key (void) { facetshow_flag = !facetshow_flag; graph_timestamp = ++global_timestamp; newarraysflag = 1;} void CALLBACK g_key (void) { normflag = !normflag; graph_timestamp = ++global_timestamp; newarraysflag = 1;} void CALLBACK m_key (void) { view_transform("m"); } void CALLBACK i_key (void) { interleaved_flag = !interleaved_flag; outstring(interleaved_flag ? "Interleaving ON.\n":"Interleaving OFF.\n"); outstring(current_prompt); newarraysflag = 1; } void CALLBACK I_key (void) { indexing_flag = !indexing_flag; outstring(indexing_flag ? "Array indexing ON.\n":"Array indexing OFF.\n"); outstring(current_prompt); newarraysflag = 1; } void CALLBACK S_key (void) { strips_flag = !strips_flag; outstring(strips_flag ? "Element strips ON.\n":"Element strips OFF.\n"); outstring(current_prompt); normflag = 1; /* gourard shading */ indexing_flag = 1; newarraysflag = 1; } void CALLBACK Y_key (void) /* color strips */ { int *fcolors; int i; fcolors = (int*)temp_calloc(web.skel[FACET].maxcount,sizeof(int)); for ( i = 0 ; i < web.skel[FACET].maxcount ; i++ ) fcolors[i] = get_facet_color(i); if ( !strips_flag ) S_key(); /* make sure strips on */ strip_color_flag = 1; newarraysflag = 1; draw_screen(); /* get facets colored */ newarraysflag = 1; draw_screen(); /* draw colored facets */ strip_color_flag = 0; for ( i = 0 ; i < web.skel[FACET].maxcount ; i++ ) set_facet_color(i,fcolors[i]); temp_free((char*)fcolors); } void CALLBACK F_key (void) { if ( pickvnum > 0 ) { int i,m; REAL *x,xx[MAXCOORD]; REAL focus[MAXCOORD]; focus_vertex_id = get_ordinal_id(VERTEX,pickvnum-1); x = get_coord(focus_vertex_id); for ( m = 0 ; m < SDIM ; m++ ) xx[m] = x[m]; if ( torus_display_mode == TORUS_CLIPPED_MODE ) { for ( m = 0 ; m < SDIM ; m++ ) { int wrap = (int)floor(SDIM_dot(web.inverse_periods[m],x)); for ( i = 0 ; i < SDIM ; i++ ) xx[i] -= wrap*web.torus_period[m][i]; } } matvec_mul(view,xx,focus,HOMDIM-1,HOMDIM-1); for ( i = 0 ; i < SDIM ; i++ ) { to_focus[i][HOMDIM-1] = -focus[i] - view[i][HOMDIM-1]; from_focus[i][HOMDIM-1] = focus[i] + view[i][HOMDIM-1]; } } } void CALLBACK D_key (void) { dlistflag = dlistflag ? NOLIST:RESETLIST; if ( dlistflag ) outstring("\nOpenGL display list now ON.\n"); else outstring("\nOpenGL display list now OFF.\n"); outstring(current_prompt); graph_timestamp = ++global_timestamp; /* force recalculate arrays */ } void CALLBACK a_key (void) { if ( strcmp(opengl_version,"1.1") < 0 ) { sprintf(errmsg,"Vertex arrays require OpenGL version at least 1.1. This is %s.\n", opengl_version); kb_error(2542,errmsg,WARNING); return; } arraysflag = !arraysflag; if ( arraysflag ) { /* Workaround really bizarre line-drawing bug */ if ( linewidth == 1.0 ) { linewidth = 0.5; glLineWidth(0.5); } outstring("\nOpenGL vertex arrays now ON.\n"); glInterleavedArrays(GL_C4F_N3F_V3F,0,(void*)fullarray); newarraysflag = 1; } else { glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); outstring("\nOpenGL vertex_arrays now OFF.\n"); } outstring(current_prompt); } void CALLBACK s_key (void) { stereomode = (stereomode==NO_STEREO) ? CROSS_STEREO:NO_STEREO; if (stereomode) { if ( dlistflag == NOLIST ) dlistflag = RESETLIST;} else if ( arraysflag ) dlistflag = NOLIST; reshape_func(xsize,ysize); } void CALLBACK p_key (void) { if ( stereomode ) outstring("\nOpenGL projection now CROSS-EYED STEREO.\n"); else if ( projmode==P_ORTHO ) { projmode=P_PERSP; outstring("\nOpenGL projection now PERSPECTIVE.\n");} else { projmode=P_ORTHO; outstring("\nOpenGL projection now ORTHOGONAL.\n");} outstring(current_prompt); reshape_func(xsize,ysize); } void CALLBACK Q_key (void) { q_flag = !q_flag; outstring(q_flag ? "\nPrinting drawing stats ON\n":"\nPrinting drawing stats OFF\n"); outstring(current_prompt); } void CALLBACK help_key (void) { outstring("\nGraphics window help:\n"); outstring("Left mouse: move Right mouse: pick\n"); outstring("Graphics window keys:\n"); outstring(mouse_mode==MM_ROTATE?"r Rotate mode for left mouse button, now ACTIVE\n": "r Rotate mode for left mouse button\n"); outstring(mouse_mode==MM_TRANSLATE?"t Translate mode for left mouse button, now ACTIVE\n": "t Translate mode for left mouse button\n"); outstring(mouse_mode==MM_SCALE?"z Zoom mode for left mouse button, now ACTIVE\n": "z Zoom mode for left mouse button\n"); outstring(mouse_mode==MM_SPIN?"c Clockwise/counterclockwise mode, now ACTIVE\n": "c Clockwise/counterclockwise mode\n"); outstring("W Widen edges\n"); outstring("w Narrow edges\n"); outstring("B Increase edge front bias by 0.001\n"); outstring("b Decrease edge front bias by 0.001\n"); outstring(normflag? "g Gourard shading (smooth shading) toggle, now ON\n": "g Gourard shading (smooth shading) toggle, now OFF\n" ); outstring("R Reset view\n"); outstring("m Center image\n"); outstring(edgeshow_flag?"e Toggle showing all edges, now ON\n": "e Toggle showing all edges, now OFF\n"); outstring(facetshow_flag?"f Toggle showing facets, now ON\n": "f Toggle showing facets, now OFF\n"); outstring(projmode==P_PERSP?"p Toggle orthogonal/perspective projection, now perspective\n": "p Toggle orthogonal/perspective projection, now orthogonal\n"); outstring("s Toggle cross-eyed stereo\n"); outstring("F Set rotate/zoom focus to last picked vertex\n"); outstring("arrow keys translate image\n"); outstring("H Guru-level help items\n"); outstring(current_prompt); } void CALLBACK H_key(void) /* guru level help */ { outstring("\nFollowing for fiddling with OpenGL drawing modes:\n"); outstring(normflag? "g Gourard shading (smooth shading) toggle, now ON\n": "g Gourard shading (smooth shading) toggle, now OFF\n" ); outstring(dlistflag?"D Toggle using display list, now ON\n": "D Toggle using display list, now OFF\n"); outstring(arraysflag?"a Toggle using vertex and color arrays, now ON\n" :"a Toggle using vertex and color arrays, now OFF\n"); outstring(indexing_flag ? "I Indexed vertex arrays, now ON\n" : "I Indexed vertex arrays, now OFF\n"); outstring(interleaved_flag ? "i Interleaved vertex arrays, now ON\n" : "i Interleaved vertex arrays, now OFF\n"); outstring(strips_flag? "S Use element strips, now ON (indexed elements automatically turned on)\n" :"S Use element strips, now OFF\n"); outstring("Y One-time coloring of strips generated in S mode.\n"); outstring(" Caution: assumes display facets same as real, with no gaps.\n"); outstring("Q Toggle printing some statistics during drawing\n"); outstring(current_prompt); } /* lighting info for surface */ static GLfloat mat_specular[] = {.5f,.5f,.5f,1.0f}; static GLfloat mat_shininess[] = {10.0f}; static GLfloat mat_diffuse[] = {1.0f,1.0f,1.0f,1.0f}; static GLfloat mat_white[] = {1.0f,1.0f,1.0f,1.0f}; static GLfloat mat_emission[] = {0.3f,0.3f,0.3f,1.0f}; #define INTENSITY1 0.5f static GLfloat light0_position[] = {1.0f,0.0f,1.0f,0.0f}; // front static GLfloat light0_diffuse[] = {INTENSITY1,INTENSITY1,INTENSITY1,1.0f}; static GLfloat light0_ambient[] = {.3f,.3f,.3f,1.0f}; #define INTENSITY2 0.5f static GLfloat light1_position[] = {0.0f,0.0f,1.0f,0.0f}; // above static GLfloat light1_diffuse[] = {INTENSITY2,INTENSITY2,INTENSITY2,1.0f}; static GLfloat light_none[] = {0.f,0.f,0.f,.0f}; /***************************************************************** * * function: draw_thread() * * purpose: Create OpenGL display thread and window. */ //unsigned long __stdcall draw_thread(void *arglist) void __cdecl draw_thread(void *arglist) { int i,j; draw_thread_id = GetCurrentThreadId(); for ( i = 0 ; i < 16 ; i++ ) for ( j = 0 ; j < 4 ; j++ ) rgba[i][j] = (float)rgb_colors[i][j]; background_color = LIGHTBLUE; auxInitPosition(0,0,400,400); auxInitDisplayMode(AUX_DOUBLE | AUX_RGBA | AUX_DEPTH); auxInitWindow(datafilename); auxMouseFunc(AUX_LEFTBUTTON,AUX_MOUSEUP,mouse_up_func); auxMouseFunc(AUX_LEFTBUTTON,AUX_MOUSELOC,mouse_loc_func); auxMouseFunc(AUX_LEFTBUTTON,AUX_MOUSEDOWN,mouse_down_func); auxMouseFunc(AUX_RIGHTBUTTON,AUX_MOUSEDOWN,pick_func); auxKeyFunc(AUX_UP,up_arrow); auxKeyFunc(AUX_LEFT,left_arrow); auxKeyFunc(AUX_RIGHT,right_arrow); auxKeyFunc(AUX_DOWN,down_arrow); auxKeyFunc(AUX_t,t_key); auxKeyFunc(AUX_z,z_key); auxKeyFunc(AUX_r,r_key); auxKeyFunc(AUX_l,l_key); auxKeyFunc(AUX_R,R_key); auxKeyFunc(AUX_D,D_key); auxKeyFunc(AUX_p,p_key); auxKeyFunc(AUX_s,s_key); auxKeyFunc(AUX_S,S_key); auxKeyFunc(AUX_Y,Y_key); auxKeyFunc(AUX_e,e_key); auxKeyFunc(AUX_f,f_key); auxKeyFunc(AUX_g,g_key); auxKeyFunc(AUX_F,F_key); auxKeyFunc(AUX_c,c_key); auxKeyFunc(AUX_B,B_key); auxKeyFunc(AUX_b,b_key); auxKeyFunc(AUX_W,plus_key); auxKeyFunc(AUX_w,minus_key); auxKeyFunc(AUX_h,help_key); auxKeyFunc(AUX_H,H_key); auxKeyFunc(AUX_a,a_key); auxKeyFunc(AUX_m,m_key); auxKeyFunc(AUX_i,i_key); auxKeyFunc(AUX_I,I_key); auxKeyFunc(AUX_Q,Q_key); auxReshapeFunc(reshape_func); hwnd = auxGetHWND(); ShowWindow(hwnd,SW_SHOW); SetWindowPos(hwnd,HWND_NOTOPMOST ,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE); SetForegroundWindow(hwnd); glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE); //glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular); //glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_white); //glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, mat_emission); glEnable(GL_LIGHTING); glLightfv(GL_LIGHT0, GL_POSITION, light0_position); glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse); glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient); glLightfv(GL_LIGHT1, GL_POSITION, light1_position); glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, light_none); glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE); /* screen corners */ scrx[0] = scrx[1] = -1.4; scrx[2] = scrx[3] = 1.4; scry[0] = scry[3] = 1.4; scry[1] = scry[2] = -1.4; go_display_flag = 1; /* default, since fast graphics */ /* version check */ strncpy(opengl_version,glGetString(GL_VERSION),sizeof(opengl_version)); if ( strcmp(opengl_version,"1.1") >= 0 ) arraysflag = 1; auxMainLoop(draw_screen); //return 0; } /************************************************************************ * * function: init_Oglz() * * purpose: start up graphics thread */ #include void init_Oglz(void) { int n; if ( !initz_flag ) { initz_flag = 1; if ( !to_focus ) { to_focus = dmatrix(0,MAXCOORD,0,MAXCOORD); from_focus = dmatrix(0,MAXCOORD,0,MAXCOORD); for ( n = 0 ; n <= MAXCOORD ; n++ ) to_focus[n][n] = from_focus[n][n] = 1.0; } //th = CreateThread(NULL,0,draw_thread,0,0,&draw_thread_id); th=_beginthread(draw_thread,0,0); } } /**************************************************************** * * function: Oglz_start() * * purpose: To be called at the start of each Evolver-initiated redraw. */ void Oglz_start(void) { if ( initz_flag == 0 ) init_Oglz(); SetWindowText(hwnd,datafilename); if ( !to_focus ) { int n; to_focus = dmatrix(0,MAXCOORD,0,MAXCOORD); from_focus = dmatrix(0,MAXCOORD,0,MAXCOORD); for ( n = 0 ; n <= MAXCOORD ; n++ ) to_focus[n][n] = from_focus[n][n] = 1.0; } } /****************************************************************** * * function: Oglz_edge() * * purpose: graph one edge. */ void Oglz_edge( struct graphdata *g, edge_id e_id) { int k; int e_color; e_color = g[0].ecolor; if ( e_color == CLEAR ) return; if ( (e_color < 0) || (e_color >= IRIS_COLOR_MAX) ) e_color = DEFAULT_EDGE_COLOR; glLoadName(e_id); // for picking /* display */ e_glColor(e_color); glBegin(GL_LINES); for ( k = 0 ; k < 2 ; k++ ) e_glVertex3dv(g[k].x); glEnd(); } /****************************************************************** * * function: Oglz_facet() * * purpose: graph one facet. */ static float norm[3]; void Oglz_facet( struct graphdata *g, facet_id f_id) { int i,k; REAL len; facetedge_id fe = NULLID; /* need normal for lighting */ for ( i = 0 ; i < 3 ; i++ ) { int ii = (i+1)%3; int iii = (i+2)%3; norm[i] = (float)((g[1].x[ii]-g[0].x[ii])*(g[2].x[iii]-g[0].x[iii]) - (g[1].x[iii]-g[0].x[iii])*(g[2].x[ii]-g[0].x[ii])); } len = sqrt(dotf(norm,norm,3)); if ( len <= 0.0 ) return; for ( i = 0 ; i < 3 ; i++ ) norm[i]= (float)(norm[i]/len); if ( web.hide_flag && (g[0].color != UNSHOWN) && facetshow_flag ) { glLoadName(f_id); // for picking if ( g[0].color != CLEAR ) { if ( color_flag ) f_glColor(g->color); else f_glColor(INDEX_TO_RGBA(WHITE)); kb_glNormal3fv(norm); glBegin(GL_TRIANGLES); for ( k = 0 ; k < 3 ; k++ ) { if ( normflag ) kb_glNormal3dv(g[k].norm); f_glVertex3dv(g[k].x); } glEnd(); } if ( (g->color != g->backcolor) && (g->backcolor != CLEAR) ) { REAL x[MAXCOORD]; f_glColor(g->backcolor); for ( i = 0 ; i < 3 ; i++ ) norm[i] = -norm[i]; kb_glNormal3fv(norm); glBegin(GL_TRIANGLES); for ( k = 2 ; k >= 0 ; k-- ) { for ( i = 0 ; i < 3 ; i++ ) x[i] = g[k].x[i] + thickness*norm[i]; if ( normflag ) kb_glAntiNormal3dv(g[k].norm); f_glVertex3dv(x); } glEnd(); } } fe = valid_id(f_id) ? get_facet_fe(f_id) : NULLID; for ( k = 0 ; k < 3 ; k++, fe = valid_id(fe)?get_next_edge(fe):NULLID ) { if ( g[k].ecolor == CLEAR ) continue; if ( !edgeshow_flag || (g[0].color == UNSHOWN) ) { if ( (g[k].etype&EBITS) == INVISIBLE_EDGE ) continue; } glLoadName(g[k].id); /* for picking */ e_glColor(g[k].ecolor); kb_glNormal3fv(norm); glBegin(GL_LINES); if ( normflag ) kb_glNormal3dv(g[k].norm); e_glVertex3dv(g[k].x); if ( normflag ) kb_glNormal3dv(g[(k+1)%3].norm); e_glVertex3dv(g[(k+1)%3].x); glEnd(); } } /********************************************************************** * * function: Oglz_end() * * purpose: to be called at end of presenting data. */ void Oglz_end(void) { prev_timestamp = graph_timestamp; } void Ogl_close(void) { //close_flag = 1; //InvalidateRect(hwnd,NULL,FALSE); kb_error(2175,"Sorry, OpenGL version can't restart show after close, so won't close.\n", WARNING); graph_timestamp = ++global_timestamp; draw_screen(); } void really_close(void) { auxCloseWindow(); glDeleteLists(dindex,1); init_flag = 0; initz_flag = 0; win_id = -1; go_display_flag = 0; ExitThread(0); } /********************************************************************** * * function: vercolcomp() * * purpose: comparison function for sorting vertex data in case of indexed arrays. * */ int vercolcomp(struct vercol *a, struct vercol *b) { int i; for ( i = 0 ; i < 10 ; i++ ) { REAL diff = ((float*)a)[i] - ((float*)b)[i]; if ( diff < -gleps ) return -1; if ( diff > gleps ) return 1; } return 0; } /*********************************************************************** * * function: eecomp() * * purpose: comparison function for sorting indexed edges */ int eecomp(const int *a, const int *b) { if ( *a < *b ) return -1; if ( *a > *b ) return 1; if ( a[1] > b[1] ) return 1; if ( a[1] < b[1] ) return -1; return 0; } /********************************************************************** * * function: draw_screen() * * purpose: Handle redraw messages from operating system. */ void CALLBACK draw_screen(void) { static int olddim = 0; Matrix viewf; int i,j; ENTER_GRAPH_MUTEX // Set up longjmp to return here in case of error if ( setjmp(graphjumpbuf) ) { return; } //if ( close_flag ) really_close(); can't restart /* build arrays if needed */ if ( arraysflag && ( ((graph_timestamp != arrays_timestamp) && go_display_flag ) || newarraysflag || (dlistflag == RESETLIST)) ) { int oldflag; if ( fullarray ) { myfree((char*)fullarray); fullarray = NULL; } edgecount = 0; emax = (web.representation==SIMPLEX) ? SDIM*web.skel[FACET].count + 100 : 2*web.skel[EDGE].count+10; // 2 vertices per edge edgearray = (struct vercol *)mycalloc(emax,sizeof(struct vercol)); facetcount = 0; fmax = 3*web.skel[FACET].count+10; // 3 vertices per facet facetarray = (struct vercol *)mycalloc(fmax,sizeof(struct vercol)); if ( !edgearray || !facetarray ) { erroutstring("Cannot allocate memory for graphics.\n"); goto bailout; } graph_start = Oglz_start; graph_facet = Oglz_facet; graph_edge = Oglz_edge; graph_end = Oglz_end; init_graphics = Ogl_init; finish_graphics = Ogl_finish; close_graphics = Ogl_close; //glDisableClientState(GL_COLOR_ARRAY); //glDisableClientState(GL_NORMAL_ARRAY); //glDisableClientState(GL_VERTEX_ARRAY); oldflag = markedgedrawflag; markedgedrawflag = 1; if ( !transform_colors_flag ) lazy_transforms_flag = 1; /* for graphgen use */ doing_lazy = lazy_transforms_flag; /* for use by oglgraph.c */ arrays_timestamp = graph_timestamp; /* here to prevent stale data */ graphgen(); /* fill in arrays */ lazy_transforms_flag = 0; markedgedrawflag = oldflag; if ( q_flag ) { sprintf(msg,"\n%d edges, %d facets\n",edgecount/2,facetcount/3); outstring(msg); } /* doing this here since facet_alpha_flag set in graphgen */ if ( facet_alpha_flag ) glEnable(GL_BLEND); else glDisable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); /* unify lists */ fullarray = (struct vercol *)kb_realloc((char*)edgearray, (edgecount+facetcount)*sizeof(struct vercol)); memcpy((char*)(fullarray+edgecount),(char*)facetarray,facetcount*sizeof(struct vercol)); myfree((char*)facetarray); facetarray = NULL; edgearray = NULL; edgestart = 0; facetstart = edgecount; /* Workaround really bizarre line-drawing bug */ if ( linewidth == 1.0 ) { linewidth = (float)1.1; glLineWidth(linewidth); } if ( indexing_flag ) { make_indexlists(); if ( q_flag ) { sprintf(msg,"After indexing: %d unique vertices, %d unique edges\n", vertexcount,edgecount/2); outstring(msg); } if ( strips_flag ) make_strips(); } /* declare arrays to OpenGL */ if ( interleaved_flag ) glInterleavedArrays(GL_C4F_N3F_V3F,sizeof(struct vercol),(void*)fullarray); else // kludge for broken nVidia Detonater 2.08 driver { static float *colorarray; if ( colorarray ) myfree((char*)colorarray); colorarray = (float*)mycalloc(edgecount+facetcount,4*sizeof(float)); for ( i = 0 ; i < edgecount+facetcount ; i++ ) for ( j = 0 ; j < 4 ; j++ ) colorarray[4*i+j] = fullarray[i].c[j]; glColorPointer(4,GL_FLOAT,0,colorarray); glNormalPointer(GL_FLOAT,sizeof(struct vercol),fullarray->n); glVertexPointer(3,GL_FLOAT,sizeof(struct vercol),fullarray->x); } newarraysflag = 0; if ( dlistflag ) { glNewList(dindex,GL_COMPILE); if ( indexing_flag ) { glDrawElements(GL_TRIANGLES,facetcount,GL_UNSIGNED_INT,indexarray+facetstart); glMatrixMode(GL_PROJECTION); glTranslated(edge_bias,0.0,0.0); /* edges in front */ glDrawElements(GL_LINES,edgecount,GL_UNSIGNED_INT,indexarray+edgestart); glTranslated(-edge_bias,0.0,0.0); glMatrixMode(GL_MODELVIEW); } else { glDrawArrays(GL_TRIANGLES,facetstart,facetcount); glMatrixMode(GL_PROJECTION); glTranslated(edge_bias,0.0,0.0); /* edges in front */ glDrawArrays(GL_LINES,edgestart,edgecount); glTranslated(-edge_bias,0.0,0.0); glMatrixMode(GL_MODELVIEW); } glEndList(); dlistflag = NORMALLIST; } if ( q_flag ) outstring(current_prompt); } else if ( (dlistflag != NORMALLIST) || ((graph_timestamp != prev_timestamp) && go_display_flag ) ) { /* regenerate display list */ graph_start = Oglz_start; graph_facet = Oglz_facet; graph_edge = Oglz_edge; graph_end = Oglz_end; init_graphics = Ogl_init; finish_graphics = Ogl_finish; close_graphics = Ogl_close; if ( dlistflag != NOLIST ) { glNewList(dindex,GL_COMPILE); graphgen(); glEndList(); dlistflag = NORMALLIST; } } if ( SDIM != olddim ) { reshape_func(xsize,ysize); /* in case dimension changes */ olddim = SDIM; } glEnable(GL_LIGHT0); //glEnable(GL_LIGHT1); /* clear screen and zbuffer */ glClearColor(rgba[background_color][0], rgba[background_color][1], rgba[background_color][2], rgba[background_color][3] ); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); if ( backcull_flag ) { glEnable(GL_CULL_FACE); } else { glDisable(GL_CULL_FACE); } glDepthFunc(GL_LEQUAL); /* so later things get drawn */ glMatrixMode(GL_MODELVIEW); for ( i = 0 ; i < 4 ; i++ ) for ( j = 0 ; j < 4 ; j++ ) viewf[i][j] = view[(j<3)?j:(SDIM)][(i<3)?i:(SDIM)]; if ( SDIM == 2 ) { for ( i = 0 ; i < 3 ; i++ ) viewf[i][2] = viewf[2][i] = 0.0; viewf[2][2] = 1.0; } /* transpose, picking first 3 coordinates */ if ( (projmode == P_PERSP) || stereomode ) if ( SDIM == 2 ) viewf[3][2] -= 16; else viewf[3][0] -= 16.0; glLoadMatrixd(viewf[0]); // for picking if ( !arraysflag ) { glInitNames(); glPushName(0);} // Now the actual drawing if ( dlistflag ) { if ( stereomode ) { int w = (SDIM==2) ? 0 : 1; // Stereo mode always perspective viewf[3][w] -= 1.5; glLoadMatrixd(viewf[0]); glCallList(dindex); viewf[3][w] += 3.0; glLoadMatrixd(viewf[0]); glCallList(dindex); } else glCallList(dindex); } else if ( arraysflag ) { int i,j,m; for ( m = 0 ; (m < transform_count) || (m < 1) ; m++ ) { float tmat[4][4]; /* transform matrix in proper form */ if ( transforms_flag && doing_lazy && view_transform_det && (view_transform_det[m] == -1.0) ) { /* have to flip normals */ for ( i = 0 ; i < edgecount+facetcount ; i++ ) for ( j = 0 ; j < 3 ; j++ ) fullarray[i].n[j] *= -1.0; } if ( transforms_flag && doing_lazy && view_transforms ) { int hi = (SDIM <= 3) ? SDIM : 3; for ( i = 0 ; i < hi; i++ ) { for ( j = 0 ; j < hi; j++ ) tmat[i][j] = (float)view_transforms[m][j][i]; for ( ; j < 3 ; j++ ) tmat[i][j] = 0.0; tmat[i][3] = (float)view_transforms[m][i][SDIM]; } for ( ; i < 3 ; i++ ) { for ( j = 0 ; j < 4 ; j++ ) tmat[i][j] = 0.0; tmat[i][i] = 1.0; } for ( j = 0 ; j < hi ; j++ ) tmat[3][j] = (float)view_transforms[m][SDIM][j]; for ( ; j < 3 ; j++ ) tmat[3][j] = 0.0; tmat[3][3] = (float)view_transforms[m][SDIM][SDIM]; glMatrixMode(GL_MODELVIEW); glPushMatrix(); glMultMatrixf((float*)tmat); } if ( strips_flag ) { int i; /* do facets first, then edges in front */ for ( i = estripcount ; i < stripcount ; i++ ) glDrawElements(striparray[i].mode,striparray[i].count,GL_UNSIGNED_INT,stripdata+striparray[i].start); glMatrixMode(GL_PROJECTION); glTranslated(edge_bias,0.0,0.0); for ( i = 0 ; i < estripcount ; i++ ) glDrawElements(striparray[i].mode,striparray[i].count,GL_UNSIGNED_INT,stripdata+striparray[i].start); glTranslated(-edge_bias,0.0,0.0); } else if ( indexing_flag ) { glDrawElements(GL_TRIANGLES,facetcount,GL_UNSIGNED_INT,indexarray+facetstart); glMatrixMode(GL_PROJECTION); glTranslated(edge_bias,0.0,0.0); glDrawElements(GL_LINES,edgecount,GL_UNSIGNED_INT,indexarray+edgestart); glTranslated(-edge_bias,0.0,0.0); } else { glDrawArrays(GL_TRIANGLES,facetstart,facetcount); glMatrixMode(GL_PROJECTION); glTranslated(edge_bias,0.0,0.0); glDrawArrays(GL_LINES,edgestart,edgecount); glTranslated(-edge_bias,0.0,0.0); } if ( transforms_flag && doing_lazy ) { glMatrixMode(GL_MODELVIEW); glPopMatrix(); } if ( transforms_flag && doing_lazy && view_transform_det && (view_transform_det[m] == -1.0) ) { /* have to flip normals back */ for ( i = 0 ; i < edgecount+facetcount ; i++ ) for ( j = 0 ; j < 3 ; j++ ) fullarray[i].n[j] *= -1.0; } if ( !doing_lazy || !transforms_flag ) break; } /* end transform loop */ } /* end arrays_flag */ else /* not using display list or arrays */ { graphgen(); } glFlush(); auxSwapBuffers(); bailout: LEAVE_GRAPH_MUTEX; } /*********************************************************************** * * function: display() * * purpose: called by Evolver to display on screen. */ void display(void) { close_flag = 0; Oglz_start(); InvalidateRect(hwnd,NULL,FALSE); /* generate redraw message */ } /**************************************************************************** * * function: make_strips() * * purpose: Converts indexed arrays of edges and triangles to strips * for more efficient plotting. * Input: global array indexarray * global variables edgecount,facetcount * Output: striparray */ int ecomp(const void *a, const void *b) { int ai = indexarray[edgestart+*(int*)a]; int bi = indexarray[edgestart+*(int*)b]; if ( ai < bi ) return -1; if ( bi < ai ) return 1; return 0; } struct festruct { int v[3]; /* head and tail and opposite vertices */ int f; /* facet on left */ }; int fecomp ( const struct festruct *a, const struct festruct *b) { if ( a->v[0] < b->v[0] ) return -1; if ( a->v[0] > b->v[0] ) return 1; if ( a->v[1] < b->v[1] ) return -1; if ( a->v[1] > b->v[1] ) return 1; return 0; } void make_strips() { int *edgeinx; /* oriented edge numbers, sorted by first vertex */ int *evlist; /* vertex indexed list of offsets into edgeinx */ int v; int i,k,kk; int *estripno; /* number of current strip */ int *fstripno; /* number of current strip */ int stripnum; int dataspot; struct festruct *felist; int striplength[3]; /* for testing 3 ways from each starting facet */ int *trialstrip; /* for unerasing markings */ int bestlength; int *bestverts; int *bestfacets; int way; int bestway; /* strip arrays */ if ( stripdata ) myfree((char*)stripdata); if ( striparray ) myfree((char*)striparray); stripdata = (int*)mycalloc(edgecount+facetcount,sizeof(int)); striparray = (struct stripstruct *)mycalloc(edgecount/2+facetcount/3, sizeof(struct stripstruct)); dataspot = 0; stripnum = 0; /* edges */ /* make list indexed by vertex */ /* in einx, entry is 2*edgeindex+orientationbit */ edgeinx = (int *)temp_calloc(edgecount,sizeof(int)); for ( i = 0 ; i < edgecount ; i++ ) { edgeinx[i] = i; } /* using bit for orientation */ qsort((void*)edgeinx,edgecount,sizeof(int),FCAST ecomp); /* find where individual vertex segments start */ evlist = (int *)temp_calloc(edgecount+1,sizeof(int)); for ( i = 0, v = 0 ; i < edgecount ; i++ ) { while ( indexarray[edgestart+edgeinx[i]] > v ) evlist[++v] = i; } evlist[++v] = i; /* last sentinel */ /* now make strips. start with some edge and just keep going. */ estripno = (int*)temp_calloc(edgecount/2,sizeof(int)); for ( i = 0 ; i < edgecount/2 ; i++ ) { int nexte,headv; if ( estripno[i] ) continue; /* new strip */ striparray[stripnum].mode = GL_LINE_STRIP; striparray[stripnum].start = dataspot; nexte = 2*i; for (;;) { estripno[nexte>>1] = stripnum+1; headv = indexarray[edgestart+(nexte^1)]; stripdata[dataspot++] = headv; for ( k = evlist[headv] ; k < evlist[headv+1] ; k++ ) { if ( estripno[edgeinx[k]>>1] == 0 ) { nexte = edgeinx[k]; break; } } if ( k == evlist[headv+1] ) break; /* end of strip */ } /* flip list around */ for ( k = striparray[stripnum].start, kk = dataspot-1 ; k < kk ; k++,kk-- ) { int temp = stripdata[k]; stripdata[k] = stripdata[kk]; stripdata[kk] = temp; } /* now backwards */ nexte = 2*i+1; for (;;) { estripno[nexte>>1] = stripnum+1; headv = indexarray[edgestart+(nexte^1)]; stripdata[dataspot++] = headv; for ( k = evlist[headv] ; k < evlist[headv+1] ; k++ ) { if ( estripno[edgeinx[k]>>1] == 0 ) { nexte = edgeinx[k]; break; } } if ( k == evlist[headv+1] ) break; /* end of strip */ } striparray[stripnum].count = dataspot - striparray[stripnum].start; stripnum++; } estripcount = stripnum; temp_free((char*)evlist); temp_free((char*)estripno); /* facets */ /* make list of edges with left-hand facets */ felist = (struct festruct *)temp_calloc(facetcount,sizeof(struct festruct)); for ( i = 0, k = 0 ; i < facetcount ; i += 3, k++ ) { felist[i].v[0] = indexarray[facetstart+i]; felist[i].v[1] = indexarray[facetstart+i+1]; felist[i].v[2] = indexarray[facetstart+i+2]; felist[i].f = k; felist[i+1].v[0] = indexarray[facetstart+i+1]; felist[i+1].v[1] = indexarray[facetstart+i+2]; felist[i+1].v[2] = indexarray[facetstart+i]; felist[i+1].f = k; felist[i+2].v[0] = indexarray[facetstart+i+2]; felist[i+2].v[1] = indexarray[facetstart+i]; felist[i+2].v[2] = indexarray[facetstart+i+1]; felist[i+2].f = k; } qsort((void*)felist,facetcount,sizeof(struct festruct),FCAST fecomp); fstripno = (int *)temp_calloc(facetcount/3,sizeof(int)); trialstrip = (int *)temp_calloc(facetcount/3+10,sizeof(int)); bestverts = (int *)temp_calloc(facetcount/3+10,sizeof(int)); bestfacets = (int *)temp_calloc(facetcount/3,sizeof(int)); /* now make strips. start with some facet and just keep going. */ for ( i = 0 ; i < facetcount/3 ; i++ ) { int nextf,va,vb,vc,whichway; int firstcount,secondcount; /* for checking orientation at start */ if ( fstripno[i] ) continue; /* new strip */ striparray[stripnum].mode = GL_TRIANGLE_STRIP; striparray[stripnum].start = dataspot; bestlength = 0; for ( way = 0 ; way < 3 ; way++) { int m = 0; /* trialstrip index */ dataspot = striparray[stripnum].start; nextf = 3*i; stripdata[dataspot++] = va = indexarray[facetstart+nextf+((1+way)%3)]; stripdata[dataspot++] = vb = indexarray[facetstart+nextf+way]; whichway = 1; for (;;) { struct festruct *fe,key; //find in felist if ( whichway ) { key.v[0] = va; key.v[1] = vb; } else { key.v[1] = va; key.v[0] = vb; } fe = bsearch(&key,(void*)felist,facetcount,sizeof(struct festruct), FCAST fecomp); if ( fe==NULL ) break; // done; maybe hit edge of surface //see if facet done yet if ( fstripno[fe->f] != 0 ) break; // done in this direction //add opposite vertex vc = fe->v[2]; stripdata[dataspot++] = vc; fstripno[fe->f] = stripnum+1; trialstrip[m++] = fe->f; // ready for next time around va = vb; vb = vc; whichway = !whichway; } firstcount = dataspot - striparray[stripnum].start; /* flip list around */ for ( k = striparray[stripnum].start, kk = dataspot-1 ; k < kk ; k++,kk-- ) { int temp = stripdata[k]; stripdata[k] = stripdata[kk]; stripdata[kk] = temp; } /* now backwards */ va = indexarray[facetstart+nextf+way]; vb = indexarray[facetstart+nextf+((way+1)%3)]; whichway = 1; for (;;) { struct festruct *fe,key; //find in felist if ( whichway ) { key.v[0] = va; key.v[1] = vb; } else { key.v[1] = va; key.v[0] = vb; } fe = bsearch(&key,(void*)felist,facetcount,sizeof(struct festruct), FCAST fecomp); if ( fe==NULL ) break; // done; maybe hit edge of surface //see if facet done yet if ( fstripno[fe->f] != 0 ) break; // done in this direction //add opposite vertex vc = fe->v[2]; stripdata[dataspot++] = vc; fstripno[fe->f] = stripnum+1; trialstrip[m++] = fe->f; // ready for next time around va = vb; vb = vc; whichway = !whichway; } striplength[way] = dataspot - striparray[stripnum].start; secondcount = striplength[way] - firstcount; // check orientation at start if ( firstcount & 1 ) { if ( secondcount & 1 ) { striplength[way]--; /* omit last, if necessary */ if ( i == trialstrip[m-1] ) i--; /* so loop doesn't skip omitted facet */ } /* flip order */ for ( k = striparray[stripnum].start, kk = striparray[stripnum].start+striplength[way]-1 ; k < kk ; k++,kk-- ) { int temp = stripdata[k]; stripdata[k] = stripdata[kk]; stripdata[kk] = temp; } } if ( striplength[way] > bestlength ) { bestlength = striplength[way]; bestway = way; memcpy(bestverts,stripdata+striparray[stripnum].start,bestlength*sizeof(int)); memcpy(bestfacets,trialstrip,(bestlength-2)*sizeof(int)); } for ( k = 0 ; k < m ; k++ ) /* unmark */ fstripno[trialstrip[k]] = 0; } /* end ways */ memcpy(stripdata+striparray[stripnum].start,bestverts,bestlength*sizeof(int)); for ( k = 0 ; k < bestlength-2 ; k++ ) /* remark */ { fstripno[bestfacets[k]] = stripnum+1; if ( strip_color_flag && (bestfacets[k] < web.skel[FACET].maxcount) ) set_facet_color(bestfacets[k],(stripnum % 14) + 1); } striparray[stripnum].count = bestlength; dataspot = striparray[stripnum].start + bestlength; stripnum++; } temp_free((char*)evlist); temp_free((char*)estripno); temp_free((char*)fstripno); temp_free((char*)trialstrip); temp_free((char*)bestverts); temp_free((char*)bestfacets); stripcount = stripnum; fstripcount = stripcount - estripcount; /* cut down arrays to needed size */ stripdata = (int*)kb_realloc((char*)stripdata,dataspot*sizeof(int)); striparray = (struct stripstruct *)kb_realloc((char*)striparray, stripcount*sizeof(struct stripstruct)); if ( q_flag ) { sprintf(msg,"After stripping: %d edgestrips, %d facetstrips\n", estripcount,fstripcount); outstring(msg); } } /*************************************************************************** * * function: hashfunc() * * purpose: Compute hash value for vertex. */ int hashsize; /* size of hashtable */ int hashfunc(struct vercol *a) { int h; int scale = 100000; double eps = 3.e-6; /* to prevent coincidences */ h = 15187*(int)(scale*a->x[0]+eps); h += 4021*(int)(scale*a->x[1]+eps); h += 2437*(int)(scale*a->x[2]+eps); h += 7043*(int)(scale*a->n[0]+eps); h += 5119*(int)(scale*a->n[1]+eps); h += 8597*(int)(scale*a->n[2]+eps); h += 1741*(int)(scale*a->c[0]+eps); h += 4937*(int)(scale*a->c[1]+eps); h += 1223*(int)(scale*a->c[2]+eps); h = h % hashsize; if ( h < 0 ) h += hashsize; return h; } /*************************************************************************** * * function: make_indexlists() * * purpose: Uniquify vertex and edge lists, and create index array for * OpenGL. Also needed for strips. */ void make_indexlists() { int i,j; int rawcount = edgecount+facetcount; /* number of unsorted vertices */ struct vercol **hashlist; float mat[4][4]; /* get reasonable epsilon for identifying vertices */ glGetFloatv(GL_MODELVIEW_MATRIX,mat[0]); gleps = 1e-5/sqrt(mat[0][0]*mat[0][0]+mat[0][1]*mat[0][1] +mat[0][2]*mat[0][2]); /* Uniquify using hash table */ /* qsort here is a time hog */ if ( indexarray ) myfree((char*)indexarray); indexarray = (int*)mycalloc(rawcount,sizeof(int)); hashlist = (struct vercol**)temp_calloc(2*rawcount,sizeof(struct vercol *)); hashsize = 2*rawcount; hashlist[hashfunc(fullarray)] = fullarray; indexarray[0] = 0; for ( i = 1, j = 1 ; i < rawcount ; i++ ) { int h = hashfunc(fullarray+i); while ( hashlist[h] && vercolcomp(hashlist[h],fullarray+i) ) { h++; if ( h == hashsize ) h = 0; } if ( hashlist[h] == NULL ) /* new one */ { fullarray[j] = fullarray[i]; hashlist[h] = fullarray+j; j++; } indexarray[i] = hashlist[h]-fullarray; } temp_free((char*)hashlist); vertexcount = j; // Uniquify edges for ( i = edgestart ; i < edgestart+edgecount ; i += 2 ) { if ( indexarray[i] > indexarray[i+1] ) { int temp = indexarray[i]; indexarray[i] = indexarray[i+1]; indexarray[i+1] = temp; } } /* qsort here relatively minor in time */ qsort((void*)(indexarray+edgestart),edgecount/2,2*sizeof(int), FCAST eecomp); for ( i = 2, j = 0 ; i < edgecount ; i += 2 ) { if ( eecomp(indexarray+edgestart+i,indexarray+edgestart+j) != 0 ) { j += 2; if ( i > j ) { indexarray[edgestart+j] = indexarray[edgestart+i]; indexarray[edgestart+j+1] = indexarray[edgestart+i+1]; } } } if ( edgecount ) edgecount = j+2; } /* end make_indexlists() */ evolver-2.30c.dfsg/src/painter.c0000644000175300017530000026462411410765113017041 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /***************************************************************** * * File: painter.c * * Contents: Routines to accumulate and sort facets for display * back to front. Not device specific, but calls * device specific routines for actual display. * Also does transformations on edges. * painter_end() uses Newell-Newell-Sancha algorithm * to depth sort and display facets. * This version uses a separate quadtree of depth-ordered * lists from the drawing-order list to minimize comparisons * needed for large elements. */ /* Some timings with version 2.18j on starfish31.dmp (2560 facets) Without visibility test: Shape Fund. time,sec filesize Fund 1 0.093 190,829 Cubelet 12 1.08 2,276,025 Cube 48 4.64 9,100,293 Rhomb 96 9.625 18,199,633 With visibility test: Shape Fund. time,sec filesize Fund 1 0.165 168,332 Cubelet 12 1.89 1,444,778 Cube 48 9.56 4,456,944 Rhomb 96 24.7 7,162,980 */ #include "include.h" static int count; /* number of facets */ static int maxcount; /* number allocated */ struct tsort *trilist; /* list for depth sorting triangles */ static int gdim = 3; /* dimension doing graphics in */ /* results of comparing depth of facets */ #define DISJOINT 1 #define FIRST_BACK 2 #define SECOND_BACK 4 #define ASPLITTINGB 8 #define BSPLITTINGA 16 #define NOTKNOWN 32 #define COPLANAR 64 #define TEXTRA 100 REAL tableau[7][3]; /* simplex tableau */ void pivot ARGS((int ,int )); int newell_split ARGS((struct tsort *,struct tsort *,struct tsort *,struct tsort *)); int backstamp; /* for timestamping being at back of list */ int plane_test ARGS((struct tsort *,struct tsort *)); int setquadcode ARGS((struct tsort *)); void find_bbox ARGS((struct tsort *)); int separating_line ARGS((struct tsort*,struct tsort*)); int separating_plane ARGS((struct tsort*,struct tsort*,int)); static int ttcompare(t1,t2) /* depth comparison for sort */ struct tsort **t1,**t2; { /* First, compare back z */ if ( t1[0]->mins[2] > t2[0]->mins[2] ) return 1; if ( t1[0]->mins[2] < t2[0]->mins[2] ) return -1; /* Break ties with front z */ if ( t1[0]->maxs[2] > t2[0]->maxs[2] ) return 1; if ( t1[0]->maxs[2] < t2[0]->maxs[2] ) return -1; /* Finally, break ties with id to get consistent ordering, independent of how many elements are being sorted */ if ( t1[0]->f_id > t2[0]->f_id ) return 1; if ( t1[0]->f_id < t2[0]->f_id ) return -1; return 0; } /* quadtree of depth lists */ struct qtree_t { struct tsort *depthhead; float maxdepth; /* of all in subtree */ } *qtree; int maxquaddepth = 8; /* maximum depth of quadtree */ int get_quadindex ARGS((unsigned int)); void qdepth_insert ARGS((struct tsort *)); struct tsort *search_subtree ARGS((int,struct tsort *,int *)); /* visibility stuff */ void visibility_stage ARGS((struct tsort *)); void visibility_end ARGS((void)); int vis_count; /* used structures */ int vis_max; /* allocated structures */ struct tsort *vis_list; /* storage */ /***************************************************************** * * Function: find_bbox() * * Purpose: find 3D bounding box for edge or facet. * */ void find_bbox(t) struct tsort *t; { int n; REAL dx,dy,len; if ( (t->flag & 0xF) == EDGE ) { if ( t->flag & EDGE_ARC ) { REAL w1[MAXCOORD],w2[MAXCOORD],mag1,mag2,w1w2,center[2],radius; REAL det,angle1,angle2; int i; for (i = 0 ; i < SDIM ; i++ ) { w1[i] = t->x[1][i] - t->x[0][i]; w2[i] = t->x[2][i] - t->x[0][i]; } det = w1[0]*w2[1] - w1[1]*w2[0]; mag1 = SDIM_dot(w1,w1); mag2 = SDIM_dot(w2,w2); w1w2 = w1[0]*w2[0] + w1[1]*w2[1]; for ( n = 0 ; n < gdim ; n++ ) { /* endpoints first */ if ( t->x[0][n] < t->x[2][n] ) { t->maxs[n] = t->x[2][n]; t->mins[n] = t->x[0][n]; } else { t->maxs[n] = t->x[0][n]; t->mins[n] = t->x[2][n]; } } if ( 4000*det*det > mag1*mag1*mag2 + mag1*mag2*mag2 - 2*mag1*w1w2*mag2 ) { /* circle rather that straight line */ center[0] = t->x[0][0] + 0.5*(w2[1]*mag1 - w1[1]*mag2)/det; center[1] = t->x[0][1] + 0.5*(-w2[0]*mag1 + w1[0]*mag2)/det; radius = sqrt((mag1*mag1*mag2+mag1*mag2*mag2-2*mag1*w1w2*mag2) /4/det/det); angle1 = atan2(t->x[0][1]-center[1],t->x[0][0]-center[0]); angle2 = atan2(t->x[2][1]-center[1],t->x[2][0]-center[0]); if ( det < 0 ) { REAL temp = angle1; angle1 = angle2; angle2 = temp; } if ( angle2 < angle1 ) angle2 += 2*M_PI; if ( (angle1 < 0.0 && angle2 > 0.0 ) || (angle1 < 2*M_PI && angle2 > 2*M_PI) ) t->maxs[0] = (float)(center[0] + radius); if ( (angle1 < M_PI && angle2 > M_PI ) || (angle1 < 3*M_PI && angle2 > 3*M_PI) ) t->mins[0] = (float)(center[0] - radius); if ( (angle1 < M_PI/2 && angle2 > M_PI/2 ) || (angle1 < 5*M_PI/2 && angle2 > 5*M_PI/2) ) t->maxs[1] = (float)(center[1] + radius); if ( (angle1 < -M_PI/2 && angle2 > -M_PI/2 ) || (angle1 < 3*M_PI/2 && angle2 > 3*M_PI/2) ) t->mins[1] = (float)(center[1] - radius); return; } } /* end EDGE_ARC */ else /* just a straight segment */ for ( n = 0 ; n < gdim ; n++ ) { if ( t->x[0][n] < t->x[1][n] ) { t->maxs[n] = t->x[1][n]; t->mins[n] = t->x[0][n]; } else { t->maxs[n] = t->x[0][n]; t->mins[n] = t->x[1][n]; } } /* adjust extents for thickness */ dx = fabs(t->x[1][0] - t->x[0][0]); dy = fabs(t->x[1][1] - t->x[0][1]); len = sqrt(dx*dx+dy*dy); if ( len > 0.0 ) { t->maxs[0] += (float)(dy/len*t->width/2); t->mins[0] -= (float)(dy/len*t->width/2); t->maxs[1] += (float)(dx/len*t->width/2); t->mins[1] -= (float)(dx/len*t->width/2); } return; } /* end EDGE */ /* facet */ for ( n = 0 ; n < gdim ; n++ ) { if ( t->x[0][n] < t->x[1][n] ) { if ( t->x[2][n] < t->x[0][n] ) { t->maxs[n] = t->x[1][n]; t->mins[n] = t->x[2][n]; } else if ( t->x[1][n] < t->x[2][n] ) { t->maxs[n] = t->x[2][n]; t->mins[n] = t->x[0][n]; } else { t->maxs[n] = t->x[1][n]; t->mins[n] = t->x[0][n]; } } else { if ( t->x[2][n] < t->x[1][n] ) { t->maxs[n] = t->x[0][n]; t->mins[n] = t->x[2][n]; } else if ( t->x[0][n] < t->x[2][n] ) { t->maxs[n] = t->x[2][n]; t->mins[n] = t->x[1][n]; } else { t->maxs[n] = t->x[0][n]; t->mins[n] = t->x[1][n]; } } } } /* For setting quadtree code and checking if in bounding box. */ /* Coded for 32 bit ints. */ #define OUTOFBOX 0 #define INTHEBOX 1 int setquadcode(t) struct tsort *t; { unsigned int q = 0; /* the quadcode */ unsigned int bit = 1; /* for shifting to quad bit position */ int n; REAL midx = (minclipx+maxclipx)/2; REAL midy = (minclipy+maxclipy)/2; REAL deltax = (maxclipx-minclipx)/4; REAL deltay = (maxclipy-minclipy)/4; if ( t->maxs[0] < minclipx || t->mins[0] > maxclipx || t->maxs[1] < minclipy || t->mins[1] > maxclipy ) return OUTOFBOX; for ( n = 0 ; n < 8 ; n++, deltax /= 2, deltay /= 2 ) { if ( t->maxs[0] <= midx ) { q |= bit; midx -= deltax; } else if ( t->mins[0] >= midx ) { q |= bit<<1; midx += deltax; } else break; bit <<= 2; if ( t->maxs[1] <= midy ) { q |= bit; midy -= deltay; } else if ( t->mins[1] >= midy ) { q |= bit<<1; midy += deltay; } else break; bit <<= 2; } t->quadcode = q; return INTHEBOX; } void painter_start() { int dummy; long allocsize; gdim = (SDIM <= 3) ? SDIM : 3; /* allocate space for depth sort list */ if ( web.representation == STRING ) { maxcount = web.skel[EDGE].count + 5; if ( web.torus_flag ) maxcount *= 2; } else { if ( web.torus_flag ) if ( torus_display_mode == TORUS_CLIPPED_MODE ) maxcount = 5*web.skel[FACET].count+ bare_edge_count + 5; else maxcount = 2*web.skel[FACET].count+ bare_edge_count + 5; else maxcount = web.skel[FACET].count + bare_edge_count + 5; } if ( transforms_flag ) maxcount *= transform_count; if ( web.dimension > 2 ) maxcount *= web.dimension+1; /* each simplex face becomes facet */ allocsize = (long)maxcount*sizeof(struct tsort); if ( allocsize >= MAXALLOC ) maxcount = MAXALLOC/sizeof(struct tsort); trilist = (struct tsort *)temp_calloc(maxcount,sizeof(struct tsort)); count = 0; vis_count = 0; vis_list = NULL; vis_max = 0; backstamp = 0; ps_widthattr = find_extra(PS_WIDTHNAME,&dummy); } void painter_edge(gdata,e_id) struct graphdata *gdata; edge_id e_id; { struct tsort *t; int i,j; REAL a[MAXCOORD+1],b[MAXCOORD+1]; REAL dx,dy,dz,mag,width; int ctrl_pts = gdata[0].flags & EDGE_ARC ? 3 : 2; if ( gdata->color == CLEAR ) return; if ( count >= maxcount-2 ) { trilist = (struct tsort *)temp_realloc((char*)trilist, (maxcount+200)*sizeof(struct tsort)); maxcount += 200; } t = trilist + count; t->flag = EDGE; t->flag |= (gdata->flags & (EDGE_ARC|LABEL_EDGE|LABEL_HEAD|LABEL_TAIL)); for ( j = SDIM ; j < HOMDIM-1 ; j++ ) a[j] = 0.0; /* filler */ for ( i = 0 ; i < ctrl_pts ; i++ ) { for ( j = 0 ; (j < SDIM) && (j < HOMDIM-1) ; j++ ) a[j] = gdata[i].x[j]; a[HOMDIM-1] = 1.0; matvec_mul(view,a,b,HOMDIM,HOMDIM); /* transform */ if ( SDIM <= 2 ) for ( j = 0 ; j < 3 ; j++ ) t->x[i][j] = (float)b[j]; else for ( j = 0 ; j < 3 ; j++ ) t->x[i][j] = (float)b[(j+1)%3]; t->x[i][2] += (float).0001; /* bias edges in front of facets */ } /* width of edge, in descending order of thickness */ if ( ps_widthattr >= 0 ) width = *EREAL(t->f_id,ps_widthattr); else if ( gdata->etype & BARE_EDGE ) width = ps_bareedgewidth; else if ( gdata->etype & FIXED_EDGE ) width = ps_fixededgewidth; else if ( gdata->etype & CONSTRAINT_EDGE ) width = ps_conedgewidth; else if ( gdata->etype & BOUNDARY_EDGE ) width = ps_conedgewidth; else if ( gdata->etype & SINGLE_EDGE ) width = ps_stringwidth; else if ( gdata->etype & TRIPLE_EDGE ) width = ps_tripleedgewidth; else width = ps_gridedgewidth; /* regular grid interior edge */ t->width = (float)width; t->f_id = e_id; t->color = t->ecolor[0] = gdata->ecolor; t->etype[0] = gdata->etype; /* find extents */ find_bbox(t); /* normal vector, closest to z axis */ dx = t->x[1][0] - t->x[0][0]; dy = t->x[1][1] - t->x[0][1]; dz = t->x[1][2] - t->x[0][2]; t->normal[0] = (float)(dx*dz); t->normal[1] = (float)(dy*dz); t->normal[2] = -(float)(dx*dx+dy*dy); mag = sqrt(dotf(t->normal,t->normal,3)); if ( mag != 0.0 ) for ( i = 0 ; i < 3; i++ ) t->normal[i] /= (float)mag; if ( setquadcode(t) == OUTOFBOX ) return; count++; } void painter_facet(gdata,f_id) struct graphdata *gdata; facet_id f_id; { int i,j; REAL a[MAXCOORD+1],b[FACET_VERTS][MAXCOORD+1]; struct tsort *t; REAL normal[MAXCOORD],mag; if ( gdata[0].color == UNSHOWN ) { /* just do edges */ struct graphdata ggdata[2]; facetedge_id fe=NULLID; if ( valid_id(f_id) ) fe = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { if ( (gdata[i].etype&EBITS) == INVISIBLE_EDGE ) continue; ggdata[0] = gdata[i]; ggdata[0].color = gdata[i].ecolor; ggdata[1] = gdata[i==2 ? 0 : i+1]; if ( valid_id(fe) ) { painter_edge(ggdata,get_fe_edge(fe)); fe = get_next_edge(fe); } else painter_edge(ggdata,NULLID); } return; } if ( count >= maxcount ) { trilist = (struct tsort *)temp_realloc((char*)trilist, (maxcount+200)*sizeof(struct tsort)); maxcount += 200; } t = trilist + count; t->flag = FACET | (gdata->flags&LABEL_FACET); t->f_id = f_id; t->color = gdata[0].backcolor; /* not sure why, but works */ for ( i = 0 ; i < FACET_EDGES ; i++ ) { t->ecolor[i] = gdata[i].ecolor; t->etype[i] = gdata[i].etype; t->v_id[i] = gdata[i].v_id; } /* accumulate list of triangles to display */ for ( j = SDIM ; j < HOMDIM-1 ; j++ ) a[j] = 0.0; for ( i = 0 ; i < FACET_VERTS ; i++ ) { for ( j = 0 ; (j < SDIM) && (j < HOMDIM-1) ; j++ ) a[j] = gdata[i].x[j]; a[HOMDIM-1] = 1.0; matvec_mul(view,a,b[i],HOMDIM,HOMDIM); /* transform */ if ( SDIM <= 2 ) { t->x[i][0] = (float)b[i][0]; t->x[i][1] = (float)b[i][1]; } else { t->x[i][0] = (float)b[i][1]; t->x[i][1] = (float)b[i][2]; t->x[i][2] = (float)b[i][0]; } } if ( SDIM <= 2 ) { t->normal[0] = t->normal[1] = 0.0; t->normal[2] = 1.0; } else { vnormal(b[0],b[1],b[2],normal); mag = sqrt(SDIM_dot(normal,normal)); if ( mag > 0.0 ) { t->normal[0] = (float)(normal[1]/mag); t->normal[1] = (float)(normal[2]/mag); t->normal[2] = (float)(normal[0]/mag); if ( fabs(t->normal[2]) < 1e-6 ) t->normal[2] = 0.0; } else { t->normal[0] = 0.0f; t->normal[1] = 0.0f; t->normal[2] = 1.0f; } } if ( t->normal[2] > (float)0.0 ) /* frontward normal */ { int c; vertex_id tv; for ( i = 0 ; i < gdim ; i++ ) { float temp = (float)t->x[1][i]; t->x[1][i] = t->x[2][i]; t->x[2][i] = temp; t->normal[i] = -t->normal[i]; } c = t->ecolor[0]; t->ecolor[0] = t->ecolor[2]; t->ecolor[2] = c; c = t->etype[0] ^ LABEL_REVERSED; t->etype[0] = t->etype[2] ^ LABEL_REVERSED; t->etype[2] = (short)c; t->etype[1] ^= LABEL_REVERSED; t->color = gdata[0].color; tv = t->v_id[1]; t->v_id[1] = t->v_id[2]; t->v_id[2] = tv; t->flag |= FLIPPED_FACET; } else { if ( backcull_flag && (gdata[0].color == gdata[0].backcolor) ) return; } /* find extents */ find_bbox(t); if ( setquadcode(t) == OUTOFBOX ) return; count++; } /* stats for analyzing performance; REAL to handle large counts */ REAL in_back_calls; REAL box_overlaps; REAL facetfacet; REAL facetedge; REAL edgeedge; REAL crossings; REAL swaps; REAL done; REAL loopbailouts; REAL sep_line_calls; REAL sep_plane_calls; struct tsort **tlist; /*struct tsort *depthhead;*/ /************************************************************************* * * function: search_subtree() * * purpose: search node and subtree of quadtree depth lists for an * element obscured by given element. * return: pointer to obscured element, or NULL if none found. * also retval to indicate type of relationship */ struct tsort *search_subtree(qinx,tk,retval) int qinx; /* index of node in quadtree */ struct tsort *tk; /* given element */ int *retval; { struct tsort *tj; *retval = 0; /* check overall subtree max depth */ if ( tk->maxs[2] <= qtree[qinx].maxdepth ) return NULL; /* the node itself */ for ( tj = qtree[qinx].depthhead ; tj != NULL ; tj = tj->next ) { if ( tj == tk ) continue; if ( tk->maxs[2] <= tj->mins[2] ) break; *retval = in_back(tk,tj); if ( *retval & (FIRST_BACK|COPLANAR|DISJOINT) ) continue; return tj; } /* one child */ tj = search_subtree(2*qinx,tk,retval); if ( tj ) return tj; /* other child */ return search_subtree(2*qinx+1,tk,retval); } #ifdef XXXXX /* for debugging */ void loopchecker() { int counter; int qinx; int i; for ( i = 0 ; i < count+TEXTRA ; i++ ) if ( tlist[i]->spot != i ) kb_error(2893,"Bad spot\n",RECOVERABLE); for ( qinx = 0 ; qinx < 0x40000 ; qinx++ ) { struct tsort *tj; int q; struct tsort *slowboat = qtree[qinx].depthhead; /* likewise */ struct tsort *prev = NULL; if ( slowboat == NULL ) continue; counter = 0; q = slowboat->quadcode; if ( qinx != get_quadindex(q) ) kb_error(2585,"Bad qinx.\n",RECOVERABLE); for ( tj = qtree[qinx].depthhead ; tj != NULL ; prev=tj,tj = tj->next ) { /* loop detection */ if ( tj->prev != prev ) kb_error(2892,"bad prev\n",RECOVERABLE); if ( tj->quadcode != q ) kb_error(2891,"Bad qinx.\n",RECOVERABLE); if ( counter & 1) slowboat = slowboat->next; if ( (slowboat == tj) && (counter > 1) ) kb_error(2591,"Internal error: loop in loopchecker()\n", RECOVERABLE); counter++; } } } #endif /************************************************************************** * * function: painter_end() * * purpose: sort and display facets and edges from trilist. */ /* for debugging; just displays list as is after given number of facets */ int debug_k = 0x7FFFFFFF; void painter_end() { int k; int loopcount; /* for emergency loop bailout */ struct tsort **ll,*tt; int quadalloc; int k_top; /* top of tlist */ struct tsort textra[TEXTRA]; /* for triangle fragments */ in_back_calls = box_overlaps = facetfacet = facetedge = edgeedge = crossings = sep_plane_calls = sep_line_calls = 0; loopbailouts = 0; if ( count > maxcount ) count = maxcount; /* in case there was excess */ /* find bounding box */ if ( need_bounding_box ) { struct tsort *t; bbox_minx = bbox_miny = 1e20; bbox_maxx = bbox_maxy = -1e20; for ( k = 0, t = trilist ; k < count ; k++,t++ ) { if ( t->mins[0] < bbox_minx ) bbox_minx = (REAL)t->mins[0]; if ( t->mins[1] < bbox_miny ) bbox_miny = (REAL)t->mins[1]; if ( t->maxs[0] > bbox_maxx ) bbox_maxx = (REAL)t->maxs[0]; if ( t->maxs[1] > bbox_maxy ) bbox_maxy = (REAL)t->maxs[1]; } } (*init_graphics)(); if ( SDIM == 2 ) /* don't bother with depth */ { for ( k = 0 ; k < count ; k++ ) visibility_stage(trilist+k); goto end_exit; } /* now sort on min z, moving pointers instead of structures */ /* leaving room at front of list for extra fragments */ tlist = (struct tsort **)temp_calloc(count+TEXTRA,sizeof(struct tsort *)); for ( k = 0, ll=tlist+TEXTRA, tt=trilist ; k < count ; k++ ) *(ll++) = tt++; qsort((char *)(tlist+TEXTRA),count,sizeof(struct tsort *),FCAST ttcompare); for ( k = 0 ; k < TEXTRA ; k++ ) { tlist[k] = textra+k; tlist[k]->spot = k; tlist[k]->flag = 0; } for ( k = TEXTRA ; k < TEXTRA+count ; k++ ) tlist[k]->spot = k; /* quadtree of depth lists */ maxquaddepth = 8; /* maybe make this adjustable later */ quadalloc = 2 << (2*maxquaddepth + 1); qtree = (struct qtree_t *)temp_calloc(quadalloc,sizeof(struct qtree_t)); for ( k = 0 ; k < quadalloc ; k++ ) qtree[k].maxdepth = 1e30f; for ( k = count+TEXTRA-1 ; k >= TEXTRA ; k-- ) qdepth_insert(tlist[k]); /* display */ loopcount = 0; k_top = count+TEXTRA; for ( k = TEXTRA ; k < k_top ; ) { struct tsort *tk = tlist[k]; struct tsort *tj; int sinx,qinx=0; int retval; if ( breakflag ) break; if ( !tk->flag ) { k++; continue; } /* for debugging and testing */ if ( k > debug_k ) goto draw_it; /* tk is current candidate back facet */ /* search quadtree list for any z overlap */ /* First, node to root */ qinx = get_quadindex(tk->quadcode); for ( sinx = qinx >> 1 ; sinx != 0 ; sinx >>= 1 ) { for ( tj = qtree[sinx].depthhead ; tj != NULL ; tj = tj->next ) { if ( tj == tk ) continue; if ( tk->maxs[2] <= tj->mins[2] ) break; retval = in_back(tk,tj); if ( retval & (FIRST_BACK|COPLANAR|DISJOINT) ) continue; goto have_conflict; } } /* now search subtree for conflicts */ tj = search_subtree(qinx,tk,&retval); if ( tj==NULL ) goto draw_it; have_conflict: /* Now have conflict, tk obscuring tj */ /* test for possible looping, and if found, split tk */ if ( (tj->backstamp == backstamp) ) { int ret; crossings++; if ( ++loopcount > count ) { loopbailouts++; goto draw_it; } /* need to split */ if ( k < 2 ) { /* not enough room, so expand tlist allocation, with free at start */ int newsize = 2*k_top; int n; struct tsort *more = (struct tsort*)temp_calloc(k_top,sizeof(struct tsort)); tlist = (struct tsort**)temp_realloc((char*)tlist,newsize*sizeof(struct tsort*)); for ( n = 0 ; n < k_top ; n++ ) { tlist[n+k_top] = tlist[n]; tlist[n] = more+n; } for ( n = 0 ; n < newsize ; n++ ) tlist[n]->spot = n; k += k_top; k_top = newsize; } if( retval & BSPLITTINGA ) { ret = newell_split(tk,tj,tlist[k-1],tlist[k-2]); if ( ret ) { k -= ret; goto repeat_tests; /* might not have split */ } } else if ( retval & ASPLITTINGB ) { /* try splitting the other way */ ret = newell_split(tj,tk,tlist[k-1],tlist[k-2]); if ( ret ) { k -= ret; goto repeat_tests; } } else if ( ((tk->flag & 0xF) == EDGE) && ((tj->flag & 0xF) == EDGE) ) { ret = newell_split(tk,tj,tlist[k-1],tlist[k-2]); if ( ret ) { k -= ret; goto repeat_tests; /* might not have split */ } } } tk->backstamp = backstamp; /* swap tj and tk */ tlist[k] = tj; tlist[tj->spot] = tk; tk->spot = tj->spot; tj->spot = k; swaps++; goto repeat_tests; draw_it: visibility_stage(tk); loopcount = 0; tk->flag = 0; /* to indicate empty structure */ /* remove from depth list */ if ( tk == qtree[qinx].depthhead ) qtree[qinx].depthhead = tk->next; sinx = qinx; /* fix up subtree maxdepths */ while ( sinx && ( tk->mins[2] <= qtree[sinx].maxdepth ) ) { float maxd = 1e30f; if ( qtree[sinx].depthhead ) maxd = qtree[sinx].depthhead->mins[2]; if ( sinx < (1 << (2*maxquaddepth)) ) { if ( maxd > qtree[2*sinx].maxdepth ) maxd = qtree[2*sinx].maxdepth; if ( maxd > qtree[2*sinx+1].maxdepth ) maxd = qtree[2*sinx+1].maxdepth; } qtree[sinx].maxdepth = maxd; sinx >>= 1; } if ( tk->prev ) tk->prev->next = tk->next; if ( tk->next ) tk->next->prev = tk->prev; repeat_tests: continue; } end_exit: if ( verbose_flag ) { printf("in_back_calls: %g\n",in_back_calls); printf(" facetfacet: %g\n",facetfacet); printf(" facetedge: %g\n",facetedge); printf(" edgeedge: %g\n",edgeedge); printf("box_overlaps: %g\n",box_overlaps); printf("sep_line_calls: %g\n",sep_line_calls); printf("sep_plane_calls: %g\n",sep_plane_calls); printf("crossings: %g\n",crossings); printf("swaps: %g\n",swaps); printf("loop bailouts: %g\n",loopbailouts); } if ( tlist ) temp_free((char *)tlist); if ( qtree ) temp_free((char *)qtree); temp_free((char *)trilist); trilist = NULL; if ( visibility_test ) visibility_end(); (*finish_graphics)(); } /* end old painter_end() */ /********************************************************************* * * function: in_back() * * purpose: see if one facet or edge obscures another. * * returns DISJOINT, FIRST_BACK, SECOND_BACK, ASPLITTINGB, BSPLITTINGA, * or COPLANAR (possibly bitwise OR) */ int in_back(ta,tb) struct tsort *ta,*tb; { int n; int retval; if ( verbose_flag ) { in_back_calls++; if ( (ta->flag & 0xF) == FACET ) { if ( (tb->flag & 0xF) == FACET ) facetfacet++; else facetedge++; } else { if ( (tb->flag & 0xF) == FACET ) facetedge++; else edgeedge++; } } /* quick test with quadcodes */ if ( ((ta->quadcode & tb->quadcode) != ta->quadcode) && ((ta->quadcode & tb->quadcode) != tb->quadcode) ) return DISJOINT; /* test x and y extent overlap */ for ( n = 0 ; n < 2 ; n++ ) if ( (tb->maxs[n] <= ta->mins[n]) || (tb->mins[n] >= ta->maxs[n]) ) return DISJOINT; if ( ta->maxs[2] <= tb->mins[2] ) return FIRST_BACK; box_overlaps++; /* for verbose stats */ if ( separating_line(ta,tb) == DISJOINT ) return DISJOINT; retval = plane_test(ta,tb); if ( retval & (FIRST_BACK|COPLANAR|DISJOINT) ) return retval; /* now the nitty gritty check to see if they overlap */ #ifdef ZZZ if ( (ta->flag & 0xF) == FACET ) { if ( (tb->flag & 0xF) == FACET ) return facetfacetcompare(ta,tb); else { retval = edgefacetcompare(tb,ta); if ( retval & (FIRST_BACK|SECOND_BACK) ) return retval ^ (FIRST_BACK|SECOND_BACK); else return retval; } } else { if ( (tb->flag & 0xF) == FACET ) return edgefacetcompare(ta,tb); else { retval = edgeedgecompare(tb,ta); if ( retval & (FIRST_BACK|SECOND_BACK) ) return retval ^ (FIRST_BACK|SECOND_BACK); else return retval; } } #endif return retval; } /************************************************************************** * * function: get_quadindex() * * purpose: convert quadcode to index number in quadtree list. * */ int get_quadindex(q) unsigned int q; { int inx,k; inx = 1; for ( k = 0 ; k < 2*maxquaddepth ; k++, q >>= 2 ) { int bits = q & 0x3; if ( bits == 0 ) return inx; inx <<= 1; if ( bits == 2 ) inx++; } return inx; } /************************************************************************** * * function: qdepth_insert() * * purpose: insert new fragment in proper place in quadtree depth list. * For now, crude linear search. */ void qdepth_insert(tc) struct tsort *tc; { struct tsort *prev,*next; int qinx = get_quadindex(tc->quadcode); int sinx; /* take care of maxdepths of subtrees */ sinx = qinx; while ( sinx && (tc->mins[2] < qtree[sinx].maxdepth) ) { qtree[sinx].maxdepth = tc->mins[2]; sinx >>= 1; } prev = NULL; for ( next=qtree[qinx].depthhead; next != NULL; prev=next, next=next->next ) { if ( tc->mins[2] < next->mins[2] ) { tc->next = next; tc->prev = prev; if ( prev ) prev->next = tc; else qtree[qinx].depthhead = tc; next->prev = tc; goto qdepth_insert_exit; } } /* at end of list */ if ( prev ) prev->next = tc; else qtree[qinx].depthhead = tc; tc->next = NULL; tc->prev = prev; qdepth_insert_exit:; } /************************************************************************* * * function: newell_split() * * purpose: split one triangle by plane of another. * * return: number of new elements generated. */ int newell_split(ta,tb,tc,td) struct tsort *ta; /* splittee and fragment return */ struct tsort *tb; /* splitter */ struct tsort *tc; /* fragment return */ struct tsort *td; /* fragment return */ { int i; REAL d0,d1,d2,db; int retval; int tmpspot; backstamp++; /* clear loop indications */ if ( (tb->flag & 0xF) == EDGE ) { struct tsort *t; if ( (ta->flag & 0xF) == EDGE ) { /* cut first edge in half */ tmpspot = tc->spot; *tc = *ta; tc->spot = tmpspot; for ( i = 0 ; i < gdim ; i++ ) { float mid = (ta->x[0][i] + ta->x[1][i])/2; if ( ta->x[0][2] > ta->x[1][2] ) /* ta is back half */ { ta->x[0][i] = mid; tc->x[1][i] = mid; } else { ta->x[1][i] = mid; tc->x[0][i] = mid; } } for ( i = 0 ; i < gdim ; i++ ) { ta->mins[i] = (ta->x[0][i]x[1][i]) ? ta->x[0][i] : ta->x[1][i]; tc->mins[i] = (tc->x[0][i]x[1][i]) ? tc->x[0][i] : tc->x[1][i]; ta->maxs[i] = (ta->x[0][i]>ta->x[1][i]) ? ta->x[0][i] : ta->x[1][i]; tc->maxs[i] = (tc->x[0][i]>tc->x[1][i]) ? tc->x[0][i] : tc->x[1][i]; } /* find_bbox(tc); */ if ( setquadcode(tc) == INTHEBOX ) { qdepth_insert(tc); return 1; } else { tc->flag = 0; return 0; } } t = ta; ta = tb; tb = t; /* swap so edge first */ } if ( (ta->flag & 0xF) == EDGE ) /* tb assumed to be facet */ { db = dotf(tb->normal,tb->x[0],gdim); d0 = dotf(tb->normal,ta->x[0],gdim); d1 = dotf(tb->normal,ta->x[1],gdim); /* fill in fragment info */ tmpspot = tc->spot; *tc = *ta; tc->spot = tmpspot; for ( i = 0 ; i < gdim ; i++ ) { double mid = (((db-d0)*ta->x[1][i] + (d1-db)*ta->x[0][i])/(d1-d0)); if ( ta->x[0][2] < ta->x[1][2] ) /* keep ta as back part */ ta->x[1][i] = tc->x[0][i] = (float)mid; else ta->x[0][i] = tc->x[1][i] = (float)mid; } for ( i = 0 ; i < gdim ; i++ ) { ta->mins[i] = (ta->x[0][i]x[1][i]) ? ta->x[0][i] : ta->x[1][i]; tc->mins[i] = (tc->x[0][i]x[1][i]) ? tc->x[0][i] : tc->x[1][i]; ta->maxs[i] = (ta->x[0][i]>ta->x[1][i]) ? ta->x[0][i] : ta->x[1][i]; tc->maxs[i] = (tc->x[0][i]>tc->x[1][i]) ? tc->x[0][i] : tc->x[1][i]; } /* find_bbox(tc); */ if ( setquadcode(tc) == INTHEBOX ) { qdepth_insert(tc); return 1; } else { tc->flag = 0; return 0; } } /* figure out which vertices of ta on same side, and get as 0,1 */ db = dotf(tb->normal,tb->x[0],gdim); d0 = dotf(tb->normal,ta->x[0],gdim); d1 = dotf(tb->normal,ta->x[1],gdim); d2 = dotf(tb->normal,ta->x[2],gdim); if ( (d0db) && (d1>db) && (d2>db) ) { return 0; } retval = 0; if ( db == d0 ) /* split thru vertex 0 of ta */ { tmpspot = tc->spot; *tc = *ta; tc->spot = tmpspot; /* fill in fragment info */ for ( i = 0 ; i < gdim ; i++ ) { ta->x[2][i] = tc->x[1][i] = (float)(((db-d1)*ta->x[2][i] + (d2-db)*ta->x[1][i])/(d2-d1)); } /* internal edges invisible */ ta->etype[2] = tc->etype[0] = SPLITTING_EDGE; retval = 1; } if ( db == d1 ) /* split thru vertex 1 of ta */ { tmpspot = tc->spot; *tc = *ta; tc->spot = tmpspot; /* fill in fragment info */ for ( i = 0 ; i < gdim ; i++ ) { ta->x[2][i] = tc->x[0][i] = (float)(((db-d0)*ta->x[2][i] + (d2-db)*ta->x[0][i])/(d2-d0)); } /* internal edges invisible */ ta->etype[1] = tc->etype[0] = SPLITTING_EDGE; retval = 1; } if ( db == d2 ) /* split thru vertex 2 of ta */ { tmpspot = tc->spot; *tc = *ta; tc->spot = tmpspot; /* fill in fragment info */ for ( i = 0 ; i < gdim ; i++ ) { ta->x[1][i] = tc->x[0][i] = (float)(((db-d0)*ta->x[1][i] + (d1-db)*ta->x[0][i])/(d1-d0)); } /* internal edges invisible */ ta->etype[1] = tc->etype[2] = SPLITTING_EDGE; retval = 1; } if ( retval == 1 ) { /* set mins and maxs */ for ( i = 0 ; i < gdim ; i++ ) { int j; ta->mins[i] = tc->mins[i] = (float)1e30; ta->maxs[i] = tc->maxs[i] = (float)(-1e30); for ( j = 0 ; j < FACET_VERTS ; j++ ) { if ( ta->x[j][i] < ta->mins[i] ) ta->mins[i] = ta->x[j][i]; if ( tc->x[j][i] < tc->mins[i] ) tc->mins[i] = tc->x[j][i]; if ( ta->x[j][i] > ta->maxs[i] ) ta->maxs[i] = ta->x[j][i]; if ( tc->x[j][i] > tc->maxs[i] ) tc->maxs[i] = tc->x[j][i]; } } if ( ta->mins[2] > tc->mins[2] ) { struct tsort tmp; /* get ta as back part */ tmp = *ta; *ta = *tc; *tc = tmp; tmpspot = ta->spot; ta->spot = tc->spot; tc->spot = tmpspot; } /* find_bbox(tc); */ if ( setquadcode(tc) == INTHEBOX ) { qdepth_insert(tc); return 1; } else { tc->flag = 0; return 0; } } if ( (d0-db)*(d2-db) > 0.0 ) { int c = ta->ecolor[1]; short e = ta->etype[1]; REAL d = d1; for ( i = 0 ; i < gdim ; i++ ) { float temp = ta->x[1][i]; ta->x[1][i] = ta->x[0][i]; ta->x[0][i] = ta->x[2][i]; ta->x[2][i] = temp; } ta->ecolor[1] = ta->ecolor[0]; ta->ecolor[0] = ta->ecolor[2]; ta->ecolor[2] = c; ta->etype[1] = ta->etype[0]; ta->etype[0] = ta->etype[2]; ta->etype[2] = e; d1 = d0; d0 = d2; d2 = d; } else if ( (d1-db)*(d2-db) > 0.0 ) { int c = ta->ecolor[1]; short e = ta->etype[1]; REAL d = d1; for ( i = 0 ; i < gdim ; i++ ) { float temp = ta->x[1][i]; ta->x[1][i] = ta->x[2][i]; ta->x[2][i] = ta->x[0][i]; ta->x[0][i] = temp; } ta->ecolor[1] = ta->ecolor[2]; ta->ecolor[2] = ta->ecolor[0]; ta->ecolor[0] = c; ta->etype[1] = ta->etype[2]; ta->etype[2] = ta->etype[0]; ta->etype[0] = e; d1 = d2; d2 = d0; d0 = d; } /* copy all info to fragments */ tmpspot = tc->spot; *tc = *ta; tc->spot = tmpspot; tmpspot = td->spot; *td = *ta; td->spot = tmpspot; retval = 2; /* fill in fragment info */ for ( i = 0 ; i < gdim ; i++ ) { ta->x[2][i] = tc->x[0][i] = td->x[0][i] = (float)(((db-d0)*td->x[2][i] + (d2-db)*ta->x[0][i])/(d2-d0)); tc->x[2][i] = td->x[1][i] = (float)(((db-d1)*td->x[2][i] + (d2-db)*ta->x[1][i])/(d2-d1)); } /* internal edges invisible */ ta->etype[1] = tc->etype[0] = tc->etype[2] = td->etype[0] = SPLITTING_EDGE; /* set mins and maxs */ for ( i = 0 ; i < gdim ; i++ ) { int j; ta->mins[i] = tc->mins[i] = td->mins[i] = (float)1e30; ta->maxs[i] = tc->maxs[i] = td->maxs[i] = (float)(-1e30); for ( j = 0 ; j < 3 ; j++ ) { if ( ta->x[j][i] < ta->mins[i] ) ta->mins[i] = ta->x[j][i]; if ( tc->x[j][i] < tc->mins[i] ) tc->mins[i] = tc->x[j][i]; if ( td->x[j][i] < td->mins[i] ) td->mins[i] = td->x[j][i]; if ( ta->x[j][i] > ta->maxs[i] ) ta->maxs[i] = ta->x[j][i]; if ( tc->x[j][i] > tc->maxs[i] ) tc->maxs[i] = tc->x[j][i]; if ( td->x[j][i] > td->maxs[i] ) td->maxs[i] = td->x[j][i]; } } /* get ta as back part */ if ( (tc->mins[2] < ta->mins[2]) && (tc->mins[2] <= td->mins[2]) ) { struct tsort tmp; /* get ta as back part */ tmp = *ta; *ta = *tc; *tc = tmp; tmpspot = ta->spot; ta->spot = tc->spot; tc->spot = tmpspot; } else if ( td->mins[2] < ta->mins[2] ) { struct tsort tmp; /* get ta as back part */ tmp = *ta; *ta = *td; *td = tmp; tmpspot = ta->spot; ta->spot = td->spot; td->spot = tmpspot; } /* find_bbox(tc); */ /* find_bbox(td); */ if ( setquadcode(tc) == INTHEBOX ) { qdepth_insert(tc); if ( setquadcode(td) == INTHEBOX ) qdepth_insert(td); else { retval--; td->flag = 0; td->next = (struct tsort *)0xFF; } } else { /* discard tc */ tmpspot = tc->spot; *tc = *td; tc->spot = tmpspot; td->flag = 0; td->next = (struct tsort *)0xAA; retval--; if ( setquadcode(tc) == INTHEBOX ) qdepth_insert(tc); else { retval--; tc->flag = 0; tc->next = (struct tsort *)0xEE; } } return retval; } /********************************************************************* * * function: separating_plane() * * purpose: See if two facets have a separating plane in 3D. * Meant to be called when known the facets overlap in 2D. * * returns FIRST_BACK, SECOND_BACK, or ASPLITTINGB | BSPLITTINGA */ int separating_plane(ta,tb,depth) struct tsort *ta,*tb; int depth; /* to limit recursion to depth 2 */ { int i,j; int na,nb; /* vertices on respective elements */ int nna; /* number of vertex pairs to check on ta */ float *a[3],*b[3]; int retval; REAL da,db,d; int n,k,bnear=0,beq=0,bfar=0,anear=0,afar=0,aeq=0; sep_plane_calls++; /* for verbose statistics */ /* see where tb is with respect to ta plane */ if ( (ta->flag & 0xF) == FACET ) { da = dotf(ta->normal,ta->x[0],gdim); n = ((tb->flag & 0xF) == FACET) ? 3 : 2; for ( k = 0 ; k < n ; k++ ) { d = dotf(ta->normal,tb->x[k],gdim); if ( d < da - 0.0001 ) { bnear++; continue; } if ( d < da + 0.0001 ) { beq++; continue; } bfar++; } if ( beq == n ) return ((tb->flag&0xF)==EDGE)?FIRST_BACK:DISJOINT; /* in same plane */ if ( bfar == 0 ) return FIRST_BACK; } /* see where ta is with respect to tb plane */ if ( (tb->flag & 0xF) == FACET ) { db = dotf(tb->normal,tb->x[0],gdim); n = ((ta->flag & 0xF) == FACET) ? 3 : 2; for ( k = 0 ; k < n ; k++ ) { d = dotf(tb->normal,ta->x[k],gdim); if ( d < db - 0.0001 ) { anear++; continue; } if ( d < db + 0.0001 ) { aeq++; continue; } afar++; } if ( aeq == n ) return ((ta->flag&0xF)==EDGE)?SECOND_BACK:DISJOINT; /* same plane */ if ( anear == 0 ) return FIRST_BACK; } na = (ta->flag & 0xF) == FACET ? 3 : 2; nna = (ta->flag & 0xF) == FACET ? 3 : 1; nb = (tb->flag & 0xF) == FACET ? 3 : 2; for ( i = 0 ; i < nna ; i++ ) for ( j = 0 ; j < nb ; j++ ) { REAL c[3],d; /* coefficients for plane */ REAL da[3],db[3],minarea,s[3],length,dar[3],dbr[3]; int ii,jj,jjj; for ( ii = 0 ; ii < na ; ii++ ) a[ii] = ta->x[(i+ii)%na]; for ( jj = 0 ; jj < nb ; jj++ ) b[jj] = tb->x[(j+jj)%nb]; for ( ii = 0 ; ii < 3 ; ii++ ) s[ii] = a[1][ii]-a[0][ii]; length = sqrt(s[0]*s[0]+s[1]*s[1]+s[2]*s[2]); minarea = 1e-4*length; c[0] = s[1]*(b[0][2]-a[0][2]) - s[2]*(b[0][1]-a[0][1]); c[1] = s[2]*(b[0][0]-a[0][0]) - s[0]*(b[0][2]-a[0][2]); c[2] = s[0]*(b[0][1]-a[0][1]) - s[1]*(b[0][0]-a[0][0]); if ( c[0]*c[0] + c[1]*c[1] + c[2]*c[2] <= minarea*minarea ) continue; /* degenerate */ d = c[0]*a[0][0] + c[1]*a[0][1] + c[2]*a[0][2]; for ( ii = 2 ; ii < na ; ii++ ) { dar[ii] = c[0]*a[ii][0] + c[1]*a[ii][1] +c[2]*a[ii][2] - d; da[ii] = ( dar[ii] < -minarea ? -1.0 : ( dar[ii] > minarea ? 1.0 : 0.0)); } for ( jj = 1 ; jj < nb ; jj++ ) { dbr[jj] = c[0]*b[jj][0] + c[1]*b[jj][1] +c[2]*b[jj][2] - d; db[jj] = ( dbr[jj] < -minarea ? -1.0 : ( dbr[jj] > minarea ? 1.0 : 0.0)); } /* test opposite sidedness */ for ( jj = 1 ; jj < nb ; jj++ ) for ( jjj = jj+1 ; jjj < nb ; jjj++ ) if ( db[jj]*db[jjj] < 0.0 ) goto keeptrying; for ( ii = 2 ; ii < na ; ii++ ) for ( jj = 1 ; jj < nb ; jj++ ) if ( da[ii]*db[jj] > 0.0 ) goto keeptrying; /* have separating plane */ { REAL asum,bsum; /* decide which is in front */ for ( ii = 2, asum = 0.0 ; ii < na ; ii++ ) asum += c[2]*da[ii]; for ( jj = 1, bsum = 0.0 ; jj < nb ; jj++ ) bsum += c[2]*db[jj]; if ( asum > 0.0 || bsum < 0.0 ) return SECOND_BACK; else return FIRST_BACK; } keeptrying: ; } /* might have case of separating plane having two vertices on tb */ if ( depth == 2 ) return ASPLITTINGB|BSPLITTINGA; retval = separating_plane(tb,ta,2); if ( retval == FIRST_BACK ) return SECOND_BACK; if ( retval == SECOND_BACK ) return FIRST_BACK; return retval; } /********************************************************************* * * function: separating_line() * * purpose: See if two elements have a separating line in 2D. * To be called after bounding box tests. * Includes small tolerance. * * returns DISJOINT or NOTKNOWN */ int separating_line(ta,tb) struct tsort *ta,*tb; { int i; int same = 0; int na,nb; /* vertices on respective elements */ int nna,nnb; /* number of lines to try */ int apos,aneg,bpos,bneg; float *a[3],*b[3]; REAL width; /* thickness of separating line */ sep_line_calls++; /* for verbose statistics */ /* get edge first, if any */ if ( ((ta->flag & 0xF) == FACET) && ((tb->flag & 0xF) == EDGE ) ) { struct tsort *tmp = ta; ta = tb; tb = tmp; } /* want to prevent overlap of facet with thick edge; not going to worry about edge-edge overlap, since that too weird. */ if ( ((ta->flag & 0xF) == EDGE) && ((tb->flag & 0xF) == FACET ) ) { width = ta->width/2; /* actually need half-width */ } else width = -1e-5; /* allow slight overlap for numerical purposes */ na = (ta->flag & 0xF) == FACET ? 3 : 2; nb = (tb->flag & 0xF) == FACET ? 3 : 2; nna = (ta->flag & 0xF) == FACET ? 3 : 1; nnb = (tb->flag & 0xF) == FACET ? 3 : 1; /* Try using edges of ta */ for ( i = 0 ; i < nna ; i++ ) { REAL cx,cy,d; /* coefficients for line */ REAL dar[3],dbr[3],minarea; int ii,jj; for ( ii = 0 ; ii < na ; ii++ ) a[ii] = ta->x[(i+ii)%na]; for ( jj = 0 ; jj < nb ; jj++ ) b[jj] = tb->x[jj]; cx = a[1][1] - a[0][1]; cy = a[0][0] - a[1][0]; d = cx*a[0][0] + cy*a[0][1]; minarea = width*sqrt(cx*cx + cy*cy); if ( fabs(minarea) < 1e-20 ) { same++; continue; /* same point */ } apos = aneg = bpos = bneg = 0; for ( ii = 2 ; ii < na ; ii++ ) { dar[ii] = cx*a[ii][0] + cy*a[ii][1] - d; if ( dar[ii] > minarea ) apos++; if ( dar[ii] < -minarea ) aneg++; } for ( jj = 0 ; jj < nb ; jj++ ) { dbr[jj] = cx*b[jj][0] + cy*b[jj][1] - d; if ( dbr[jj] > minarea ) bpos++; if ( dbr[jj] < -minarea ) bneg++; } /* test opposite sidedness */ if ( apos == (na-2) && bneg == nb ) return DISJOINT; if ( aneg == (na-2) && bpos == nb ) return DISJOINT; } /* Try using edges of tb */ for ( i = 0 ; i < nnb ; i++ ) { REAL cx,cy,d; /* coefficients for line */ REAL dar[3],dbr[3],minarea; int ii,jj; for ( ii = 0 ; ii < nb ; ii++ ) a[ii] = tb->x[(i+ii)%nb]; for ( jj = 0 ; jj < na ; jj++ ) b[jj] = ta->x[jj]; cx = a[1][1] - a[0][1]; cy = a[0][0] - a[1][0]; d = cx*a[0][0] + cy*a[0][1]; width = -1e-5; minarea = width*sqrt(cx*cx + cy*cy); if ( fabs(minarea) < 1e-20 ) { same++; continue; /* same point */ } apos = aneg = bpos = bneg = 0; for ( ii = 2 ; ii < nb ; ii++ ) { dar[ii] = cx*a[ii][0] + cy*a[ii][1] - d; if ( dar[ii] > minarea ) apos++; if ( dar[ii] < -minarea ) aneg++; } for ( jj = 0 ; jj < na ; jj++ ) { dbr[jj] = cx*b[jj][0] + cy*b[jj][1] - d; if ( dbr[jj] > minarea ) bpos++; if ( dbr[jj] < -minarea ) bneg++; } /* test opposite sidedness */ if ( apos == (nb-2) && bneg == na ) return DISJOINT; if ( aneg == (nb-2) && bpos == na ) return DISJOINT; } return NOTKNOWN; } /********************************************************************* * * function: plane_test() * * purpose: See if one facet or edge is in front or back of element plane. * Suitable for Newell-Newell-Sancha algorithm. * * returns DISJOINT, FIRST_BACK, SECOND_BACK, ASPLITTINGB, BSPLITTINGA, or COPLANAR * Returns FIRST_BACK if guaranteed first does not obscure any of second. * Possibly bitwise OR of properties. */ int plane_test(ta,tb) struct tsort *ta,*tb; { REAL da,db; int k,n; int afar=0,aeq=0,anear=0; /* count of ta vertices relative to tb plane */ int bfar=0,beq=0,bnear=0; /* count of tb vertices relative to ta plane */ REAL d; int retval = NOTKNOWN; /* if two edges */ if ( ((ta->flag & 0xF) == EDGE) && ((tb->flag & 0xF) == EDGE) ) { REAL ab1x = tb->x[0][0] - ta->x[0][0]; REAL ab1y = tb->x[0][1] - ta->x[0][1]; REAL ab1z = tb->x[0][2] - ta->x[0][2]; REAL ab2x = tb->x[1][0] - ta->x[0][0]; REAL ab2y = tb->x[1][1] - ta->x[0][1]; REAL ab2z = tb->x[1][2] - ta->x[0][2]; REAL aax = ta->x[1][0] - ta->x[0][0]; REAL aay = ta->x[1][1] - ta->x[0][1]; REAL aaz = ta->x[1][2] - ta->x[0][2]; REAL area = ab1x*ab2y - ab1y*ab2x; REAL vol = (ab1x*ab2y - ab1y*ab2x)*aaz + (ab1y*ab2z - ab1z*ab2y)*aax + (ab1z*ab2x - ab1x*ab2z)*aay; if ( vol == 0.0 ) return COPLANAR; if ( area == 0.0 ) return DISJOINT; if ( area*vol > 0 ) return SECOND_BACK; return FIRST_BACK; } /* see where tb is with respect to ta plane */ da = dotf(ta->normal,ta->x[0],gdim); n = ((tb->flag & 0xF) == FACET) ? 3 : 2; for ( k = 0 ; k < n ; k++ ) { d = dotf(ta->normal,tb->x[k],gdim); if ( d < da - 0.0001 ) { bnear++; continue; } if ( d < da + 0.0001 ) { beq++; continue; } bfar++; } if ( beq == n ) return COPLANAR; /* both in same plane */ if ( bfar == 0 ) return FIRST_BACK; if ( bnear > 0 ) { if ( (ta->flag & 0xF) == FACET ) retval = ASPLITTINGB; } else retval = SECOND_BACK; /* see where ta is with respect to tb plane */ db = dotf(tb->normal,tb->x[0],gdim); n = ((ta->flag & 0xF) == FACET) ? 3 : 2; for ( k = 0 ; k < n ; k++ ) { d = dotf(tb->normal,ta->x[k],gdim); if ( d < db - 0.0001 ) { anear++; continue; } if ( d < db + 0.0001 ) { aeq++; continue; } afar++; } if ( aeq == n ) return COPLANAR; /* both in same plane */ if ( anear == 0 ) return FIRST_BACK; if ( afar > 0 ) { if ( (tb->flag & 0xf) == FACET ) retval |= BSPLITTINGA; } else retval |= SECOND_BACK; /* might still not have properly detected order, so try this */ /* if ( !(retval & (FIRST_BACK|SECOND_BACK)) ) retval = separating_plane(ta,tb,0); */ return retval; } /* end plane_test() */ /************************************************************************ ************************************************************************ Visibility testing. Takes output of depth sort and deletes hidden elements. Uses sweep line (at random angle) to track topology. Much like Stuart Sechrest and Donald P. Greenberg, A Visible Polygon Reconstruction Algorithm, ACM Transactions on Graphics, vol 1, no 1, Jan 1982, pp 25-42. General strategy remarks: The image is broken down into individual polygon edges. Each edge is "above" or "below" a polygon. For now, each polygon is just a facet, but this could change if thick edges were added in the form of rectangles. The algorithm proceeds by moving a sweep line across the image, keeping track of "active edges" that intersect the sweep line. The active edge list is altered at "events": edge starts, edge ends, and edge crossings. The sweep line is tilted at an arbitrary angle to prevent degenerate vertices, except vertices coincident in projection. Pre-ordered event lists are kept for edge starts and ends, and a heap for upcoming crossings. Attached to each active edge is a list of layers of facets in the area immediately above it. *************************************************************************/ int visdebuglevel; #define VIS_TIMING 1 #define VIS_LAYERCHECK 2 #define VIS_EVENTDUMP 3 struct vis_conedge; struct vis_vertex; /* Heap for ordering upcoming events. */ int vis_heap_count; /* heap spots used */ int vis_heap_max; /* heap spots allocated */ struct vis_event *vis_heap; void vis_insert_heap ARGS((struct vis_event *)); void vis_delete_heap ARGS((int)); void find_next_event ARGS((struct vis_conedge *)); void find_next_event2 ARGS(( struct vis_conedge *, struct vis_conedge *)); void vis_crossing ARGS((struct vis_conedge *,struct vis_conedge *)); int vecount; /* number of edges in edge list */ int vis_crossing_count; /* just for info */ int add_layer ARGS((struct vis_conedge *, struct tsort *)); int delete_layer ARGS((struct vis_conedge *, struct tsort *)); void check_layers ARGS(( struct vis_conedge *, REAL , REAL)); int vvcomp ARGS(( struct vis_vertex *, struct vis_vertex *)); REAL activate_edge ARGS(( struct vis_conedge *)); void check_deactivate ARGS(( struct vis_conedge *)); /* Edge list */ #define MAXLAYERS 20 struct vis_rawedge { struct vis_vertex *v[2]; /* endpoints */ struct tsort *t; /* facet it borders */ struct vis_conedge *conedge; int flags; /* see below */ }; struct vis_rawedge *vis_rawedges; struct vis_rawedge **rawplist; /* pointers for sorting */ int vecomp ARGS((struct vis_rawedge **, struct vis_rawedge **)); /* vis_rawedge flag bits */ #define V_FACET_BOTTOM 1 #define V_FACET_TOP 2 #define V_FACET_LEFT 4 #define V_FACET_RIGHT 8 #define V_LAYER_CHECK 0x10 /* Consolidated vertices */ struct vis_vertex { REAL x[2]; /* u, v */ struct vis_vertex **fixup[2]; /* pre-cons reverse pointer */ }; struct vis_vertex *vis_vertices; int vis_vertex_max; int vis_vertex_count; /* Consolidated edges */ struct vis_conedge { struct vis_vertex *v[2]; /* endpoints */ REAL m; /* line slope */ int rawstart; /* associated raw edge start */ int rawend; /* last associated raw edge */ int use_count; /* times in use as boundary */ struct vis_conedge *prev_active; /* active list pointer */ struct vis_conedge *next_active; /* active list pointer */ int flags; /* see below */ int layers; /* number of layers above edge */ struct tsort **layer; /* facets "above" edge */ int maxlayers; /* allocated space */ int seqno; /* sequence number, for debugging */ }; struct vis_conedge *vis_conedges; int vis_conedge_max; int vis_conedge_count; /* Crossing event */ struct vis_event { REAL time; /* sweep time of event */ int type; struct vis_conedge *e1; struct vis_conedge *e2; struct tsort *t; }; int vis_event_comp ARGS((struct vis_event *, struct vis_event *)); /* Event types, ordered in way wanted in sorting */ #define V_FACET_END 1 #define V_FACET_TOPMIDDLE 2 #define V_FACET_BOTTOMMIDDLE 3 #define V_FACET_START 4 #define V_EDGE_CROSSING 5 int wrong_middles; /* for some debugging */ /* Margin to shorten edge ends, so don't get spurious crossings */ REAL veps = 1e-14; /* Active edge list */ struct vis_conedge *active_edge_first; struct vis_conedge sentinel; struct vis_vertex sentinelv[4]; /* for sentinel endpoints */ /* List of edges to check for top layer */ struct vis_conedge **check_list; int check_list_count; int check_list_max; void check_visible ARGS((REAL)); /* random tilt coefficients for sweep line */ REAL va = 0.8432848996472634; REAL vb = 0.5869487870825054; REAL sweep_u; /* current sweep position */ struct vis_event *facet_events; int f_event_count; int facet_start_event ARGS((struct vis_event *)); int facet_middle_event ARGS((struct vis_event *)); int facet_end_event ARGS((struct vis_event *)); /* For brute force verification */ int brute_force_flag = 1; void brute_force_times ARGS((void)); int brutecount; /* number of brute force times */ struct brute { REAL time; struct vis_conedge *e1,*e2; int type; } *brute_times; void brute_section ARGS((REAL)); void brute_visible ARGS((REAL)); int maxbrute; struct brute_cut { REAL v; /* height */ struct vis_edge *e; } *brute_cuts; int brute_cut_count; #ifdef PROFILING_ENABLED /* Profiling cycle counters */ __int32 visibility_stage_elapsed_time[2]; __int32 visibility_end_elapsed_time[2]; __int32 visibility_end1_elapsed_time[2]; __int32 visibility_end2_elapsed_time[2]; __int32 visibility_end3_elapsed_time[2]; __int32 visibility_end4_elapsed_time[2]; __int32 visibility_end5_elapsed_time[2]; __int32 vis_insert_heap_elapsed_time[2]; __int32 vis_delete_heap_elapsed_time[2]; __int32 vis_crossing_elapsed_time[2]; __int32 facet_start_event_elapsed_time[2]; __int32 facet_middle_event_elapsed_time[2]; __int32 facet_end_event_elapsed_time[2]; __int32 add_layer_elapsed_time[2]; __int32 delete_layer_elapsed_time[2]; __int32 check_visible_elapsed_time[2]; __int32 find_next_event_elapsed_time[2]; __int32 all_visibility_elapsed_time[2]; __int32 activate_edge_elapsed_time[2]; __int32 check_deactivate_elapsed_time[2]; #endif /************************************************************************ * * function: visibility_stage() * * purpose: Adds element to list for visibility testing. * Enabled by visibility_test toggle. * */ void visibility_stage(t) struct tsort *t; { PROF_START(all_visibility) PROF_START(visibility_stage) if ( !visibility_test ) { if ( (t->flag & 0xF) == FACET ) (*display_facet)(t); else (*display_edge)(t); return; } /* accumulate */ if ( vis_list == NULL ) { vis_max = maxcount; vis_list = (struct tsort *)temp_calloc(vis_max,sizeof(struct tsort)); } else if ( vis_count >= vis_max - 1 ) { vis_list = (struct tsort *)temp_realloc((char*)vis_list, 2*vis_max*sizeof(struct tsort)); vis_max *= 2; } vis_list[vis_count++] = *t; PROF_FINISH(visibility_stage) PROF_FINISH(all_visibility) } /* FOR DEBUGGING */ void active_list_check ARGS((void)); void active_list_check() { struct vis_conedge *e; for ( e = active_edge_first ; e != &sentinel ; e = e->next_active ) { if ( e->next_active->prev_active != e ) kb_error(2418,"Visibility edge active list bad.\n",RECOVERABLE); } } /************************************************************************ * * function: vvcomp() * * purpose: comparison of vertices, for consolidation * */ int vvcomp(a,b) struct vis_vertex *a,*b; { if ( a->x[0] < b->x[0] ) return -1; if ( a->x[0] > b->x[0] ) return 1; if ( a->x[1] < b->x[1] ) return -1; if ( a->x[1] > b->x[1] ) return 1; return 0; } /************************************************************************ * * function: vecomp() * * purpose: comparison of raw edges, for consolidation * */ int vecomp(a,b) struct vis_rawedge **a,**b; { if ( a[0]->v[0] < b[0]->v[0] ) return -1; if ( a[0]->v[0] > b[0]->v[0] ) return 1; if ( a[0]->v[1] < b[0]->v[1] ) return -1; if ( a[0]->v[1] > b[0]->v[1] ) return 1; return 0; } /************************************************************************ * * function: visibility_end() * * purpose: Run visibility algorithm after accumulation of data. * */ int debug_seq = 0; /* for debugging */ void visibility_end() { int k,i,ii,iii,j; struct tsort *t; struct vis_rawedge *ve; struct vis_conedge *vc; struct vis_vertex *vv; REAL next_u; struct vis_event *f_ev; int facet_event_spot; int vis_display_count; int tops,bottoms,lefts,rights; debug_seq = 0; PROF_START(all_visibility) PROF_START(visibility_end) PROF_START(visibility_end1) /* List of edges to check top facet for */ check_list_max = 1000; check_list = (struct vis_conedge **)temp_calloc(check_list_max, sizeof(struct vis_conedge *)); check_list_count = 0; /* Sorted list of facet starts, middle vertices, and ends */ facet_events = (struct vis_event *)temp_calloc(3*vis_count, sizeof(struct vis_event)); f_event_count = 0; f_ev = facet_events; /* Populate raw edge and vertex lists */ vis_vertex_max = 3*vis_count; vis_vertices = (struct vis_vertex *)temp_calloc(vis_vertex_max, sizeof(struct vis_vertex)); vv = vis_vertices; vis_vertex_count = 0; vis_rawedges = (struct vis_rawedge *)temp_calloc(3*vis_count, sizeof(struct vis_rawedge)); ve = vis_rawedges; vecount = 0; for ( k = 0, t = vis_list ; k < vis_count ; k++,t++ ) { if ( (t->flag & 0xF) == FACET ) { REAL minu = 1e30, maxu = -1e30; if ( t->color == CLEAR ) { t->flag |= VISIBLE; continue; } /* kludge for now */ /* rotate coordinates */ for ( i = 0 ; i < FACET_VERTS ; i++ ) { vv[i].x[0] = va*t->x[i][0] + vb*t->x[i][1]; if ( vv[i].x[0] < minu ) minu = vv[i].x[0]; if ( vv[i].x[0] > maxu ) maxu = vv[i].x[0]; vv[i].x[1] = -vb*t->x[i][0] + va*t->x[i][1]; } /* now, the edges */ tops = 0; bottoms = 0; lefts = 0; rights = 0; for ( i = 0 ; i < FACET_VERTS ; i++ ) { REAL area; ii = (i+1 >= FACET_VERTS) ? 0 : i+1; if ( vv[i].x[0] <= vv[ii].x[0] ) /* leftmost vertex first */ { ve->v[0] = vv+i; ve->v[1] = vv+ii; vv[i].fixup[0] = &ve->v[0]; /* so can adjust edges after sorting vertices */ vv[ii].fixup[1] = &ve->v[1]; } else { ve->v[0] = vv+ii; ve->v[1] = vv+i; vv[i].fixup[0] = &ve->v[1]; /* so can adjust edges after sorting vertices */ vv[ii].fixup[1] = &ve->v[0]; } iii = (ii+1 >= FACET_VERTS) ? 0 : ii+1; /* third vertex */ area = (ve->v[1]->x[0]-ve->v[0]->x[0])*(vv[iii].x[1]-ve->v[0]->x[1]) -(vv[iii].x[0]-ve->v[0]->x[0])*(ve->v[1]->x[1]-ve->v[0]->x[1]); if ( fabs(area) < 1e-14 ) break; if ( area > 0 ) { ve->flags |= V_FACET_BOTTOM; bottoms++; } else { ve->flags |= V_FACET_TOP; tops++; } if ( ve->v[0]->x[0] == minu ) { ve->flags |= V_FACET_LEFT; lefts++; } if ( ve->v[1]->x[0] == maxu ) { ve->flags |= V_FACET_RIGHT; rights++; } ve->t = t; ve++; vecount++; } /* check we successfully found things, and skip edge-on facets */ if ( (tops==0) || (bottoms==0) || (lefts!=2) || (rights!=2) ) continue; /* facet start event */ f_ev->type = V_FACET_START; f_ev->time = minu; f_ev->t = t; for ( i = -FACET_EDGES ; i < 0 ; i++ ) { if ( ve[i].flags & V_FACET_LEFT ) { if ( ve[i].flags & V_FACET_BOTTOM ) f_ev->e1 = (struct vis_conedge*)(ve + i); else f_ev->e2 = (struct vis_conedge*)(ve + i); } } if ( !f_ev->e1 || !f_ev->e2 ) { f_ev->e1 = f_ev->e2 = NULL; continue; } /* skip edge-on */ f_ev++; f_event_count++; /* facet middle event */ f_ev->t = t; for ( i = -FACET_EDGES ; i < 0 ; i++ ) { if ( ve[i].flags & V_FACET_LEFT ) { if ( !(ve[i].flags & V_FACET_RIGHT) ) { f_ev->e1 = (struct vis_conedge*)(ve + i); f_ev->time = ve[i].v[1]->x[0]; } } if ( ve[i].flags & V_FACET_RIGHT ) { if ( !(ve[i].flags & V_FACET_LEFT) ) f_ev->e2 = (struct vis_conedge*)(ve + i); } } if ( !f_ev->e1 || !f_ev->e2 ) /* skip edge-on */ { f_ev->e1 = f_ev->e2 = NULL; f_ev--; f_event_count--; continue; } if ( ((struct vis_rawedge *)f_ev->e1)->flags & V_FACET_BOTTOM ) f_ev->type = V_FACET_BOTTOMMIDDLE; else f_ev->type = V_FACET_TOPMIDDLE; f_ev++; f_event_count++; /* facet end event */ f_ev->type = V_FACET_END; f_ev->time = maxu; f_ev->t = t; for ( i = -FACET_EDGES ; i < 0 ; i++ ) { if ( ve[i].flags & V_FACET_RIGHT ) { if ( ve[i].flags & V_FACET_BOTTOM ) f_ev->e1 = (struct vis_conedge*)(ve + i); else f_ev->e2 = (struct vis_conedge*)(ve + i); } } if ( !f_ev->e1 || !f_ev->e2 ) /* skip edge-on */ { f_ev->e1 = f_ev->e2 = NULL; f_ev-=2; f_event_count-=2; continue; } f_ev++; f_event_count++; vv += FACET_VERTS; vis_vertex_count += FACET_VERTS; } else /* lone edge */ { /* for now, make all lone edges visible */ t->flag |= VISIBLE; } } PROF_FINISH(visibility_end1) if ( f_event_count == 0 ) goto draw_visible; PROF_START(visibility_end2) /* Consolidation of vertices and edges */ /* First, consolidation of vertices */ qsort(vis_vertices,vis_vertex_count,sizeof(struct vis_vertex),FCAST vvcomp); for ( i = -1, j = 0 ; j < vis_vertex_count ; j++ ) { int m; if ( (i < 0) || (vvcomp(vis_vertices+i,vis_vertices+j) != 0) ) { vis_vertices[++i] = vis_vertices[j]; } /* use reverse pointers to fix up edges */ for ( m = 0 ; m < 2 ; m++ ) *(vis_vertices[j].fixup[m]) = vis_vertices+i; } vis_vertex_count = i+1; PROF_FINISH(visibility_end2) /* Next, consolidation of edges. Use intermediate list of pointers, since events point to raw edges. */ PROF_START(visibility_end3) rawplist = (struct vis_rawedge **)temp_calloc(vecount, sizeof(struct vis_rawedge *)); for ( i = 0 ; i < vecount ; i++ ) rawplist[i] = vis_rawedges+i; qsort(rawplist,vecount,sizeof(struct vis_rawedge*),FCAST vecomp); vis_conedge_max = vecount; vis_conedges = (struct vis_conedge *) temp_calloc(vis_conedge_max, sizeof(struct vis_conedge) ); vis_conedges[0].v[0] = rawplist[0]->v[0]; vis_conedges[0].v[1] = rawplist[0]->v[1]; vis_conedges[0].rawstart = 0; vis_conedges[0].rawend = 0; rawplist[0]->conedge = vis_conedges; for ( i = 0, j = 1 ; j < vecount ; j++ ) { if ( (vis_conedges[i].v[0] != rawplist[j]->v[0]) || (vis_conedges[i].v[1] != rawplist[j]->v[1]) ) { i++; vis_conedges[i].v[0] = rawplist[j]->v[0]; vis_conedges[i].v[1] = rawplist[j]->v[1]; vis_conedges[i].rawstart = j; vis_conedges[i].rawend = j; } else vis_conedges[i].rawend++; rawplist[j]->conedge = vis_conedges+i; } i++; vis_conedges = (struct vis_conedge*)temp_realloc((char*)vis_conedges, i*sizeof(struct vis_conedge)); vis_conedge_max = vis_conedge_count = i; PROF_FINISH(visibility_end3) PROF_START(visibility_end4) /* Initialize line coefficients */ for ( i = 0, vc = vis_conedges; i < vis_conedge_count; i++, vc++ ) { REAL du = vc->v[1]->x[0] - vc->v[0]->x[0]; REAL dv = vc->v[1]->x[1] - vc->v[0]->x[1]; vc->m = dv/du; } /* change facet events over to consolidated edges */ for ( i = 0 ; i < f_event_count ; i++ ) { facet_events[i].e1 = ((struct vis_rawedge*)(facet_events[i].e1))->conedge; facet_events[i].e2 = ((struct vis_rawedge*)(facet_events[i].e2))->conedge; } PROF_FINISH(visibility_end4) /* and for later use */ brute_cuts=(struct brute_cut *)temp_calloc(vecount,sizeof(struct brute_cut)); #ifdef BRUTE /* do brute-force list of all event times */ brute_force_times(); for ( i = 0 ; i < brutecount-1 ; i++ ) { if ( brute_times[i+1].time-brute_times[i].time > 1e-8 ) brute_visible((brute_times[i].time+brute_times[i+1].time)/2); } #endif #define FINESSE #ifdef FINESSE /* sort facet event list */ qsort((char*)facet_events,f_event_count,sizeof(struct vis_event), FCAST vis_event_comp); /* Initialize crossing event heap with sentinel */ vis_heap_max = vecount > 100 ? vecount : 100; vis_heap = (struct vis_event *)temp_calloc(vis_heap_max, sizeof(struct vis_event)); vis_heap[0].time = 1e30; vis_heap_count = 1; vis_crossing_count = 0; /* Initialize active list */ sentinel.v[0] = sentinelv; sentinel.v[1] = sentinelv+1; sentinelv[0].x[0] = -1e20; sentinelv[0].x[1] = 1e20; sentinelv[1].x[0] = 1e20; sentinelv[1].x[1] = 1e20; sentinel.m = 0; sentinel.prev_active = NULL; sentinel.next_active = NULL; active_edge_first = &sentinel; PROF_START(visibility_end5) /* Sweep */ facet_event_spot = 0; while ( facet_event_spot < f_event_count ) { struct vis_event *fe = facet_events + facet_event_spot; int retval; /* return code from event handlers; < 0 for error */ /* print current active list and event heap */ if ( visdebuglevel >= VIS_EVENTDUMP ) { struct vis_conedge *es; if ( active_edge_first->prev_active != NULL ) printf("(%d<-) ",active_edge_first->prev_active-vis_conedges); for ( es = active_edge_first ; es != &sentinel ; es = es->next_active ) { if ( es->next_active->prev_active != es ) printf("(%d<-) ",es->next_active->prev_active-vis_conedges); printf("%d ",es-vis_conedges); } for ( i = 0 ; i < vis_heap_count ; i++ ) if ( vis_heap[i].time > 1e20 ) printf("(sentinel) "); else printf("(%d %d %f)",vis_heap[i].e1-vis_conedges,vis_heap[i].e2-vis_conedges, vis_heap[i].time); printf("\n"); } /* end debug */ /* find which is next event and process */ if ( (vis_heap_count <= 0) || (fe->time < vis_heap[0].time) ) { /* do facet event */ next_u = fe->time; PROF_FINISH(visibility_end) if ( (next_u - sweep_u) > 1e-10 ) check_visible((next_u+sweep_u)/2); sweep_u = next_u; switch ( fe->type ) { case V_FACET_START: retval = facet_start_event(fe); if ( retval < 0 ) goto bail_out; break; case V_FACET_TOPMIDDLE: retval = facet_middle_event(fe); if ( retval < 0 ) goto bail_out; break; case V_FACET_BOTTOMMIDDLE: retval = facet_middle_event(fe); if ( retval < 0 ) goto bail_out; break; case V_FACET_END: retval = facet_end_event(fe); if ( retval < 0 ) goto bail_out; break; } PROF_START(visibility_end) facet_event_spot++; continue; } else /* do crossing event */ { struct vis_conedge *e1,*e2; next_u = vis_heap[0].time; PROF_FINISH(visibility_end) if ( (next_u - sweep_u) > 1e-10 ) check_visible((next_u+sweep_u)/2); e1 = vis_heap[0].e1; e2 = vis_heap[0].e2; vis_delete_heap(0); if ( visdebuglevel >= VIS_EVENTDUMP ) printf("crossing %d %d at %20.15f\n",e1-vis_conedges,e2-vis_conedges,(double)next_u); sweep_u = next_u; vis_crossing(e1,e2); PROF_START(visibility_end) continue; } } #endif goto draw_visible; bail_out: /* error handling: mark all as visible */ erroutstring("Abandoning visibility test and drawing all facets.\n"); for ( k = 0, t = vis_list, vis_display_count = 0 ; k < vis_count ; k++,t++ ) t->flag |= VISIBLE; draw_visible: PROF_FINISH(visibility_end5) PROF_FINISH(visibility_end) PROF_FINISH(all_visibility) /* Display elements marked visible */ for ( k = 0, t = vis_list, vis_display_count = 0 ; k < vis_count ; k++,t++ ) if ( t->flag & VISIBLE ) { if ( (t->flag & 0xF) == FACET ) (*display_facet)(t); else (*display_edge)(t); vis_display_count++; } temp_free((char*)vis_heap); temp_free((char*)vis_conedges); temp_free((char*)vis_rawedges); temp_free((char*)vis_vertices); temp_free((char*)check_list); temp_free((char*)rawplist); temp_free((char*)vis_list); temp_free((char*)facet_events); temp_free((char*)brute_cuts); if (visdebuglevel >= VIS_TIMING) { fprintf(stderr,"Visible: %d facets out of %d\n",vis_display_count,vis_count); fprintf(stderr,"Crossing count: %d\n",vis_crossing_count); fprintf(stderr,"Wrong middles: %d\n",wrong_middles); wrong_middles = 0; #ifdef MSC /* print out profile times, only for Visual Studio */ printf("CPU clock cycles in various visibility routines:\n"); PROF_PRINT(all_visibility) PROF_PRINT(visibility_stage) PROF_PRINT(visibility_end) PROF_PRINT(visibility_end1) PROF_PRINT(visibility_end2) PROF_PRINT(visibility_end3) PROF_PRINT(visibility_end4) PROF_PRINT(visibility_end5) PROF_PRINT(check_visible) PROF_PRINT(vis_crossing) PROF_PRINT(facet_start_event) PROF_PRINT(facet_middle_event) PROF_PRINT(facet_end_event) PROF_PRINT(vis_insert_heap) PROF_PRINT(vis_delete_heap) PROF_PRINT(find_next_event) PROF_PRINT(add_layer) PROF_PRINT(delete_layer) PROF_PRINT(activate_edge) PROF_PRINT(check_deactivate) #endif } } /************************************************************************ * * function: vis_event_comp() * * purpose: compare times of two events. Sorts on time, then type, * then average slope, then edges. */ int vis_event_comp(a,b) struct vis_event *a, *b; { REAL ma,mb; if ( a->time < b->time ) return -1; if ( a->time > b->time ) return 1; if ( a->type < b->type ) return -1; if ( a->type > b->type ) return 1; ma = (a->e1->m+a->e2->m); mb = (b->e1->m+b->e2->m); if ( ma < mb ) return -1; if ( ma > mb ) return 1; if ( a->e1 < b->e1 ) return -1; if ( a->e1 > b->e1 ) return 1; if ( a->e2 < b->e2 ) return -1; if ( a->e2 > b->e2 ) return 1; return 0; } /************************************************************************ * * function: vis_insert_heap() * * purpose: Add edge event to heap list. Not detecting duplicates; * leaving that to validity test when crossing is handled. * */ void vis_insert_heap(e) struct vis_event *e; { int k,kk; int result; PROF_START(vis_insert_heap) if ( vis_heap_count >= vis_heap_max-1 ) { vis_heap = (struct vis_event *)kb_realloc((char*)vis_heap, 2*vis_heap_max*sizeof(struct vis_event)); vis_heap_max *= 2; } for ( k = vis_heap_count ; k > 0 ; k = kk ) { kk = (k-1)/2; result = vis_event_comp(e,&vis_heap[kk]); if ( result < 0 ) { vis_heap[k] = vis_heap[kk]; } else break; } vis_heap[k] = *e; vis_heap_count++; PROF_FINISH(vis_insert_heap) } /*************************************************************************** * * function: vis_delete_heap() * * purpose: Delete element n of heap and adjust heap. */ void vis_delete_heap(n) int n; { int k,kk; int result; struct vis_event e; PROF_START(vis_delete_heap) if ( n == vis_heap_count-1 ) { vis_heap_count--; goto vis_delete_heap_exit; } if ( vis_heap_count == 1 ) kb_error(2421,"vis_delete_heap trying to delete sentinel.\n",RECOVERABLE); /* replace with top event */ e = vis_heap[--vis_heap_count]; /* check direction to percolate */ result = (n==0) ? 1 : vis_event_comp(&e,vis_heap+(n-1)/2); if ( result < 0 ) { /* downward */ k = n; kk = (n-1)/2; vis_heap[k] = vis_heap[kk]; for ( k = kk ; k > 0 ; k = kk ) { kk = (k-1)/2; result = vis_event_comp(&e,vis_heap+kk); if ( result < 0 ) { vis_heap[k] = vis_heap[kk]; continue; } else { break; } } vis_heap[k] = e; goto vis_delete_heap_exit; } else { /* upward */ for ( k = n; 2*k+1 < vis_heap_count ; k = kk ) { if ( 2*k+2 >= vis_heap_count ) { /* only one parent */ kk = 2*k+1; result = vis_event_comp(&e,vis_heap+kk); if ( result > 0 ) { vis_heap[k] = vis_heap[kk]; vis_heap[kk] = e; goto vis_delete_heap_exit; } else { vis_heap[k] = e; goto vis_delete_heap_exit; } } else { /* two parents */ result = vis_event_comp(&vis_heap[2*k+1],&vis_heap[2*k+2]); kk = (result < 0) ? 2*k+1 : 2*k+2; result = vis_event_comp(&e,vis_heap+kk); if ( result < 0 ) { /* done */ vis_heap[k] = e; goto vis_delete_heap_exit; } else if ( result > 0 ) { /* keep going up */ vis_heap[k] = vis_heap[kk]; continue; } } } vis_heap[k] = e; /* in case at top of heap */ } vis_delete_heap_exit: ; PROF_FINISH(vis_delete_heap) } /**************************************************************************** * * function: add_layer() * * purpose: Add a facet to the layers above an active edge. * * return value: 1 if added, 0 if already there. */ int add_layer(ee,f) struct vis_conedge *ee; struct tsort *f; { int i; int retval; PROF_START(add_layer) for ( i = 0 ; i < ee->layers ; i++ ) if ( ee->layer[i] == f ) break; if ( i == ee->maxlayers ) { int newcount = (ee->maxlayers > 10) ? 2*ee->maxlayers : ee->maxlayers + 10; ee->layer = (struct tsort **)temp_realloc((char*)(ee->layer), newcount*sizeof(struct tsort*)); ee->maxlayers = newcount; } if ( i == ee->layers ) { /* not already found in layer list */ ee->layer[ee->layers++] = f; if ( visdebuglevel >= VIS_EVENTDUMP ) fprintf(stderr,"Adding facet %d to edge %d layers.\n", f-vis_list,ee-vis_conedges); if ( !(ee->flags & V_LAYER_CHECK) ) { if ( check_list_count >= check_list_max ) { check_list = (struct vis_conedge **)temp_realloc((char*)check_list, 2*check_list_max*sizeof(struct vis_conedge *)); check_list_max *= 2; } check_list[check_list_count++] = ee; ee->flags |= V_LAYER_CHECK; } retval = 1; } else retval = 0; PROF_FINISH(add_layer) return retval; } /**************************************************************************** * * function: delete_layer() * * purpose: Delete a facet from the layers above an active edge. * Not keeping remaining facets in depth order. * * return value: 1 if found, 0 if not. */ int delete_layer(ee,f) struct vis_conedge *ee; struct tsort *f; { int i; int retval = 0; PROF_START(delete_layer) for ( i = 0 ; i < ee->layers ; i++ ) if ( ee->layer[i] == f ) { ee->layer[i] = ee->layer[--ee->layers]; if ( visdebuglevel >= VIS_EVENTDUMP ) fprintf(stderr,"Deleting facet %d from edge %d layers.\n", f-vis_list,ee-vis_conedges); if ( !(ee->flags & V_LAYER_CHECK) ) { if ( check_list_count >= check_list_max ) { check_list = (struct vis_conedge **)temp_realloc((char*)check_list, 2*check_list_max*sizeof(struct vis_conedge *)); check_list_max *= 2; } check_list[check_list_count++] = ee; ee->flags |= V_LAYER_CHECK; } retval = 1; break; } PROF_FINISH(delete_layer) return retval; } /************************************************************************* * * function: find_next_event() * * purpose: Check for upcoming crossing event. Doesn't count crossing * near end of edge. Need to do very robust crossing * calculation, so not to be fooled by numerical glitches. * */ void find_next_event(e) struct vis_conedge *e; { struct vis_event ev; PROF_START(find_next_event) ev.time = 1e30; /* Forward */ if ( (e->m > e->next_active->m ) ) { REAL u; u = e->v[0]->x[0] + (e->v[0]->x[1]-e->next_active->v[0]->x[1] + e->next_active->m*(e->next_active->v[0]->x[0]-e->v[0]->x[0]))/ (e->next_active->m - e->m); if ( (u > e->v[0]->x[0]-1e-8) && (u < ev.time) && (u < e->v[1]->x[0] - 1e-10) && (u < e->next_active->v[1]->x[0] - 1e-10) ) { ev.e1 = e; ev.e2 = e->next_active; ev.time = u; } } /* Backward */ if ( e->prev_active && (e->m < e->prev_active->m)) { REAL u; u = e->v[0]->x[0] + (e->v[0]->x[1]-e->prev_active->v[0]->x[1] + e->prev_active->m*(e->prev_active->v[0]->x[0]-e->v[0]->x[0]))/ (e->prev_active->m - e->m); if ( (u > e->v[0]->x[0]-1e-8) && (u < ev.time) && (u < e->v[1]->x[0] - 1e-10) && (u < e->prev_active->v[1]->x[0] - 1e-10) ) { ev.e1 = e->prev_active; ev.e2 = e; ev.time = u; } } PROF_FINISH(find_next_event) if ( ev.time < 1e20 ) { if ( visdebuglevel >= VIS_EVENTDUMP ) printf("next crossing %d %d at %20.15f\n", ev.e1-vis_conedges,ev.e2-vis_conedges, (double)ev.time); vis_insert_heap(&ev); } } /************************************************************************* * * function: find_next_event2() * * purpose: Check for upcoming crossing event between two given edges. * Doesn't count crossing * near end of edge. Need to do very robust crossing * calculation, so not to be fooled by numerical glitches. * */ void find_next_event2(e,ee) struct vis_conedge *e; struct vis_conedge *ee; { struct vis_event ev; if ( !e || !ee ) return; ev.time = 1e30; /* Forward */ if ( (e->m > ee->m ) ) { REAL u; u = e->v[0]->x[0] + (e->v[0]->x[1]-e->next_active->v[0]->x[1] + e->next_active->m*(e->next_active->v[0]->x[0]-e->v[0]->x[0]))/ (e->next_active->m - e->m); if ( (u > e->v[0]->x[0]-1e-8) && (u < ev.time) && (u < e->v[1]->x[0] - 1e-10) && (u < e->next_active->v[1]->x[0] - 1e-10) ) { ev.e1 = e; ev.e2 = e->next_active; ev.time = u; } } if ( ev.time < 1e20 ) { if ( visdebuglevel >= VIS_EVENTDUMP ) printf("next crossing %d %d at %20.15f\n", ev.e1-vis_conedges,ev.e2-vis_conedges, (double)ev.time); vis_insert_heap(&ev); } } #ifdef XXXXXX /* for debugging */ /************************************************************************** * * function: dump_vislist() * * purpose: dump edge list in case of error. */ void dump_vislist() { struct vis_conedge *e; REAL v; int i; fprintf(stderr,"Visibility edge list debug dump at u = %18.15f; debug_seq %d\n", sweep_u,debug_seq); for ( e = active_edge_first ; e != &sentinel ; e = e->next_active ) { if ( e == NULL ) { fprintf(stderr,"NULL next_active.\n"); break; } v = e->m*(sweep_u-e->v[0]->x[0]) + e->v[0]->x[1]; fprintf(stderr,"%3d %5d v: %18.15f layers:",e->seqno,e-vis_conedges,v); for ( i = 0 ; i < e->layers ; i++ ) fprintf(stderr," %3d",ordinal(e->layer[i]->f_id)+1); fprintf(stderr,"\n"); } fprintf(stderr,"\n"); } void check_vislist() /* for v in ascending sequence */ { struct vis_conedge *e; REAL v,prev = -1e30; for ( e = active_edge_first ; e != &sentinel ; e = e->next_active ) { if ( e == NULL ) { fprintf(stderr,"NULL next_active.\n"); break; } v = e->m*(sweep_u-e->v[0]->x[0]) + e->v[0]->x[1]; if ( prev > v+1e-5 ) { dump_vislist(); printf("Bad vislist at debug_seq %d\n",debug_seq); } prev = v; } } #endif /*************************************************************************** * * function: activate_edge() * * purpose: add edge to active edge list, if it is not there. * Returns insertion coordinate for debugging. * Does linear search, so could be made more efficient. * */ REAL vis_eps = 1e-13; /* for equality detection */ REAL activate_edge(e) struct vis_conedge *e; { struct vis_conedge *spot; REAL v,vprev; int seq = 0; v = e->m*(sweep_u-e->v[0]->x[0]) + e->v[0]->x[1]; if ( e->next_active ) return v; /* already active */ PROF_START(activate_edge) vprev = -1e30; for ( spot = active_edge_first ; spot != NULL ; spot = spot->next_active ) { REAL spotv = spot->m*(sweep_u-spot->v[0]->x[0]) + spot->v[0]->x[1]; if ( spotv < vprev-1e-5 ) { sprintf(errmsg,"Internal error: visibility list out of order by %f.\n", vprev-spotv); kb_error(3509,errmsg,WARNING); } vprev = spotv; /* debugging */ spot->seqno = seq++; if ( spotv > v+vis_eps ) break; if ( (spotv > v-vis_eps) && (spot->m > e->m) ) break; } vprev = v; /* Have now located insertion spot; "spot" comes after e */ if ( active_edge_first == spot ) active_edge_first = e; if ( spot->prev_active ) spot->prev_active->next_active = e; e->prev_active = spot->prev_active; spot->prev_active = e; e->next_active = spot; if ( e->prev_active ) { int i; e->layer = (struct tsort**)temp_calloc( e->prev_active->layers+4, sizeof(struct tsort*)); e->maxlayers = e->prev_active->layers+4; for ( i = 0 ; i < e->prev_active->layers ; i++ ) { add_layer(e,e->prev_active->layer[i]); } } else { e->layer = (struct tsort**)temp_calloc(10, sizeof(struct tsort*)); e->maxlayers = 10; } /* update sequence numbers; time waster, but we're doing linear search anyway. */ for ( spot = e ; spot != NULL ; spot = spot->next_active ) { REAL spotv = spot->m*(sweep_u-spot->v[0]->x[0]) + spot->v[0]->x[1]; if ( spotv < vprev-1e-5 ) { sprintf(errmsg,"Internal error: visibility list out of order by %f.\n", vprev-spotv); kb_error(2509,errmsg,WARNING); } vprev = spotv; /* debugging */ spot->seqno = seq++; } PROF_FINISH(activate_edge) return v; } /***************************************************************************** * * function: check_deactivate() * * purpose: see if edge can be deleted from active list, since no longer * separating facets. */ void check_deactivate(e) struct vis_conedge *e; { if ( e->use_count != 0 ) return; PROF_START(check_deactivate) if ( e->prev_active ) e->prev_active->next_active = e->next_active; else active_edge_first = e->next_active; e->next_active->prev_active = e->prev_active; find_next_event2(e->prev_active,e->next_active); e->next_active = e->prev_active = NULL; temp_free((char*)e->layer); e->layer = NULL; PROF_FINISH(check_deactivate) } /*************************************************************************** * * function: facet_start_event() * * purpose: handle starts of two edges of facet. * * return: -1 if error, 1 if ok. */ int facet_start_event(fe) struct vis_event *fe; { REAL v1,v2; /* for some debugging */ struct vis_conedge *spot; if ( visdebuglevel >= VIS_EVENTDUMP ) printf("start edges %d %d facet %d at %20.15f\n", fe->e1-vis_conedges,fe->e2-vis_conedges, fe->t-vis_list, (double)sweep_u); PROF_START(facet_start_event) sweep_u = fe->time; v1 = activate_edge(fe->e1); fe->e1->use_count++; v2 = activate_edge(fe->e2); fe->e2->use_count++; if ( v2 < v1 ) { kb_error(2505,"Internal: Visibility list insertion out of order.\n", WARNING); return -1; } if ( fe->e1->seqno > fe->e2->seqno ) { kb_error(2508,"Internal: Visibility list insertion out of order.\n", WARNING); return -1; } /* add this facet to layers */ for ( spot = fe->e1 ; spot != fe->e2 ; spot = spot->next_active ) { if ( spot == NULL ) { kb_error(2506,"Internal: Visibility list bad in facet_start_event().\n", WARNING); return -1; } add_layer(spot,fe->t); } PROF_FINISH(facet_start_event) find_next_event(fe->e1); find_next_event(fe->e2); return 1; } /* end facet_start_event() */ /*************************************************************************** * * function: facet_middle_event() * * purpose: handle edge transition in middle of facet * * return: -1 for error, 1 for ok. */ int facet_middle_event(fe) struct vis_event *fe; { struct vis_conedge *e; if ( visdebuglevel >= VIS_EVENTDUMP ) printf("middle edges %d %d facet %d at %20.15f\n", fe->e1-vis_conedges,fe->e2-vis_conedges, fe->t-vis_list,(double)sweep_u); PROF_START(facet_middle_event) sweep_u = fe->time; activate_edge(fe->e2); fe->e2->use_count++; if ( fe->type == V_FACET_TOPMIDDLE ) { if ( delete_layer(fe->e2,fe->t) ) { /* second edge got put in the right way */ for ( e = fe->e2->next_active ; e != fe->e1 ; e = e->next_active ) { if ( e == NULL ) { kb_error(2575, "Internal: Visibility list bad in facet_middle_event().\n", WARNING); return -1; } delete_layer(e,fe->t); } } else /* got inserted above old edge */ { for ( e = fe->e1; e != fe->e2 ; e = e->next_active ) { if ( e == NULL ) { kb_error(2507, "Internal: Visibility list bad in facet_middle_event(). \n", WARNING); return -1; } add_layer(e,fe->t); } wrong_middles++; } } else /* bottom middle */ { if ( add_layer(fe->e2,fe->t) ) { /* new edge snuck in below old */ for ( e = fe->e2->next_active ; e != fe->e1 ; e = e->next_active ) add_layer(e,fe->t); wrong_middles++; } else /* new edge got in above old */ { for ( e = fe->e1 ; e != fe->e2 ; e = e->next_active ) delete_layer(e,fe->t); } } fe->e1->use_count--; check_deactivate(fe->e1); PROF_FINISH(facet_middle_event) /* let event crossings take care of place in active list */ find_next_event(fe->e2); return 1; } /* end facet_middle_event() */ /*************************************************************************** * * function: facet_end_event() * * purpose: handle deletion of active edges at end of facet * * return: -1 for error, 1 for ok. */ int facet_end_event(fe) struct vis_event *fe; { struct vis_conedge *spot; if ( visdebuglevel >= VIS_EVENTDUMP ) printf("end edges %d %d facet %d at %20.15f\n", fe->e1-vis_conedges,fe->e2-vis_conedges, fe->t-vis_list,(double)sweep_u); for ( spot = fe->e1 ; spot != fe->e2 ; spot = spot->next_active ) { if ( spot == NULL ) { struct vis_conedge *e; /* Oops. Something went wrong, so delete facet from entire list */ active_list_check(); for ( e = active_edge_first ; e != &sentinel ; e = e->next_active ) delete_layer(e,fe->t); break; } else delete_layer(spot,fe->t); } PROF_START(facet_end_event) sweep_u = fe->time; fe->e1->use_count--; check_deactivate(fe->e1); fe->e2->use_count--; check_deactivate(fe->e2); PROF_FINISH(facet_end_event) return 1; } /* end facet_end_event() */ /*************************************************************************** * * function: vis_crossing() * * purpose: Handle crossing of two edges. Have to check to be sure * switching order is needed, since crossing events can be * listed twice. * */ void vis_crossing(ea,eb) struct vis_conedge *ea,*eb; /* ea below eb */ { struct vis_conedge *te; struct vis_rawedge *ra,*rb; int i; if ( ea->next_active != eb ) goto vis_crossing_exit; /* not adjacent */ if ( ea != eb->prev_active ) kb_error(2547,"Inconsistent active list.\n",RECOVERABLE); vis_crossing_count++; /* fix up layer lists, using info back in raw edge list */ for ( i = ea->rawstart; i <= ea->rawend ; i++ ) { ra = rawplist[i]; if ( ra->flags & V_FACET_BOTTOM ) delete_layer(eb,ra->t); if ( ra->flags & V_FACET_TOP ) add_layer(eb,ra->t); } for ( i = eb->rawstart; i <= eb->rawend ; i++ ) { rb = rawplist[i]; if ( rb->flags & V_FACET_BOTTOM ) add_layer(ea,rb->t); if ( rb->flags & V_FACET_TOP ) delete_layer(ea,rb->t); } PROF_START(vis_crossing) /* switch order in active list */ eb->next_active->prev_active = ea; if ( ea->prev_active ) ea->prev_active->next_active = eb; else active_edge_first = eb; te = ea->prev_active; ea->prev_active = eb; eb->prev_active = te; te = eb->next_active; eb->next_active = ea; ea->next_active = te; PROF_FINISH(vis_crossing) /* Test for next events */ find_next_event(ea); find_next_event(eb); vis_crossing_exit: ; } /* check_layers() - Brute force check of facet layers at point. */ void check_layers(e,u,v) struct vis_conedge *e; REAL u,v; { int i,j,jj,k; int found=0; REAL *x[3]; REAL orientation,d; for ( i = 0 ; i < f_event_count ; i++ ) { struct vis_event *fe = facet_events + i; if ( (fe->type != V_FACET_TOPMIDDLE) && (fe->type != V_FACET_BOTTOMMIDDLE)) continue; x[0] = fe->e1->v[0]->x; x[1] = fe->e1->v[1]->x; x[2] = fe->e2->v[1]->x; orientation = (x[1][0]-x[0][0])*(x[2][1]-x[0][1]) - (x[1][1]-x[0][1])*(x[2][0]-x[0][0]); for ( j = 0 ; j < 3 ; j++ ) { jj = (j==2) ? 0 : j+1; d = (x[jj][0]-x[j][0])*(v-x[j][1]) - (x[jj][1]-x[j][1])*(u-x[j][0]); if ( d*orientation < 0.0 ) break; } if ( j < 3 ) continue; /* not all on proper side */ /* now check layers */ for ( k = 0 ; k < e->layers ; k++ ) if ( fe->t == e->layer[k] ) { found++; break; } if ( k == e->layers ) { fprintf(stderr,"Edge %d missing facet %d at (%f,%f).\n",e-vis_conedges, fe->t - vis_list,u,v); } } /* report */ if ( found < e->layers ) fprintf(stderr,"Edge %d extra %d layers(facet %d) at (%f,%f).\n", e-vis_conedges,e->layers-found,e->layer[0]-vis_list,u,v); } /************************************************************************* * * function: check_visible() * * purpose: Inspect layers of changed edge stacks and mark topmost facets. * Uses facet order from painter algorithm rather than * calculating local z, since that is subject to problems with * abutting facets. * */ void check_visible(u) REAL u; /* sweep time to check */ { REAL v,vv; /* height on sweep line of edge and next */ int i,j; PROF_START(check_visible) for ( i = 0 ; i < check_list_count ; i++ ) { struct vis_conedge *e = check_list[i]; struct tsort *topf; e->flags &= ~V_LAYER_CHECK; if ( e->next_active == NULL ) continue; v = e->m*(u-e->v[0]->x[0]) + e->v[0]->x[1]; vv = e->next_active->m*(u-e->next_active->v[0]->x[0]) + e->next_active->v[0]->x[1]; if ( fabs(v-vv) < 1e-10 ) continue; /* ignore cracks */ v = (v+vv)/2; if ( visdebuglevel >= VIS_LAYERCHECK ) check_layers(e,u,v); /* debugging */ topf = NULL; for ( j = 0 ; j < e->layers ; j++ ) { struct tsort *f = e->layer[j]; if ( f > topf ) /* using painter facet order */ { topf = f; } } if ( topf ) { topf->flag |= VISIBLE; if ( visdebuglevel >= VIS_EVENTDUMP ) printf("Marking facet %d visible.\n",topf-vis_list); } } check_list_count = 0; PROF_FINISH(check_visible) } #ifdef BRUTE /************************************************************************* Brute force visibility. **************************************************************************/ /************************************************************************* * * function: brutecomp * * purpose: comparison function for sorting brute force event times. */ int brutecomp (const void *a, const void *b) { struct brute *aa = (struct brute *)a; struct brute *bb = (struct brute *)b; if ( aa->time < bb->time ) return -1; if ( aa->time > bb->time ) return 1; if ( aa->e1 < bb->e1 ) return -1; if ( aa->e1 > bb->e1 ) return 1; if ( aa->e2 < bb->e2 ) return -1; if ( aa->e2 > bb->e2 ) return 1; return 0; } /************************************************************************* * * function: brutecut_comp * * purpose: comparison function for sorting brute force cut heights. */ int brute_cut_comp (const void *a, const void *b) { struct brute_cut *aa = (struct brute_cut *)a; struct brute_cut *bb = (struct brute_cut *)b; if ( aa->v < bb->v ) return -1; if ( aa->v > bb->v ) return 1; if ( aa->e < bb->e ) return -1; if ( aa->e > bb->e ) return 1; return 0; } /*************************************************************************** * * function: brute_section * * purpose: Find all edge intersections at a particular sweep position * by brute force, and order by height. */ void brute_section(u) REAL u; /* sweep position */ { struct brute_cut *b; int i; struct vis_edge *e; brute_cut_count = 0; for ( i = 0 ; i < vecount ; i++ ) { e = vis_edges + i; if ( e->x[0][0] > u ) continue; if ( e->x[1][0] < u ) continue; b = brute_cuts + brute_cut_count++; b->v = e->m*(u-e->x[0][0]) + e->x[0][1]; b->e = e; } qsort(brute_cuts,brute_cut_count,sizeof(struct brute_cut),FCAST brute_cut_comp); } /************************************************************************* * * function: brute_visible() * * purpose: Mark as visible those facets along sweep line, using edge * order found by brute_section(). * */ struct vis_facet *brute_column[100]; /* list of facets at spot */ void brute_visible(u) REAL u; /* sweep line position */ { int i,j; int depth = 0; /* number of facets stacked up */ REAL v,vv,z; brute_section(u); for ( i = 0 ; i < brute_cut_count-1 ; i++ ) { struct vis_edge *e = brute_cuts[i].e; struct vis_edge *ee = brute_cuts[i+1].e; if ( e->flags & V_FACET_BOTTOM ) { /* add to column */ brute_column[depth++] = e->f; } else if ( e->flags & V_FACET_TOP ) { /* delete from column */ for ( j = 0 ; j < depth ; j++ ) if ( brute_column[j] == e->f ) brute_column[j] = brute_column[--depth]; } /* check halfway in between for top facet */ v = e->m*(u-e->x[0][0]) + e->x[0][1]; vv = ee->m*(u-ee->x[0][0]) + ee->x[0][1]; if ( (depth > 0) && (fabs(v-vv) > 1e-8) ) { REAL topz = -1e30; int topj = -1; v = (v+vv)/2; for ( j = 0 ; j < depth ; j++ ) { struct vis_facet *f = brute_column[j]; z = f->a*u + f->b*v + f->c; if ( z > topz ) { topj = j; topz = z; } } brute_column[topj]->t->flag |= VISIBLE; } } } #endif evolver-2.30c.dfsg/src/proto.h0000644000175300017530000013773511410765113016551 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************* * * File: proto.h * * Purpose: ANSI function prototypes. * Note: Some compilers still don't like ANSI prototypes, so * ARGS() is a macro defined in include.h to take care * of that. */ #ifdef __cplusplus void loadstub(void); extern "C" { #endif #ifndef drand48 extern double drand48 ARGS((void)); /* may not be in header file */ #endif #ifdef MSC extern void srand48 ARGS((int)); #endif #ifdef MPI_EVOLVER #include "mpi_proto.h" #endif void read_array_initializer ARGS((struct array *)); int simple_unstar ARGS((facetedge_id)); int check_recalc_attr ARGS((int)); void check_readonly_attr ARGS((int)); void check_special_attr ARGS((int)); int get_name_dim ARGS((int,struct locallist_t *)); int get_name_datatype ARGS((int,struct locallist_t *)); char * get_name_name ARGS((int,struct locallist_t *)); struct array *get_name_arrayptr ARGS((int,REAL*,struct locallist_t *)); int check_array_dims_same ARGS((int ,int)); void one_sided_volume_adjust ARGS((vertex_id)); int one_sided_lagrange_adjust ARGS((vertex_id)); int mat_approx_solve ARGS((REAL**,int,REAL*)); REAL vertex_total_vol_grad ARGS((vertex_id,REAL*)); void calc_one_sided_grads ARGS((void)); void mpi_hessian_cleanup ARGS((void)); void do_multigrid ARGS((struct linsys*)); void mark_recalc_params ARGS((void)); int mark_recalc_expr ARGS((struct expnode*)); int vertex_facet_check ARGS((void)); WRAPTYPE identity_inverse ARGS((WRAPTYPE)); WRAPTYPE identity_compose ARGS((WRAPTYPE,WRAPTYPE)); void identity_wrap ARGS((REAL *,REAL *,WRAPTYPE)); void identity_form_pullback ARGS((REAL *,REAL *,REAL *,WRAPTYPE)); void graph_string_facet ARGS((facet_id,struct graphdata *,int)); void plain_string_facets ARGS((void)); void print_profiling ARGS((void)); void reset_profiling ARGS((void)); void convert_constraint_to_quantities ARGS((int)); int create_body_boundary_content_method ARGS((body_id,int)); int create_body_constraint_content_method ARGS((body_id,int)); void simplex_to_fe ARGS((void)); void check_forwards ARGS((void)); int star_finagle ARGS((edge_id)); void cg_restart ARGS((void)); REAL cg_sum_calc ARGS((void)); void cg_direction_local ARGS((void)); REAL ribiere_calc ARGS((void)); REAL v_estimate ARGS((void)); void add_vgrads_to_update ARGS(( struct linsys *)); void slice_facet ARGS((struct graphdata *)); void slice_edge ARGS((struct graphdata *)); void clip_facet ARGS((struct graphdata *,facet_id,int)); void clip_edge ARGS((struct graphdata *,edge_id,int)); void graph_edge_clip ARGS((struct graphdata *,edge_id)); void mark_element_loops ARGS((struct treenode *, int)); void mem_list_dump ARGS((void)); struct element * elhash_lookup ARGS((element_id)); void elhash_delete ARGS((element_id)); void elhash_bigger ARGS((void)); void elhash_extend ARGS((int,int)); struct element *elhash_new_element ARGS((int,element_id)); void elhash_insert ARGS((element_id,struct element *)); void elhash_replace ARGS((element_id,struct element *)); void matrix_multiply_command ARGS((struct array*,struct array*,struct array*,REAL*,REAL*,REAL*)); int matrix_inverse_command ARGS((struct array*,struct array*,REAL*,REAL*)); REAL matrix_determinant_command ARGS((struct array*,REAL*)); void tree_analyze ARGS((struct linsys *)); void fixup_edge_content_meths ARGS((edge_id)); void fixup_vertex_content_meths ARGS((vertex_id)); REAL tetra_vol ARGS((REAL*,REAL*,REAL*,REAL*)); int read_single_value ARGS((int,char*)); void set_ctypes ARGS((void)); void kb_unput ARGS((char)); void list_quantity ARGS((int)); void list_method_instance ARGS((int)); void list_boundary ARGS((int)); void list_constraint ARGS((int)); void torus_unwrap_edge ARGS(( edge_id )); int pop_vertex_to_tri ARGS(( vertex_id, edge_id *)); int pop_vertex_to_quad ARGS(( vertex_id, edge_id *)); int cubecone_pop ARGS(( vertex_id, int )); void wrap_vertex ARGS((vertex_id,int)); void find_cpu_speed ARGS((void)); void reset_counts ARGS((void)); int pop_constrained_vertex ARGS((vertex_id)); int one_con_pop_2 ARGS((vertex_id,edge_id *,edge_id *,int)); int one_con_pop_3 ARGS((vertex_id,int ,edge_id *,edge_id *,int)); int one_con_pop_4 ARGS((vertex_id,edge_id *,int)); int double_con_pop ARGS((vertex_id,edge_id,edge_id *)); int triple_con_pop ARGS((vertex_id)); void flush_counts ARGS((void)); void merge_vertex ARGS((vertex_id,vertex_id)); void merge_edge ARGS((edge_id,edge_id)); void merge_facet ARGS((facet_id,facet_id)); void binary_tree_add ARGS((REAL*,REAL)); void torus_display_period_init ARGS((void)); REAL bezier_eval ARGS((int, int, int*, REAL *)); void bezier_convert_1D_init ARGS(( int )); void bezier_convert_2D_init ARGS(( int )); int t1_edgeswap ARGS((edge_id)); void get_edge_tail_tangent ARGS((edge_id,REAL*)); int valid_element ARGS((element_id)); void bezier_trans_1d ARGS((int,int,REAL**)); void bezier_eval_1d ARGS((int,int,REAL,REAL**,REAL*)); void lagrange_eval_1d ARGS((int,int,REAL,REAL**,REAL*)); void bezier_refine_1d_init ARGS((int)); void lagrange_to_bezier ARGS((void)); void bezier_to_lagrange ARGS((void)); void bezier_trans_2d ARGS((int,int,REAL**)); void lagrange_eval_2d ARGS((int,int,REAL*,REAL**,REAL*)); void bezier_eval_2d ARGS((int,int,REAL*,REAL**,REAL*)); void bezier_refine_2d_init ARGS((int)); void OFF_start ARGS((void)); void OFF_edge ARGS((struct graphdata *, edge_id)); void OFF_facet ARGS((struct graphdata *, facet_id)); void OFF_end ARGS((void)); void binary_OFF_start ARGS((void)); void binary_OFF_edge ARGS((struct graphdata *, edge_id)); void binary_OFF_facet ARGS((struct graphdata *, facet_id)); void binary_OFF_end ARGS((void)); unsigned int int32hash ARGS((unsigned int)); #ifndef NOLONGLONG unsigned long long int64hash ARGS((unsigned long long)); #endif void partner_move ARGS((void)); void partner_shift_grads ARGS((int)); void partner_hit_velocity_fix ARGS((void)); void partner_hit_volgrad_fix ARGS((void)); void detect_bdry_hits ARGS((void)); void thread_move_vertices ARGS((void)); void torus_period_init ARGS((void)); void set_eigenvalue_list_global ARGS((REAL*,int)); void set_view_transforms_global ARGS((void)); void set_torus_periods_global ARGS((void)); void set_inverse_periods_global ARGS((void)); void set_view_matrix_global ARGS((void)); void initialize_perm_globals ARGS((void)); void allocate_transform_colors ARGS((int)); int pop_tri_to_edge ARGS((facet_id)); int pop_edge_to_tri ARGS((edge_id)); int pop_edge_to_tri_con ARGS((edge_id)); int pop_quad_to_quad ARGS((facet_id)); void stack_usage ARGS((struct expnode *)); void list_top_procedures ARGS((int)); void list_procedure ARGS((struct global *)); void list_procedure_proto ARGS((struct global *)); void list_function_proto ARGS((struct global *)); void list_function ARGS((struct global *)); void init_local_scope ARGS((ident_t)); void exit_local_scope ARGS((void)); void begin_local_scope ARGS((void)); void end_local_scope ARGS((void)); void locals_copy ARGS((struct locallist_t **,struct locallist_t *)); void locals_copy_perm ARGS((struct locallist_t **,struct locallist_t *)); ident_t lookup_local_var ARGS((char*)); ident_t add_local_var ARGS((char*,int)); void read_attribute_value ARGS((struct extra *, void*)); int equiangulate_edge ARGS((edge_id)); int calc_view_transform_gens ARGS((void)); void force_project ARGS(( REAL*, vertex_id, REAL *)); int global_meth_needs ARGS((int)); int pop_given_vertex ARGS((vertex_id)); int pop_string_vertex ARGS((vertex_id)); void print_array ARGS((struct array *,void*)); void print_array_attribute ARGS(( struct extra*, void*)); void recalc_facet_area ARGS((facet_id)); void rewind_globals ARGS((int)); void perm_rewind_globals ARGS((int)); void add_warning_message ARGS((char*)); void graph_new_surface ARGS((void)); void LD_solve ARGS((REAL**,REAL*,REAL*,int)); int LD_factor ARGS(( REAL**, int )); int lookup_global_hash ARGS((char*,int,int,int)); void dy_check ARGS((void)); void report_quantities ARGS((void)); void list_free ARGS((char *,int)); void list_free_all ARGS((int)); #ifdef MEMSTRINGS char * list_calloc ARGS((size_t,size_t,int,char*,size_t)); char * list_realloc ARGS((char *,size_t,int,char*,size_t)); #else char * list_calloc ARGS((size_t,size_t,int)); char * list_realloc ARGS((char *,size_t,int)); #endif void mem_list_summary ARGS((void)); void mem_check_sanity ARGS((void)); void change_hessian_functions ARGS((int,int)); int LD_block_factor ARGS((REAL **, int)); void LD_block_solve ARGS((REAL **, REAL *, REAL *, int)); int find_fixed ARGS((void)); void sp_hash_init ARGS((struct linsys *,int)); void sp_hash_expand ARGS((struct linsys *)); void sp_hash_search ARGS((struct linsys *,int,int,REAL)); int sp_hash_end ARGS((struct linsys *,int,int,int)); void my_pvm_exit ARGS((void)); void pvm_calc_quants ARGS((int)); void pvm_calc_quant_grads ARGS((int)); void pvm_facet_knot_init ARGS((REAL*)); int two_split ARGS((facetedge_id,int)); int vertex_degfree ARGS((vertex_id)); int edge_degfree ARGS((edge_id)); int facet_degfree ARGS((facet_id)); void divide_quad ARGS((facetedge_id)); int pop_one_edge ARGS((edge_id)); void set_scroll_size ARGS((int)); REAL ellipticK ARGS((REAL)); REAL ellipticE ARGS((REAL)); REAL ellipticKdm ARGS((REAL)); REAL ellipticEdm ARGS((REAL)); REAL ellipticKdmdm ARGS((REAL)); REAL ellipticEdmdm ARGS((REAL)); REAL incompleteEllipticF ARGS((REAL,REAL)); REAL incompleteEllipticFdphi ARGS((REAL,REAL)); REAL incompleteEllipticFdm ARGS((REAL,REAL)); REAL incompleteEllipticFseconds ARGS((REAL,REAL,REAL*,REAL*,REAL*,REAL*,REAL*)); REAL incompleteEllipticE ARGS((REAL,REAL)); REAL incompleteEllipticEdphi ARGS((REAL,REAL)); REAL incompleteEllipticEdm ARGS((REAL,REAL)); REAL incompleteEllipticEseconds ARGS((REAL,REAL,REAL*,REAL*,REAL*,REAL*,REAL*)); int INDEX_TO_RGBA ARGS((int)); void multi_calc_quants ARGS((int)); void m_fix_grads ARGS((void)); void m_calc_quant_grads ARGS((int)); int mylock_web ARGS((void)); int myunlock_web ARGS((void)); int mylock_element ARGS((element_id)); int myunlock_element ARGS((element_id)); element_id thread_next_element ARGS((void)); void thread_launch ARGS((int,int)); void thread_project_all ARGS((int)); void thread_calc_facet_energy ARGS((void)); void thread_calc_facet_forces ARGS((void)); REAL interp_edge_attribute ARGS(( edge_id ,struct extra *, int ,int)); REAL interp_facet_attribute ARGS(( edge_id ,struct extra *, int ,int)); REAL get_extra_attrib_value ARGS(( element_id, struct extra *, int)); void define_array ARGS((void)); void userfunc_init ARGS((void)); void break_check ARGS((void)); int lagrange_facet_normal ARGS((vertex_id,facet_id,REAL**)); void test_axial_points ARGS(( facet_id )); int compare_edge_attr ARGS((edge_id, edge_id)); int compare_edge_facet_attr ARGS((edge_id,facet_id)); int compare_vertex_edge_attr ARGS((vertex_id, edge_id)); int compare_vertex_attr ARGS((vertex_id, vertex_id)); void set_body_volume ARGS((body_id,REAL,int)); void add_body_volume ARGS((body_id,REAL)); void add_body_volume_plain ARGS((body_id,REAL)); void add_body_abstotal ARGS((body_id,REAL)); void set_body ARGS((facet_id,body_id)); int kb_yylex ARGS((YYSTYPE*)); void reset_inputbuffer ARGS((void)); char *my_fgets ARGS((char *,int,FILE *stream)); int method_check ARGS((void)); void renumber_all ARGS((void)); void reorder_storage ARGS((void)); int identtype ARGS((char*)); REAL kb_drand ARGS((void)); void kb_initr ARGS((int)); void dump_macros ARGS((void)); void move_to_free_front ARGS((int,int)); void calc_all_grads ARGS((int)); void check_pinning ARGS((void)); void convert_forms_to_vectors ARGS((int)); void local_convert_forms_to_vectors ARGS((int)); void clear_v_conmap ARGS((vertex_id )); void list_attributes ARGS((void)); void reset_rot_order ARGS((void)); void start_logfile ARGS((char*)); void stop_logfile ARGS((void)); void start_keylogfile ARGS((char*)); void stop_keylogfile ARGS((void)); void one_sided_adjust ARGS((int)); void calc_leftside ARGS((void)); void local_calc_leftside ARGS((void)); void local_calc_rightside ARGS((void)); void load_library ARGS((char *)); void unload_libraries ARGS((void)); dll_func_type search_libraries ARGS((char*)); REAL get_internal_variable ARGS((int )); void check_element_type ARGS(( int, int )); int read_pick ARGS((void)); int count_fixed_vol ARGS((void)); int oid ARGS((element_id)); void flip_toggle ARGS((int*,int,char*)); int gram_schmidt ARGS((REAL**,int,int)); int new_vertex_average ARGS((vertex_id,int)); int lagrange_vertex_average ARGS((vertex_id,REAL*)); void keyword_help ARGS((char *)); void error_help ARGS((char *)); void write_to_console ARGS((char *)); void read_line_from_console ARGS((char *)); int do_edgeswap ARGS((edge_id)); void apply_h_inverse_metric ARGS((void)); void list_proc ARGS((struct global *,int)); void list_procedures ARGS((int)); void linear_to_lagrange ARGS((int)); void quad_to_lagrange ARGS((int)); void lagrange_to_linear ARGS((void)); void lagrange_to_quad ARGS((void)); void sparse_permute ARGS((struct linsys *)); void do_tree_factor ARGS((struct linsys *)); void do_tree_solve ARGS((struct linsys *,REAL *,REAL*)); int find_method_instance ARGS((char*)); int find_quantity ARGS((char*)); void reconvert_bodies_to_quantities ARGS((void)); void convert_bodies_to_quantities ARGS((void)); int string_rebody ARGS((void)); void lagrange_to_lagrange ARGS((int)); void simplex_lagrange_to_lagrange ARGS((int)); void metis_partition ARGS((int,int)); void metis_partition_plain ARGS((int,int)); void metis_partition_body ARGS((int,int)); void metis_partition_dual ARGS((int,int)); void metis_order ARGS((struct linsys *)); void metis_vertex_order ARGS((int)); int lagrange_index ARGS((int,int,int*)); int increment_lagrange_index ARGS((int,int*)); void gauss_lagrange_setup ARGS((int,int,int)); void det_hess ARGS((REAL **,REAL ****,int )); int simplex_delete_facet ARGS((facet_id)); int simplex_delete_edge ARGS((vertex_id,vertex_id)); void write_hessian ARGS((struct linsys *)); REAL gravity_all ARGS((struct qinfo *,int)); void LQ_decomp ARGS((REAL**,int,int,REAL**,REAL**,struct linsys *)); void q_info_free ARGS((struct qinfo *)); void q_info_init ARGS((struct qinfo *,int)); void make_el_list ARGS((int)); int Ord ARGS((element_id)); void toggle_save ARGS((char*)); void toggle_save_off ARGS((char*)); int get_v_constraint_status ARGS((vertex_id,int)); int v_hit_constraint_count ARGS((vertex_id)); REAL square_grad ARGS((void)); void square_grad_forces ARGS((void)); void cg_ritz ARGS((struct linsys *,int,REAL**,REAL*)); int check_pick ARGS((void)); void read_extra ARGS((element_id,int)); int simplex_quadrature ARGS((int,int,int,REAL**,REAL*)); int eartest ARGS((vertex_id,vertex_id)); void convert_string ARGS((char*,char*)); void hessian_legal ARGS((void)); int check_one_sided_constraints ARGS((void)); void set_v_global ARGS((vertex_id)); void set_v_conmap ARGS((vertex_id,conmap_t*)); void set_e_conmap ARGS((edge_id,conmap_t*)); void set_f_conmap ARGS((facet_id,conmap_t*)); void set_v_constraint_map ARGS((vertex_id,int)); void unset_v_constraint_map ARGS((vertex_id,int)); void set_v_constraint_status ARGS((vertex_id,int)); void unset_v_constraint_status ARGS((vertex_id,int)); void clear_v_constraint_status ARGS((vertex_id)); void set_e_constraint_map ARGS((edge_id,int)); void unset_e_constraint_map ARGS((edge_id,int)); void set_f_constraint_map ARGS((facet_id,int)); void unset_f_constraint_map ARGS((facet_id,int)); int v_on_constraint ARGS((vertex_id,int)); int e_on_constraint ARGS((edge_id,int)); int f_on_constraint ARGS((facet_id,int)); void get_v_common_conmap ARGS((vertex_id,vertex_id,conmap_t *)); int simplex_tiny_edges ARGS((REAL)); int get_tag ARGS((facet_id)); void set_tag ARGS((facet_id,int)); void ritz_command ARGS((REAL,int)); void do_ritz ARGS((struct linsys *,REAL,int,REAL **)); void print_hessian_menu ARGS((void)); void expand_attribute ARGS((int,int,int*)); void expand_global_hash ARGS((void)); int edgeswap ARGS(( edge_id)); REAL sparse_metric_dot ARGS((REAL*,REAL*,struct linsys *)); void gauss_jacobi ARGS((int, REAL , REAL *, REAL *)); void jacobi_eigenpairs ARGS(( REAL **, int, REAL *, REAL **, REAL *)); void star_metric_setup ARGS(( struct linsys *S, struct linsys *)); void linear_metric_setup ARGS((struct linsys *, struct linsys *)); void linear_metric_setup_quadratic ARGS(( struct linsys *, struct linsys *)); void linear_metric_setup_lagrange ARGS(( struct linsys *, struct linsys *)); REAL get_vertex_length_star ARGS((vertex_id)); REAL get_vertex_area_star ARGS((vertex_id)); void convert_to_quantities ARGS((void)); void convert_body_to_quantity ARGS((body_id)); void convert_new_body_to_quantity ARGS((body_id)); void long_jiggle ARGS((void)); void free_system ARGS((struct linsys *)); int simplex_long_edges ARGS((REAL)); void lanczos_command ARGS((REAL,int)); void eigenprobe_command ARGS((REAL,int)); void myfree ARGS((char *)); void option_facet ARGS((struct graphdata*,facet_id)); void geomview_command ARGS((char*)); void ysmp_numeric_factor ARGS((void)); void sp_hessian_mult ARGS((struct linsys *S, REAL *B, REAL *C)); void sp_solution ARGS((struct linsys *S, REAL *B, REAL *C)); void hessian_exit ARGS((REAL *)); void hessian_cleanup ARGS((void)); void hessian_solve ARGS((void)); void hessian_downhill ARGS((void)); void hessian_move ARGS((REAL,int,REAL *)); int lanczos ARGS((struct linsys*,int,REAL*,int)); int selective_lanczos ARGS((struct linsys*,int,REAL*,int)); void tridiag_QL ARGS((REAL*,REAL*,int)); void intersect_detect ARGS((void)); void bk_mul ARGS((struct linsys *, REAL*,REAL*)); void bk_AIJ_setup ARGS((int,struct linsys*)); void bk_eigenprobe ARGS((struct linsys *)); void bk_inverse_it ARGS((struct linsys *, REAL *)); void sp_hessian_solve ARGS((struct linsys *,REAL *, REAL *,int)); void sp_hessian_solve_multi ARGS((struct linsys *,REAL **, REAL **,int)); void sp_Hessian_solver ARGS((struct linsys *, REAL *, REAL **)); void bk_constraint_setup ARGS((int, struct linsys *)); void BK_hess_project_setup ARGS((struct linsys *)); void xmd_factor ARGS((struct linsys *)); void xmd_solve ARGS((struct linsys *,REAL *,REAL *)); void xmd_solve_multi ARGS((struct linsys *,REAL **,REAL **,int)); void sp_CHinvC ARGS((struct linsys *)); void tree_factor ARGS((struct linsys *)); void tree_solve ARGS((struct linsys *,REAL *,REAL *)); void tree_solve_multi ARGS((struct linsys *,REAL **,REAL **,int)); void ysmp_factor ARGS((struct linsys *)); void ysmp_solve ARGS((struct linsys *,REAL *,REAL *)); void ysmp_solve_multi ARGS((struct linsys *,REAL **,REAL **,int)); void find_ordering ARGS((struct hess_verlist *,int,int *)); void hessian_saddle ARGS((REAL)); REAL lowest_eigenpair ARGS((struct linsys *,REAL *)); REAL cg_lowest_eigenpair ARGS((struct linsys *,REAL *)); REAL old_cg_lowest_eigenpair ARGS((struct linsys *,REAL *)); REAL new_cg_lowest_eigenpair ARGS((struct linsys *,REAL *)); element_id get_ordinal_id ARGS((int,int)); element_id get_full_id ARGS((int,element_id)); void project_all ARGS((int,int)); void chebychev_hess ARGS((struct linsys *)); void chebychev_ev ARGS((struct linsys *)); void fill_self_entry ARGS((struct linsys *,vertex_id,REAL**)); void fill_grad ARGS((struct linsys *,struct hess_verlist *,REAL*,REAL *)); int add_attribute ARGS((int,char*,int,int,int*,int,struct expnode *)); int find_attribute ARGS((int,char*)); int find_extra ARGS((char *, int *)); int matrix_index ARGS((REAL**,int)); int exec_commands ARGS((FILE *,char *)); void *mac_exec_commands ARGS((FILE *)); void push_fd ARGS((FILE *,char *)); void pop_fd ARGS((void)); facet_id get_vertex_facet ARGS((vertex_id)); facet_id get_vertex_first_facet ARGS((vertex_id)); facet_id get_next_vertex_facet ARGS((vertex_id,facet_id)); facet_id get_simplex_next_vertex_facet ARGS((vertex_id,facet_id)); void macro_init ARGS((void)); REAL ** mat2d_setup ARGS((REAL**,REAL*,int,int)); REAL *** mat3d_setup ARGS((REAL***,REAL*,int,int,int)); REAL **** mat4d_setup ARGS((REAL****,REAL*,int,int,int,int)); void zerohess ARGS((struct qinfo *)); void get_facet_normal ARGS((facet_id,REAL*)); int get_toggle_value ARGS((int)); REAL user_attribute ARGS((element_id)); void force_normalization ARGS((void)); void insert_vertex_edge ARGS((vertex_id,edge_id)); void remove_vertex_edge ARGS((vertex_id,edge_id)); void insert_vertex_facet ARGS((vertex_id,facet_id)); void remove_vertex_facet ARGS((vertex_id,facet_id)); void catfulltext ARGS((char*)); int identcase ARGS((char*)); int eliminate_facet ARGS((facet_id)); int string_eliminate_facet ARGS((facet_id)); REAL quantity_attribute ARGS(( element_id, int )); REAL instance_attribute ARGS(( element_id, int )); int kb_checksum ARGS((char *,int)); void apply_quantity ARGS((element_id,int)); void unapply_quantity ARGS((element_id,int)); void read_instance_attr ARGS(( int )); int read_named_quantity ARGS((void)); DY_OFFSET dy_calloc ARGS((int,int)); DY_OFFSET dy_realloc ARGS((DY_OFFSET,int,int)); void dy_free ARGS((DY_OFFSET)); void dump_method_specs ARGS(( int )); void read_method_attr ARGS(( int )); void calc_periods ARGS((int)); int read_method_instance ARGS((void)); int add_standard_quantity ARGS((char *,REAL)); int new_method_instance ARGS((char*,char*)); int attach_method ARGS(( int, char *)); int attach_method_num ARGS(( int, int)); void apply_method ARGS(( element_id , char *)); void apply_method_num ARGS(( element_id , int)); void unapply_method ARGS(( element_id , int)); int rebody ARGS((void)); int merge_bodies ARGS((void)); int get_body_valence ARGS((body_id )); int get_vertex_evalence ARGS((vertex_id )); int get_vertex_fvalence ARGS((vertex_id )); void make_vfacet_lists ARGS((void)); void make_bfacet_lists ARGS((void)); void make_vedge_lists ARGS((void)); REAL dirichlet ARGS((int)); REAL sobolev ARGS((int)); int unstar ARGS((facetedge_id)); void reduce_string ARGS((char *)); REAL vertex_angle ARGS((vertex_id)); void fix_ctm ARGS((REAL**,REAL,REAL)); void my_exit ARGS((int)); void reverse_orientation_edge ARGS((edge_id)); void reverse_orientation_facet ARGS((facet_id)); int dissolve_vertex ARGS((vertex_id)); int dissolve_edge ARGS((edge_id)); int dissolve_facet ARGS((facet_id)); int dissolve_body ARGS((body_id)); REAL vertex_sq_mean_curvature ARGS((vertex_id)); REAL vertex_mean_curvature ARGS((vertex_id)); void burchard ARGS((int)); void do_gfile ARGS((int,char*)); int mat_simplify ARGS((REAL***,int)); void transform_gen_expr ARGS((char *)); void generate_transforms ARGS((int)); void read_transform_generators ARGS((int)); int const_expr ARGS((char *,REAL *)); REAL wedge_angle ARGS((vertex_id)); void begin_normal_motion ARGS((void)); void end_normal_motion ARGS((void)); REAL gray_level ARGS((float *)); void metric_form_to_vector ARGS((REAL*,REAL*)); void end_geomview_object ARGS((void)); void calc_quant_grads ARGS((int)); int simplex_element_id_edges ARGS((REAL)); void quantity_init ARGS((void)); int new_quantity ARGS((char *,int)); void add_quantity ARGS((element_id,char*,char*)); REAL calc_quants ARGS((int)); void graph_edge_transforms ARGS(( struct graphdata *, edge_id )); void graph_facet_transforms ARGS(( struct graphdata *, facet_id )); void other_stuff ARGS((struct treenode *,int*,int*,element_id,REAL*,struct locallist_t*)); void more_other_stuff ARGS((struct treenode *,int*,int*,element_id,REAL*,struct locallist_t*)); void ask_wrap_display ARGS((void)); void print_attr ARGS(( struct treenode *, char *)); void print_set_attr ARGS(( struct treenode *, char *)); void set_print ARGS((struct treenode *, char *, char *,int)); void binary_print ARGS(( struct treenode *, int, int, char *, int)); int lookup_global ARGS((char*)); int lookup_perm_global ARGS((char*)); void klein_area_grad ARGS((REAL **,REAL**)); REAL klein_area ARGS((REAL**)); void klein_length_grad ARGS((REAL*,REAL*,REAL*,REAL*)); REAL klein_length ARGS((REAL*,REAL*)); void geomview_start ARGS((void)); void geomview_facet ARGS((struct graphdata *, facet_id)); void geomview_edge ARGS((struct graphdata *, edge_id)); void geomview_end ARGS((void)); void tree_copy ARGS(( struct expnode *, struct treenode *)); void perm_tree_copy ARGS(( struct expnode *, struct treenode *)); void conf_edge_curv_energy ARGS((void)); void conf_edge_curv_force ARGS((void)); REAL dihedral ARGS((edge_id)); void clear_symtable ARGS((void)); void clear_globals ARGS((void)); int add_global ARGS((char *)); int add_perm_global ARGS((char *)); char *keywordname ARGS((int)); void subtree_swap ARGS((int*,int*)); int begin_scope ARGS((void)); void end_scope ARGS((void)); void set_scope ARGS((int)); struct sym * symbol_add ARGS((char*,int)); struct sym *symbol_lookup ARGS((char*)); vertex_id void_test ARGS((vertex_id *,int)); char *tokname ARGS((int)); void update_aggr ARGS((int ,REAL *,REAL )); void hessian_init ARGS((struct linsys *,REAL **)); void hessian_fill ARGS((struct linsys *,REAL **)); void find_hess_entry ARGS((struct linsys *, struct hess_verlist*, struct hess_entry *[MAXCOORD][MAXCOORD])); void fill_hess_entry ARGS((struct linsys *, vertex_id,REAL**)); int find_mixed_entry ARGS((struct linsys *, struct hess_verlist*, struct hess_verlist* ,struct hess_entry *[MAXCOORD][MAXCOORD])); void fill_mixed_entry ARGS((struct linsys *, vertex_id,vertex_id, REAL **)); int find_hess_entries ARGS((struct linsys *, struct hess_verlist*, struct hess_verlist*, struct hess_entry *[MAXCOORD][MAXCOORD], struct hess_entry *[MAXCOORD][MAXCOORD], struct hess_entry *[MAXCOORD][MAXCOORD])); void area_hessian ARGS((struct linsys *,REAL *)); void calc_quant_hess ARGS((struct linsys *,int,int,REAL *)); void m_calc_quant_hess ARGS((int,int,REAL *)); void m_fix_hess ARGS((struct linsys *S)); void difference_hessian ARGS((struct linsys *,struct hess_verlist*,REAL *)); void edge_energy_hessian ARGS((struct linsys *,REAL *)); void simplex_hessian ARGS((struct linsys *,REAL *)); void simplex_facet_hessian ARGS((struct linsys *,REAL *)); void simplex_edge_hessian ARGS((struct linsys *,edge_id,REAL**,REAL****)); void edge_constr_hessian ARGS((struct linsys *,edge_id,REAL**,REAL****)); int body_hessian ARGS((struct linsys *,REAL*)); void tree_exec ARGS(( struct treenode *)); void letter_command ARGS((int)); void pop_commandfd ARGS((void)); void push_commandfd ARGS((FILE*,char*)); void pop_datafd ARGS((void)); void push_datafd ARGS((FILE*,char*)); void exec_file ARGS((FILE*,char*)); void unput_tok ARGS((void)); void unput_string ARGS((char*)); void set_f_phase_density ARGS((facet_id)); void set_e_phase_density ARGS((edge_id)); int bdry_basis ARGS((vertex_id,REAL**)); int constr_basis ARGS((vertex_id,REAL**)); void sp_factor ARGS((struct linsys *)); void stability_test ARGS((void)); void mobility_cleanup ARGS((void)); void mobility_mult ARGS((REAL*)); void mobility_setup ARGS((void)); void approx_curv_calc ARGS((int)); void approx_curvature ARGS((void)); int new_popverst ARGS((vertex_id,int)); void phase_initialize ARGS((char *)); void check_vertex_fe ARGS((vertex_id)); void information ARGS((void)); void show_volumes ARGS((void)); int set_parameters ARGS((void)); int minus_type ARGS((int)); void string_fixup ARGS((void)); void runge_kutta ARGS((void)); void autopop_init ARGS((void)); void autopop_cleanup ARGS((void)); void autopop_pop ARGS((void)); void autochop_chop ARGS((void)); void autopop_detect ARGS((REAL)); vertex_id find_other_vertex ARGS((vertex_id *,int,vertex_id)); void simplex_delaunay_test ARGS((void)); void push_face ARGS((vertex_id *)); int pop_face ARGS((vertex_id *)); void end_face_stack ARGS((void)); void init_face_stack ARGS((int)); int simplex_equiangulate ARGS((void)); void calc_simplex_edge_force ARGS((edge_id)); int kernel_basis ARGS((REAL**,REAL**,int,int)); int kernel_basis_rows ARGS((REAL**,REAL**,int,int)); void tr_mat_mul ARGS((REAL**,REAL**,REAL**,int,int,int)); void mat_mul_tr ARGS((REAL**,REAL**,REAL**,int,int,int)); void mat_tsquare ARGS((REAL**,REAL**,int,int)); void calc_simplex_edge_energy ARGS((edge_id)); void exterior_product ARGS((REAL **,REAL *,int,int)); int binom_coeff ARGS((int,int)); void hi_dim_graph ARGS((void)); void recalc ARGS((void)); void reset_conj_grad ARGS((void)); void kusner_energy ARGS((void)); void kusner_force ARGS((void)); void sqgauss_energy ARGS((void)); void sqgauss_force ARGS((void)); int prompt ARGS((char*,char*,int)); #ifdef USE_READLINE /* CSL */ void save_readline_history ARGS((void)); #endif void update_display ARGS((void)); void local_update_display ARGS((void)); void set_spinr ARGS((REAL)); void set_spinl ARGS((REAL)); void set_tipup ARGS((REAL)); void set_tipdown ARGS((REAL)); void set_clockwise ARGS((REAL)); void set_counterclockwise ARGS((REAL)); void set_zoom ARGS((REAL)); void skinny_histogram ARGS((void)); int skinny ARGS((REAL)); int get_edge_valence ARGS((edge_id)); int get_facet_valence ARGS((edge_id)); void alice ARGS((void)); void sqcurve_force_string ARGS((vertex_id)); void sqcurve_force_string_end ARGS((void)); void sqcurve_energy_string ARGS((vertex_id)); void sqcurve_energy_string_init ARGS((void)); void bad_function ARGS((void)); void sqcurve_force_init ARGS((void)); void sqcurve_energy_init ARGS((void)); void sqcurve_energy ARGS((vertex_id*, REAL (*)[MAXCOORD])); void sqcurve_force ARGS((vertex_id*,edge_id*,REAL(*)[MAXCOORD])); void sqcurve_energy_end ARGS((void)); void sqcurve_force_end ARGS((void)); void top_dump ARGS((FILE *)); void bottom_dump ARGS((FILE *)); void vertex_dump ARGS((vertex_id,FILE *)); void edge_dump ARGS((edge_id,FILE *)); void facet_dump ARGS((facet_id,FILE *)); void facetedge_dump ARGS((facetedge_id,FILE *)); void body_dump ARGS((body_id,FILE *)); WRAPTYPE torus_inverse ARGS((WRAPTYPE)); WRAPTYPE torus_compose ARGS((WRAPTYPE,WRAPTYPE)); void torus_wrap ARGS((REAL *,REAL *,WRAPTYPE)); void torus_form_pullback ARGS((REAL *,REAL *,REAL *,WRAPTYPE)); WRAPTYPE group_inverse ARGS((WRAPTYPE)); WRAPTYPE group_compose ARGS((WRAPTYPE,WRAPTYPE)); void group_wrap ARGS((REAL *,REAL *,WRAPTYPE)); void group_form_pullback ARGS((REAL *,REAL *,REAL *,WRAPTYPE)); void print_matrix ARGS(( REAL **, int , int )); REAL simplex_energy_metric ARGS((vertex_id*,REAL * *)); void simplex_force_metric ARGS((vertex_id *,REAL**, REAL,REAL **)); void gauss_setup ARGS((void)); void simplex_grad_l ARGS((void)); void end_hash_table ARGS((void)); void rehash ARGS((void)); void init_hash_table ARGS((void)); void check_orientation ARGS((struct simplex *,int)); void refine_simplex_init ARGS((void)); void refine_simplex ARGS((int,int *,struct divedge *,struct simplex *)); void refine_all_simplices ARGS((void)); vertex_id simplex_edge_divide ARGS((vertex_id,vertex_id)); int poponest ARGS((vertex_id,int)); void free_discards ARGS((int)); int find_vertex_to_pop ARGS((void)); void puff ARGS((void)); REAL splinepoly ARGS((int,REAL)); REAL splinederiv ARGS((int,REAL)); void spline_partial ARGS((int *,REAL,int,REAL *)); void spline_partial_t ARGS((int *,REAL,REAL *)); void dump_buff ARGS((char*,size_t)); int command ARGS((char *,int)); void newcommand ARGS((char *)); void new_history ARGS((char *)); int old_history ARGS((char *)); int qqparse ARGS((void)); #ifdef MEMSTRINGS char *kb_calloc ARGS((size_t,size_t,char*,size_t)); char *KB_realloc ARGS((char*,size_t,char*,size_t)); char *kb_temp_calloc ARGS((size_t,size_t,char*,size_t)); char *kb_temp_realloc ARGS((char*,size_t,char*,size_t)); #else char *kb_calloc ARGS((size_t,size_t)); char *KB_realloc ARGS((char*,size_t)); char *kb_temp_calloc ARGS((size_t,size_t)); char *kb_temp_realloc ARGS((char*,size_t)); #endif void temp_free ARGS((char *)); void temp_free_all ARGS((void)); void cg_direction ARGS((void)); void cg_calc_gamma ARGS((void)); void simplex_facet_average ARGS((facet_id,int)); void facet_average ARGS((facet_id,int)); void edge_average ARGS((edge_id,int)); void recalc_verts ARGS((void)); void grule ARGS((int , REAL *, REAL *)); void read_facet_edges ARGS((void)); void read_faces ARGS((void)); void read_edges ARGS((void)); void read_vertices ARGS((void)); void read_bodies ARGS((void)); int read_quantity ARGS((void)); int read_const ARGS((REAL *)); void lowbound ARGS((void)); void ridge_histogram ARGS((void)); int collapse_check ARGS((void)); #ifndef TCPP int vvvvcomp ARGS((struct vvvv *,struct vvvv *)); #endif void End_OOGL ARGS((void )); void End_geomview ARGS((void )); void UpdateOOGL ARGS((void )); void Begin_OOGL ARGS((void )); void Begin_geomview ARGS((char*)); void softimage ARGS((void )); void constraint_init ARGS((struct constraint *)); void constraint_free ARGS((struct constraint *)); REAL find_flux ARGS((facet_id)); int is_constant ARGS((int)); void free_expr ARGS((struct expnode *)); void perm_free_expr ARGS((struct expnode *)); void startup ARGS((char *)); void reset_view ARGS((void)); void read_parameter ARGS((void)); void read_transforms ARGS((int)); void zoom_vertex ARGS((vertex_id,REAL)); REAL distance ARGS((vertex_id,vertex_id)); void merge_collapsed_facets ARGS((void)); void constr_vol_grad_q ARGS((edge_id)); void homothety ARGS((void)); void ex_fold ARGS((struct treenode *)); void fold_recur ARGS((struct treenode *, int *)); int yybegin ARGS((void)); void yylex_init ARGS((void)); int yyerror ARGS((char *)); int yyparse ARGS((void)); int yylook ARGS((void)); int yywrap ARGS((void)); int yylex ARGS((void)); int yyreject ARGS((void)); int gettok ARGS((int)); int kb_input ARGS((void)); void rawunput ARGS((int)); int macro ARGS((void)); void record_macro ARGS((void)); void fe_reorder ARGS((edge_id)); void pix_start ARGS((void)); void pix_facet ARGS((struct graphdata *,facet_id)); void pix_end ARGS((void)); void constr_edge_force_q ARGS((edge_id)); void restore_vertex ARGS((vertex_id,struct oldcoord *,int)); REAL normal_change_check ARGS((void)); FILE *path_open ARGS((char *,int)); void fil_finish ARGS((void)); void fil_facet ARGS((struct tsort *)); void fil_edge ARGS((struct tsort *)); void fil_init ARGS((void)); void ps_finish ARGS((void)); void ps_facet ARGS((struct tsort *)); void ps_edge ARGS((struct tsort *)); void ps_init ARGS((void)); void display_file ARGS((int)); void graph_help ARGS((void)); void main_help ARGS((void)); REAL edge_grav_density ARGS((edge_id)); int facet_body_check ARGS((void)); int facetedge_check ARGS((int)); int list_check ARGS((void)); int run_checks ARGS((void)); int pop_vertex ARGS((vertex_id,int)); int kraynik_pop ARGS((vertex_id,int)); int popfilm ARGS((void)); void face_triangulate ARGS((facet_id,int)); void file_wulff ARGS((REAL *,REAL *)); void lens_wulff ARGS((REAL *,REAL *)); void hemi_wulff ARGS((REAL *,REAL *)); int curtest_facet ARGS((facet_id)); int curtest_edge ARGS((edge_id,facetedge_id,facetedge_id)); void curtest ARGS((void)); void fix_volconst ARGS((void)); element_id upgrade ARGS((element_id)); facet_id dup_facet ARGS((facet_id)); body_id dup_body ARGS((body_id)); REAL estimate_decrease ARGS((void)); void constr_edge_content_q ARGS((edge_id)); void constr_edge_energy_q ARGS((edge_id)); void tordup ARGS((int)); void add_outside ARGS((void)); REAL calc_content ARGS((int)); void local_calc_content ARGS((int)); int equal_constr ARGS((element_id ,element_id)); void bdry_spring_energy ARGS((edge_id)); void constr_spring_energy ARGS((edge_id)); void torus_cells ARGS((void)); void torus_edge_clip ARGS((struct graphdata *,int )); void torus_arc_clip ARGS((struct graphdata *,int )); void null_function ARGS((void)); void diffuse ARGS((void)); int cone_analyze ARGS((struct verfacet *,int)); vertex_id dup_vertex ARGS((vertex_id)); edge_id dup_edge ARGS((edge_id)); void versplit ARGS((facetedge_id ,facetedge_id,int)); int try_prop ARGS((facetedge_id *, facetedge_id,int)); int edgepop_film ARGS((void)); char * print_express ARGS((struct expnode *,int)); void exprint_recur ARGS((struct treenode *,int)); char *kb_strstr ARGS((char *,char *)); int kb_stricmp ARGS((char *,char *)); int kb_strnicmp ARGS((char *,char *, int)); void kb_memmove ARGS((char *,char *,size_t)); int pixdump ARGS((void)); void constr_springs ARGS((edge_id)); void save_coords ARGS((struct oldcoord *,int)); void local_save_coords ARGS((struct oldcoord *,int)); void restore_coords ARGS((struct oldcoord *,int)); void local_restore_coords ARGS((struct oldcoord *,int)); void unsave_coords ARGS((struct oldcoord *,int)); void local_unsave_coords ARGS((struct oldcoord *,int)); void enforce_constraints ARGS((void)); void kb_strupr ARGS((char *)); void b_proj ARGS((struct boundary *,REAL *,REAL * *,int, vertex_id )); void b_extrapolate ARGS((struct boundary *,REAL *,REAL *,REAL *,REAL *, REAL *,vertex_id)); void bdry_force ARGS((edge_id )); void calc_bdry_force_v ARGS((vertex_id )); void calc_bdry_force_e ARGS((edge_id )); void calc_bdry_energy_v ARGS((vertex_id )); void calc_bdry_energy_e ARGS((edge_id )); void calc_bdry_content_v ARGS((vertex_id )); void calc_bdry_content_e ARGS((edge_id )); void calc_force ARGS((void )); void local_calc_force ARGS((void )); void calc_energy ARGS((void)); void local_calc_energy ARGS((void)); void calc_pressure ARGS((void )); void display ARGS((void )); int constr_proj ARGS((int ,int ,struct constraint * *,REAL *,REAL *, REAL *,int *,int,vertex_id)); int constr_proj_matrix ARGS(( vertex_id, REAL ** )); int constr_proj_matrix_wall ARGS(( vertex_id, REAL ** )); int project_v_constr ARGS((vertex_id,int,int )); void calc_constr_force_v ARGS((vertex_id )); void calc_constr_force_e ARGS((edge_id )); void calc_constr_energy_v ARGS((vertex_id )); void calc_constr_energy_e ARGS((edge_id )); void calc_constr_content_v ARGS((vertex_id )); void calc_constr_content_e ARGS((edge_id )); void dump ARGS((void )); void do_dump ARGS((char *)); REAL eval ARGS((struct expnode *,REAL *,element_id,struct eval_frame*)); void eval_all ARGS((struct expnode *,REAL *,int,REAL *,REAL *,element_id)); void eval_second ARGS((struct expnode *,REAL *,int,REAL *,REAL *, REAL**,element_id)); REAL tree_eval ARGS((struct treenode *,REAL *)); REAL eval_deriv ARGS((struct expnode *,REAL *,int )); REAL tree_eval_deriv ARGS((struct treenode *,REAL *,int )); int exparse ARGS((int ,struct expnode *,int)); void calc_simplex_forces ARGS((facet_id)); void calc_simplex_energy ARGS((facet_id)); void calc_simplex_volume ARGS((facet_id)); void facet_force_l ARGS((facet_id )); void facet_energy_l ARGS((facet_id,int)); void facet_force_l_hi_d ARGS((facet_id )); void facet_energy_l_hi_d ARGS((facet_id)); void facet_volume_l ARGS((facet_id )); void film_grad_l ARGS((void )); void film_bdry_grad ARGS((void )); void film_constr_grad ARGS((void )); void facet_force_q ARGS((facet_id )); void facet_energy_q ARGS((facet_id,int )); void facet_volume_q ARGS((facet_id )); void film_grad_q ARGS((void )); REAL tq7_integral ARGS((REAL (*)(REAL,REAL))); REAL intpoly6 ARGS((int ,REAL ,REAL )); REAL intpoly6part ARGS((int ,int ,REAL ,REAL )); REAL vintzf ARGS((REAL ,REAL )); void vcoeff_init ARGS((void )); void calc_volgrads ARGS((int)); void local_calc_volgrads ARGS((int)); void pressure_forces ARGS((void)); void calc_lagrange ARGS((void)); void lagrange_adjust ARGS((void)); int local_lagrange_adjust ARGS((void)); void volume_restore ARGS((REAL,int)); void local_volume_restore ARGS((REAL,int)); int graphgen ARGS((void )); void plain_facets ARGS((void )); void plain_edges ARGS((void )); void bare_edges ARGS((void )); void triple_edges ARGS((void )); void torus_clip ARGS((struct graphdata *,int, facet_id )); int bfcomp ARGS((struct bodyface *,struct bodyface *)); void torus_bodies ARGS((void )); void reset_web ARGS((void )); void initialize ARGS((void)); void read_periods ARGS((void )); void read_display_periods ARGS((void )); void wulff_initialize ARGS((char *)); int read_boundary ARGS((void)); int read_constraint ARGS((void)); void read_surface_energy ARGS((void)); void iterate ARGS((void )); void fix_vertices ARGS((void )); void move_vertices ARGS((int,REAL )); void local_move_vertices ARGS((int,REAL )); void jiggle ARGS((void )); void element_id_jiggle ARGS((void )); REAL gaussian ARGS((void )); void nrerror ARGS((char *)); void matcopy ARGS((REAL * *,REAL * *,int ,int )); REAL **perm_matrix2 ARGS((int,int)); #ifdef MEMSTRINGS REAL * *kb_dmatrix ARGS((int ,int ,int ,int,char*,int )); REAL * * * kb_dmatrix3 ARGS((int ,int ,int,char*,int )); REAL * * * * kb_dmatrix4 ARGS((int ,int ,int, int,char*,int )); REAL * *kb_temp_dmatrix ARGS((int ,int ,int ,int,char*,int )); REAL * * * kb_temp_dmatrix3 ARGS((int ,int ,int,char*,int )); REAL * * * * kb_temp_dmatrix4 ARGS((int ,int ,int, int,char*,int )); #else REAL * *kb_dmatrix ARGS((int ,int ,int ,int )); REAL * * * kb_dmatrix3 ARGS((int ,int ,int )); REAL * * * * kb_dmatrix4 ARGS((int ,int ,int, int )); REAL * *kb_temp_dmatrix ARGS((int ,int ,int ,int )); REAL * * * kb_temp_dmatrix3 ARGS((int ,int ,int )); REAL * * * * kb_temp_dmatrix4 ARGS((int ,int ,int, int )); #endif REAL *** matrix3_reorder ARGS((REAL***,int,int,int)); int *ivector ARGS((int ,int )); REAL *vector ARGS((int ,int )); void free_ivector ARGS((int *,int ,int )); void free_vector ARGS((REAL *,int ,int )); void free_matrix ARGS((REAL * *)); void free_matrix3 ARGS((REAL * * *)); void free_matrix4 ARGS((REAL * * * *)); void free_temp_matrix ARGS((REAL * *)); void free_temp_matrix3 ARGS((REAL * * *)); void free_temp_matrix4 ARGS((REAL * * * *)); void vector_add ARGS((REAL*,REAL*,int)); void vector_add_smul ARGS((REAL*,REAL*,REAL,int)); void vector_sub ARGS((REAL*,REAL*,int)); void vnormal ARGS((REAL *,REAL *,REAL *,REAL *)); void cross_prod ARGS((REAL *,REAL *,REAL *)); REAL triple_prod ARGS((REAL *,REAL *,REAL *)); REAL dot ARGS((REAL *,REAL *,int )); REAL dotf ARGS((float *,float *,int )); REAL dotdf ARGS((REAL *,float *,int )); void matvec_mul ARGS((REAL * *,REAL *,REAL *,int ,int )); void vec_mat_mul ARGS((REAL *,REAL ** ,REAL *,int ,int )); void mat_mult ARGS((REAL * *,REAL * *,REAL * *,int ,int ,int )); REAL quadratic_form ARGS((REAL *,REAL **,REAL *,int)); int mat_inv ARGS((REAL * *,int )); int mat_inv_sym ARGS((REAL * *,int )); int mat_inv_sparse ARGS((REAL * *,int )); REAL determinant ARGS((REAL * *,int )); REAL det_adjoint ARGS((REAL * *,int )); void change_model ARGS((void )); void linear_to_quad ARGS((void )); void quad_to_linear ARGS((void )); edge_id edge_divide ARGS((edge_id )); void cross_cut ARGS((facetedge_id ,facetedge_id )); void painter_start ARGS((void )); void painter_facet ARGS((struct graphdata *,facet_id )); void painter_end ARGS((void )); int in_back ARGS((struct tsort *,struct tsort *)); int separating_plane ARGS((struct tsort *,struct tsort *,int)); int separating_line ARGS((struct tsort *,struct tsort *)); void painter_edge ARGS((struct graphdata *,edge_id)); int verpop_film ARGS((void )); void do_save ARGS((void )); void do_restore ARGS((void )); int odd4cone_pop ARGS((vertex_id,int)); void set_facet_fe ARGS((facet_id ,facetedge_id )); vertex_id new_vertex ARGS((REAL *,element_id)); edge_id new_edge ARGS((vertex_id ,vertex_id,element_id )); facet_id new_facet ARGS((void )); body_id new_body ARGS((void )); facetedge_id new_facetedge ARGS((facet_id ,edge_id )); REAL get_edge_length ARGS((edge_id )); REAL get_facet_pressure ARGS((facet_id )); #ifndef elptr struct element *elptr ARGS((element_id )); #endif void extend ARGS((int,int)); void expand ARGS((int,int )); void unfree_element ARGS((element_id)); int generate_all ARGS((int ,element_id *,element_id *)); void memory_report ARGS((void )); void reset_skeleton ARGS((void )); void vgrad_init ARGS((int )); void vgrad_end ARGS((void )); struct volgrad *get_vertex_vgrad ARGS((vertex_id)); void set_vertex_vgrad ARGS((vertex_id,struct volgrad*)); struct volgrad *get_next_vgrad ARGS((struct volgrad *)); struct volgrad *new_vgrad ARGS((void )); struct volgrad *get_bv_vgrad ARGS((int ,vertex_id )); struct volgrad *get_bv_new_vgrad ARGS((int ,vertex_id )); element_id new_element ARGS((int,element_id,element_id)); void free_element ARGS((element_id )); void edge_force_l ARGS((edge_id )); void edge_energy_l ARGS((edge_id )); void edge_area_l ARGS((edge_id )); void edge_force_l_metric ARGS((edge_id )); void edge_energy_l_metric ARGS((edge_id )); void edge_force_q_metric ARGS((edge_id )); void edge_energy_q_metric ARGS((edge_id )); void string_grad_l ARGS((void )); void string_bdry_grad ARGS((void )); void string_constr_grad ARGS((void )); REAL interpoly ARGS((int ,REAL )); REAL interpolyderiv ARGS((int ,REAL )); void scoeff_init ARGS((void )); void edge_force_q ARGS((edge_id )); void edge_energy_q ARGS((edge_id )); void edge_area_q ARGS((edge_id )); void string_grad_q ARGS((void )); void do_show ARGS((void )); int view_transform ARGS((char *)); void init_view ARGS((void )); void resize ARGS((void )); int old_menu ARGS((char * )); void extrapolate ARGS((void )); void save ARGS((void )); void restore ARGS((char *)); void torvol ARGS((void)); void torvol_project ARGS((int )); void refine ARGS((void )); void local_refine ARGS((void )); int areaweed ARGS((REAL )); void area_histogram ARGS((void )); int edgeweed ARGS((REAL )); void edge_histogram ARGS((void )); int eliminate_edge ARGS((edge_id )); void change_vertex ARGS((facetedge_id ,vertex_id ,vertex_id ,WRAPTYPE)); int articulate ARGS((REAL )); edge_id edge_refine ARGS((edge_id )); int equiangulate ARGS((void )); int ridge_notcher ARGS((REAL )); void outstring ARGS(( CONST char *)); void erroutstring ARGS((char *)); void getstring ARGS((char *,int)); void kb_error ARGS((int, char *,int )); void catcher ARGS((int )); void calc_edge ARGS((edge_id )); void get_edge_side ARGS((edge_id ,REAL *)); void get_edge_adjust ARGS((edge_id ,REAL *)); REAL calc_vertex_normal ARGS((vertex_id,facetedge_id ,REAL *)); void calc_vertex_smooth_normal ARGS((vertex_id,facetedge_id ,REAL *)); int new_calc_vertex_normal ARGS((vertex_id,REAL **)); int project_vertex_normals ARGS(( vertex_id, REAL **, int )); int simplex_vertex_normal ARGS((vertex_id,REAL **)); void get_edge_verts ARGS((edge_id ,REAL * *,WRAPTYPE*)); void get_facet_verts ARGS((facet_id ,REAL * *,WRAPTYPE*)); void get_facet_verts_special ARGS((facet_id ,REAL * *,WRAPTYPE*)); void get_facet_verts_q ARGS((facet_id ,REAL * *,WRAPTYPE*)); int verpop_str ARGS((void )); void dump_force ARGS((void)); void vertex_average ARGS((int)); void hessian_menu ARGS((void)); void hessian_auto ARGS((void)); REAL hessian_seek ARGS((REAL)); REAL hessian_line_seek ARGS((struct linsys *,REAL,REAL*)); void set_facet_body ARGS((facet_id ,body_id )); int makenode ARGS((NTYPE,NTYPE,NTYPE)); void set_body_fixvol ARGS((body_id,REAL)); facet_id get_next_body_facet ARGS((facet_id)); facet_id get_prev_body_facet ARGS((facet_id)); void set_next_vertex_facet ARGS((vertex_id,facet_id,facet_id)); void set_next_body_facet ARGS((facet_id,facet_id)); void set_prev_body_facet ARGS((facet_id,facet_id)); void set_fe_facet ARGS((facetedge_id ,facet_id )); #if !defined (INLINE) /* some functions replaced by macros in serial version */ int get_meth_offset ARGS((int)); char *get_extra ARGS((element_id,int)); char *get_extra_ptr ARGS((element_id,struct extra *)); void set_body_volconst ARGS((body_id,REAL)); void set_fe_edge ARGS((facetedge_id ,edge_id )); facet_id get_fe_facet ARGS((facetedge_id )); facetedge_id get_prev_facet ARGS((facetedge_id )); facetedge_id get_next_facet ARGS((facetedge_id )); void set_prev_edge ARGS((facetedge_id ,facetedge_id )); void set_next_edge ARGS((facetedge_id ,facetedge_id )); void set_prev_facet ARGS((facetedge_id ,facetedge_id )); void set_next_facet ARGS((facetedge_id ,facetedge_id )); void set_edge_wrap ARGS((edge_id ,WRAPTYPE )); WRAPTYPE get_edge_wrap ARGS((edge_id )); void set_edge_fe ARGS((edge_id ,facetedge_id )); facetedge_id get_edge_fe ARGS((edge_id )); void set_edge_tailv ARGS((edge_id ,vertex_id )); void set_edge_headv ARGS((edge_id ,vertex_id )); void set_edge_midv ARGS((edge_id ,vertex_id )); body_id get_facet_body ARGS((facet_id )); facetedge_id get_facet_fe ARGS((facet_id )); void set_attr ARGS((element_id ,ATTR )); void unset_attr ARGS((element_id ,ATTR )); edge_id get_fe_edge ARGS((facetedge_id)); facetedge_id get_prev_edge ARGS((facetedge_id )); facetedge_id get_next_edge ARGS((facetedge_id )); vertex_id get_edge_tailv ARGS((edge_id )); vertex_id get_edge_headv ARGS((edge_id )); edge_id get_next_tail_edge ARGS((edge_id)); edge_id get_next_head_edge ARGS((edge_id)); void set_next_tail_edge ARGS((edge_id,edge_id)); facetedge_id get_body_fe ARGS((body_id)); facetedge_id get_body_facet ARGS((body_id)); void set_body_facet ARGS((body_id,facetedge_id)); REAL get_body_density ARGS((body_id)); REAL get_body_abstotal ARGS((body_id)); REAL get_body_volume ARGS((body_id)); REAL get_body_fixvol ARGS((body_id)); REAL get_body_pressure ARGS((body_id)); REAL get_body_volconst ARGS((body_id)); void set_body_density ARGS((body_id,REAL)); void set_body_pressure ARGS((body_id,REAL)); facetedge_id get_vertex_fe ARGS((vertex_id)); #endif #ifdef __cplusplus } #endif evolver-2.30c.dfsg/src/express.h0000644000175300017530000006526211410765113017072 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /******************************************************************** * * File: express.h * * Contents: defines for expression parsing and evaluation. */ #ifdef __cplusplus extern "C" { #endif /* node types, numbered above 1512 to avoid yacc token numbers */ #define GEN_ERROR_TOKEN 10000 #define IMMEDIATE_AUTOPOP_ 2337 #define PROCEDURE_CALL_RETURN_ 2336 #define FUNCTION_CALL_RETURN_ 2335 #define SETUP_FRAME_ 2334 #define CONTENT_RANK_ 2333 #define SLICE_COEFF 2332 #define CLIP_COEFF 2331 #define AUTOPOP_QUARTIC_ 2330 #define STAR_FINAGLING_ 2329 #define FORCE_DELETION_ 2328 #define DEFINE_FIXED_LOCAL_ARRAY_ 2327 #define ARRAY_VERTEX_NORMAL_ 2326 #define ARRAY_EDGE_VECTOR_ 2325 #define ARRAY_FACET_NORMAL_ 2324 #define PRINT_ARRAY_LVALUE_ 2323 #define ARRAY_EVAL_ 2322 #define PRINT_PROFILING_ 2321 #define ARRAY_ASSIGNOP_FACET_NORMAL_ 2320 #define ARRAY_ASSIGNOP_EDGE_VECTOR_ 2319 #define ARRAY_ASSIGNOP_VERTEX_NORMAL_ 2318 #define ARRAY_ASSIGNOP_SINGLE_ 2317 #define ARRAY_LVALUE_INDEXED_ 2316 #define ATTRIB_LVALUE_ 2315 #define ARRAY_RVALUE_ 2314 #define ARRAY_ASSIGNOP_ARRAY_ 2312 #define ARRAY_ASSIGNOP_SCALAR_ 2311 #define ARRAY_ASSIGNOP_S_X_A_ 2310 #define ARRAY_ASSIGNOP_A_P_A_ 2309 #define ARRAY_ASSIGNOP_A_S_A_ 2308 #define V_HIGH_BOUNDARY 2307 #define V_HIGH_CONSTRAINT 2306 #define LOCAL_LIST_START_ 2305 #define CLIP_VIEW_ 2304 #define V_STRING_CURVE_TOLERANCE 2303 #define TEXT_SPOT_ 2302 #define WHEREAMI_COMMAND_ 2301 #define UNSET_BREAKPOINT_ 2300 #define SET_BREAKPOINT_ 2299 #define PUSH_PARAM_FIXED 2298 #define V_CORONA_STATE 2297 #define PUSH_ELEMENT_ID_ 2296 #define ELINDEX_ 2295 #define SET_CONSTRAINT_GLOBAL 2294 #define UNSET_CONSTRAINT_GLOBAL 2293 #define SET_CONSTRAINT_NAME_GLOBAL 2292 #define UNSET_CONSTRAINT_NAME_GLOBAL 2291 #define FUNCTION_QUANTITY_SPARSE_ 2290 #define SLICE_VIEW_ 2289 #define V_MPI_MAXTASK 2288 #define DISPLAY_ORIGIN_ 2287 #define V_AUTOCHOP_LENGTH 2286 #define QUIETLOAD_ 2285 #define LITTLE_ENDIAN_ 2284 #define BIG_ENDIAN_ 2283 #define BINARY_PRINTFHEAD_ 2282 #define V_FACET_REVERSE_COUNT 2281 #define V_EDGE_REVERSE_COUNT 2280 #define V_THIS_TASK 2279 #define POP_ENJOIN_ 2278 #define V_WINDOW_ASPECT_RATIO 2277 #define PRINT_SINGLE_LETTER_ 2276 #define PDELTA_LVALUE_ 2275 #define PSCALE_LVALUE_ 2274 #define INIT_VERTEX_EDGE_ 1513 #define INIT_VERTEX_FACET_ 1514 #define INIT_VERTEX_BODY_ 1515 #define INIT_EDGE_VERTEX_ 1516 #define INIT_EDGE_FACET_ 1517 #define INIT_EDGE_BODY_ 1518 #define INIT_FACET_VERTEX_ 1519 #define INIT_FACET_EDGE_ 1520 #define INIT_FACET_BODY_ 1521 #define INIT_BODY_VERTEX_ 1522 #define INIT_BODY_EDGE_ 1523 #define INIT_BODY_FACET_ 1524 #define NEXT_VERTEX_EDGE_ 1525 #define NEXT_VERTEX_FACET_ 1526 #define NEXT_VERTEX_BODY_ 1527 #define NEXT_EDGE_VERTEX_ 1528 #define NEXT_EDGE_FACET_ 1529 #define NEXT_EDGE_BODY_ 1530 #define NEXT_FACET_VERTEX_ 1531 #define NEXT_FACET_EDGE_ 1532 #define NEXT_FACET_BODY_ 1533 #define NEXT_BODY_VERTEX_ 1534 #define NEXT_BODY_EDGE_ 1535 #define NEXT_BODY_FACET_ 1536 #define NEXT_VERTEX_ 1537 #define NEXT_EDGE_ 1538 #define NEXT_FACET_ 1539 #define NEXT_BODY_ 1540 #define NEXT_ELEMENT_ 1541 #define INIT_VERTEX_ 1542 #define INIT_EDGE_ 1543 #define INIT_FACET_ 1544 #define INIT_BODY_ 1545 #define INIT_ELEMENT_ 1546 #define SET_GRAVITY_ 1547 #define SET_DIFFUSION_ 1548 #define SET_GLOBAL_ 1549 #define SET_COLOR_ 1550 #define SET_DENSITY_ 1551 #define SET_PRESSURE_ 1552 #define SET_VOLUME_ 1553 #define SET_CONSTRAINT_ 1554 #define SET_TAG_ 1555 #define SET_OPACITY_ 1556 #define SET_SCALE_ 1557 #define SET_COORD_ 1558 #define SET_COORD_1 1559 #define SET_COORD_2 1560 #define SET_COORD_3 1561 #define SET_COORD_4 1562 #define SET_COORD_5 1563 #define SET_COORD_6 1564 #define SET_COORD_7 1565 #define SET_COORD_8 1566 #define SET_PROCEDURE_ 1567 #define SET_PROC_END_ 1568 #define SET_AUTOCHOP_ 1570 #define SET_GAP_CONSTANT_ 1571 #define SET_AMBIENT_PRESSURE_ 1572 #define SET_FIXED_AREA_ 1573 #define SET_COLORMAP_ 1574 #define SET_THICKEN_ 1575 #define SET_BACKGROUND_ 1576 #define SET_OPTIMIZE_ 1577 #define SET_FIXED_ 1578 #define GET_FIXED_ 1579 #define GET_LENGTH_ 1580 #define GET_VALENCE_ 1581 #define GET_AREA_ 1582 #define GET_VOLUME_ 1583 #define GET_DENSITY_ 1584 #define GET_ID_ 1585 #define GET_TAG_ 1586 #define GET_ORIGINAL_ 1587 #define INIT_AVG_ 1588 #define INIT_SUM_ 1589 #define INIT_MAX_ 1590 #define INIT_MIN_ 1591 #define INIT_COUNT_ 1592 #define AGGREGATE_INIT_ 1593 #define AGGREGATE_END_ 1594 #define AGGREGATE_ 1595 #define SET_INIT_ 1596 #define UNSET_CONSTRAINT_ 1597 #define UNSET_BOUNDARY_ 1598 #define GET_COLOR_ 1599 #define GET_SQ_MEAN_CURV_ 1600 #define GET_INTERNAL_ 1601 #define V_VERTEXCOUNT 1602 #define V_EDGECOUNT 1603 #define V_FACETCOUNT 1604 #define V_BODYCOUNT 1605 #define V_FACETEDGECOUNT 1606 #define V_ENERGY 1607 #define V_AREA 1608 #define V_LENGTH 1609 #define V_SCALE 1610 #define LIST_PROCS_ 1611 #define PRINTFHEAD_ 1612 #define PREPRINTF_ 1613 #define EXPRLIST_ 1614 #define SPRINTFHEAD_ 1615 #define SET_SGLOBAL_ 1616 #define GET_OID_ 1617 #define GET_FRONTCOLOR_ 1618 #define GET_BACKCOLOR_ 1619 #define SET_FRONTCOLOR_ 1620 #define SET_BACKCOLOR_ 1621 #define SET_PARAM_ 1622 #define SET_PARAM_1 1623 #define SET_PARAM_2 1624 #define SET_PARAM_3 1625 #define SET_PARAM_4 1626 #define SET_PARAM_5 1627 #define SET_PARAM_6 1628 #define SET_PARAM_7 1629 #define SET_ATTRIBUTE_ 1630 #define SET_PHASE_ 1631 #define GET_PHASE_ 1632 #define LEXERROR 1633 #define PRESPRINTF_ 1634 #define V_SURFACE_DIMENSION 1635 #define V_SPACE_DIMENSION 1636 #define TOGGLEVALUE 1637 #define V_TORUS 1638 #define V_TORUS_FILLED 1639 #define V_SYMMETRY_GROUP 1640 #define V_SIMPLEX 1641 #define V_INTEGRAL_ORDER 1642 #define GET_STAR_ 1643 #define GET_PRESSURE_ 1644 #define SET_INTERNAL_ 1645 #define V_TOLERANCE 1646 #define AUTODISPLAY_ 1647 #define V_EQUI_COUNT 1648 #define V_DELETE_COUNT 1650 #define V_REFINE_COUNT 1651 #define V_NOTCH_COUNT 1652 #define V_DISSOLVE_COUNT 1653 #define V_POP_COUNT 1654 #define V_WHERE_COUNT 1655 #define PUSH_NAMED_QUANTITY 1656 #define SET_NAMED_QUANTITY_ 1657 #define GET_USERATTR_ 1658 #define GET_QUANTITY_ 1659 #define UNSET_NAMED_QUANTITY_ 1660 #define GET_WRAP_ 1661 #define FINISHED 1701 #define PUSHCONST 1702 #define PUSHPARAM 1703 #define PUSHPI 1704 #define PUSHE 1705 #define PLUS 1706 #define MINUS 1707 #define TIMES 1708 #define DIVIDE 1709 #define INTPOW 1710 #define POW 1711 #define SIN 1712 #define COS 1713 #define TAN 1714 #define SQRT 1715 #define LOG 1716 #define EXP 1717 #define COPY 1718 #define ACOS 1719 #define ASIN 1720 #define ATAN 1721 #define CHS 1722 #define INV 1723 #define SQR 1724 #define PUSHG 1725 #define EQUATE 1726 #define PUSHADJUSTABLE 1727 #define ABS 1728 #define USERFUNC 1729 #define REPEAT_ 1730 #define FULLEXPR 1731 #define SINH 1732 #define COSH 1733 #define REPLACECONST 1734 #define REALMOD 1735 #define CEIL_ 1736 #define FLOOR_ 1737 #define ATAN2_ 1738 #define NOP_ 1739 #define V_HESS_EPSILON 1740 #define HESSIAN_DIFF_ 1741 #define SHOW_INNER_ 1742 #define SHOW_OUTER_ 1743 #define CLIPPED_CELLS_ 1744 #define RAW_CELLS_ 1745 #define CONNECTED_CELLS_ 1746 #define NORMAL_MOTION_ 1747 #define RUNGE_KUTTA_ 1748 #define DETURCK_ 1749 #define KUSNER_ 1750 #define VIEW_4D_ 1751 #define CONF_EDGE_SQCURV_ 1752 #define SQGAUSS_ 1753 #define AUTOPOP_ 1754 #define OLD_AREA_ 1755 #define APPROX_CURV_ 1756 #define CHECK_INCREASE_ 1757 #define DEBUG_ 1758 #define MEMDEBUG_ 1759 #define EFFECTIVE_AREA_ 1760 #define ESTIMATE_ 1761 #define POST_PROJECT_ 1762 #define TRANSFORMS_ 1763 #define QUIET_ 1764 #define CONJ_GRAD_ 1765 #define HOMOTHETY_ 1766 #define FACET_COLORS_ 1767 #define SHADING_ 1768 #define DIV_NORMAL_CURVATURE_ 1769 #define NORMAL_CURVATURE_ 1770 #define BOUNDARY_CURVATURE_ 1771 #define SELF_SIMILAR_ 1772 #define GV_BINARY_ 1773 #define METRIC_CONVERSION_ 1774 #define AUTORECALC_ 1775 #define PINNING_ 1776 #define FORCE_POS_DEF_ 1777 #define V_SCALE_SCALE 1778 #define GET_EXTRA_ATTR_ 1779 #define SET_EXTRA_ATTR_ 1780 #define SET_ATTRIBUTE_LOOP_ 1781 #define SET_ATTRIBUTE_L 1782 #define TANH 1783 #define ATANH 1784 #define ASINH 1785 #define ACOSH 1786 #define V_ITER_COUNTER 1787 #define QUIETGO_ 1788 #define GET_MIDV_ 1789 #define RIBIERE_CG_ 1790 #define ASSUME_ORIENTED_ 1791 #define HESSIAN_QUIET_ 1792 #define CONJUNCTION_END 1793 #define JIGGLE_TOGGLE_ 1794 #define V_TIME 1795 #define V_JIG_TEMP 1796 #define SYMBOL_ELEMENT_ 1797 #define SINGLE_ELEMENT_ 1798 #define INDEXED_SUBTYPE_ 1799 #define INDEXED_ATTRIBUTE 1800 #define QUALIFIED_ATTRIBUTE 1801 #define STRPRINT_ 1802 #define GET_TRIPLE_PT_ 1804 #define SET_TRIPLE_PT_ 1805 #define GET_TETRA_PT_ 1806 #define SET_TETRA_PT_ 1807 #define UNSET_TETRA_PT_ 1808 #define UNSET_TRIPLE_PT_ 1809 #define PUSHQPRESSURE_ 1810 #define PUSHQTARGET_ 1811 #define PUSHQVALUE_ 1812 #define PUSHQMODULUS_ 1813 #define SET_QMODULUS_ 1814 #define SET_QTARGET_ 1815 #define YSMP_ 1816 #define BUNCH_KAUFMAN_ 1817 #define V_EIGENPOS 1818 #define V_EIGENNEG 1819 #define V_EIGENZERO 1820 #define QUANTITIES_ONLY_ 1821 #define EVERYTHING_QUANTITIES_ 1822 #define METRIC_CONVERT_ 1823 #define GET_TARGET_ 1824 #define MAXIMUM_ 1825 #define UNSET_FACET_BODY_ 1826 #define SET_ORIENTATION_ 1827 #define V_PICKVNUM 1828 #define V_PICKENUM 1829 #define V_PICKFNUM 1830 #define LINEAR_METRIC_ 1831 #define V_LINEAR_METRIC_MIX 1832 #define INVOKE_P_MENU_ 1833 #define GEOMVIEW_TOGGLE_ 1834 #define DO_TOP_ 1835 #define DO_END_ 1836 #define DO_ENTRY_ 1837 #define SET_MODEL_ 1838 #define V_RANDOM_SEED 1839 #define MINIMUM_ 1840 #define V_INTEGRAL_ORDER_1D 1841 #define V_INTEGRAL_ORDER_2D 1842 #define GET_ORIENTATION_ 1843 #define SET_TARGET_ 1844 #define UNSET_FIXED_ 1845 #define UNSET_DENSITY_ 1846 #define UNSET_VOLUME_ 1847 #define UNSET_PRESSURE_ 1848 #define UNSET_TARGET_ 1849 #define GET_VOLCONST_ 1850 #define SET_VOLCONST_ 1851 #define GET_TORUS_PERIODS_ 1852 #define PUSHQVOLCONST_ 1853 #define GET_FIXEDVOL_ 1854 #define SET_QVOLCONST_ 1855 #define SET_QPARAMETER_1_ 1856 #define PUSHQPARAMETER_1_ 1857 #define SINGLE_ELEMENT_INIT_ 1858 #define REDEFINE_SINGLE_ 1859 #define UNREDEFINE_SINGLE_ 1860 #define V_QUADRATIC_METRIC_MIX 1861 #define GEOMPIPE_TOGGLE_ 1862 #define V_LAST_EIGENVALUE 1863 #define V_LAST_HESSIAN_SCALE 1864 #define SQUARED_GRADIENT_ 1865 #define PRINT_LETTER_ 1866 #define REDIRECT_END_ 1867 #define PIPE_END_ 1868 #define V_LAGRANGE_ORDER 1869 #define SET_AXIAL_POINT_ 1870 #define UNSET_AXIAL_POINT_ 1871 #define GET_AXIAL_POINT_ 1872 #define H_INVERSE_METRIC_ 1873 #define HELP_KEYWORD 1874 #define SKINNY_ 1875 #define TORDUP_ 1876 #define V_GAP_CONSTANT 1877 #define V_THICKNESS 1878 #define V_TARGET_TOLERANCE 1879 #define V_CLOCK 1880 #define FIX_QUANTITY_ 1881 #define UNFIX_QUANTITY_ 1882 #define SET_ORIGINAL_ 1883 #define V_SCALE_LIMIT 1884 #define SET_MMODULUS_ 1885 #define GET_INSTANCE_ 1886 #define PUSHMMODULUS_ 1887 #define PUSHMVALUE_ 1888 #define PSCOLORFLAG_ 1889 #define GRIDFLAG_ 1890 #define CROSSINGFLAG_ 1891 #define LABELFLAG_ 1892 #define SHOW_ALL_QUANTITIES_ 1893 #define GET_INVERSE_PERIODS_ 1894 #define CREATE_EDGE_ 1895 #define SET_NO_REFINE_ 1896 #define UNSET_NO_REFINE_ 1897 #define GET_NO_REFINE_ 1898 #define CREATE_VERTEX_ 1899 #define CREATE_FACET_ 1900 #define CREATE_BODY_ 1901 #define SET_FRONTBODY_ 1902 #define SET_BACKBODY_ 1903 #define V_TRANSFORM_COUNT 1904 #define GT_ 1905 #define LT_ 1906 #define GET_BACKBODY_ 1907 #define GET_FRONTBODY_ 1908 #define GET_TRANSFORM_EXPR_ 1909 #define LOGFILE_TOGGLE_ 1910 #define SET_WRAP_ 1911 #define PLUSASSIGN_ 1912 #define SUBASSIGN_ 1913 #define MULTASSIGN_ 1914 #define DIVASSIGN_ 1915 #define NULLBLOCK_ 1916 #define SINGLE_ASSIGN_ 1917 #define SET_ATTRIBUTE_A 1918 #define SET_METHOD_INSTANCE_ 1919 #define UNSET_METHOD_INSTANCE_ 1920 #define PUSH_METHOD_INSTANCE_ 1921 #define LIST_ATTRIBUTES_ 1922 #define NULLCMD_ 1923 #define FIX_PARAMETER_ 1924 #define UNFIX_PARAMETER_ 1925 #define ITDEBUG_ 1926 #define ZENER_DRAG_ 1927 #define VOLGRADS_EVERY_ 1928 #define V_RANDOM 1929 #define PUSHQTOLERANCE_ 1930 #define SET_QTOLERANCE_ 1931 #define PUSHDELTA_ 1932 #define SET_DELTA_ 1933 #define BACKCULL_ 1934 #define V_BRIGHTNESS 1935 #define V_DIFFUSION 1936 #define DEFINE_IDENT_ 1937 #define V_BACKGROUND 1938 #define SET_NO_DISPLAY_ 1939 #define UNSET_NO_DISPLAY_ 1940 #define GET_NO_DISPLAY_ 1941 #define V_MEMARENA 1942 #define V_MEMUSED 1943 #define INDEXED_COORD_ 1944 #define V_LAST_ERROR 1945 #define VERBOSE_ 1946 #define ACOMMANDEXPR_ 1947 #define CMDLIST_ 1948 #define ULONG_TYPE_ 1949 #define INDEXED_ELEMENT_ 1950 #define MEAN_CURV_ 1951 #define REPEAT_INIT_ 1952 #define SYMATTR_ 1953 #define INIT_SUBELEMENT_ 1954 #define GET_DIHEDRAL_ 1955 #define INIT_FACETEDGE_ 1956 #define GET_EDGE_ 1957 #define GET_FACET_ 1958 #define NEXT_FACETEDGE_ 1959 #define PUSHGLOBAL_ 1960 #define COMMAND_BLOCK_ 1961 #define IFTEST_ 1962 #define WHILE_TOP_ 1963 #define WHILE_END_ 1964 #define PRINT_PROCEDURE_ 1965 #define SHOW_END_ 1966 #define AMBIENT_PRESSURE_ 1967 #define INTERP_NORMALS_ 1969 #define MEAN_CURV_INT_ 1970 #define COND_TEST_ 1971 #define COND_EXPR_ 1972 #define COND_ELSE_ 1973 #define BACKGROUND_ 1974 #define V_AMBIENT_PRESSURE 1975 #define V_HESSIAN_SLANT_CUTOFF 1976 #define WRAP_COMPOSE_ 1977 #define WRAP_INVERSE_ 1978 #define GET_VERTEXNORMAL_ 1979 #define SET_VIEW_MATRIX_ 1980 #define VIEW_MATRIX_LVALUE_ 1981 #define ACTUAL_VOLUME_ 1982 #define SELF_ELEMENT_ 1983 #define UNSET_FRONTBODY_ 1984 #define UNSET_BACKBODY_ 1985 #define V_CHECK_COUNT_ 1986 #define V_BREAKFLAG_ 1987 #define SET_CONSTRAINT_NAME 1988 #define UNSET_CONSTRAINT_NAME 1989 #define SET_BOUNDARY_NAME 1990 #define UNSET_BOUNDARY_NAME 1991 #define ON_CONSTRAINT_NAME 1992 #define HIT_CONSTRAINT_NAME 1993 #define ON_BOUNDARY_NAME 1994 #define SET_PARAM_SCALE 1995 #define PUSH_PARAM_SCALE 1996 #define EXPRINT_PROCEDURE_ 1997 #define ERRPRINTFHEAD_ 1998 #define INDEXSET_ 1999 #define DEFINE_ARRAY_ 2000 #define ARRAYEVAL 2001 #define ARRAYASSIGN 2002 #define ARRAY_HEAD_ 2003 #define VIEW_TRANSFORMS_NOP_ 2004 #define VIEW_TRANSFORMS_ELEMENT_ 2005 #define GET_SHOW_ 2006 #define ATTR_FUNCTION_ 2007 #define ATTR_FUNCTION_END_ 2008 #define SET_Q_FIXED_ 2009 #define SET_Q_ENERGY_ 2010 #define SET_Q_INFO_ 2011 #define SET_Q_CONSERVED_ 2012 #define KEYLOGFILE_TOGGLE_ 2013 #define ELLIPTICE 2014 #define ELLIPTICK 2015 #define INCOMPLETE_ELLIPTICF 2016 #define INCOMPLETE_ELLIPTICE 2017 #define V_VISIBILITY_DEBUG_ 2018 #define V_SCROLLBUFFERSIZE_ 2019 #define PUSH_PARAM_EXTRA_ 2020 #define PRINT_ARRAY_ 2021 #define SET_BARE_ 2022 #define GET_BARE_ 2023 #define UNSET_BARE_ 2024 #define V_PS_STRINGWIDTH_ 2025 #define V_PS_FIXEDEDGEWIDTH_ 2026 #define V_PS_TRIPLEEDGEWIDTH_ 2027 #define V_PS_CONEDGEWIDTH_ 2028 #define V_PS_BAREEDGEWIDTH_ 2029 #define V_PS_GRIDEDGEWIDTH_ 2030 #define SET_NONCONTENT_ 2031 #define UNSET_NONCONTENT_ 2032 #define GET_NONCONTENT_ 2033 #define BACKQUOTE_START_ 2034 #define BACKQUOTE_END_ 2035 #define DEFINE_EXTRA_INDEX_ 2036 #define FOR_END_ 2037 #define FOR_HEAD_ 2038 #define FOR_TOP_ 2039 #define FOR_ENTRY_ 2040 #define SET_PERM_GLOBAL_ 2041 #define SET_PERM_SGLOBAL_ 2042 #define PUSH_PERM_GLOBAL_ 2043 #define SET_PERM_PROCEDURE_ 2044 #define PUSH_PERM_GLOBAL 2045 #define PRINT_PERM_PROCEDURE_ 2046 #define SET_PERM_PROC_END_ 2047 #define DIMENSIONSET_ 2048 #define PRINT_ARRAYPART_ 2049 #define DECLARE_LOCAL_ 2050 #define FUNCTION_HEAD_ 2051 #define ARGLIST_ 2052 #define SET_FUNCTION_ 2053 #define SET_FUNC_END_ 2054 #define FUNCTION_START_ 2055 #define FUNCTION_DEF_START_ 2056 #define FUNCTION_EXIT_ 2057 #define FUNCTION_CALL_ 2058 #define FUNCTION_PROTO_START_ 2059 #define FUNCTION_PROTO_ 2060 #define SET_ARGSPROC_ 2061 #define SET_ARGSPROC_END_ 2062 #define PROCEDURE_START_ 2063 #define PROCEDURE_DEF_START_ 2064 #define PROCEDURE_EXIT_ 2065 #define PROCEDURE_CALL_ 2066 #define PROCEDURE_PROTO_START_ 2067 #define PROCEDURE_PROTO_ 2068 #define PROCEDURE_HEAD_ 2069 #define PRINT_ATTR_ARRAY_ 2070 #define PRINT_VERTEXNORMAL_ 2071 #define SIZEOF_ATTR_ 2072 #define SIZEOF_ARRAY_ 2073 #define SIZEOF_STRING_ 2074 #define BEZIER_BASIS_ 2075 #define GET_MEANCURV_ 2076 #define DEFINE_EXTRA_ 2077 #define SET_BOUNDARY_ 2078 #define GET_HIT_PARTNER_ 2079 #define SET_HIT_PARTNER_ 2080 #define UNSET_HIT_PARTNER_ 2081 #define DEFINE_QUANTITY_ 2082 #define DEFINE_METHOD_INSTANCE_ 2083 #define PUSHQFIXED_ 2084 #define PUSHQENERGY_ 2085 #define PUSHQINFO_ONLY_ 2086 #define PUSHQCONSERVED_ 2087 #define SMOOTH_GRAPH_ 2088 #define MPI_DEBUG_ 2089 #define GET_MPI_TASK_ 2090 #define POP_DISJOIN_ 2091 #define DEFINE_CONSTRAINT_ 2092 #define DEFINE_BOUNDARY_ 2093 #define V_DIFFUSION_ 2094 #define V_VERTEX_DISSOLVE_COUNT 2095 #define V_EDGE_DISSOLVE_COUNT 2096 #define V_FACET_DISSOLVE_COUNT 2097 #define V_BODY_DISSOLVE_COUNT 2098 #define V_EDGE_REFINE_COUNT 2099 #define V_FACET_REFINE_COUNT 2100 #define V_VERTEX_POP_COUNT 2101 #define V_EDGE_POP_COUNT 2102 #define V_POP_TRI_TO_EDGE_COUNT 2104 #define V_POP_EDGE_TO_TRI_COUNT 2105 #define V_POP_QUAD_TO_QUAD_COUNT 2106 #define V_EDGESWAP_COUNT 2107 #define V_T1_EDGESWAP_COUNT 2108 #define V_SKINNY_REFINE_COUNT 2109 #define V_VERTEX_DELETE_COUNT 2110 #define V_EDGE_DELETE_COUNT 2111 #define V_FACET_DELETE_COUNT 2112 #define V_BODY_DELETE_COUNT 2113 #define V_FIX_COUNT 2114 #define V_UNFIX_COUNT 2115 #define V_PS_LABELSIZE_ 2116 #define V_CPU_COUNTER 2117 #define GET_MID_EDGE_ 2118 #define GET_MID_FACET_ 2119 #define INIT_FACETEDGE_EDGE_ 2120 #define INIT_FACETEDGE_FACET_ 2121 #define NEXT_FACETEDGE_EDGE_ 2122 #define NEXT_FACETEDGE_FACET_ 2123 /* stuff transferred over from lex.h */ #define NO_TOKEN 0 #define WULFF_ 2257 #define PERIODS_ 2256 #define TORUS_ 2252 #define TORUS_FILLED_ 2251 #define SOAPFILM_ 2249 #define MOBILITY_ 2248 #define MOBILITY_TENSOR_ 2247 #define ENVECT_ 2245 #define CONVECT_ 2244 #define MERITFACTOR_ 2243 #define GRAV_CONST_ 2242 #define SPRING_CONSTANT_ 2241 #define TEMPERATURE_ 2239 #define FACES_ 2237 #define CONVEX_ 2230 #define NONNEGATIVE_ 2229 #define NONPOSITIVE_ 2228 #define PARAMETERS_ 2227 #define CONTENT_ 2222 #define BCOORD_ 2219 #define SURFACE_ENERGY_ 2216 #define SYMMETRIC_CONTENT_ 2215 #define SCALE_LIMIT_ 2214 #define CONSTRAINT_TOLERANCE_ 2211 #define ZOOM_VERTEX_ 2210 #define ZOOM_RADIUS_ 2209 #define QVECT_ 2206 #define SPACE_DIMENSION_ 2205 #define SURFACE_DIMENSION_ 2204 #define SIMPLEX_REP_ 2203 #define METRIC_ 2202 #define SYMMETRY_GROUP_ 2201 #define UNKNOWN 2199 #define CONFORMAL_ 2198 #define SQUARE_CURVATURE_ 2197 #define PARAMETER_FILE_ 2196 #define TOTAL_TIME_ 2191 #define PHASEFILE_ 2190 #define KLEIN_METRIC_ 2187 #define GLOBAL_METHOD_ 2185 #define EFIXED_ 2184 #define VIEW_TRANSFORM_GENS_ 2182 #define GAUSS_CURVATURE_ 2181 #define INSULATING_KNOT_ENERGY_ 2179 #define CONDUCTING_KNOT_ENERGY_ 2178 #define METHOD_ 2177 #define NONWALL_ 2176 #define SCALAR_INTEGRAND_ 2175 #define VECTOR_INTEGRAND_ 2174 #define FORM_INTEGRAND_ 2173 #define PARAMETER_1_ 2172 #define OPTIMIZING_PARAMETER_ 2171 #define K_VEC_ORDER_ 2170 #define LAGRANGE_ORDER_ 2169 #define HESSIAN_DOUBLE_NORMAL_ 2168 #define INTERP_BDRY_PARAM_ 2167 #define HESSIAN_NORMAL_ 2166 #define HESSIAN_NORMAL_ONE_ 2165 #define HESSIAN_NORMAL_PERP_ 2164 #define HESSIAN_SPECIAL_NORMAL_ 2163 #define LOAD_LIBRARY_ 2162 #define IGNORE_CONSTRAINTS_ 2161 #define VERSION_ 2160 #define KEEP_MACROS_ 2159 #define LAGRANGE_MULTIPLIER_ 2158 #define SWAP_COLORS_ 2157 #define STRING_TYPE_ 2156 #define IGNORE_FIXED_ 2155 #define KEEP_ORIGINALS_ 2154 #define ELEMENT_MODULUS_ 2153 #define VOLUME_METHOD_NAME_ 2152 #define DIRICHLET_MODE_ 2151 #define SOBOLEV_MODE_ 2150 #define KRAYNIKPOPVERTEX_FLAG_ 2148 #define KRAYNIKPOPEDGE_FLAG_ 2147 #define VERSIONTOKEN_ 2146 #define HESSIAN_SPECIAL_NORMAL_VECTOR_ 2145 #define RGB_COLORS_FLAG_ 2144 #define CIRCULAR_ARC_DRAW_ 2143 #define VISIBILITY_TEST_ 2142 #define SPARSE_CONSTRAINTS_ 2141 #define BLAS_FLAG_ 2140 #define AUGMENTED_HESSIAN_ 2139 #define AREA_METHOD_NAME_ 2138 #define LENGTH_METHOD_NAME_ 2137 #define BREAK_AFTER_WARNING_ 2136 #define PARTNER_HITTING_ 2135 #define DISPLAY_PERIODS_ 2132 /* end stuff transferred over from lex.h */ #define FULL_BOUNDING_BOX_ 2258 #define LIST_CONSTRAINT_ 2259 #define LIST_BOUNDARY_ 2260 #define LIST_QUANTITY_ 2261 #define LIST_METHOD_INSTANCE_ 2262 #define INIT_EDGE_FACETEDGE_ 2263 #define NEXT_EDGE_FACETEDGE_ 2264 #define POP_TO_FACE_ 2265 #define POP_TO_EDGE_ 2266 #define LINE_CONTINUATION 2267 #define V_MINDEG_DEBUG_LEVEL 2268 #define V_MINDEG_MARGIN 2269 #define V_MINDEG_MIN_REGION_SIZE 2270 #define SET_ELEMENT_GLOBAL_ 2271 #define SINGLE_ELEMENT_EXPR_ 2272 #define UNPUTTED_ 2273 /* for BREAK and CONTINUE */ extern int loopdepth; /* tree node for expression trees */ struct treenode { int type; /* type of node */ int left; /* left subexpression index offset */ int right; /* right subexpression index offset */ int line_no; /* line number of source file */ int file_no; /* number of source file */ int datatype; /* type of expression value */ int flags; union { int intval; /* misc. integer data */ int skipsize; /* nodes to skip over */ int indexcount; /* number of indices */ int argcount; /* number of function arguments */ int assigntype; /* for assignments */ int aggrtype; /* aggregate type */ int eltype; /* element type */ int maxsteps; /* for burchard() */ int coordnum; REAL real; /* constant value */ struct sym *symptr; /* symbol table ptrs */ int localnum; /* where element id stored*/ int extranum; /* number of extra attr */ element_id id; /* element id */ int name_id; /* name identifier */ int letter; /* for redefine */ int quant_id; /* named quantity id */ int meth_id; /* named method id */ int con_id; /* number of constraint */ int bdry_id; /* number of boundary */ int toggle_id; int toggle_state; /* ON_ or OFF_ */ int bool_val; /* 0 or 1 */ int intpow; /* integer power */ int wherecount; int userfunc; char *string; /* string value */ struct expnode enode; /* for expression */ dll_func_type funcptr; /* DLL function */ } op1; /* operand 1*/ union { int intval; /* misc. integer data */ int eltype; /* element type */ int valtype; /* data type */ int breakdepth; /* number of loop to break out of */ struct sym *symptr; /* symbol table ptrs */ char *string; int localnum; /* where loop element id stored*/ int coordnum; int attr_kind; int extranum; /* number of extra attr */ int jumpsize; int assigntype; int argcount; int indexcount; int name_id; int quant_id; int meth_id; } op2; /* operand 2 */ union { int intval[2]; /* misc. integer data */ int breakjump; int argcount; int argtype; int extra_info; /* extra attr eltype and number */ int extranum; int connum; /* constraint number */ int bdrynum; int name_id; int localnum; /* where element id stored*/ } op3; union { int contjump; int argtype; int extranum; int ret_type; int eltype; /* element type */ int name_id; } op4; union { struct sym *symptr; /* symbol table ptrs */ char *string; struct locallist_t *locals; /* for procedures */ } op5; int stack_delta; /* what node does to runtime stack */ #ifdef _DEBUG int stack_spot; /* where stack should be after evaluation of this node. */ #endif int stackpos; /* local variable on stack for storing stack position for chopping stack after break or continue */ }; /* flags, also used for expnode */ #define LOCAL_VAR_REF 4 #define LOCAL_VAR_REF_2 8 #define LOCAL_VAR_REF_3 0x10 #define HAS_STRING 0x20 #define EPHEMERAL 0x40 /* refers to a nonpermanent name */ #define PERMNODE 0x80 /* part of permanent command */ #define HAS_LOCALLIST 0x100 #define HAS_STRING_5 0x200 #define DEALLOCATE_POINTER 0x400 #define IN_ELEMENT_LOOP 0x800 #define BREAKPOINT_NODE 0x1000 #define IS_RVALUE 0x2000 #define SET_ASSIGNOP 0x4000 #define RECALC_NODE 0x8000 #define IS_VIRTUAL_ATTR 0x10000 /* for some bit packing */ #define ESHIFT 12 /* for some type and number packing */ #define YYTYPESHIFT 25 #define YYSHIFTMASK (((1<id,e2; REAL *x1,*x2,*yy1,*y2; /* end coordinates */ REAL power; REAL energy = 0.0; REAL dx1[MAXCOORD]; REAL dx2[MAXCOORD]; REAL r[MAXCOORD]; REAL LL1,L1,LL2,L2,rr,re1,re2; REAL p; REAL b1,b2,c1,c2; int j; power = globals(exponent_param)->value.real; x1 = get_coord(get_edge_tailv(e1)); x2 = get_coord(get_edge_headv(e1)); for ( j = 0 ; j < SDIM ; j++ ) dx1[j] = x2[j] - x1[j]; LL1 = SDIM_dot(dx1,dx1); L1 = sqrt(LL1); FOR_ALL_EDGES(e2) { if ( e2 <= e1 ) continue; /* each pair once */ yy1 = get_coord(get_edge_tailv(e2)); y2 = get_coord(get_edge_headv(e2)); for ( j = 0 ; j < SDIM ; j++ ) dx2[j] = y2[j] - yy1[j]; LL2 = SDIM_dot(dx2,dx2); L2 = sqrt(LL2); for ( j = 0 ; j < SDIM ; j++ ) r[j] = (yy1[j] + y2[j] - x1[j] - x2[j])/2; rr = SDIM_dot(r,r); re1 = SDIM_dot(r,dx1); re2 = SDIM_dot(r,dx2); p = pow(rr ,-power/2); b1 = 1 - re1*re1/rr/LL1; b2 = 1 - re2*re2/rr/LL2; c1 = pow(b1,power/2); c2 = pow(b2,power/2); energy += L1*L2*p*(c1 + c2); } return 2*energy; /* since doing each pair once */ } /************************************************************** * * function: proj_knot_energy_gradient() * * purpose: calculates energy of one edge due to potential * with all others. * * input: info about edge is in qinfo structure. * */ REAL proj_knot_energy_gradient(e_info) struct qinfo *e_info; { edge_id e1 = e_info->id,e2; REAL *x1,*x2,*yy1,*y2; /* end coordinates */ REAL power,poww; REAL energy = 0.0; REAL dx1[MAXCOORD]; REAL dx2[MAXCOORD]; REAL r[MAXCOORD]; REAL LL1,L1,LL2,L2,rr,re1,re2; REAL p,en; REAL c1,c2; REAL b1,b2; int i,j; power = globals(exponent_param)->value.real; poww = power/2; for ( i = 0 ; i < 2 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) e_info->grad[i][j] = 0.0; x1 = get_coord(get_edge_tailv(e1)); x2 = get_coord(get_edge_headv(e1)); for ( j = 0 ; j < SDIM ; j++ ) dx1[j] = x2[j] - x1[j]; LL1 = SDIM_dot(dx1,dx1); L1 = sqrt(LL1); FOR_ALL_EDGES(e2) { if ( e2 == e1 ) continue; yy1 = get_coord(get_edge_tailv(e2)); y2 = get_coord(get_edge_headv(e2)); for ( j = 0 ; j < SDIM ; j++ ) dx2[j] = y2[j] - yy1[j]; LL2 = SDIM_dot(dx2,dx2); L2 = sqrt(LL2); for ( j = 0 ; j < SDIM ; j++ ) r[j] = (yy1[j] + y2[j] - x1[j] - x2[j])/2; rr = SDIM_dot(r,r); re1 = SDIM_dot(r,dx1); re2 = SDIM_dot(r,dx2); p = pow(rr ,-power/2); b1 = 1 - re1*re1/rr/LL1; if ( b1 <= 0.0 ) continue; b2 = 1 - re2*re2/rr/LL2; if ( b2 <= 0.0 ) continue; if ( poww == 0.0 ) c1 = c2 = 1.0; else { c1 = pow(b1,poww); c2 = pow(b2,poww); } en = L1*L2*p*(c1*b1 + c2*b2); energy += en; for ( j = 0 ; j < SDIM ; j++ ) { e_info->grad[0][j] += 2*en/LL1*(-dx1[j]) + (-power)*en/rr*(-r[j]) + L1*L2*p*(power)*c1*(-1)* (-re1*re1/LL1/rr/rr*(-r[j]) -2*re1*re1/rr/LL1/LL1*(-dx1[j]) + re1/rr/LL1*(-dx1[j] - 2*r[j])) + L1*L2*p*(power)*c2*(-1)* (-re2*re2/LL2/rr/rr*(-r[j]) + re2/rr/LL2*(-dx2[j])); e_info->grad[1][j] += 2*en/LL1*(dx1[j]) + (-power)*en/rr*(-r[j]) + L1*L2*p*(power)*c1*(-1)* (-re1*re1/LL1/rr/rr*(-r[j]) -2*re1*re1/rr/LL1/LL1*(dx1[j]) + re1/rr/LL1*(-dx1[j] + 2*r[j])) + L1*L2*p*(power)*c2*(-1)* (-re2*re2/LL2/rr/rr*(-r[j]) + re2/rr/LL2*(-dx2[j])); } } return energy; /* since doing all pairs */ } /****************************************************************** Local hooke energy Suggested and programmed by John Sullivan Energy tries to equalize lengths of edges coming in to any one vertex */ /*************************************************************** * * function: local_hooke_init() * * purpose: initialization for local_hooke() * * Get global variable local_hooke_flag */ static int local_hooke_flag; #define HOOKE_FLAG_NAME "local_hooke_flag" void local_hooke_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { int flag_var; flag_var = lookup_global(HOOKE_FLAG_NAME); if ( flag_var < 0 ) /* missing, so add */ { flag_var = add_global(HOOKE_FLAG_NAME); globals(flag_var)->value.real = 0.0; /* default */ globals(flag_var)->flags |= ORDINARY_PARAM | RECALC_PARAMETER | ALWAYS_RECALC; } local_hooke_flag = (int)globals(flag_var)->value.real; } /************************************************************** * * function: local_hooke() * * purpose: calculates local_hooke number of one pair of edges. * * input: info about edge is in qinfo structure. * */ REAL local_hooke(v_info) struct qinfo *v_info; { vertex_id v0 = v_info->id; edge_id e1,e2; REAL l1,l2,energy; if ((!local_hooke_flag) && (get_vertex_evalence(v0) != 2)) return 0.; /* for now calculation is wrong if flag is set */ /* e1 = get_vertex_edge(v0); */ e1 = get_fe_edge(get_vertex_fe(v0)); e2 = get_next_tail_edge(e1); l1 = get_edge_length(e1); l2 = get_edge_length(e2); energy = (l1-l2)/(l1+l2); energy = energy*energy; return energy; } /************************************************************** * * function: local_hooke_gradient() * * purpose: calculates energy gradient of one edge due to potential * with all others. Not yet implemented. * * input: info about edge is in qinfo structure. * */ REAL local_hooke_gradient(v_info) struct qinfo *v_info; { vertex_id v0 = v_info->id; edge_id e1,e2, e0,e3; REAL l1,l2,grad,sum,energy, l0,l3; REAL ev1[MAXCOORD], ev2[MAXCOORD]; int i; for (i=0; igrad[0][i] = 0.; if (!local_hooke_flag && (get_vertex_evalence(v0) != 2)) return 0.; /* for now calculation is wrong if flag is set */ e1 = get_fe_edge(get_vertex_fe(v0)); e2 = get_next_tail_edge(e1); l1 = get_edge_length(e1); l2 = get_edge_length(e2); sum = l1+l2; energy = (l1-l2)/sum; grad = 4*energy/sum/sum; /* dbydl1 = grad*l2; dbydl2 = -grad*l1 */ get_edge_side(e1,ev1); get_edge_side(e2,ev2); for (i=0; igrad[0][i] = -ev1[i]*grad*l2/l1 + ev2[i]*grad*l1/l2; e0 = get_next_head_edge(e1); e3 = get_next_head_edge(e2); /* ---><---.---><--- ** ** e3 e2 v e1 e0 */ l0 = get_edge_length(e0); l3 = get_edge_length(e3); sum = l0+l1; grad = 4*(l0-l1)/sum/sum/sum; for (i=0; igrad[0][i] += ev1[i]*grad*l0/l1; sum = l2+l3; grad = 4*(l2-l3)/sum/sum/sum; for (i=0; igrad[0][i] -= ev2[i]*grad*l3/l2; return energy*energy; } /****************************************************************** average crossing number for knots Suggested by Doug Zare Programmed by John Sullivan Between pairs of edges, energy is inverse cube power of distance between midpoints of edges, times triple product of edge vectors and distance vector. E = 1/d^3 * (e1,e2,d) */ /************************************************************** * * function: average_crossing() * * purpose: calculates average crossing number of one pair of edges. * * input: info about edge is in qinfo structure. * */ REAL average_crossing(e_info) struct qinfo *e_info; { edge_id e1 = e_info->id,e2; REAL *x1,*x2,*yy1,*y2; /* end coordinates */ REAL ee,energy = 0.0; REAL dx1[MAXCOORD]; REAL dx2[MAXCOORD]; REAL LL1,LL2,dd,de1,de2; REAL e1e2; int j; x1 = get_coord(get_edge_tailv(e1)); x2 = get_coord(get_edge_headv(e1)); for ( j = 0 ; j < SDIM ; j++ ) dx1[j] = x2[j] - x1[j]; LL1 = SDIM_dot(dx1,dx1); FOR_ALL_EDGES(e2) { if ( e2 <= e1 ) continue; /* each pair once */ yy1 = get_coord(get_edge_tailv(e2)); y2 = get_coord(get_edge_headv(e2)); LL2 = dd = de1 = de2 = e1e2 = 0.0; for ( j = 0 ; j < SDIM ; j++ ) { REAL rj; dx2[j] = y2[j] - yy1[j]; LL2 += dx2[j]*dx2[j]; rj = (yy1[j] + y2[j] - x1[j] - x2[j])/2; dd += rj*rj; de1 += rj*dx1[j]; de2 += rj*dx2[j]; e1e2 += dx1[j]*dx2[j]; } ee = (LL1*LL2*dd + 2*e1e2*de1*de2 - LL1*de2*de2 - LL2*de1*de1 - dd*e1e2*e1e2)/dd; if (ee>0.) energy += sqrt(ee)/dd; } return energy/2/M_PI; } /***************************************************** twisting number for curves Suggested and programmed by John Sullivan Integral of torsion is approximated by looking at triples of adjacent edges; if A,B,C are the edge vectors, then the sin of the angle the osculating plane twists by (from AxB to BxC) is [A,B,C] |B| ----------- |AxB| |BxC| (This is analogous to t = [T,T',T'']/k^2.) This function assumes the edges in each component are consistently oriented. Gradient not implemented. */ /************************************************************** * * function: twist() * * purpose: calculates average crossing number of one pair of edges. * * input: info about edge is in qinfo structure. * */ REAL twist(e_info) struct qinfo *e_info; { edge_id e1 = e_info->id, e0,e2; REAL a0[MAXCOORD],a1[MAXCOORD],a2[MAXCOORD]; /* edge vectors */ REAL a01[MAXCOORD], a12[MAXCOORD]; REAL l0 = 0.0, l1 = 0.0, l2 = 0.0; int j; e0 = get_next_tail_edge(e1); e2 = get_next_head_edge(e1); get_edge_side(e0,a0); get_edge_side(e1,a1); get_edge_side(e2,a2); cross_prod(a0,a1,a01); cross_prod(a1,a2,a12); for ( j = 0 ; j < SDIM ; j++ ) { l0 += a01[j]*a01[j]; l1 += a1[j]*a1[j]; l2 += a12[j]*a12[j]; } if ( l0*l2 == 0.0 ) return 0.0; return asin(triple_prod(a0,a1,a2)*sqrt(l1/l0/l2))/2/M_PI; } /***************************************************** writhing number for curves Suggested by Hermann Gluck Programmed by John Sullivan Between pairs of edges, energy is inverse cube power of distance between midpoints of edges, times triple product of edge vectors and distance vector. E = 1/d^3 * (e1,e2,d) */ /************************************************************** * * function: writhe() * * purpose: calculates average crossing number of one pair of edges. * * input: info about edge is in qinfo structure. * */ REAL writhe(e_info) struct qinfo *e_info; { edge_id e1 = e_info->id,e2; vertex_id e1h,e2h,e1t,e2t; REAL *x1,*x2,*yy1,*y2; /* end coordinates */ REAL energy = 0.0; REAL dx1[MAXCOORD]; REAL dx2[MAXCOORD]; REAL d[MAXCOORD]; REAL dd; int j; e1t = get_edge_tailv(e1); e1h = get_edge_headv(e1); x1 = get_coord(e1t); x2 = get_coord(e1h); for ( j = 0 ; j < SDIM ; j++ ) dx1[j] = x2[j] - x1[j]; FOR_ALL_EDGES(e2) { if ( e2 == e1 ) continue; /* equal edges */ e2t = get_edge_tailv(e2); e2h = get_edge_headv(e2); if (e2t==e1h || e2h==e1t) continue; /* adjacent edges */ yy1 = get_coord(e2t); y2 = get_coord(e2h); dd = 0.0; for ( j = 0 ; j < SDIM ; j++ ) { dx2[j] = y2[j] - yy1[j]; d[j] = (yy1[j] + y2[j] - x1[j] - x2[j])/2; dd += d[j]*d[j]; } energy += triple_prod(dx1,d,dx2)/dd/sqrt(dd); } return energy/4/M_PI; } /************************************************************** * * function: writhe_gradient() * * purpose: calculates gradient of writhe for one edge * * input: info about edge is in qinfo structure. * */ REAL writhe_gradient(e_info) struct qinfo *e_info; { edge_id e1 = e_info->id,e2; vertex_id e1h,e2h,e1t,e2t; REAL *x1,*x2,*yy1,*y2; /* end coordinates */ REAL triple,ee,energy = 0.0; REAL dx1[MAXCOORD]; REAL dx2[MAXCOORD]; REAL d[MAXCOORD]; REAL e1xe2[MAXCOORD]; REAL dxe2[MAXCOORD]; REAL dd; int i,j; e1t = get_edge_tailv(e1); e1h = get_edge_headv(e1); x1 = get_coord(e1t); x2 = get_coord(e1h); for ( i = 0 ; i < 2 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) e_info->grad[i][j] = 0.0; for ( j = 0 ; j < SDIM ; j++ ) dx1[j] = x2[j] - x1[j]; FOR_ALL_EDGES(e2) { if ( e2 == e1 ) continue; /* equal edges */ e2t = get_edge_tailv(e2); e2h = get_edge_headv(e2); if (e2t==e1h || e2h==e1t) continue; /* adjacent edges */ yy1 = get_coord(e2t); y2 = get_coord(e2h); dd = 0.0; for ( j = 0 ; j < SDIM ; j++ ) { dx2[j] = y2[j] - yy1[j]; d[j] = (yy1[j] + y2[j] - x1[j] - x2[j])/2; dd += d[j]*d[j]; } triple = triple_prod(dx1,d,dx2); ee = triple/dd/sqrt(dd); energy += ee; cross_prod(dx1,dx2,e1xe2); cross_prod(d,dx2,dxe2); for ( j = 0 ; j < SDIM ; j++ ) { register REAL common1 = 3*ee/dd * d[j]/2; register REAL common2 = e1xe2[j]/dd/sqrt(dd)/2; register REAL oppose = dxe2[j]/dd/sqrt(dd); e_info->grad[0][j] += (common1 + common2 - oppose)/2/M_PI; e_info->grad[1][j] += (common1 + common2 + oppose)/2/M_PI; } } return energy/4/M_PI; } /****************************************************************** true average crossing number and writhe for knots Suggested and Programmed by John Sullivan Between pairs of edges, energy is area of spherical quadrilateral of visibility from one to the other. Thus if endpoints are a,b and c,d, let q1=d-a, q2=c-a, q3=c-b, q4=d-b. Then let ci = (q(i-1) x qi) . (qi x q(i+1)) / |q(i-1) x qi| |qi x q(i+1)| This is the cosine of one external angle of the spherical quadrilateral. Thus the area is 2pi - sum(arccos(ci)) Actually, since arccos always gives a positive angle, this assumes the quadrilateral is convex; luckily it will be. Actually, for writhe we want to recover the sign of these angles, to see if we're tracing the quadrilateral clockwise or counterclockwise. This sign just comes from the sign of the volume of the tetrahedron abcd. */ /************************************************************** * * function: true_average_crossing() * * purpose: calculates average crossing number of one pair of edges. * * input: info about edge is in qinfo structure. * */ REAL true_ax_wr ARGS((struct qinfo *, int)); REAL true_ax_wr(e_info, absval) struct qinfo *e_info; int absval; { edge_id e1 = e_info->id,e2; vertex_id e1t,e1h,e2t,e2h; REAL *a,*b,*c,*d; /* end coordinates */ REAL energy = 0.0; REAL q[4][MAXCOORD]; REAL qq[4]; REAL qr[4]; REAL pr[4]; REAL sum; int i,j; for ( i = 0 ; i < 4 ; i++ ) qq[i] = qr[i] = pr[i] = 0.0; e1t = get_edge_tailv(e1); e1h = get_edge_headv(e1); a = get_coord(e1t); b = get_coord(e1h); FOR_ALL_EDGES(e2) { if ( e2 <= e1 ) continue; /* each pair once */ e2t = get_edge_tailv(e2); e2h = get_edge_headv(e2); if (e2t==e1h || e2h==e1t) continue; /* adjacent edges */ c = get_coord(e2t); d = get_coord(e2h); for ( j = 0 ; j < SDIM ; j++ ) { q[0][j] = d[j]-a[j]; q[1][j] = c[j]-a[j]; q[2][j] = c[j]-b[j]; q[3][j] = d[j]-b[j]; } for (i=0; i<4; i++) { int im1 = (i-1)&3, ip1 = (i+1)&3; qq[i] = SDIM_dot(q[i],q[i]); qr[i] = SDIM_dot(q[i],q[ip1]); pr[i] = SDIM_dot(q[im1],q[ip1]); } sum=0.; for (i=0; i<4; i++) { int im1 = (i-1)&3, ip1 = (i+1)&3; REAL denom=(qq[im1]*qq[i]-qr[im1]*qr[im1])*(qq[i]*qq[ip1]-qr[i]*qr[i]); REAL cosine; if (denom>0.) cosine = (qr[im1]*qr[i] - pr[i]*qq[i]) / sqrt(denom); else cosine = 0.; if (cosine>1.) cosine=1.; else if (cosine<-1.) cosine=-1.; sum += acos(cosine); } sum = 1-sum/2/M_PI; if (!absval && triple_prod(q[0],q[1],q[2]) < 0.) sum = -sum; energy += sum; } return energy; } REAL true_average_crossing(e_info) struct qinfo *e_info; { return true_ax_wr(e_info, 1); } REAL true_writhe(e_info) struct qinfo *e_info; { return true_ax_wr(e_info, 0); } #ifdef XXXX /************************************************************** * * function: true_writhe_gradient() * * purpose: calculates gradient of writhe for one edge * * input: info about edge is in qinfo structure. */ NOT IMPLEMENTED YET REAL true_writhe_gradient(e_info) struct qinfo *e_info; { edge_id e1 = e_info->id,e2; vertex_id e1h,e2h,e1t,e2t; REAL *x1,*x2,*yy1,*y2; /* end coordinates */ REAL triple,ee,energy = 0.0; REAL dx1[MAXCOORD]; REAL dx2[MAXCOORD]; REAL d[MAXCOORD]; REAL e1xe2[MAXCOORD]; REAL dxe2[MAXCOORD]; REAL dd; int i,j; e1t = get_edge_tailv(e1); e1h = get_edge_headv(e1); x1 = get_coord(e1t); x2 = get_coord(e1h); for ( i = 0 ; i < 2 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) e_info->grad[i][j] = 0.0; for ( j = 0 ; j < SDIM ; j++ ) dx1[j] = x2[j] - x1[j]; FOR_ALL_EDGES(e2) { if ( e2 == e1 ) continue; /* equal edges */ e2t = get_edge_tailv(e2); e2h = get_edge_headv(e2); if (e2t==e1h || e2h==e1t) continue; /* adjacent edges */ yy1 = get_coord(e2t); y2 = get_coord(e2h); dd = 0.0; for ( j = 0 ; j < SDIM ; j++ ) { dx2[j] = y2[j] - yy1[j]; d[j] = (yy1[j] + y2[j] - x1[j] - x2[j])/2; dd += d[j]*d[j]; } triple = triple_prod(dx1,d,dx2); ee = triple/dd/sqrt(dd); energy += ee; cross_prod(dx1,dx2,e1xe2); cross_prod(d,dx2,dxe2); for ( j = 0 ; j < SDIM ; j++ ) { register REAL common1 = 3*ee/dd * d[j]/2; register REAL common2 = e1xe2[j]/dd/sqrt(dd)/2; register REAL oppose = dxe2[j]/dd/sqrt(dd); e_info->grad[0][j] += (common1 + common2 - oppose)/2/M_PI; e_info->grad[1][j] += (common1 + common2 + oppose)/2/M_PI; } } return energy/4/M_PI; } #endif evolver-2.30c.dfsg/src/klein.c0000644000175300017530000002540311410765113016467 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /****************************************************************** * These functions calculate the length of an edge in the Klein * model of the hyperbolic plane. */ #include "include.h" #undef SQR #define SQR(a) ((a)*(a)) REAL coshKleinLength ARGS((REAL*,REAL*)); /***************************************************************** * This function takes the inverse hyperbolic cosine of a number * who's value is greater than or equal to one and returns the * positive answer. Included here since some libraries don't * have it or long double version. */ REAL kb_acosh ARGS((REAL)); /* prototype */ REAL kb_acosh(value) REAL value; /* the value who's acosh is being taken */ { if(value>=1) return(log(value + sqrt(SQR(value) - 1))); else return(0); } /**************************************************************** * This function finds the hyperbolic cosine of an edge in the Klein * model of the hyperbolic plane. It is used to calculate edge * lengths and angles in the model. */ REAL coshKleinLength(head, tail) REAL *head; /* coordinates of edge */ REAL *tail; { REAL num; /* numerator and den0minator of returned value */ REAL den; REAL temp; REAL retval; num = 1 - SDIM_dot(head,tail); den = 1 - SDIM_dot(head,head); temp = 1 - SDIM_dot(tail,tail); if((den*=temp)>0) { den = sqrt(den); retval = num/den; } else retval = 0.0; if ( retval < 1.0 ) kb_error(2087,"Points outside unit disk in Klein model.\n",RECOVERABLE); return(retval); } /*********************************************************************** * This function finds the length of an edge in the klein model of * hyperbolic space. */ REAL klein_length(head, tail) REAL *head; /* coordinates of edge */ REAL *tail; { return(kb_acosh(coshKleinLength(head, tail))); } /********************************************************************** * This function finds the contribution to the gradient of the edge with * endpoints tail and head at those endpoints. * Actually, this adds neg grad to output. */ void klein_length_grad(head, tail, head_grad, tail_grad) REAL *head, *tail; /* coordinates of edge */ REAL *head_grad, *tail_grad; /* gradients of head and tail */ { int i; REAL aa,bb,ab,den,disc; aa = 1 - SDIM_dot(head,head); bb = 1 - SDIM_dot(tail,tail); ab = 1 - SDIM_dot(head,tail); disc = ab*ab - aa*bb; if ( disc == 0.0 ) { kb_error(3107,"Vertices outside Klein disk, or zero length edge.\n", WARNING); return; } if ( disc < 0.0 ) { kb_error(1664,"Vertices outside Klein disk.\n",WARNING); return; } den = sqrt(disc); for ( i = 0 ; i < SDIM ; i++ ) { head_grad[i] -= (ab*head[i]/aa- tail[i])/den; tail_grad[i] -= (ab*tail[i]/bb - head[i])/den; } } /*********************************************************************** * This function finds the area of a triangle in the klein model of the * hyperbolic plane by using the formula: * AREA = PI - angle1 - angle2 - angle3 * * Angles found via hyperbolic law of cosines * * cosh(C) = cosh(A)cosh(B) + sinh(A)sinh(B)cos(c) */ REAL klein_area(triangle) REAL **triangle; { REAL area; /* returned area */ int i; /* loop iterator */ int v; /* loop iterator */ int s; /* side opposite vertex */ REAL coshs[MAXCOORD], /* the coshs of the sides */ sinhs[MAXCOORD], /* the sinhs of the sides */ coss[MAXCOORD]; /* cosines of vertices */ for ( s = 0 ; s < 3 ; s++ ) { coshs[s] = coshKleinLength(triangle[(s+1)%3], triangle[(s+2)%3]); sinhs[s] = sqrt(coshs[s]*coshs[s] - 1); if ( sinhs[s] == 0.0 ) return 0.0; } for(v=0;v<3;v++) coss[v] = (coshs[(v+1)%3]*coshs[(v+2)%3] - coshs[v]) / sinhs[(v+1)%3]/sinhs[(v+2)%3]; area = M_PI; for(i=0;i<3;i++) area -= acos(coss[i]); return(area); } /************************************************************************** * This function calculates the area gradient at the vertices of a triangle. */ void klein_area_grad(triangle, force) REAL **triangle; /* the face */ REAL **force; /* the gradients */ { int v; /* loop iterator */ int s; /* side opposite vertex */ int k,j; /* indices */ REAL coshs[MAXCOORD], /* the coshs of the sides */ sinhs[MAXCOORD], /* the sinhs of the sides */ coss[MAXCOORD], /* cosines of vertices */ sins[MAXCOORD], /* sines of vertices */ ngrad[3][3][MAXCOORD]; /* neg grad of side wrt vertex */ for ( s = 0 ; s < 3 ; s++ ) { coshs[s] = coshKleinLength(triangle[(s+1)%3], triangle[(s+2)%3]); sinhs[s] = sqrt(coshs[s]*coshs[s] - 1); if ( sinhs[s] == 0.0 ) return ; } for(v=0;v<3;v++) { coss[v] = (coshs[(v+1)%3]*coshs[(v+2)%3] - coshs[v]); coss[v] /= sinhs[(v+1)%3]*sinhs[(v+2)%3]; sins[v] = sqrt(1 - coss[v]*coss[v]); } memset((char*)ngrad,0,sizeof(ngrad)); for ( s = 0 ; s < 3 ; s++ ) klein_length_grad(triangle[(s+1)%3],triangle[(s+2)%3], ngrad[s][(s+1)%3],ngrad[s][(s+2)%3]); for ( v = 0 ; v < 3 ; v++ ) /* vertex angle */ { int vb = (v+1)%3; int vc = (v+2)%3; REAL denom = sinhs[vb]*sinhs[vc]*sins[v]; REAL coeffa=sinhs[v]/denom; REAL coeffb=(coshs[vc]-coshs[v]*coshs[vb])/sinhs[vb]/denom; REAL coeffc=(coshs[vb]-coshs[v]*coshs[vc])/sinhs[vc]/denom; for ( k = 0 ; k < 3 ; k++ ) /* variable vertex */ for ( j = 0 ; j < SDIM ; j++ ) /* coordinate */ { force[k][j] -= coeffb*ngrad[vb][k][j] + coeffc*ngrad[vc][k][j] + coeffa*ngrad[v][k][j]; } } } /************************************************************************** Named quantity methods for 2D and 3D **************************************************************************/ /*********************************************************************** * This function finds the length of an edge in the klein model of * hyperbolic space. */ REAL klein_length_method(e_info) struct qinfo *e_info; { REAL area; area = kb_acosh(coshKleinLength(e_info->x[1], e_info->x[0])); if ( everything_quantities_flag && (METH_INSTANCE(e_info->method)->quant == default_area_quant_num) ) area *= get_edge_density(e_info->id); return(area); } /********************************************************************** * This function finds the contribution to the gradient of the edge with * endpoints tail and head at those endpoints. */ REAL klein_length_method_grad(e_info) struct qinfo *e_info; { int i; REAL aa,bb,ab,den,disc; REAL *head = e_info->x[1]; REAL *tail = e_info->x[0]; REAL fudge; if ( everything_quantities_flag && (METH_INSTANCE(e_info->method)->quant == default_area_quant_num) ) fudge = get_edge_density(e_info->id); else fudge = 1.0; aa = 1 - SDIM_dot(head,head); bb = 1 - SDIM_dot(tail,tail); ab = 1 - SDIM_dot(head,tail); disc = ab*ab - aa*bb; if ( disc == 0.0 ) { kb_error(3359,"Vertices outside Klein disk, or zero length edge.\n", WARNING); return 0.0; } if ( disc < 0.0 ) { kb_error(1665,"Vertices outside Klein disk.\n",WARNING); return 0.0; } den = sqrt(disc); for ( i = 0 ; i < SDIM ; i++ ) { e_info->grad[1][i] += fudge*(ab*head[i]/aa - tail[i])/den; e_info->grad[0][i] += fudge*(ab*tail[i]/bb - head[i])/den; } return(fudge*kb_acosh(coshKleinLength(head, tail))); } /*********************************************************************** * This function finds the area of a triangle in the klein model of the * hyperbolic plane by using the formula: * AREA = PI - angle1 - angle2 - angle3 */ REAL klein_area_method(f_info) struct qinfo *f_info; { REAL area; /* returned area */ int i; /* loop iterator */ int v; /* loop iterator */ int s; /* side opposite vertex */ REAL **triangle = f_info->x; REAL coshs[MAXCOORD], /* the coshs of the sides */ sinhs[MAXCOORD], /* the sinhs of the sides */ coss[MAXCOORD]; /* cosines of vertices */ for ( s = 0 ; s < 3 ; s++ ) { coshs[s] = coshKleinLength(triangle[(s+1)%3], triangle[(s+2)%3]); sinhs[s] = sqrt(coshs[s]*coshs[s] - 1); if ( sinhs[s] == 0.0 ) return 0.0; } for(v=0;v<3;v++) coss[v] = (coshs[(v+1)%3]*coshs[(v+2)%3] - coshs[v]) / sinhs[(v+1)%3]/sinhs[(v+2)%3]; area = M_PI; for(i=0;i<3;i++) area -= acos(coss[i]); if ( everything_quantities_flag && (METH_INSTANCE(f_info->method)->quant == default_area_quant_num) ) area *= get_facet_density(f_info->id); return(area); } /************************************************************************** * This function calculates the area gradient at the vertices of a triangle. */ REAL klein_area_method_grad(f_info) struct qinfo *f_info; { REAL area; int v; /* loop iterator */ int s; /* side opposite vertex */ int i, k,j; /* indices */ REAL **triangle = f_info->x; REAL coshs[MAXCOORD], /* the coshs of the sides */ sinhs[MAXCOORD], /* the sinhs of the sides */ coss[MAXCOORD], /* cosines of vertices */ sins[MAXCOORD], /* sines of vertices */ ngrad[3][3][MAXCOORD]; /* neg grad of side wrt vertex */ REAL fudge; if ( everything_quantities_flag && (METH_INSTANCE(f_info->method)->quant == default_area_quant_num) ) fudge = get_facet_density(f_info->id); else fudge = 1.0; for ( s = 0 ; s < 3 ; s++ ) { coshs[s] = coshKleinLength(triangle[(s+1)%3], triangle[(s+2)%3]); sinhs[s] = sqrt(coshs[s]*coshs[s] - 1); if ( sinhs[s] == 0.0 ) return 0.0 ; } for(v=0;v<3;v++) { coss[v] = (coshs[(v+1)%3]*coshs[(v+2)%3] - coshs[v]); coss[v] /= sinhs[(v+1)%3]*sinhs[(v+2)%3]; sins[v] = sqrt(1 - coss[v]*coss[v]); } memset((char*)ngrad,0,sizeof(ngrad)); for ( s = 0 ; s < 3 ; s++ ) klein_length_grad(triangle[(s+1)%3],triangle[(s+2)%3], ngrad[s][(s+1)%3],ngrad[s][(s+2)%3]); for ( v = 0 ; v < 3 ; v++ ) /* vertex angle */ { int vb = (v+1)%3; int vc = (v+2)%3; REAL denom = sinhs[vb]*sinhs[vc]*sins[v]; REAL coeffa=sinhs[v]/denom; REAL coeffb=(coshs[vc]-coshs[v]*coshs[vb])/sinhs[vb]/denom; REAL coeffc=(coshs[vb]-coshs[v]*coshs[vc])/sinhs[vc]/denom; for ( k = 0 ; k < 3 ; k++ ) /* variable vertex */ for ( j = 0 ; j < SDIM ; j++ ) /* coordinate */ { f_info->grad[k][j] += fudge*(coeffb*ngrad[vb][k][j] + coeffc*ngrad[vc][k][j] + coeffa*ngrad[v][k][j]); } } area = M_PI; for(i=0;i<3;i++) area -= acos(coss[i]); return(fudge*area); } evolver-2.30c.dfsg/src/xgraph.c0000644000175300017530000002206211410765113016654 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************ * * File: xgraph.c * * Contents: Graphics routines for display on X-Windows. */ /* From bink@athena.mit.edu Wed Apr 10 16:05:07 1991 Received: from ATHENA.MIT.EDU by poincare.susqu.edu ; Wed, 10 Apr 91 16:04:59 CDT Received: from M4-167-10.MIT.EDU by ATHENA.MIT.EDU with SMTP id AA09847; Wed, 10 Apr 91 17:04:52 EDT From: bink@athena.mit.edu Received: by m4-167-10.MIT.EDU (5.61/4.7) id AA24288; Wed, 10 Apr 91 17:04:36 -0400 Message-Id: <9104102104.AA24288@m4-167-10.MIT.EDU> To: brakke@poincare Subject: xgraph.c Date: Wed, 10 Apr 91 17:04:31 EDT Status: R Here is the X windows interface I mentioned. Just a few comments about it; the author, Tim Shepard, a graduate student in the MIT Laboratory for Computer Science, says, "It is a two hour hack, and not the best possible job." My own remarks are that xgraph_edge is untested to date, because I have yet to use that feature. Also, despite the author's complaints about the ugliness of the code, it has worked very nicely for everything I've needed thus far. Besides the following, three lines need to be added to the Makefile. They are: CFLAGS= -DGENERIC GRAPH= xgraph.o GRAPHLIB= -lX11 Livia Racz (racz@zither.mit.edu) */ /************************************************************* * This file is for the Surface Evolver source code. * * by Tim Shepard for Livia Racz (March 1991) * * Modified 9-9-92 for colors and shading - K. Brakke * *************************************************************/ /* (based on tcgraph.c) */ /* X11 graphics module */ /* All coordinates in absolute pixels in window */ #include "include.h" #ifndef NOPROTO #define NeedFunctionPrototypes 1 #endif #include static Display *dpy; static Screen *screen; static Window rootwin; static Window win; static XGCValues gcv; static GC gc; static XEvent event; Colormap cmap; static int maxx,maxy; /* max viewport coordinates */ static REAL xscale,yscale; /* for scaling to screen size */ #define MAXGRAY 15 #define CINDEX(color,gray) ((gray)*(MAXGRAY+1) + (color)) unsigned long pixel_values[256]; /* colors not alloced till needed to save colormap */ #define NOT_ALLOC 0xFFA93871 static int White,Black; /* color indices in default colormap */ unsigned long get_a_color ARGS((int,int)); unsigned long get_a_color(c,g) int c; /* color, 0-15 */ int g; /* gray level, 0-15 */ { XColor xc; unsigned long col; col = pixel_values[CINDEX(c,g)]; if ( col != NOT_ALLOC ) return col; xc.red = (unsigned short)(0xffff*(g+12.)/(MAXGRAY+12.)*rgb_colors[c][0]); xc.green = (unsigned short)(0xffff*(g+12.)/(MAXGRAY+12.)*rgb_colors[c][1]); xc.blue = (unsigned short)(0xffff*(g+12.)/(MAXGRAY+12.)*rgb_colors[c][2]); /* if (xc.blue < 0x4444) xc.blue = 0x4444; */ xc.flags = DoRed|DoGreen|DoBlue; if (XAllocColor(dpy, cmap, &xc) == 0) { xc.pixel = g>=8?WhitePixelOfScreen(screen): BlackPixelOfScreen(screen); } pixel_values[CINDEX(c,g)] = xc.pixel; return xc.pixel; } void init_xgraph() { int g; int waiting_for_window = 0; if ( init_flag == 0 ) { waiting_for_window = 1; init_flag = 1; event.type = 0; for ( g = 0 ; g < 256 ; g++ ) pixel_values[g] = NOT_ALLOC; dpy = XOpenDisplay(""); if (dpy == NULL) kb_error(2213,"Unable to open X display.\n", RECOVERABLE); screen = XDefaultScreenOfDisplay(dpy); if (screen == NULL) kb_error(2214,"Unable to get default screen of X display.\n", RECOVERABLE); rootwin = XRootWindowOfScreen(screen); if (rootwin == 0) kb_error(2215,"X display: Unable to get root window of screen.\n", RECOVERABLE); win = XCreateSimpleWindow(dpy, rootwin, 0, 0, 500, 500, 1, WhitePixelOfScreen(screen), BlackPixelOfScreen(screen)); if (win == 0) kb_error(2216,"X display: Unable to create window\n", RECOVERABLE); cmap = DefaultColormap(dpy,/*XScreenNumberOfScreen(screen)*/0); XSelectInput(dpy, win, StructureNotifyMask); XMapRaised(dpy, win); White = WhitePixelOfScreen(screen); Black = BlackPixelOfScreen(screen); gcv.line_width = 0; gc = XCreateGC(dpy, win, GCForeground|GCLineWidth, &gcv); /* wait for window to appear */ XFlush(dpy); while ( event.type != MapNotify ) { XNextEvent(dpy, &event); switch(event.type) { case ConfigureNotify: maxx = event.xconfigure.width; maxy = event.xconfigure.height; xscale = (maxx 0) { XNextEvent(dpy, &event); switch(event.type) { case ConfigureNotify: maxx = event.xconfigure.width; maxy = event.xconfigure.height; if ( maxx < maxy ) { minclipx = -1.5; maxclipx = 1.5; minclipy = -1.5*maxy/maxx; maxclipy = 1.5*maxy/maxx; } else { minclipx = -1.5*maxx/maxy; maxclipx = 1.5*maxx/maxy; minclipy = -1.5; maxclipy = 1.5; } xscale = (maxxetype[0]&EBITS == INVISIBLE_EDGE ) return; if ( t->etype[0]&EBITS == SPLITTING_EDGE ) return; if ( t->ecolor[0] == CLEAR ) return; for ( i = 0 ; i < 2 ; i++ ) { REAL temp; temp = (t->x[i][0]*xscale) + maxx/2; if ( fabs(temp) > 32000. ) return; /* off scale */ p[i].x = (short)temp; temp = maxy/2 - (t->x[i][1]*yscale); if ( fabs(temp) > 32000. ) return; p[i].y = (short)temp; } if ( !shading_flag && !color_flag ) color = Black; else color = get_a_color(t->color,MAXGRAY); if( gcv.foreground != color) { gcv.foreground = color; XChangeGC(dpy, gc, GCForeground, &gcv); } XDrawLine(dpy, win, gc, p[0].x, p[0].y, p[1].x, p[1].y); } void xgraph_facet(t) struct tsort *t; { int n = FACET_VERTS; /* vertices in polygon */ XPoint p[6]; /* vertex coords */ int i; int icolor; for ( i = 0 ; i < FACET_VERTS ; i++ ) { REAL temp; temp = (t->x[i][0]*xscale) + maxx/2; if ( fabs(temp) > 32000. ) return; /* off scale */ p[i].x = (short)temp; temp = maxy/2 - (t->x[i][1]*yscale); if ( fabs(temp) > 32000. ) return; p[i].y = (short)temp; } p[FACET_VERTS] = p[0]; /* convenient */ if ( web.hide_flag && (t->color != CLEAR) && (t->color != UNSHOWN) ) { if ( shading_flag && color_flag ) icolor = get_a_color(t->color,(int)(MAXGRAY*gray_level(t->normal))); else if ( color_flag ) icolor = get_a_color(t->color,MAXGRAY); else if ( shading_flag ) icolor = get_a_color(WHITE,(int)(MAXGRAY*gray_level(t->normal))); else icolor = White; if (gcv.foreground != icolor) { gcv.foreground = icolor; XChangeGC(dpy, gc, GCForeground, &gcv); } XFillPolygon(dpy, win, gc, p, n, Nonconvex, CoordModeOrigin); } /* show designated edges */ for ( i = 0 ; i < 3 ; i++ ) { if ( t->ecolor[i] == CLEAR ) continue; if ( t->etype[i]&EBITS & SPLITTING_EDGE ) continue; if ( !edgeshow_flag || (t->color == UNSHOWN) ) { if ( t->etype[i]&EBITS == INVISIBLE_EDGE ) continue; } icolor = get_a_color(t->ecolor[i],MAXGRAY); if (gcv.foreground != icolor) { gcv.foreground = icolor; XChangeGC(dpy, gc, GCForeground, &gcv); } XDrawLine(dpy, win, gc, p[i].x, p[i].y, p[i+1].x, p[i+1].y); } } void finish_xgraph() { XFlush(dpy); } void close_xgraph() { XCloseDisplay(dpy); init_flag = 0; } void display() { ENTER_GRAPH_MUTEX init_graphics = init_xgraph; finish_graphics = finish_xgraph; close_graphics = close_xgraph; graph_start = painter_start; graph_edge = painter_edge; display_edge = xgraph_edge; graph_facet = painter_facet; display_facet = xgraph_facet; graph_end = painter_end; graphgen(); XFlush(dpy); LEAVE_GRAPH_MUTEX } void graph_new_surface() {} evolver-2.30c.dfsg/src/ytab.h0000644000175300017530000003656411410765113016343 0ustar hazelscthazelsct/* A Bison parser, made by GNU Bison 2.1. */ /* Skeleton parser for Yacc-like parsing with Bison, Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* As a special exception, when this file is copied by Bison into a Bison output file, you may use that output file without restriction. This special exception was added by the Free Software Foundation in version 1.24 of Bison. */ /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { EXPRESSION_START_ = 258, COMMAND_START_ = 259, HISTORY_ = 260, GEOMVIEW_ = 261, VIEW_MATRIX_ = 262, LEAD_INTEGER_ = 263, INTEGER_ = 264, REAL_ = 265, SIGNED_NUMBER_ = 266, NEWIDENT_ = 267, REDEFINE_ = 268, MATHFUNC_ = 269, MATHFUNC2_ = 270, POW_ = 271, USERFUNC_ = 272, MIDV_ = 273, DATAFILENAME_ = 274, LOGFILE_ = 275, PI_ = 276, E_ = 277, G_ = 278, PARAM_ = 279, SYMBOL_ = 280, TOTAL_ = 281, EXTRA_ATTRIBUTE_ = 282, FIXEDVOL_ = 283, IDENT_ = 284, UMINUS_ = 285, SHELL_ = 286, COLOR_ = 287, HESSIAN_ = 288, VOLCONST_ = 289, TORUS_PERIODS_ = 290, VERTICES_ = 291, EDGES_ = 292, FACETS_ = 293, BODIES_ = 294, HESSIAN_MENU_ = 295, POSTSCRIPT_ = 296, LENGTH_ = 297, AREA_ = 298, VOLUME_ = 299, ID_ = 300, OID_ = 301, TAG_ = 302, ORIGINAL_ = 303, FACETEDGES_ = 304, WRAP_ = 305, QUOTATION_ = 306, UNSET_ = 307, TOPINFO_ = 308, OPACITY_ = 309, VALENCE_ = 310, HESSIAN_SADDLE_ = 311, SET_ = 312, FIXED_ = 313, DENSITY_ = 314, PRESSURE_ = 315, CONSTRAINT_ = 316, COORD_ = 317, DISSOLVE_ = 318, WHERE_ = 319, LIST_ = 320, SHOW_ = 321, DELETE_ = 322, REFINE_ = 323, RECALC_ = 324, SHOWQ_ = 325, EDGESWAP_ = 326, FIX_ = 327, UNFIX_ = 328, TOGGLENAME_ = 329, TOGGLEVALUE_ = 330, STAR_ = 331, QUANTITY_NAME_ = 332, PAUSE_ = 333, GO_ = 334, SHOW_VOL_ = 335, CHECK_ = 336, READ_ = 337, ZOOM_ = 338, ON_ = 339, OFF_ = 340, GEOMPIPE_ = 341, SELF_ = 342, SINGLE_LETTER_ = 343, LONG_JIGGLE_ = 344, RAW_VERAVG_ = 345, COUNTS_ = 346, CHDIR_ = 347, ALICE_ = 348, STABILITY_TEST_ = 349, DEFINE_ = 350, UPLUS_ = 351, DATATYPE_ = 352, FLUSH_COUNTS_ = 353, AUTOCHOP_ = 354, UTEST_ = 355, ATTRIBUTE_ = 356, RITZ_ = 357, MOVE_ = 358, VERTEXNORMAL_ = 359, POP_ = 360, SYSTEM_ = 361, TETRA_POINT_ = 362, TRIPLE_POINT_ = 363, LANCZOS_ = 364, EIGENPROBE_ = 365, EXEC_ = 366, AREAWEED_ = 367, EDGEWEED_ = 368, GRAVITY_ = 369, EDGEDIVIDE_ = 370, LINEAR_ = 371, QUADRATIC_ = 372, DIFFUSION_ = 373, EXTRAPOLATE_ = 374, TRANSFORM_DEPTH_ = 375, PRINTF_ = 376, ERRPRINTF_ = 377, PRINT_ = 378, MAX_ = 379, MIN_ = 380, COUNT_ = 381, SUM_ = 382, AVG_ = 383, BREAK_ = 384, CONTINUE_ = 385, SIZEOF_ = 386, TRANSFORM_EXPR_ = 387, BARE_ = 388, BOTTOMINFO_ = 389, METIS_ = 390, KMETIS_ = 391, KEYLOGFILE_ = 392, SCALE_ = 393, BURCHARD_ = 394, REBODY_ = 395, BOUNDARY_ = 396, ORIENTATION_ = 397, OMETIS_ = 398, SQ_MEAN_CURV_ = 399, FRONTCOLOR_ = 400, SINGLE_REDEFD_ = 401, METHOD_NAME_ = 402, TASK_EXEC_ = 403, RAWEST_VERAVG_ = 404, SINGLE_LETTER_ARG_ = 405, BACKCOLOR_ = 406, LAGRANGE_ = 407, RETURN_ = 408, TRANSFORM_EXPR_VERB_ = 409, OOGLFILE_ = 410, PARALLEL_EXEC_ = 411, BINARY_OFF_FILE_ = 412, SPRINTF_ = 413, CONVERT_TO_QUANTS_ = 414, METIS_FACTOR_ = 415, FUNCTION_ = 416, EXPRINT_ = 417, DIHEDRAL_ = 418, WRAP_VERTEX_ = 419, ARRAYIDENT_ = 420, DATE_AND_TIME_ = 421, LOCAL_ = 422, SHOW_EXPR_ = 423, SHOW_TRANS_ = 424, AXIAL_POINT_ = 425, ENERGY_ = 426, CONSERVED_ = 427, INFO_ONLY_ = 428, ASSIGN_ = 429, PROCEDURE_ = 430, FOREACH_ = 431, STRINGGLOBAL_ = 432, EQUIANGULATE_ = 433, HISTOGRAM_ = 434, LOGHISTOGRAM_ = 435, AREA_FIXED_ = 436, QUIT_ = 437, WARNING_MESSAGES_ = 438, IF_ = 439, WHILE_ = 440, DO_ = 441, NO_REFINE_ = 442, STRING_ = 443, NONCONTENT_ = 444, FOR_ = 445, HIT_PARTNER_ = 446, FRONTBODY_ = 447, BACKBODY_ = 448, COLORFILE_ = 449, PERM_STRINGGLOBAL_ = 450, FUNCTION_IDENT_ = 451, THICKEN_ = 452, COLORMAP_ = 453, REDIRECT_ = 454, NEWVERTEX_ = 455, NEWEDGE_ = 456, NEWFACET_ = 457, MODULUS_ = 458, TARGET_ = 459, VALUE_ = 460, INVERSE_PERIODS_ = 461, NEWBODY_ = 462, DELTA_ = 463, GAP_CONSTANT_ = 464, DUMP_ = 465, NOTCH_ = 466, QUANTITY_ = 467, LOAD_ = 468, PERM_PROCEDURE_ = 469, PROCEDURE_WORD_ = 470, DYNAMIC_LOAD_FUNC_ = 471, PERM_IDENT_ = 472, PERMLOAD_ = 473, HELP_ = 474, VERTEX_AVERAGE_ = 475, METHOD_INSTANCE_ = 476, RAW_VERTEX_AVERAGE_ = 477, OPTIMIZE_ = 478, REDIRECTOVER_ = 479, TOLERANCE_ = 480, RAWEST_VERTEX_AVERAGE_ = 481, JIGGLE_ = 482, VIEW_TRANSFORMS_ = 483, CLOSE_SHOW_ = 484, IS_DEFINED_ = 485, NODISPLAY_ = 486, PERM_ASSIGN_ = 487, PHASE_ = 488, VIEW_TRANSFORM_SWAP_COLORS_ = 489, BACKQUOTE_COMMA_ = 490, INTERNAL_VARIABLE_ = 491, DIRICHLET_ = 492, SOBOLEV_ = 493, VIEW_TRANSFORM_PARITY_ = 494, SOBOLEV_SEEK_ = 495, DIRICHLET_SEEK_ = 496, HESSIAN_SEEK_ = 497, REORDER_STORAGE_ = 498, RENUMBER_ALL_ = 499, CONSTRAINT_NAME_ = 500, BOUNDARY_NAME_ = 501, PROCEDURE_IDENT_ = 502, POP_TRI_TO_EDGE_ = 503, POP_EDGE_TO_TRI_ = 504, POP_QUAD_TO_QUAD_ = 505, SHOWVERB_ = 506, PROCEDURES_ = 507, MPI_TASK_ATTR_ = 508, T1_EDGESWAP_ = 509, MERGE_EDGE_ = 510, MERGE_FACET_ = 511, MERGE_VERTEX_ = 512, RESET_COUNTS_ = 513, VALID_ELEMENT_ = 514, MID_EDGE_ = 515, MID_FACET_ = 516, GO_COUNT_ = 517, ELEMENT_IDENT_ = 518, BODY_METIS_ = 519, REVERSE_ORIENTATION_ = 520, MATRIX_MULTIPLY_ = 521, MATRIX_INVERSE_ = 522, BINARY_PRINTF_ = 523, DUMP_MEMLIST_ = 524, FREE_DISCARDS_ = 525, REPARTITION_ = 526, METIS_READJUST_ = 527, MEAN_CURVATURE_ = 528, GLOBAL_ = 529, LEAD_INTEGER_AT_ = 530, INTEGER_AT_ = 531, MATRIX_DETERMINANT_ = 532, SUBCOMMAND_ = 533, ABORT_ = 534, BREAKPOINT_ = 535, WHEREAMI_ = 536, ADDLOAD_ = 537, SIMPLEX_TO_FE_ = 538, DISPLAY_TEXT_ = 539, DELETE_TEXT_ = 540, SUPPRESS_WARNING_ = 541, UNSUPPRESS_WARNING_ = 542, RESET_PROFILING_ = 543, VALID_CONSTRAINT_ = 544, VALID_BOUNDARY_ = 545, ARRAY_ATTRIBUTE_ = 546, PROFILING_ = 547, ASSIGNOP_ = 548, PIPE_ = 549, THEN_ = 550, ELSE_ = 551, OR_ = 552, AND_ = 553, NOT_ = 554, NE_ = 555, GE_ = 556, LE_ = 557, EQ_ = 558, ON_CONSTRAINT_ = 559, HIT_CONSTRAINT_ = 560, ON_BOUNDARY_ = 561, ON_QUANTITY_ = 562, ON_METHOD_INSTANCE_ = 563, DOT_ = 564, IDIV_ = 565, IMOD_ = 566, EPRINT_ = 567 }; #endif /* Tokens. */ #define EXPRESSION_START_ 258 #define COMMAND_START_ 259 #define HISTORY_ 260 #define GEOMVIEW_ 261 #define VIEW_MATRIX_ 262 #define LEAD_INTEGER_ 263 #define INTEGER_ 264 #define REAL_ 265 #define SIGNED_NUMBER_ 266 #define NEWIDENT_ 267 #define REDEFINE_ 268 #define MATHFUNC_ 269 #define MATHFUNC2_ 270 #define POW_ 271 #define USERFUNC_ 272 #define MIDV_ 273 #define DATAFILENAME_ 274 #define LOGFILE_ 275 #define PI_ 276 #define E_ 277 #define G_ 278 #define PARAM_ 279 #define SYMBOL_ 280 #define TOTAL_ 281 #define EXTRA_ATTRIBUTE_ 282 #define FIXEDVOL_ 283 #define IDENT_ 284 #define UMINUS_ 285 #define SHELL_ 286 #define COLOR_ 287 #define HESSIAN_ 288 #define VOLCONST_ 289 #define TORUS_PERIODS_ 290 #define VERTICES_ 291 #define EDGES_ 292 #define FACETS_ 293 #define BODIES_ 294 #define HESSIAN_MENU_ 295 #define POSTSCRIPT_ 296 #define LENGTH_ 297 #define AREA_ 298 #define VOLUME_ 299 #define ID_ 300 #define OID_ 301 #define TAG_ 302 #define ORIGINAL_ 303 #define FACETEDGES_ 304 #define WRAP_ 305 #define QUOTATION_ 306 #define UNSET_ 307 #define TOPINFO_ 308 #define OPACITY_ 309 #define VALENCE_ 310 #define HESSIAN_SADDLE_ 311 #define SET_ 312 #define FIXED_ 313 #define DENSITY_ 314 #define PRESSURE_ 315 #define CONSTRAINT_ 316 #define COORD_ 317 #define DISSOLVE_ 318 #define WHERE_ 319 #define LIST_ 320 #define SHOW_ 321 #define DELETE_ 322 #define REFINE_ 323 #define RECALC_ 324 #define SHOWQ_ 325 #define EDGESWAP_ 326 #define FIX_ 327 #define UNFIX_ 328 #define TOGGLENAME_ 329 #define TOGGLEVALUE_ 330 #define STAR_ 331 #define QUANTITY_NAME_ 332 #define PAUSE_ 333 #define GO_ 334 #define SHOW_VOL_ 335 #define CHECK_ 336 #define READ_ 337 #define ZOOM_ 338 #define ON_ 339 #define OFF_ 340 #define GEOMPIPE_ 341 #define SELF_ 342 #define SINGLE_LETTER_ 343 #define LONG_JIGGLE_ 344 #define RAW_VERAVG_ 345 #define COUNTS_ 346 #define CHDIR_ 347 #define ALICE_ 348 #define STABILITY_TEST_ 349 #define DEFINE_ 350 #define UPLUS_ 351 #define DATATYPE_ 352 #define FLUSH_COUNTS_ 353 #define AUTOCHOP_ 354 #define UTEST_ 355 #define ATTRIBUTE_ 356 #define RITZ_ 357 #define MOVE_ 358 #define VERTEXNORMAL_ 359 #define POP_ 360 #define SYSTEM_ 361 #define TETRA_POINT_ 362 #define TRIPLE_POINT_ 363 #define LANCZOS_ 364 #define EIGENPROBE_ 365 #define EXEC_ 366 #define AREAWEED_ 367 #define EDGEWEED_ 368 #define GRAVITY_ 369 #define EDGEDIVIDE_ 370 #define LINEAR_ 371 #define QUADRATIC_ 372 #define DIFFUSION_ 373 #define EXTRAPOLATE_ 374 #define TRANSFORM_DEPTH_ 375 #define PRINTF_ 376 #define ERRPRINTF_ 377 #define PRINT_ 378 #define MAX_ 379 #define MIN_ 380 #define COUNT_ 381 #define SUM_ 382 #define AVG_ 383 #define BREAK_ 384 #define CONTINUE_ 385 #define SIZEOF_ 386 #define TRANSFORM_EXPR_ 387 #define BARE_ 388 #define BOTTOMINFO_ 389 #define METIS_ 390 #define KMETIS_ 391 #define KEYLOGFILE_ 392 #define SCALE_ 393 #define BURCHARD_ 394 #define REBODY_ 395 #define BOUNDARY_ 396 #define ORIENTATION_ 397 #define OMETIS_ 398 #define SQ_MEAN_CURV_ 399 #define FRONTCOLOR_ 400 #define SINGLE_REDEFD_ 401 #define METHOD_NAME_ 402 #define TASK_EXEC_ 403 #define RAWEST_VERAVG_ 404 #define SINGLE_LETTER_ARG_ 405 #define BACKCOLOR_ 406 #define LAGRANGE_ 407 #define RETURN_ 408 #define TRANSFORM_EXPR_VERB_ 409 #define OOGLFILE_ 410 #define PARALLEL_EXEC_ 411 #define BINARY_OFF_FILE_ 412 #define SPRINTF_ 413 #define CONVERT_TO_QUANTS_ 414 #define METIS_FACTOR_ 415 #define FUNCTION_ 416 #define EXPRINT_ 417 #define DIHEDRAL_ 418 #define WRAP_VERTEX_ 419 #define ARRAYIDENT_ 420 #define DATE_AND_TIME_ 421 #define LOCAL_ 422 #define SHOW_EXPR_ 423 #define SHOW_TRANS_ 424 #define AXIAL_POINT_ 425 #define ENERGY_ 426 #define CONSERVED_ 427 #define INFO_ONLY_ 428 #define ASSIGN_ 429 #define PROCEDURE_ 430 #define FOREACH_ 431 #define STRINGGLOBAL_ 432 #define EQUIANGULATE_ 433 #define HISTOGRAM_ 434 #define LOGHISTOGRAM_ 435 #define AREA_FIXED_ 436 #define QUIT_ 437 #define WARNING_MESSAGES_ 438 #define IF_ 439 #define WHILE_ 440 #define DO_ 441 #define NO_REFINE_ 442 #define STRING_ 443 #define NONCONTENT_ 444 #define FOR_ 445 #define HIT_PARTNER_ 446 #define FRONTBODY_ 447 #define BACKBODY_ 448 #define COLORFILE_ 449 #define PERM_STRINGGLOBAL_ 450 #define FUNCTION_IDENT_ 451 #define THICKEN_ 452 #define COLORMAP_ 453 #define REDIRECT_ 454 #define NEWVERTEX_ 455 #define NEWEDGE_ 456 #define NEWFACET_ 457 #define MODULUS_ 458 #define TARGET_ 459 #define VALUE_ 460 #define INVERSE_PERIODS_ 461 #define NEWBODY_ 462 #define DELTA_ 463 #define GAP_CONSTANT_ 464 #define DUMP_ 465 #define NOTCH_ 466 #define QUANTITY_ 467 #define LOAD_ 468 #define PERM_PROCEDURE_ 469 #define PROCEDURE_WORD_ 470 #define DYNAMIC_LOAD_FUNC_ 471 #define PERM_IDENT_ 472 #define PERMLOAD_ 473 #define HELP_ 474 #define VERTEX_AVERAGE_ 475 #define METHOD_INSTANCE_ 476 #define RAW_VERTEX_AVERAGE_ 477 #define OPTIMIZE_ 478 #define REDIRECTOVER_ 479 #define TOLERANCE_ 480 #define RAWEST_VERTEX_AVERAGE_ 481 #define JIGGLE_ 482 #define VIEW_TRANSFORMS_ 483 #define CLOSE_SHOW_ 484 #define IS_DEFINED_ 485 #define NODISPLAY_ 486 #define PERM_ASSIGN_ 487 #define PHASE_ 488 #define VIEW_TRANSFORM_SWAP_COLORS_ 489 #define BACKQUOTE_COMMA_ 490 #define INTERNAL_VARIABLE_ 491 #define DIRICHLET_ 492 #define SOBOLEV_ 493 #define VIEW_TRANSFORM_PARITY_ 494 #define SOBOLEV_SEEK_ 495 #define DIRICHLET_SEEK_ 496 #define HESSIAN_SEEK_ 497 #define REORDER_STORAGE_ 498 #define RENUMBER_ALL_ 499 #define CONSTRAINT_NAME_ 500 #define BOUNDARY_NAME_ 501 #define PROCEDURE_IDENT_ 502 #define POP_TRI_TO_EDGE_ 503 #define POP_EDGE_TO_TRI_ 504 #define POP_QUAD_TO_QUAD_ 505 #define SHOWVERB_ 506 #define PROCEDURES_ 507 #define MPI_TASK_ATTR_ 508 #define T1_EDGESWAP_ 509 #define MERGE_EDGE_ 510 #define MERGE_FACET_ 511 #define MERGE_VERTEX_ 512 #define RESET_COUNTS_ 513 #define VALID_ELEMENT_ 514 #define MID_EDGE_ 515 #define MID_FACET_ 516 #define GO_COUNT_ 517 #define ELEMENT_IDENT_ 518 #define BODY_METIS_ 519 #define REVERSE_ORIENTATION_ 520 #define MATRIX_MULTIPLY_ 521 #define MATRIX_INVERSE_ 522 #define BINARY_PRINTF_ 523 #define DUMP_MEMLIST_ 524 #define FREE_DISCARDS_ 525 #define REPARTITION_ 526 #define METIS_READJUST_ 527 #define MEAN_CURVATURE_ 528 #define GLOBAL_ 529 #define LEAD_INTEGER_AT_ 530 #define INTEGER_AT_ 531 #define MATRIX_DETERMINANT_ 532 #define SUBCOMMAND_ 533 #define ABORT_ 534 #define BREAKPOINT_ 535 #define WHEREAMI_ 536 #define ADDLOAD_ 537 #define SIMPLEX_TO_FE_ 538 #define DISPLAY_TEXT_ 539 #define DELETE_TEXT_ 540 #define SUPPRESS_WARNING_ 541 #define UNSUPPRESS_WARNING_ 542 #define RESET_PROFILING_ 543 #define VALID_CONSTRAINT_ 544 #define VALID_BOUNDARY_ 545 #define ARRAY_ATTRIBUTE_ 546 #define PROFILING_ 547 #define ASSIGNOP_ 548 #define PIPE_ 549 #define THEN_ 550 #define ELSE_ 551 #define OR_ 552 #define AND_ 553 #define NOT_ 554 #define NE_ 555 #define GE_ 556 #define LE_ 557 #define EQ_ 558 #define ON_CONSTRAINT_ 559 #define HIT_CONSTRAINT_ 560 #define ON_BOUNDARY_ 561 #define ON_QUANTITY_ 562 #define ON_METHOD_INSTANCE_ 563 #define DOT_ 564 #define IDIV_ 565 #define IMOD_ 566 #define EPRINT_ 567 #if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) typedef int YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 # define YYSTYPE_IS_TRIVIAL 1 #endif evolver-2.30c.dfsg/src/tordup.c0000644000175300017530000003522111410765113016701 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /*********************************************************************** * * File: tordup.c * * Function: tordup() * * Purpose: Duplicate a torus configuration in the direction of * one period. Meant for studying long-term behavior * of Ostwald ripening on ever larger scales. Also good * for duplicating a single cell and resolving vertices * to try to beat Kelvin. */ #include "include.h" static int v_count; static int ecount; static int fcount; static int bcount; static int fecount; static element_id *vlist,*elist,*flist,*felist,*blist; void tordup(m) int m; /* period to duplicate, 0,1, or 2 */ { vertex_id v_id; edge_id e_id; facet_id f_id; body_id b_id; facetedge_id fe_id; facetedge_id new_fe; facet_id new_f; body_id new_b; edge_id new_e; int i,j; REAL *x; REAL adjust; facetedge_id *body_fe_list; v_count = web.skel[VERTEX].max_ord+1; ecount = web.skel[EDGE].max_ord+1; fcount = web.skel[FACET].max_ord+1; bcount = web.skel[BODY].max_ord+1; fecount = web.skel[FACETEDGE].max_ord+1; /* allocate room for lists of old and new elements */ vlist = (element_id *)temp_calloc(sizeof(element_id),2*v_count); elist = (element_id *)temp_calloc(sizeof(element_id),2*ecount); flist = (element_id *)temp_calloc(sizeof(element_id),2*fcount); blist = (element_id *)temp_calloc(sizeof(element_id),2*bcount); felist = (element_id *)temp_calloc(sizeof(element_id),2*fecount); body_fe_list = (element_id *)temp_calloc(sizeof(element_id),2*bcount); /* record old body data */ FOR_ALL_BODIES(b_id) { blist[loc_ordinal(b_id)] = b_id; body_fe_list[loc_ordinal(b_id)] = get_body_fe(b_id); } /* relocate new vertices */ FOR_ALL_VERTICES(v_id) { /* list old vertices */ i = loc_ordinal(v_id); vlist[i] = v_id; } for ( i = 0 ; i < v_count ; i++ ) { /* create corresponding new vertices */ v_id = vlist[i]; if ( !valid_id(v_id) ) continue; vlist[i+v_count] = dup_vertex(v_id); x = get_coord(vlist[i+v_count]); for ( j = 0 ; j < SDIM ; j++ ) x[j] += web.torus_period[m][j]; } /* fix edge endpoints */ FOR_ALL_EDGES(e_id) { /* list old edges */ i = loc_ordinal(e_id); elist[i] = e_id; } for ( i = 0 ; i < ecount ; i++ ) { /* create corresponding new edges */ vertex_id h,t; WRAPTYPE wrap; e_id = elist[i]; if ( !valid_id(e_id) ) continue; new_e = dup_edge(e_id); elist[i+ecount] = new_e; h = get_edge_headv(e_id); t = get_edge_tailv(e_id); if ( web.modeltype == QUADRATIC ) { free_element(get_edge_midv(new_e)); set_edge_midv(new_e,vlist[v_count+loc_ordinal(get_edge_midv(e_id))]); } wrap = get_edge_wrap(e_id); if ( (wrap >> (TWRAPBITS*m)) & WRAPMASK ) { remove_vertex_edge(h,inverse_id(e_id)); set_edge_headv(e_id,upgrade(h)); set_edge_headv(new_e,h); set_edge_tailv(new_e,upgrade(t)); } else { set_edge_headv(new_e,upgrade(h)); set_edge_tailv(new_e,upgrade(t)); } } /* new facets */ FOR_ALL_FACETS(f_id) { i = loc_ordinal(f_id); flist[i]= f_id; } for ( i = 0 ; i < fcount ; i++ ) { /* create corresponding new facets */ f_id = flist[i]; if ( !valid_id(f_id) ) continue; new_f = dup_facet(f_id); flist[i+fcount] = new_f; } /* new facet-edges */ FOR_ALL_FACETEDGES(fe_id) { i = loc_ordinal(fe_id); felist[i] = fe_id; } for ( i = 0 ; i < fecount ; i++ ) { /* create corresponding new facet-edges */ fe_id = felist[i]; if ( !valid_id(fe_id) ) continue; f_id = get_fe_facet(fe_id); e_id = get_fe_edge(fe_id); new_fe = new_facetedge(upgrade(f_id),upgrade(e_id)); felist[i+fecount] = new_fe; } for ( i = 0 ; i < fecount ; i++ ) { /* upgrade links */ fe_id = felist[i]; if ( !valid_id(fe_id) ) continue; /* only want old ones */ new_fe = upgrade(fe_id); set_next_edge(new_fe,upgrade(get_next_edge(fe_id))); set_prev_edge(new_fe,upgrade(get_prev_edge(fe_id))); set_next_facet(new_fe,upgrade(get_next_facet(fe_id))); set_prev_facet(new_fe,upgrade(get_prev_facet(fe_id))); } /* upgrade edge fe links */ for ( i = 0 ; i < ecount ; i++ ) { e_id = elist[i]; if ( !valid_id(e_id) ) continue; fe_id = get_edge_fe(e_id); set_edge_fe(upgrade(e_id),upgrade(fe_id)); } /* go around old facets, linking in new facetedges if necessary */ for ( i = 0 ; i < fcount ; i++ ) { int newflag; facetedge_id fe,first_fe,new_next_fe,next_fe; f_id = flist[i]; if ( !valid_id(f_id) ) continue; new_f = upgrade(f_id); newflag = 0; /* whether we should be doing new fe's */ fe = first_fe = get_facet_fe(f_id); if ( !valid_id(fe) ) continue; set_facet_fe(new_f,upgrade(fe)); do { int sign; new_fe = upgrade(fe); if ( newflag ) { set_fe_facet(fe,new_f); set_fe_facet(new_fe,f_id); } next_fe = get_next_edge(fe); new_next_fe = upgrade(next_fe); sign = 0; if ( !inverted(get_fe_edge(fe)) ) if ( get_fe_wrap(fe) & (WRAPMASK<<(TWRAPBITS*m)) ) sign++; if ( inverted(get_fe_edge(next_fe)) ) if ( get_fe_wrap(next_fe) & (WRAPMASK<<(TWRAPBITS*m)) ) sign++; if ( sign == 1 ) { /* cross-link */ set_next_edge(fe,new_next_fe); set_prev_edge(new_next_fe,fe); set_next_edge(new_fe,next_fe); set_prev_edge(next_fe,new_fe); newflag = !newflag; } fe = next_fe; } while ( !equal_id(fe,first_fe) ); if ( newflag ) { /* really only one facet, so get rid of new one */ fe = first_fe; do { set_fe_facet(fe,f_id); fe = get_next_edge(fe); } while ( !equal_id(fe,first_fe) ); free_element(new_f); } } /* unwrapping appropriate edges */ for ( i = 0 ; i < ecount ; i++ ) { WRAPTYPE mask = WRAPMASK<<(TWRAPBITS*m); WRAPTYPE wrap,oldwrap,newwrap; int wrapnum; if ( !valid_id(elist[i]) ) continue; wrap = get_edge_wrap(elist[i]); wrapnum = WRAPNUM((wrap>>(TWRAPBITS*m)) & WRAPMASK); oldwrap = (wrap & (~mask))|(((wrapnum>>1)&WRAPMASK)<<(TWRAPBITS*m)); newwrap = (wrap & (~mask))|((((wrapnum+1)>>1)&WRAPMASK)<<(TWRAPBITS*m)); set_edge_wrap(elist[i],oldwrap); set_edge_wrap(elist[i+ecount],newwrap); } /* create corresponding new facets */ for ( i = 0 ; i < fcount ; i++ ) { f_id = flist[i]; if ( !valid_id(f_id) ) continue; new_f = flist[i+fcount]; /* erase body-facet links; will reset later */ set_facet_body(f_id,NULLBODY); set_facet_body(inverse_id(f_id), NULLBODY); set_facet_body(new_f,NULLBODY); set_facet_body(inverse_id(new_f), NULLBODY); } /* new bodies */ for ( i = 0 ; i < bcount ; i++ ) { /* create corresponding new facet-edges */ b_id = blist[i]; if ( !valid_id(b_id) ) continue; new_b = dup_body(b_id); blist[i+bcount] = new_b; fe_id = body_fe_list[i]; f_id = get_fe_facet(fe_id); set_facet_body(f_id,b_id); /* reset, since was wiped earlier */ body_fe_list[bcount+i] = upgrade(fe_id); if ( get_battr(b_id) & HAVE_CENTEROFMASS ) { REAL *oldcm = get_body_cm(b_id); REAL *newcm = get_body_cm(new_b); oldcm[m] = 0.5; newcm[m] = 0.5; } } /* adjust facet bodies */ if ( web.representation == STRING ) { for ( i = 0 ; i < fcount ; i++ ) if ( valid_id(flist[i]) ) { b_id = get_facet_body(flist[i]); if ( !valid_id(b_id) ) continue; b_id = upgrade(b_id); if ( valid_id(flist[i+fcount]) ) set_facet_body(flist[i+fcount],b_id); else free_element(b_id); } } else /* SOAPFILM */ { /* have to go around finding contiguous facets of bodies */ int changeflag; facet_id ff_id; /* start with canonical facets of old bodies */ for ( i = 0 ; i < bcount ; i++ ) { b_id = blist[i]; if ( !valid_id(b_id) ) continue; f_id = get_body_facet(b_id); set_facet_body(f_id,b_id); } /* go around finding adjacent facets */ do { facetedge_id sentinel; changeflag = 0; fe_id = NULLFACETEDGE; while ( generate_all(FACETEDGE,&fe_id,&sentinel) ) { if ( equal_id(get_next_facet(fe_id),fe_id) ) continue; /* valence 1 edge */ f_id = get_fe_facet(fe_id); b_id = get_facet_body(f_id); if ( valid_id(b_id) ) { ff_id = inverse_id(get_fe_facet(get_prev_facet(fe_id))); if ( !valid_id(get_facet_body(ff_id)) ) { set_facet_body(ff_id,b_id); changeflag++; } } invert(fe_id); f_id = get_fe_facet(fe_id); b_id = get_facet_body(f_id); if ( valid_id(b_id) ) { ff_id = inverse_id(get_fe_facet(get_prev_facet(fe_id))); if ( !valid_id(get_facet_body(ff_id)) ) { set_facet_body(ff_id,b_id); changeflag++; } } } } while ( changeflag ); /* now find new bodies whose canonical faces have not been usurped */ for ( i = 0 ; i < bcount ; i++ ) { b_id = blist[i+bcount]; if ( !valid_id(b_id) ) continue; f_id = get_fe_facet(body_fe_list[i+bcount]); if ( valid_id(get_facet_body(f_id)) ) { /* two bodies are really one */ free_element(b_id); set_body_volume(blist[i],2*get_body_volume(blist[i]),SETSTAMP); set_body_oldvolume(blist[i],2*get_body_oldvolume(blist[i])); if ( get_battr(b_id) & FIXEDVOL ) set_body_fixvol(blist[i],2*get_body_fixvol(blist[i])); continue; } set_facet_body(f_id,b_id); } /* go around finding adjacent facets */ do { facetedge_id sentinel; changeflag = 0; fe_id = NULLFACETEDGE; while ( generate_all(FACETEDGE,&fe_id,&sentinel) ) { if ( equal_id(get_next_facet(fe_id),fe_id) ) continue; /* valence 1 edge */ f_id = get_fe_facet(fe_id); b_id = get_facet_body(f_id); if ( valid_id(b_id) ) { ff_id = inverse_id(get_fe_facet(get_prev_facet(fe_id))); if ( !valid_id(get_facet_body(ff_id)) ) { set_facet_body(ff_id,b_id); changeflag++; } } invert(fe_id); f_id = get_fe_facet(fe_id); b_id = get_facet_body(f_id); if ( valid_id(b_id) ) { ff_id = inverse_id(get_fe_facet(get_prev_facet(fe_id))); if ( !valid_id(get_facet_body(ff_id)) ) { set_facet_body(ff_id,b_id); changeflag++; } } } } while ( changeflag ); } /* adjust torus period and inverse period matrix */ for ( i = 0 ; i < SDIM ; i++ ) { sprintf(msg,"2*(%s)",print_express(&torus_period_expr[m][i],' ')); cmdptr = msg; exparse(0,&torus_period_expr[m][i],USERCOPY); cmdptr = NULL; } if ( web.torus_display_period ) for ( i = 0 ; i < SDIM ; i++ ) { sprintf(msg,"2*(%s)",print_express(&torus_display_period_expr[m][i],' ')); cmdptr = msg; exparse(0,&torus_display_period_expr[m][i],USERCOPY); cmdptr = NULL; } calc_periods(NO_ADJUST_VOLUMES); /* phase boundary energies */ if ( phase_flag && (web.representation == STRING) ) FOR_ALL_EDGES(e_id) set_e_phase_density(e_id); if ( phase_flag && (web.representation != STRING) ) FOR_ALL_FACETS(f_id) set_f_phase_density(f_id); if ( everything_quantities_flag ) reconvert_bodies_to_quantities(); /* free lists */ temp_free((char *)vlist); temp_free((char *)elist); temp_free((char *)flist); temp_free((char *)blist); temp_free((char *)felist); temp_free((char *)body_fe_list); /* fix up volconsts */ calc_content(Q_INFO|Q_FIXED); /* all volumes */ /* get volume of piece of unit cell */ if ( SDIM == 2 ) { adjust = web.torusv /* /2 */; } else /* web.representation == SOAPFILM */ { adjust = web.torusv /* /6 */; } /* adjust volconsts */ FOR_ALL_BODIES(b_id) { REAL vol = get_body_volume(b_id); REAL old = get_body_oldvolume(b_id); REAL vc = get_body_volconst(b_id); REAL calcvol = vol-vc; REAL newvc = old - calcvol; newvc = adjust*floor(0.5+newvc/adjust); set_body_volconst(b_id,newvc); set_body_volume(b_id,calcvol+newvc,SETSTAMP); } top_timestamp = ++global_timestamp; } /************************************************** * * Function: upgrade() * * Purpose: Find the new element corresponding * to an old element. */ element_id upgrade(id) element_id id; { int j = loc_ordinal(id); element_id new_id; if ( !valid_id(id) ) return NULLID; switch( id_type(id) ) { case VERTEX: new_id = vlist[j+v_count]; if ( !equal_id(id,vlist[j]) ) invert(new_id); break; case EDGE : new_id = elist[j+ecount]; if ( !equal_id(id,elist[j]) ) invert(new_id); break; case FACET : new_id = flist[j+fcount]; if ( !equal_id(id,flist[j]) ) invert(new_id); break; case BODY : new_id = blist[j+bcount]; if ( !equal_id(id,blist[j]) ) invert(new_id); break; case FACETEDGE: new_id = felist[j+fecount]; if ( !equal_id(id,felist[j]) ) invert(new_id); break; default: new_id = NULLID; } return new_id; } /******************************************************************** * * function: reconvert_bodies_to_quantities() * * purpose: straightens out body quantities after messing around * with rebody() or tordup(). Erases all old instances * and recreates whole structure. */ void reconvert_bodies_to_quantities() { int type; int k; struct method_instance *mi; struct element *e_ptr; body_id b_id; element_id id; for ( type = VERTEX ; type <= FACET ; type++ ) { int meth_offset = get_meth_offset(type); int *methlist; FOR_ALL_ELEMENTS(type,id) { e_ptr = elptr(id); methlist = (int*)((char*)e_ptr+meth_offset); for ( k = 0 ; k < (int)e_ptr->method_count ; k++ ) { mi = METH_INSTANCE(abs(methlist[k])); if ( mi->flags & BODY_INSTANCE ) { methlist[k] = methlist[--(e_ptr->method_count)]; k--; } } } } FOR_ALL_BODIES(b_id) convert_new_body_to_quantity(b_id); convert_bodies_to_quantities(); } evolver-2.30c.dfsg/src/userfunc.c0000644000175300017530000003237411410765113017224 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************** * * File: userfunc.c * * Purpose: Lets user compile own function definitions for use in * expressions. Function passed pointer to coordinate array. * User should also provide derivative function. * After defining functions, add names to arrays userfunc, * userfunc_deriv, and userfunc_second that follow all the * function definitions. * Names in this file don't mattter; the first function * in the arrays is usr1 in datafile or queries, etc. * When invoked in Evolver formulas, these functions do * not take arguments; arguments are implicitly point * coordinates. * Example: (usr1 + usr3)/usr10. * * Also has functions for handling dynamic load libraries. * And elliptic functions. */ #include "include.h" /**************************************************************************/ /************************************************************************** * * function: userfunc_init() * * purpose: called once at the start of a surface to give user functions * a chance to initialize. */ void userfunc_init() { /* do whatever it takes to initialize user functions */ /* Example of error reporting. kb_error(2201,"Error in userfunc_init\n",RECOVERABLE); */ } /* usr1 as defined here gives conformal metric for 3-sphere in stereographic projection */ REAL usr1 ARGS((REAL *)); REAL usr1_deriv ARGS((REAL *,REAL *)); REAL usr1_seconds ARGS((REAL *,REAL *,REAL **)); REAL usr1(x) REAL *x; /* incoming parameters */ { REAL denom; denom = 4+x[0]*x[0]+x[1]*x[1]+x[2]*x[2]; return 16/denom/denom; } REAL usr1_deriv(x,partials) REAL *x; /* incoming parameters */ REAL *partials; /* outgoing partial derivatives */ { REAL denom,cube; int i; denom = 4+x[0]*x[0]+x[1]*x[1]+x[2]*x[2]; cube = denom*denom*denom; for ( i = 0 ; i < SDIM ; i++ ) partials[i] = -64/cube*x[i]; return 16/denom/denom; } REAL usr1_seconds(x,partials,seconds) REAL *x; /* incoming parameters */ REAL *partials; /* outgoing partial derivatives */ REAL **seconds; /* outgoing second derivatives */ { REAL denom,cube,quart; int i,j; denom = 4+x[0]*x[0]+x[1]*x[1]+x[2]*x[2]; cube = denom*denom*denom; quart = cube*denom; for ( i = 0 ; i < SDIM ; i++ ) partials[i] = -64/cube*x[i]; for ( i = 0 ; i < SDIM ; i++ ) { for ( j = 0 ; j < SDIM ; j++ ) seconds[i][j] = 384*x[i]*x[j]/quart; seconds[i][i] -= 64/cube; } return 16/denom/denom; } /***************************************************************************/ /* Another example of a user function, which is a polynomial in x,y,z. */ /* This function is referred to as usr2 in expressions. */ static REAL usr_poly ARGS((REAL *)); static REAL usr_poly_grad ARGS((REAL *,REAL *)); static REAL usr_poly_hess ARGS((REAL *,REAL *,REAL **)); static REAL usr_poly(x) REAL *x; /* incoming parameters */ { return x[0]*x[0] + x[1]*x[2] + x[2]*x[2]*x[2]; } static REAL usr_poly_grad(x,partials) REAL *x; /* incoming parameters */ REAL *partials; /* outgoing partial derivatives */ { partials[0] = 2*x[0]; partials[1] = x[2]; partials[2] = x[1] + 3*x[2]*x[2]; return x[0]*x[0] + x[1]*x[2] + x[2]*x[2]*x[2]; } static REAL usr_poly_hess(x,partials,seconds) REAL *x; /* incoming parameters */ REAL *partials; /* outgoing partial derivatives */ REAL **seconds; /* outgoing second derivatives */ { partials[0] = 2*x[0]; partials[1] = x[2]; partials[2] = x[1] + 3*x[2]*x[2]; seconds[0][0] = 2.0; seconds[0][1] = seconds[1][0] = 0.0; seconds[0][2] = seconds[2][0] = 0.0; seconds[1][1] = 0.0; seconds[1][2] = seconds[2][1] = 1.0; seconds[2][2] = 6*x[2]; return x[0]*x[0] + x[1]*x[2] + x[2]*x[2]*x[2]; } /**************************************************************************/ /* Add your functions to these arrays; this is how they will be invoked! */ #ifdef NOPROTO REAL (*userfunc[])() = {usr1,usr_poly}; REAL (*userfunc_deriv[])() = {usr1_deriv,usr_poly_grad}; REAL (*userfunc_seconds[])() = {usr1_seconds,usr_poly_hess}; #else REAL (*userfunc[])(REAL*) = {usr1,usr_poly}; REAL (*userfunc_deriv[])(REAL*,REAL*) = {usr1_deriv,usr_poly_grad}; REAL (*userfunc_seconds[])(REAL*,REAL*,REAL**) = {usr1_seconds,usr_poly_hess}; #endif /**************************************************************************/ /**************************************************************************/ /* A user defined attribute function. Undocumented. */ /* Use "user_attr" in queries like length or area or id */ REAL user_attribute(id) element_id id; { /* a sample smorgasbord */ switch ( id_type(id) ) { case VERTEX: return get_coord(id)[0]; case EDGE: return get_edge_length(id); case FACET: return get_facet_area(id); case BODY: return get_body_volume(id); case FACETEDGE: return (REAL)(loc_ordinal(id)+1); } return 0.0; } /********************************************************************* ********************************************************************** D Y N A M I C L O A D L I B R A R I E S ********************************************************************** *********************************************************************/ /********************************************************************* * * function: load_library() * * purpose: Find and load dynamic library. Searches current directory, * EVOLVERPATH, default library path. */ #ifdef WIN32 #define dlopen(name,mode) LoadLibrary(name) #define dlclose(handle) FreeLibrary(handle) #define dlsym(handle,name) GetProcAddress(handle,name) #endif void load_library(libname) char *libname; { #ifdef ENABLE_DLL int k; char *env; char path[200]; int len; void *fd; for ( k = 0 ; k < MAX_DLL ; k++ ) if ( dll_list[k].name == NULL ) break; if ( k >= MAX_DLL ) kb_error(2202,"Too many dynamic load libraries.\n",DATAFILE_ERROR); env = getenv("EVOLVERPATH"); /* try current directory first */ strcpy(path,"./"); strncpy(path+2,libname,sizeof(path)-2); while ( (fd = dlopen(path,RTLD_NOW)) == NULL) { /* try paths in EVOLVERPATH */ if ( env == NULL ) break; len = strcspn(env,ENVPATHCHAR); if ( len == 0 ) break; strncpy(path,env,len); path[len] = PATHCHAR; strncpy(path+len+1,libname,sizeof(path)-len-2); if ( env[len] == 0 ) env = NULL; /* end of EVOLVERPATH */ else env += len+1; } /* try given name */ if ( ! fd ) { strncpy(path,libname,sizeof(path)); fd = dlopen(path,RTLD_NOW); } if ( ! fd ) { sprintf(errmsg,"Cannot open dynamic library %s. Reason:\n",libname); #ifndef WIN32 strncpy(errmsg+strlen(errmsg),dlerror(),sizeof(errmsg)-strlen(errmsg)-2); #endif kb_error(2203,errmsg,DATAFILE_ERROR); } dll_list[k].name = mycalloc(1,strlen(libname)+4); strcpy(dll_list[k].name,libname); dll_list[k].handle = fd; #else kb_error(2204,"This Evolver not compiled for dynamic load libraries.\n", DATAFILE_ERROR); #endif } /************************************************************************* * * function: unload_libraries * * purpose: unload dynamic link libraries */ void unload_libraries() { int k; for ( k = 0 ; k < MAX_DLL ; k++ ) if ( dll_list[k].name ) { myfree(dll_list[k].name); dll_list[k].name = NULL; #ifdef ENABLE_DLL dlclose(dll_list[k].handle); dll_list[k].handle = NULL; #endif } } /********************************************************************* * * function: search_libraries() * * purpose: find function name in dynamic load libraries. * * return: pointer to function. NULL if not found. */ dll_func_type search_libraries(funcname) char *funcname; { #ifdef ENABLE_DLL int i; dll_func_type f; for ( i = 0 ; i < MAX_DLL ; i++ ) if ( dll_list[i].handle ) { f = (dll_func_type)dlsym(dll_list[i].handle,funcname); if ( f ) return f; } #endif return NULL; } /**************************************************************************** E L L I P T I C F U N C T I O N S ****************************************************************************/ /***************************** Complete elliptic integrals *****************************/ REAL ellipticK(m) REAL m; { REAL a,b,anext; if ( m >= 1.0 ) kb_error(2422,"ellipticK domain violation, parameter >= 1.\n",RECOVERABLE); a = 1.0; b = sqrt(sqrt(1 - m)); while ( fabs(a-b) > machine_eps ) { anext = (a + b)/2; b = sqrt(sqrt(a*b*(a*a+b*b)/2)); a = anext; } return M_PI/2/a/a; } REAL ellipticE(m) REAL m; { REAL a,b,anext; REAL K,sum = 0; REAL ff = 1.0; if ( m > 1.0 ) kb_error(2423,"ellipticE domain violation, parameter > 1.\n",RECOVERABLE); if ( m == 1.0 ) return 1.0; a = 1.0; b = sqrt(sqrt(1 - m)); while ( fabs(a-b) > machine_eps ) { REAL aa = a*a, bb = b*b; sum += ff*(aa*aa - (aa+bb)*(aa+bb)/4); ff *= 4; anext = (a + b)/2; b = sqrt(sqrt(a*b*(a*a+b*b)/2)); a = anext; } K = M_PI/2/a/a; return K*(1.0 - sum); } REAL ellipticEdm(m) REAL m; { if ( m == 1.0 ) return 1.0e31; return m==0 ? -M_PI/8 : (ellipticE(m) - ellipticK(m))/2/m; } REAL ellipticKdm(m) REAL m; { return m==0 ? M_PI/8 : (ellipticE(m) - (1-m)*ellipticK(m))/2/m/(1-m); } REAL ellipticEdmdm(m) REAL m; { return (m==0) ? -3./64*M_PI : ((m-2)*ellipticE(m) - 2*(m-1)*ellipticK(m)) /4/m/m/(1-m); } REAL ellipticKdmdm(m) REAL m; { return (m == 0) ? 9./64*M_PI : ((4*m-2)*ellipticE(m) + (2-5*m+3*m*m)*ellipticK(m))/4/m/m/(1-m)/(1-m); } REAL incompleteEllipticFdphi(phi,m) REAL phi,m; { return 1/sqrt(1 - m*sin(phi)*sin(phi)); } /* following Abramowitz and Stegun 17.6 */ REAL incompleteEllipticE(phi,m) REAL phi,m; { REAL p,tanp,a,b,c,poweroftwo,csum,E,csinphisum,F,K,retval; REAL anext,bnext,cnext; if ( m > 1.0 ) kb_error(2424,"incompleteEllipticE domain violation, parameter > 1.\n", RECOVERABLE); if ( m == 0 ) return phi; p = phi; tanp = tan(p); a = 1.0; b = sqrt(1-m); c = sqrt(m); poweroftwo = 1.0; csum = c*c; csinphisum = 0; while ( c > machine_eps ) { p = 2*p + atan((b/a - 1)*tanp/(1+b/a*tanp*tanp)); tanp = (1+b/a)*tanp/(1-b/a*tanp*tanp); anext = (a+b)/2; bnext = sqrt(a*b); cnext = (a-b)/2; a = anext; b = bnext; c = cnext; poweroftwo *= 2; csum += poweroftwo*c*c; csinphisum += c*sin(p); } K = M_PI/2/a; E = K - csum*K/2; F = p/poweroftwo/a; retval = E/K*F + csinphisum; return retval; } REAL incompleteEllipticF(phi,m) REAL phi,m; { REAL p,tanp,a,b,c,poweroftwo,csum,csinphisum,F; REAL anext,bnext,cnext; p = phi; tanp = tan(p); a = 1.0; b = sqrt(1-m); c = sqrt(m); poweroftwo = 1.0; csum = c*c; csinphisum = 0; if ( m > 1.0 ) kb_error(2425,"incompleteEllipticE domain violation, parameter > 1.\n", RECOVERABLE); if ( m == 0 ) return phi; while ( c > machine_eps ) { p = 2*p + atan((b/a - 1)*tanp/(1+b/a*tanp*tanp)); tanp = (1+b/a)*tanp/(1-b/a*tanp*tanp); anext = (a+b)/2; bnext = sqrt(a*b); cnext = (a-b)/2; a = anext; b = bnext; c = cnext; poweroftwo *= 2; csum += poweroftwo*c*c; csinphisum += c*sin(p); } F = p/poweroftwo/a; return F; } REAL incompleteEllipticEdphi(phi,m) REAL phi,m; { return sqrt(1 - m*sin(phi)*sin(phi)); } REAL incompleteEllipticEdm(phi,m) REAL phi,m; { if ( m == 0 ) return -(2*phi-sin(2*phi))/8; return (incompleteEllipticE(phi,m)-incompleteEllipticF(phi,m))/2/m; } REAL incompleteEllipticFdm(phi,m) REAL phi,m; { if ( m == 0 ) return (2*phi-sin(2*phi))/8; return incompleteEllipticE(phi,m)/2/(m-1)/m - incompleteEllipticF(phi,m)/2/m + sin(2*phi)/4/(m-1)/sqrt(1-m*sin(phi)*sin(phi)); } REAL incompleteEllipticEseconds(phi,m,dphi,dm,ddphi,ddm,dphidm) REAL phi,m; /* input */ REAL *dphi,*dm,*ddphi,*ddm,*dphidm; /* output */ { REAL E,F; REAL s = sin(phi); REAL s2 = sin(2*phi); REAL d = sqrt(1-m*sin(phi)*sin(phi)); E = incompleteEllipticE(phi,m); F = incompleteEllipticF(phi,m); *dphi = d; if ( m == 0 ) { *dm = -(2*phi-s2)/8; *ddm = -1./128*(12*phi-8*s2+sin(4*phi)); } else { *dm = (E - F)/2/m; *ddm = -1./8/(m-1)/m/m*(2*(m-2)*E - 4*(m-1)*F + m*s2/d); } *ddphi = -m*cos(phi)*s/d; *dphidm = -s*s/2/d; return E; } REAL incompleteEllipticFseconds(phi,m,dphi,dm,ddphi,ddm,dphidm) REAL phi,m; /* input */ REAL *dphi,*dm,*ddphi,*ddm,*dphidm; /* output */ { REAL E,F; REAL s = sin(phi); REAL s2 = sin(2*phi); REAL d = sqrt(1-m*sin(phi)*sin(phi)); E = incompleteEllipticE(phi,m); F = incompleteEllipticF(phi,m); *dphi = 1/d; if ( m == 0 ) { *dm = (2*phi-s2)/8; *ddm = 3./128*(12*phi-8*s2+sin(2*phi)); } else { *dm = -E/2/(m-1)/m - F/2/m + s2/4/(m-1)/d; *ddm = E/2/(m-1)/m/m + E/2/(m-1)/(m-1)/m - (E-F)/4/(m-1)/m/m + F/2/m/m + s*s*s2/8/(m-1)/d/d/d - s2/4/(m-1)/(m-1)/d + E/4/(m-1)/m/m + F/4/m/m - s2/8/m/(m-1)/d; } *ddphi = m*cos(phi)*s/d/d/d; *dphidm = s*s/2/d/d/d; return F; } evolver-2.30c.dfsg/src/resource.h0000644000175300017530000000102211410765113017210 0ustar hazelscthazelsct//{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by evolver.rc // #define IDI_ICON1 101 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif evolver-2.30c.dfsg/src/include.h0000644000175300017530000004110711410765113017014 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************** * File: include.h * * Purpose: master include file for all Evolver source files. * **************************************************************/ #if defined(WINTHREADS) || defined(PTHREADS) #define THREADS #define PARALLEL_MACHINE #define SHARED_MEMORY #endif /* for tracking memory usage */ #ifdef MEMSTRINGS #define mycalloc(num,size) kb_calloc(num,size,__FILE__,__LINE__) #define temp_calloc(num,size) kb_temp_calloc(num,size,__FILE__,__LINE__) #define temp_realloc(oldptr,size) kb_temp_realloc(oldptr,size,__FILE__,__LINE__) #define kb_realloc(ptr,new) KB_realloc(ptr,new,__FILE__,__LINE__) #define dmatrix(a,b,c,d) kb_dmatrix(a,b,c,d,__FILE__,__LINE__) #define dmatrix3(a,b,c) kb_dmatrix3(a,b,c,__FILE__,__LINE__) #define dmatrix4(a,b,c,d) kb_dmatrix4(a,b,c,d,__FILE__,__LINE__) #define temp_dmatrix(a,b,c,d) kb_temp_dmatrix(a,b,c,d,__FILE__,__LINE__) #define temp_dmatrix3(a,b,c) kb_temp_dmatrix3(a,b,c,__FILE__,__LINE__) #define temp_dmatrix4(a,b,c,d) kb_temp_dmatrix4(a,b,c,d,__FILE__,__LINE__) #define my_list_calloc(a,b,c) list_calloc(a,b,c,__FILE__,__LINE__) #define my_list_realloc(a,b,c) list_realloc(a,b,c,__FILE__,__LINE__) #else #define mycalloc(num,size) kb_calloc(num,size) #define temp_calloc(num,size) kb_temp_calloc(num,size) #define temp_realloc(oldptr,size) kb_temp_realloc(oldptr,size) #define kb_realloc(ptr,new) KB_realloc(ptr,new) #define dmatrix(a,b,c,d) kb_dmatrix(a,b,c,d) #define dmatrix3(a,b,c) kb_dmatrix3(a,b,c) #define dmatrix4(a,b,c,d) kb_dmatrix4(a,b,c,d) #define temp_dmatrix(a,b,c,d) kb_temp_dmatrix(a,b,c,d) #define temp_dmatrix3(a,b,c) kb_temp_dmatrix3(a,b,c) #define temp_dmatrix4(a,b,c,d) kb_temp_dmatrix4(a,b,c,d) #define my_list_calloc(a,b,c) list_calloc(a,b,c) #define my_list_realloc(a,b,c) list_realloc(a,b,c) #endif #include /* speed up to replace rep stosd; assumes size multiple of int */ #ifdef KBMEMSET #define memset0(dest,count) { int im; int *pm = (int*)(dest) ; \ for ( im = (count)/sizeof(int) ; im > 0 ; im-- ) *(pm++) = 0; } #else #define memset0(dest,count) memset(dest,0,count) #endif /* Precision */ #ifdef LONGDOUBLE #define REAL long double #define DOT dot #define DWIDTH ((sizeof(REAL)==16) ? 35 : 22) #define DPREC ((sizeof(REAL)==16) ? 32 : 19) #else #ifdef FLOAT #define REAL float #define DOT dotf #define v3d v3f #define v2d v2f #else #define REAL double #define DOT dot #endif #endif #ifdef USE_READLINE //CSL #define MOREPROMPT (char *)1 #define CONTPROMPT (char *)2 #endif /* following also works for Cray */ #ifdef GENERIC /* Adjust header file names if yours are different */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define drand48() ((REAL)rand()/RAND_MAX) #define srand48(seed) srand(seed) #define is_finite(x) (((x)>(-1e300))&&((x)<1e300)) /* MAXALLOC is maximum size allocable by calloc() */ #define MAXALLOC 0x7FFFFFFFL /* Some don't declare calloc, getenv, and bsearch in header files */ #if defined(__GNUC__) /* CSL */ /* PATHCHAR is name-separating character in paths */ #define PATHCHAR '/' /* ENVPATHCHAR is the path separating character in environment strings */ #define ENVPATHCHAR ":" #define FCAST (int(*)(const void*,const void *)) #else char *calloc(); char *getenv(); char *bsearch(); /* PATHCHAR is name-separating character in paths */ #define PATHCHAR '/' /* ENVPATHCHAR is the path separating character in environment strings */ #define ENVPATHCHAR ":" /* NOPROTO should be defined for systems that don't do ANSI prototypes */ #define NOPROTO #endif #endif #ifdef _HPUX_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define is_finite(x) (((x)>(-1e300))&&((x)<1e300)) #define MAXALLOC 0x7FFFFFFFL char *getenv(); #define PATHCHAR '/' #define ENVPATHCHAR ":" #define FCAST (int(*)(const void*,const void *)) #endif #if defined(LINUX) || defined(__CYGWIN__) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define is_finite(x) (((x)>(-1e300))&&((x)<1e300)) /* MAXALLOC is maximum size allocable by calloc() */ #define MAXALLOC 0x7FFFFFFFL /* Some don't declare calloc, getenv, and bsearch in header files */ /* Commenting these out now, since too many conflicts with headers. #ifndef MAC_OS_X char *calloc(); char *getenv(); char *bsearch(); #endif */ /* PATHCHAR is name-separating character in paths */ #define PATHCHAR '/' /* ENVPATHCHAR is the path separating character in environment strings */ #define ENVPATHCHAR ":" #define FCAST (int(*)(const void*,const void *)) #endif #ifdef DECALPHA #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define is_finite(x) finite(x) #define MAXALLOC 0x7FFFFFFFL char *getenv(); #define PATHCHAR '/' #define ENVPATHCHAR ":" #endif #ifdef MAC_APP /* this was done with THINK C */ /* Adjust header file names if yours are different */ #include #include #include #include #include #include #include #include #include #include #include #define is_finite(x) (((x)>(-1e300))&&((x)<1e300)) /* MAXALLOC is maximum size allocable by calloc() */ #define MAXALLOC 0x7FFFFFFFL /* Macs don't have environments */ #define getenv(a) ":fe;:doc" /* PATHCHAR is name-separating character in paths */ #define PATHCHAR ':' /* ENVPATHCHAR is the path separating character in environment strings */ #define ENVPATHCHAR ";" #define drand48() ((REAL)rand()/RAND_MAX) #define srand48(seed) srand(seed) #define NOPIPE /* cast for comparison functions for qsort() and bsearch() */ #define FCAST (__cmp_func) /* to avoid Think C name conflict in a header file */ #define extend my_extend #define NO_YACC_DEBUG /* for workaround of Symantec 8.0.5 68K compiler bug */ #define ANSI_DEF #endif #ifdef MAC_CW /* this was done with CODE WARRIOR */ /* Adjust header file names if yours are different */ #include #include #include #include #include #include #include #include #include #include #include #include #define is_finite(x) (((x)>(-1e300))&&((x)<1e300)) /* MAXALLOC is maximum size allocable by calloc() */ #define MAXALLOC 0x7FFFFFFFL /* Macs don't have environments */ #define getenv(a) ":fe;:doc;::fe;::doc;:::fe;:::doc" /* PATHCHAR is name-separating character in paths */ #define PATHCHAR ':' /* ENVPATHCHAR is the path separating character in environment strings */ #define ENVPATHCHAR ";" #define drand48() ((REAL)rand()/RAND_MAX) #define srand48(seed) srand(seed) #define NOPIPE /* cast for comparison functions for qsort() and bsearch() */ #define FCAST (void*) /* to avoid Think C name conflict in a header file */ #define extend my_extend /* for workaround of Symantec 8.0.5 68K compiler bug */ #define ANSI_DEF #endif #ifdef IRIS /* this weirdness was needed on an IRIX 5.2 machine */ /* due to inconsistent use of _SIZE_T and _SIZE_T_ in headers */ #if !defined(_SIZE_T) && !defined(_SIZE_T_) typedef unsigned size_t; #endif #define _SIZE_T #define _SIZE_T_ /* Kludge for IRIX 6.5 */ #include #undef isalpha #undef isdigit #undef isprint #undef islower #undef isupper #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define is_finite(x) finite(x) #define MAXALLOC 0x7FFFFFFFL #define PATHCHAR '/' #define ENVPATHCHAR ":" #define FCAST (int(*)(const void*,const void *)) #ifdef SGI_MULTI #define PARALLEL_MACHINE #define SHARED_MEMORY #define GET_THREAD_ID m_get_myid() #include extern int _fork(void (*)(), ...); extern int _park_procs(void); extern int _rele_procs(void); extern void _sync(void); extern int _kill_procs(void); extern int _get_myid(void); extern int _set_procs(int); extern int _get_numprocs(void); extern unsigned m_next(void); extern void m_lock(void); extern void m_unlock(void); #endif #endif #ifdef SGI_MULTI #include #include #define M_LOCK(addr) (locklist[0]?ussetlock(locklist[((long)(addr))&(_MAXLOCKS-1)]):0) #define M_UNLOCK(addr) (locklist[0]?usunsetlock(locklist[((long)(addr))&(_MAXLOCKS-1)]):0) #endif #ifdef GCC #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define is_finite(x) (((x)>(-1e300))&&((x)<1e300)) #define MAXALLOC 0x7FFFFFFFL #ifdef MSDOS #define PATHCHAR '\\' #define ENVPATHCHAR ";" #else #define PATHCHAR '/' #define ENVPATHCHAR ":" #endif #define FCAST (int(*)(const void*,const void *)) #define drand48() ((REAL)random()/0x7FFFFFFF) #define srand48(seed) srandom(seed) #endif #ifdef SUN #include #include #include #include #include #ifdef strdup #undef strdup #endif #include #include #include #include #include #include #include #include #include #include #include #define is_finite(x) finite(x) #define MAXALLOC 0x7FFFFFFFL char *getenv(); /* char *calloc(); */ #define PATHCHAR '/' #define ENVPATHCHAR ":" /* #define memmove(dest,src,n) kb_memmove(dest,src,n) */ #define NOPROTO #define FCAST (int(*)(const void*,const void *)) /* following is to undo something mysterious done by prof.h */ #undef MARK #endif #ifdef MSC #define _CRT_SECURE_NO_DEPRECATE 1 #pragma warning(disable:4996) #include #define _USERENTRY #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define is_finite(x) (((x)>(-1e300))&&((x)<1e300)) #ifndef MAXINT #define MAXINT INT_MAX #endif #define MAXALLOC 0x7FFFFFFFL #define PATHCHAR '\\' #define ENVPATHCHAR ";" #define farcalloc(num,size) (void far *)halloc(num,size) #define farfree(ptr) hfree((void huge *)ptr) #define FCAST (int(*)(const void*,const void *)) #define NOPIPEXX #define popen _popen #define pclose _pclose #define _export #define FPRESET _fpreset() /* since graphics in parallel thread */ #define PARALLEL_MACHINE #endif #if defined(__WIN32__) && !defined(__CYGWIN__) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define is_finite(x) (((x)>(-1e300))&&((x)<1e300)) #define MAXALLOC 0x7FFFFFFFL #define PATHCHAR '\\' #define ENVPATHCHAR ";" #define NOPIPEXX #pragma hdrstop /* so qsort() and bsearch() below agree with prototypes */ #define FCAST (int(*)(const void*,const void *)) #define drand48() ((REAL)rand()/RAND_MAX) #define srand48(seed) srand(seed) #else #define __int32 int #endif #ifdef MAC_OS_X /* rogue constant from some header */ #endif #ifdef MPI_EVOLVER #define PARALLEL_MACHINE #include "mpi.h" #undef MPI_REAL #ifdef LONG_DOUBLE #define MPI_REAL MPI_LONG_DOUBLE #elif defined(FLOAT) #define MPI_REAL MPI_FLOAT #else #define MPI_REAL MPI_DOUBLE #endif #define MPI_EXPORT_MAX 4 #endif #ifndef SGI_MULTI #define m_get_myid() 0 #endif #ifdef TC #define atold _atold #endif #ifndef TC #ifndef huge #define huge #endif #endif #ifndef FCAST #define FCAST #endif typedef int DY_OFFSET ; /* Some don't have these manifest constants in math.h (namely, Microsoft) */ #ifndef M_LN2 #define M_E 2.71828182845904523536 #define M_PI 3.14159265358979323846 #define M_LN2 0.693147180559945309417 #endif #ifdef LONGDOUBLE #undef M_E #define M_E 2.7182818284590452353602874713527L #undef M_PI #ifdef TC #define M_PI (atan(1.0)*4) #else #define M_PI 3.1415926535897932384626433832795L #endif #undef M_LN2 #define M_LN2 0.693147180559945309417L #endif #ifndef DBL_EPSILON #define DBL_EPSILON 2.2204460492503131e-16 #endif /* can undefine or redefine these if your system has decent string functions */ #ifndef stricmp #define stricmp(s1,s2) kb_stricmp((s1),(s2)) #define strnicmp(s1,s2,n) kb_strnicmp((s1),(s2),(n)) #endif #ifndef strstr #define strstr(s1,s2) kb_strstr((s1),(s2)) #endif #ifndef strupr #define strupr(a) kb_strupr(a) #endif /* Since tolower and toupper don't always check case before converting */ #undef tolower #undef toupper #ifndef MSC #define tolower(c) (isupper(c) ? ((c)-'A'+'a') : c) #define toupper(c) (islower(c) ? ((c)-'a'+'A') : c) #endif #ifndef MAXDOUBLE #define MAXDOUBLE 1.0e38 #endif #ifdef NOPROTO /* some compilers still don't like prototypes with arguments */ #define ARGS(x) () #else #define ARGS(x) x #endif /* for converting old-style function defs to new */ #ifdef __cplusplus #define ARGS1(old,a) (a) #define ARGS2(old,a,b) (a,b) #define ARGS3(old,a,b,c) (a,b,c) #define ARGS4(old,a,b,c,d) (a,b,c,d) #define ARGS5(old,a,b,c,d,e) (a,b,c,d,e) #define ARGS6(old,a,b,c,d,e,f) (a,b,c,d,e,f) #define CONST const #else #define ARGS1(old,a) old a; #define ARGS2(old,a,b) old a; b; #define ARGS3(old,a,b,c) old a; b; c; #define ARGS4(old,a,b,c,d) old a; b; c; d; #define ARGS5(old,a,b,c,d,e) old a; b; c; d; e; #define ARGS6(old,a,b,c,d,e,f) old a; b; c; d; e; f; #ifndef CONST #define CONST #endif #endif #ifdef ENABLE_DLL #ifndef WIN32 #include #endif #endif #ifdef USE_READLINE #include #include #endif #ifdef PTHREADS #include #endif /* Evolver header files */ #ifdef __cplusplus extern "C" { #endif #include "model.h" #include "storage.h" #include "skeleton.h" #include "extern.h" #include "express.h" #include "quantity.h" #include "web.h" #include "lex.h" #ifdef __cplusplus } #endif #include "proto.h" /* in case of non-parallel machines */ #ifndef M_LOCK #define M_LOCK(addr) #define M_UNLOCK(addr) #endif #ifndef MAXINT #define MAXINT (~(1<<(8*sizeof(int)-1))) #endif #ifndef FPRESET #define FPRESET #endif #if defined(LONGDOUBLE) && !defined(NOLONGMATHFUNC) /* have to do these after math.h */ #define sin sinl #define cos cosl #define tan tanl #define asin asinl #define acos acosl #define atan atanl #define sinh sinhl #define cosh coshl #define tanh tanhl #define asinh asinhl #define acosh acoshl #define atanh atanhl #define exp expl #define log logl #define pow powl #define sqrt sqrtl #define ceil ceill #define fabs fabsl extern REAL fabsl(REAL); /* wasn't in IRIX6.1 math.h */ #define floor floorl #define fmod fmodl #define modf modfl #ifdef LINUX long double strtold(const char *, char **); #define atof(a) strtold(a,NULL) #else #define atof atold #endif #endif #ifdef INLINE #include "inline.h" #endif /* for things we really want to be plain double */ #ifndef DOUBLE #define DOUBLE double #endif #ifdef MSC #define toupper(c) kb_upper_array[c] #define tolower(c) kb_lower_array[c] #endif #ifdef MPI_EVOLVER #include "mpi_evolver.h" #endif evolver-2.30c.dfsg/src/verpopst.c0000644000175300017530000010103511410765113017243 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /****************************************************************** * * File: verpopst.c * * Purpose: In string model, pop vertices with 4 or more edges into * a configuration of vertices with only 3 edges each. * Works in 3-space. No assumptions about edges bounding * facets or not, but does treat facets properly. */ #include "include.h" /********************************************************************* * * Function: verpop_str() * * Purpose: Assembles list of edges for each vertex, and calls * single-vertex routine for vertices with more than 3 * edges. List is first made as list of edge-vertex * pairs, then sorted on vertex. Also deletes * bare, nonfixed vertices. * * Return: Number of vertices popped. */ int verpop_str() { vertex_id v_id; int popcount = 0; FOR_ALL_VERTICES(v_id) { int valence = get_vertex_evalence(v_id); if ( valence == 0 ) popcount += dissolve_vertex(v_id); else if ( valence > 3 ) popcount += poponest(v_id,valence); else if ( (valence >= 2) && (get_vattr(v_id) & (CONSTRAINT|BOUNDARY)) ) popcount += poponest(v_id,valence); } if ( popcount ) { calc_content(Q_FIXED); calc_pressure(); calc_energy(); } return popcount; } /************************************************************************** * * function: pop_string_vertex() * * purpose: try popping given vertex; implements pop vertex in string model. */ int pop_string_vertex(v_id) vertex_id v_id; { int valence = get_vertex_evalence(v_id); int popcount = 0; if ( valence == 0 ) popcount = dissolve_vertex(v_id); else if ( valence > 3 ) popcount = poponest(v_id,valence); else if ( (valence >= 2) && (get_vattr(v_id) & (CONSTRAINT|BOUNDARY)) ) popcount = poponest(v_id,valence); return popcount; } /***************************************************************************** * * function: poponest() * * purpose: pop vertex with more that 3 edges * */ /* comparison routine for qsort */ static int vecomp(a,b) struct veredge *a,*b; { if ( a->v_id < b->v_id ) return -1; if ( a->v_id > b->v_id ) return 1; return 0; } int poponest(v_id,edges) vertex_id v_id; int edges; /* number of edges at this vertex */ { edge_id e_id; int i,j; vertex_id new_v; edge_id new_e; facetedge_id fe_id; REAL cosine,maxcos; REAL *x; int si=0,sj=0; struct side_x { edge_id e_id; /* which edge */ REAL vec[MAXCOORD]; /* side vector, from vertex */ REAL norm; /* length of side */ } *side; if ( valid_id ( get_vertex_fe(v_id) ) ) return new_popverst(v_id,edges); side = (struct side_x *)temp_calloc(sizeof(struct side_x),edges); /* get edge vectors */ e_id = get_vertex_edge(v_id); for ( i = 0 ; i < edges ; i++ ) { side[i].e_id = e_id; get_edge_tail_tangent(e_id,side[i].vec); side[i].norm = sqrt(SDIM_dot(side[i].vec,side[i].vec)); e_id = get_next_tail_edge(e_id); } while ( edges > 3 ) { /* find closest pair of edges */ maxcos = -2.0; for ( i = 0 ; i < edges ; i++ ) { for ( j = i+1 ; j < edges ; j++ ) { cosine = SDIM_dot(side[i].vec,side[j].vec)/ side[i].norm/side[j].norm; if ( cosine > maxcos ) { /* toss in a little randomness if a choice of ways */ /* but big problem if edges not adjacent! */ /* if ( (maxcos > -0.5) && (rand() & 0x0100) ) continue; */ maxcos = cosine; si = i; sj = j; } } } /* split off edges si and sj */ new_v = dup_vertex(v_id); /* new vertex in same spot */ new_e = new_edge(v_id,new_v,NULLID); set_edge_color(new_e,BLACK); remove_vertex_edge(v_id,side[si].e_id); set_edge_tailv(side[si].e_id,new_v); remove_vertex_edge(v_id,side[sj].e_id); set_edge_tailv(side[sj].e_id,new_v); if ( get_vattr(v_id) & BOUNDARY ) set_edge_boundary_num(new_e,get_edge_boundary(v_id)->num); /* take care of facet incidences, assuming plane configuration */ do { facetedge_id first_fe; fe_id = first_fe = get_edge_fe(side[si].e_id); if ( valid_id(fe_id) ) do { edge_id next_e; facetedge_id next_fe,new_fe,other_fe; facet_id f_id; /* see if this facet got split */ next_fe = get_prev_edge(fe_id); next_e = get_fe_edge(next_fe); if ( equal_id(inverse_id(side[sj].e_id),next_e) ) goto loopbottom; /* install new facetedge */ f_id = get_fe_facet(fe_id); new_fe = new_facetedge(f_id,new_e); set_prev_edge(fe_id,new_fe); set_next_edge(next_fe,new_fe); set_prev_edge(new_fe,next_fe); set_next_edge(new_fe,fe_id); other_fe = get_edge_fe(new_e); if ( valid_id(other_fe) ) { set_next_facet(new_fe,other_fe); set_prev_facet(new_fe,other_fe); set_prev_facet(other_fe,new_fe); set_next_facet(other_fe,new_fe); } else { set_edge_fe(new_e,new_fe); set_next_facet(new_fe,new_fe); set_prev_facet(new_fe,new_fe); } loopbottom: fe_id = get_next_facet(fe_id); } while ( valid_id(fe_id) && !equal_id(fe_id,first_fe) ); /* swap roles of sj and si */ i = sj; sj = si; si = i; } while ( sj < si ); /* exit when swapped back */ /* adjust edge list for another round, if necessary */ /* direction of new edge is average of old two */ /* Also move new vertex a little ways away to avoid zero edge */ edges --; x = get_coord(new_v); for ( i = 0 ; i < SDIM ; i++ ) { side[si].vec[i] = side[si].vec[i]/side[si].norm + side[sj].vec[i]/side[si].norm; x[i] += 0.000001 * side[si].vec[i]; } side[si].norm = sqrt(SDIM_dot(side[si].vec,side[si].vec)); side[si].e_id = new_e; if ( sj < edges ) /* trim top of list */ side[sj] = side[edges]; if ( phase_flag && (web.representation == STRING) ) set_e_phase_density(new_e); else set_edge_density(new_e,(get_edge_density(side[si].e_id) + get_edge_density(side[sj].e_id))/2); } temp_free((char *)side); return 1; } /***************************************************************************** * * function: new_popverst() * * purpose: pop vertex with more that 3 edges with variable density * Finds pair with greatest net force, allowing for force * of introduced edge. * */ struct side_t { edge_id e_id; /* which edge */ facetedge_id fe; /* canonical fe */ REAL vec[MAXCOORD]; /* side vector, from vertex */ REAL norm; /* length of side */ REAL density; int degfree; /* degrees of freedom */ } ; int anglecomp ARGS((struct side_t *,struct side_t *)); int anglecomp(a,b) struct side_t *a,*b; { REAL aa = atan2(a->vec[1],a->vec[0]); REAL bb = atan2(b->vec[1],b->vec[0]); if ( aa < bb ) return -1; if ( aa > bb ) return 1; return 0; } int new_popverst(v_id,edges) vertex_id v_id; /* vertex being popped */ int edges; /* number of edges at this vertex */ { int i,j,m; int besti; vertex_id new_v; edge_id new_e; edge_id e_id,first_e; edge_id e1,e2; /* edges to be pulled out */ REAL *x; facet_id f1,f2; REAL ff,f[MAXCOORD],fproj[MAXCOORD],ffmax; REAL new_density=0.0; struct side_t *side; REAL bestmove[MAXCOORD]; facetedge_id fe_id,new_fe1,new_fe2,other_fe; int attr1,attr2,attrv; REAL cosa; facetedge_id fe; int degfree; /* degrees of freedom of popping vertex */ struct boundary *vbdry; /* boundary of popping vertex */ conmap_t *vmap,*emap; /* constraint list of popping vertex */ /* First, quick checks to rule out most vertices */ edges = get_vertex_evalence(v_id); attrv = get_vattr(v_id); if ( !(attrv&FIXED) && !(attrv&(BOUNDARY|CONSTRAINT)) && (edges <= 3) ) return 0; /* typical interior vertex */ if ( edges <= 1 ) return 0; /* Now, we have to get more detailed info */ /* Get degrees of freedom of vertex */ degfree = SDIM; if ( attrv & FIXED ) degfree = 0; else if ( attrv & BOUNDARY ) { vbdry = get_boundary(v_id); if ( (vbdry->attr & NONWALL) ) degfree = vbdry->pcount; } else if ( attrv & CONSTRAINT ) { vmap = get_v_constraint_map(v_id); for ( i = 1; i <= (int)vmap[0] ; i++ ) if (!((get_constraint(vmap[i])->attr)&(NONPOSITIVE|NONNEGATIVE|NONWALL))) degfree--; } side = (struct side_t *)temp_calloc(edges+1,sizeof(struct side_t)); /* get edge vectors */ e_id = first_e = get_vertex_edge(v_id); for ( i = 0 ; i < edges ; i++ ) { if ( !valid_id(e_id) ) { sprintf(errmsg,"Pop vertex: Invalid edge at vertex %s.\n", ELNAME(v_id)); kb_error(1382,errmsg,RECOVERABLE); } /* Get degrees of freedom of edge */ attr1 = get_eattr(e_id); side[i].degfree = SDIM; if ( attr1 & FIXED ) side[i].degfree = 0; else if ( attr1 & BOUNDARY ) { if ( !(get_edge_boundary(e_id)->attr & NONWALL) ) side[i].degfree = get_edge_boundary(e_id)->pcount; } else if ( attr1 & CONSTRAINT ) { emap = get_e_constraint_map(e_id); for ( j = 1; j <= (int)emap[0] ; j++ ) if (!((get_constraint(emap[j])->attr)&(NONPOSITIVE|NONNEGATIVE|NONWALL))) side[i].degfree--; } side[i].e_id = e_id; side[i].fe = get_edge_fe(e_id); if ( valid_id(side[i].fe) ) { f1 = get_fe_facet(side[i].fe); if ( valid_id(f1) && inverted(f1) ) side[i].fe = get_next_facet(side[i].fe); } get_edge_tail_tangent(side[i].e_id,side[i].vec); side[i].norm = sqrt(SDIM_dot(side[i].vec,side[i].vec)); if ( side[i].norm <= 0.0 ) { sprintf(errmsg,"verpop: edge %s length zero. \n", ELNAME(side[i].e_id)); kb_error(1383,errmsg,WARNING); temp_free((char *)side); return 0; } side[i].density = get_edge_density(side[i].e_id); e_id = get_next_tail_edge(e_id); if ( equal_id(e_id,first_e) && ( i < edges-1 ) ) { sprintf(errmsg, "Internal error: Expected %d edges; found %d.\n",edges,i+1); outstring(errmsg); kb_error(1384, "new_popverst(): Not expected number of edges around vertex\n", RECOVERABLE); } } if ( edges==3 ) if ( ( (side[0].degfree <= degfree) ? 1:0 ) + ( (side[1].degfree <= degfree) ? 1:0 ) + ( (side[2].degfree <= degfree) ? 1:0 ) >= 2 ) { temp_free((char *)side); return 0; } /* order edges around vertex geometrically */ /* if ( SDIM == 2 ) qsort((char*)side,edges,sizeof(struct side_t),FCAST anglecomp); else */ { /* see if we can use facet info */ facetedge_id fe; int top,bottom; int ends = 0; /* can have at most two ends of facet fan */ for ( i = 0 ; i < edges ; i++ ) { if ( !valid_id(get_fe_facet(side[i].fe)) ) goto order_fail; if ( equal_id(get_next_facet(side[i].fe),side[i].fe) ) ends++; } if ( (ends != 0) && (ends != 2) ) goto order_fail; if ( ends == 2 ) { /* move an end to the first slot */ for ( i = 0 ; i < edges ; i++ ) { if ( equal_id(get_next_facet(side[i].fe),side[i].fe) ) { struct side_t temp; if ( i == 0 ) break; temp = side[0]; side[0] = side[i]; side[i] = temp; break; } } } /* now trace path around facets */ fe = side[0].fe; for ( i = 1 ; i < edges ; i++ ) { facetedge_id next_fe = inverse_id(get_prev_edge(fe)); edge_id next_e = get_fe_edge(next_fe); for ( j = i; j < edges ; j++ ) if ( equal_id(side[j].e_id,next_e) ) { struct side_t temp; if ( i == j ) break; temp = side[j]; side[j] = side[i]; side[i] = temp; break; } if ( j == edges ) goto order_fail; fe = get_next_facet(next_fe); } goto order_succeed; order_fail: /* try geometric ordering by adding closest to existing chain */ /* Leave first where it is, as initial link in chain */ side[edges] = side[0]; top = edges; bottom = 0; while ( top - bottom > 1 ) { REAL topgap,bottomgap,topcos,bottomcos; int topbest=0,bottombest=0; bottomgap = topgap = -1e30; for ( i = bottom+1 ; i < top ; i++ ) { bottomcos = SDIM_dot(side[i].vec,side[bottom].vec)/ side[i].norm/side[bottom].norm; if ( bottomcos > bottomgap ) { bottomgap = bottomcos; bottombest = i; } topcos = SDIM_dot(side[i].vec,side[top].vec)/ side[i].norm/side[top].norm; if ( topcos > topgap ) { topgap = topcos; topbest = i; } } if ( topgap > bottomgap ) { /* add at top */ top--; if ( topbest != top ) { struct side_t temp = side[top]; side[top] = side[topbest]; side[topbest] = temp; } } else { /* add at bottom */ bottom++; if ( bottombest != bottom ) { struct side_t temp = side[bottom]; side[bottom] = side[bottombest]; side[bottombest] = temp; } } } order_succeed: ; } side[edges] = side[0]; /* easy wraparound */ if ( edges == 2 ) { /* must be constraint or boundary or fixed */ if ( (side[0].degfree <= degfree) || (side[1].degfree <= degfree ) ) { temp_free((char *)side); return 0; } if ( (side[0].degfree <= 1 ) && (side[1].degfree <= 1 ) ) { temp_free((char *)side); return 0; } besti = 0; /* find angle between edges to see which way we want to pop */ cosa = SDIM_dot(side[0].vec,side[1].vec); e1 = side[0].e_id; e2 = side[1].e_id; if ( (side[0].degfree > degfree) && (side[1].degfree > degfree) && (degfree >= 1) && (cosa < 0.0) ) { /* split apart along constraint rather than make Y */ if ( verbose_flag ) { sprintf(msg,"Popping vertex %s.\n",ELNAME(v_id)); outstring(msg); } new_v = dup_vertex(v_id); /* new vertex in same spot, to be pulled out */ attrv = get_vattr(new_v); remove_vertex_edge(v_id,e2); set_edge_tailv(e2,new_v); fe = get_edge_fe(e1); set_prev_edge(fe,inverse_id(fe)); fe = get_edge_fe(e2); set_prev_edge(fe,inverse_id(fe)); goto newpop_exit; } } if ( pop_disjoin_flag && (edges == 4) ) { /* see if exactly two opposite wedges have same body */ facetedge_id fe_id[4]; facet_id f_id[4]; body_id b_id[4]; int same02,same13; REAL *xnew,*xold,*x0,*x1,*x2,*x3; for ( i = 0 ; i < edges ; i++ ) { /* assume positive facets on bodies */ fe_id[i] = get_edge_fe(side[i].e_id); if ( !valid_id(fe_id[i]) ) { f_id[i] = b_id[i] = NULLID; continue; } f_id[i] = get_fe_facet(fe_id[i]); if ( inverted(f_id[i]) ) { fe_id[i] = get_next_facet(fe_id[i]); f_id[i] = get_fe_facet(fe_id[i]); if ( inverted(f_id[i]) ) { f_id[i] = b_id[i] = fe_id[i] = NULLID; continue; } } b_id[i] = get_facet_body(f_id[i]); } same02 = equal_id(b_id[0],b_id[2]) && valid_id(b_id[0]); same13 = equal_id(b_id[1],b_id[3]) && valid_id(b_id[1]); if ( same02 != same13 ) { /* do the disjoin */ if ( same13 ) { /* shuffle things so same02 */ element_id temp; struct side_t tempside = side[0]; side[0] = side[1]; side[1] = side[2]; side[2] = side[3]; side[3] = tempside; temp = fe_id[0]; fe_id[0] = fe_id[1]; fe_id[1] = fe_id[2]; fe_id[2] = fe_id[3]; fe_id[3] = temp; temp = f_id[0]; f_id[0] = f_id[1]; f_id[1] = f_id[2]; f_id[2] = f_id[3]; f_id[3] = temp; temp = b_id[0]; b_id[0] = b_id[1]; b_id[1] = b_id[2]; b_id[2] = b_id[3]; b_id[3] = temp; } /* now the real action */ new_v = dup_vertex(v_id); /* new vertex in same spot */ remove_vertex_edge(v_id,side[1].e_id); set_edge_tailv(side[1].e_id,new_v); remove_vertex_edge(v_id,side[2].e_id); set_edge_tailv(side[2].e_id,new_v); if ( valid_id(f_id[0]) ) { /* have to merge facets */ facetedge_id fe_a = fe_id[0]; facetedge_id fe_b = inverse_id(get_prev_edge(fe_a)); facetedge_id fe_c = fe_id[2]; facetedge_id fe_d = inverse_id(get_prev_edge(fe_c)); set_prev_edge(fe_a,inverse_id(fe_d)); set_prev_edge(fe_b,inverse_id(fe_c)); set_prev_edge(fe_c,inverse_id(fe_b)); set_prev_edge(fe_d,inverse_id(fe_a)); if ( !equal_id(f_id[0],f_id[2]) ) { /* merge f_id[2] into f_id[1] */ facetedge_id fe = fe_c; do { set_fe_facet(fe,f_id[0]); fe = get_next_edge(fe); } while ( valid_id(fe) && !equal_element(f_id[0],get_fe_facet(fe)) ); fe = inverse_id(fe_d); do { set_fe_facet(fe,f_id[0]); fe = get_prev_edge(fe); } while ( valid_id(fe) && !equal_element(f_id[0],get_fe_facet(fe)) ); set_facet_body(f_id[2],NULLID); free_element(f_id[2]); } } /* spread the vertices a bit */ xold = get_coord(v_id); xnew = get_coord(new_v); x0 = side[0].vec; x1 = side[1].vec; x2 = side[2].vec; x3 = side[3].vec; for ( i = 0 ; i < SDIM ; i++ ) { xold[i] += 0.05*x0[i] - 0.05*x1[i] - 0.05*x2[i] + 0.05*x3[i]; xnew[i] += -0.05*x0[i] + 0.05*x1[i] + 0.05*x2[i] - 0.05*x3[i]; } temp_free((char *)side); return 1; } /* else fall through and do ordinary wedge splitting */ } /* If here, want to do Y pull out */ /* find two edges that pull apart the most */ ffmax = 0.01; /* margin to prevent looping with autopop */ besti = -1; for ( i = 0 ; i < edges ; i++ ) { e1 = side[i].e_id; e2 = side[i+1].e_id; /* test to see if this pair is valid */ if ( side[i].degfree < 1 ) continue; if ( side[i+1].degfree < 1 ) continue; attr1 = get_eattr(e1); attr2 = get_eattr(e2); if ( get_edge_boundary(e1) != get_edge_boundary(e2) ) continue; if ( (attr1 & CONSTRAINT) || (attr2 & CONSTRAINT) ) { conmap_t *map1 = get_e_constraint_map(e1); conmap_t *map2 = get_e_constraint_map(e2); conmap_t ii,jj,found; found = 0; for ( ii = 1 ; ii <= map1[0] ; ii++ ) { for ( jj = 1; jj <= map2[0] ; jj++ ) if ( map1[ii] == map2[jj] ) found++; } if ( (found != map1[0]) && ( found != map2[0]) ) continue; } if ( (edges <= 3) && (side[(6-i-i-1)%3].degfree <= degfree) ) { if ((side[i].degfree <= degfree)||(side[i+1].degfree <= degfree) ) continue; /* fake wall ??? */ } /* check to see if better than what we have */ for ( m = 0 ; m < SDIM ; m++ ) { f[m] = (side[i].density*side[i].vec[m]/side[i].norm + side[i+1].density*side[i+1].vec[m]/side[i+1].norm); } if ( (degfree < SDIM) && ((side[i].degfree <= degfree) || (side[i+1].degfree <= degfree)) ) { force_project(f,v_id,fproj); ff = sqrt(SDIM_dot(fproj,fproj)); } else ff = sqrt(SDIM_dot(f,f)); if ( phase_flag ) { f1 = get_fe_facet(get_prev_facet(side[i].fe)); f2 = get_fe_facet(side[i+1].fe); new_density = phase_data[get_f_phase(f1)][get_f_phase(f2)]; } else /* use minimal density of wedge edges */ { REAL den1,den2; den1 = get_edge_density(e1); den2 = get_edge_density(e2); new_density = (den1 < den2) ? den1 : den2; } if ( ff - new_density > ffmax ) { REAL mag; /* size of new edge */ ffmax = ff - new_density ; besti = i; mag = 0.1*((side[i].norm < side[i+1].norm) ? side[i].norm : side[i+1].norm); for ( m = 0 ; m < SDIM ; m++ ) bestmove[m] = mag*f[m]/ff; } } if ( besti == -1 ) { temp_free((char *)side); return 0; } if ( verbose_flag ) { sprintf(msg,"Popping vertex %s.\n",ELNAME(v_id)); outstring(msg); } /* split off edges besti and besti+1 */ e1 = side[besti].e_id; e2 = side[besti+1].e_id; attr1 = get_eattr(e1); attr2 = get_eattr(e2); new_v = dup_vertex(v_id); /* new vertex in same spot, to be pulled out */ attrv = get_vattr(new_v); /* move new vertex a little ways away to avoid zero edge */ x = get_coord(new_v); for ( i = 0 ; i < SDIM ; i++ ) x[i] += bestmove[i]; new_e = new_edge(v_id,new_v,NULLID); set_edge_color(new_e,BLACK); remove_vertex_edge(v_id,e1); set_edge_tailv(e1,new_v); remove_vertex_edge(v_id,e2); set_edge_tailv(e2,new_v); set_edge_density(new_e,new_density); if ( (attr1 & NONCONTENT) || (attr2 & NONCONTENT) ) set_attr(new_e,NONCONTENT); if ( (attr1 & NO_REFINE) || (attr2 & NO_REFINE) ) set_attr(new_e,NO_REFINE); /* want to be careful here; new vertex and edge should get union of properties of the two wedge edges */ unset_attr(new_v,FIXED); if ( attrv & CONSTRAINT ) { conmap_t *map = get_v_constraint_map(new_v); for ( i = map[0] ; i >= 0 ; i-- ) map[i] = 0; unset_attr(new_v,CONSTRAINT); } if ( attrv & BOUNDARY ) { set_boundary_num(new_v,0); unset_attr(new_v,BOUNDARY); } if ( (attr1 & CONSTRAINT) || (attr2 & CONSTRAINT) ) { conmap_t *map1 = get_e_constraint_map(e1); conmap_t *map2 = get_e_constraint_map(e2); conmap_t i,j; for ( i = 1 ; i <= map1[0] ; i++ ) { set_v_constraint_map(new_v,map1[i]); set_e_constraint_map(new_e,map1[i]); } for ( j = 1 ; j <= map2[0] ; j++ ) { set_v_constraint_map(new_v,map2[j]); set_e_constraint_map(new_e,map2[j]); } } else if ( ((attr1 & BOUNDARY) || (attr2 & BOUNDARY)) && (get_vattr(v_id) & BOUNDARY) ) { struct boundary *b1 = get_edge_boundary(e1); struct boundary *b2 = get_edge_boundary(e2); struct boundary *bv = get_boundary(v_id); if ( (b1 == bv) || (b2 == bv) ) { set_edge_boundary_num(new_e,bv->num); set_boundary_num(new_v,bv->num); b_extrapolate(bv,get_coord(v_id),x,x,get_param(v_id), get_param(new_v),new_v); } } /* take care of facet incidences, assuming plane configuration */ new_fe1 = NULLID; f1 = get_fe_facet(side[besti].fe); if ( valid_id(f1) ) { /* install new facetedge */ fe_id = side[besti].fe; fe_id = inverse_id(fe_id); other_fe = get_next_edge(fe_id); if ( !equal_element(side[besti+1].e_id,get_fe_edge(other_fe)) ) { new_fe1 = new_facetedge(f1,new_e); set_edge_fe(new_e,new_fe1); set_prev_edge(other_fe,inverse_id(new_fe1)); set_next_edge(fe_id,inverse_id(new_fe1)); set_prev_edge(new_fe1,inverse_id(other_fe)); set_next_edge(new_fe1,inverse_id(fe_id)); set_next_facet(new_fe1,new_fe1); set_prev_facet(new_fe1,new_fe1); } } fe_id = get_prev_facet(side[besti].fe); if ( !equal_id(fe_id,side[besti].fe) ) { f1 = get_fe_facet(fe_id); if ( valid_id(f1) ) { /* install new facetedge */ fe_id = inverse_id(fe_id); other_fe = get_next_edge(fe_id); if ( !equal_element(side[besti+1].e_id,get_fe_edge(other_fe)) ) { new_fe1 = new_facetedge(f1,new_e); set_edge_fe(new_e,new_fe1); set_prev_edge(other_fe,inverse_id(new_fe1)); set_next_edge(fe_id,inverse_id(new_fe1)); set_prev_edge(new_fe1,inverse_id(other_fe)); set_next_edge(new_fe1,inverse_id(fe_id)); set_next_facet(new_fe1,new_fe1); set_prev_facet(new_fe1,new_fe1); } } } f2 = get_fe_facet(side[besti+1].fe); if ( valid_id(f2) ) { fe_id = side[besti+1].fe; other_fe = get_prev_edge(fe_id); if ( !equal_element(side[besti].e_id,get_fe_edge(other_fe)) ) { new_fe2 = new_facetedge(f2,new_e); set_edge_fe(new_e,new_fe2); set_next_edge(other_fe,new_fe2); set_prev_edge(fe_id,new_fe2); set_next_edge(new_fe2,fe_id); set_prev_edge(new_fe2,other_fe); if ( valid_id(new_fe1) ) { set_prev_facet(new_fe1,new_fe2); set_next_facet(new_fe1,new_fe2); set_prev_facet(new_fe2,new_fe1); set_next_facet(new_fe2,new_fe1); } else { set_next_facet(new_fe2,new_fe2); set_prev_facet(new_fe2,new_fe2); } } } fe_id = get_next_facet(side[besti+1].fe); if ( !equal_id(fe_id,side[besti+1].fe) ) { f2 = get_fe_facet(fe_id); if ( valid_id(f2) ) { other_fe = get_prev_edge(fe_id); if ( !equal_element(side[besti].e_id,get_fe_edge(other_fe)) ) { new_fe2 = new_facetedge(f2,new_e); set_edge_fe(new_e,new_fe2); set_next_edge(other_fe,new_fe2); set_prev_edge(fe_id,new_fe2); set_next_edge(new_fe2,fe_id); set_prev_edge(new_fe2,other_fe); if ( valid_id(new_fe1) ) { set_prev_facet(new_fe1,new_fe2); set_next_facet(new_fe1,new_fe2); set_prev_facet(new_fe2,new_fe1); set_next_facet(new_fe2,new_fe1); } else { set_next_facet(new_fe2,new_fe2); set_prev_facet(new_fe2,new_fe2); } } } } /* make sure new edge has at least one facetedge structure */ if ( !valid_id(get_edge_fe(new_e)) ) { facetedge_id new_fe = new_facetedge(NULLID,new_e); set_edge_fe(new_e,new_fe); set_next_edge(new_fe,inverse_id(new_fe)); set_prev_edge(new_fe,inverse_id(new_fe)); set_next_facet(new_fe,new_fe); set_prev_facet(new_fe,new_fe); } newpop_exit: temp_free((char *)side); if ( (edges > 4) || ((edges > 2) && (degfree < SDIM)) ) return 1 + new_popverst(v_id,edges-1); /* recurse if need */ return 1; } /****************************************************************************** Autopop and autochop functions. *****************************************************************************/ edge_id *autopop_list; /* list of short edges found */ edge_id *autochop_list; /* list of long edges found */ /***************************************************************************** * * Function: autopop_init() * * Purpose: Allocates lists for accumulating poppable or choppable edges. */ void autopop_init() { if ( autopop_flag ) { autopop_list = (edge_id *)temp_calloc(web.skel[EDGE].count+web.skel[FACET].count,sizeof(edge_id)); } if ( autochop_flag ) { autochop_list = (edge_id *)temp_calloc(web.skel[EDGE].count,sizeof(edge_id)); } } /* end autopop_init() */ /************************************************************************** * * Function: autopop_cleanup() * * Purpose: deallocate lists */ void autopop_cleanup() { if ( autochop_list ) { temp_free((char*)autochop_list); autochop_list = NULL; } if ( autopop_list ) { temp_free((char*)autopop_list); autopop_list = NULL; } } /* end autopop_cleanup() */ /******************************************************************** * * function: autopop_detect() * * purpose: Find which edges will shrink to zero length or beyond. * (half length when runge-kutta in effect) * Uses velocity attribute of vertices to predict motion, * so it does not chop growing small edges produced by * topology changes. * Makes a list, but does not change anything. Do * motion, then call autopop_pop() to eliminate edges. * Also detects for autochop. * * Soapfilm model: also detects for facets disappearing. */ void autopop_detect(scale) REAL scale; /* scale factor for motion */ { edge_id e_id; REAL minlength = sqrt(2*scale); /* stability critical length */ if ( autopop_quartic_flag ) minlength = 2*sqrt(sqrt(scale)); #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) { mpi_autopop_detect(scale); return; } #endif autopop_init(); autopop_count = 0; autochop_count = 0; FOR_ALL_EDGES(e_id) { vertex_id headv = get_edge_headv(e_id); REAL *headf = get_velocity(headv); vertex_id tailv = get_edge_tailv(e_id); REAL *tailf = get_velocity(tailv); REAL tailtan[MAXCOORD],headtan[MAXCOORD]; REAL length,dx; get_edge_tail_tangent(e_id,tailtan); get_edge_tail_tangent(inverse_id(e_id),headtan); length = sqrt(SDIM_dot(tailtan,tailtan)); if ( length > 0.0 ) dx = -scale*(SDIM_dot(headf,headtan) + SDIM_dot(tailf,tailtan))/length; else dx = 0.0; if ( autopop_flag && (dx <= 0.0) ) { if ( (length < minlength) || (length + dx <= (runge_kutta_flag ? 0.52*length : 0.0)) ) { if ( immediate_autopop_flag ) { int ret = eliminate_edge(e_id); if ( ret ) free_element(e_id); continue; /* next edge */ } else /* add to list */ autopop_list[autopop_count++] = e_id; } } if ( autochop_flag ) if ( length + dx > autochop_length) { /* add to list */ autochop_list[autochop_count++] = e_id; } } if ( web.representation == SOAPFILM ) { facet_id f_id; FOR_ALL_FACETS(f_id) { int i; REAL sides[FACET_EDGES][MAXCOORD]; REAL *velocities[FACET_VERTS]; REAL area=0.0,area_rate; facetedge_id fe; REAL perim = 0.0; fe = get_facet_fe(f_id); for ( i = 0 ; i < FACET_VERTS ; i++ ) { e_id = get_fe_edge(fe); get_edge_side(e_id,sides[i]); velocities[i] = get_velocity(get_edge_tailv(e_id)); fe = get_next_edge(fe); } /* get area rate of change */ area_rate = 0.0; for ( i = 0 ; i < FACET_VERTS ; i++ ) { int ii = (i==0)?2:(i-1); REAL s1s1,s1s2,s2s2,s1v,s2v; s1s1 = SDIM_dot(sides[i],sides[i]); s2s2 = SDIM_dot(sides[ii],sides[ii]); s1s2 = -SDIM_dot(sides[i],sides[ii]); s1v = SDIM_dot(sides[i],velocities[i]); s2v = -SDIM_dot(sides[ii],velocities[i]); area = sqrt(s1s1*s2s2 - s1s2*s1s2); area_rate -= (s1v*(s2s2 - s1s2) + s2v*(s1s1 - s1s2))/area; perim += sqrt(s1s1); } if ( (area_rate < 0) && (area + scale*area_rate <= minlength*perim/2) ) { if ( immediate_autopop_flag ) eliminate_facet(f_id); else /* add to list */ autopop_list[autopop_count++] = f_id; } } /* end facet loop */ } /* end facet detect */ } /* end autopop_detect() */ /*********************************************************************** * * function: autopop_pop() * * purpose: After motion, eliminate edges found by autopop_detect() * and pop any resulting bad vertices. * In soapfilm model, can also delete facets. * */ void autopop_pop() { int k; int popped = 0; #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) { mpi_autopop_pop(); return; } #endif if ( autopop_list == NULL ) return; #ifdef MPI_EVOLVER if ( this_task != MASTER_TASK ) mpi_delete_init(); #endif for ( k = 0 ; k < autopop_count ; k++ ) { if ( id_type(autopop_list[k]) == EDGE ) { if ((get_eattr(autopop_list[k])&ALLOCATED) && eliminate_edge(autopop_list[k])) free_element(autopop_list[k]); } else eliminate_facet(autopop_list[k]); } if ( autopop_count ) { if ( web.representation == STRING ) popped = verpop_str(); else popped = popfilm(); } if ( (autopop_count > 0) || (popped > 0) ) { sprintf(msg,"Autopopped %d edges, %d vertices.\n",autopop_count,popped); outstring(msg); } temp_free((char *)autopop_list); autopop_list = NULL; autopop_count = 0; #ifdef MPI_EVOLVER if ( this_task != MASTER_TASK ) mpi_task_delete_wrapup(); #endif free_discards(DISCARDS_SOME); /* prevents auto pileup */ } /*********************************************************************** * * function: autochop_chop() * * purpose: After motion, divides edges found by autochop_detect() * */ void autochop_chop() { int k; #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) { mpi_autopop_chop(); return; } #endif if ( autochop_list == NULL ) return; for ( k = 0 ; k < autochop_count ; k++ ) { /* printf("chopping edge %s\n",ELNAME(autochop_list[k])); */ if ( !valid_element(autochop_list[k]) ) continue; edge_refine(autochop_list[k]); } if ( autochop_count > 0 ) { sprintf(msg,"Autochopped %d edges.\n",autochop_count); outstring(msg); } temp_free((char *)autochop_list); autochop_list = NULL; autochop_count = 0; } evolver-2.30c.dfsg/src/mindeg.c0000644000175300017530000017641211410765113016637 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ #include "include.h" /************************************************************* * * file: mindeg.c * * Purpose: My own minimum degree sparse matrix factoring algorithm */ /* This file is basically the old version with compaction to take up less memory. */ /* Algorithm: at each stage, eliminated vertices fall into disjoint clusters, representing factored hessian of region with fixed boundary. Vertex of minimal degree is eliminated, sometimes resulting in merging of clusters. */ /* clusters or regions also called cliques in the literature */ /* Nodes are gathered into supernodes, defined as having same set of cliques. Supernodes are ordered by degree, and independent supernodes are eliminated in degree order until reach lower bound estimate for next round. Independence means not sharing region. */ /* This version has supernodes as persistent structures */ /* data structures */ static struct md_vertex { int supernode; /* which it belongs to */ int timestamp; size_t newspot; /* for merging region matrices */ } *vlist; struct region { int vercount; /* number of boundary vertices */ int verlist; /* vertex list spot, dynamically allocated */ int verpnum; /* vertex positions in parent */ int supercount; /* number of boundary supernodes */ int superlist; /* boundary supernode list spot */ int timestamp; /* marking for independence */ int merged; /* region merged with, or self */ int son; /* for start of merge list */ int size; /* number of vertices, incl eliminated */ #define BROTHER superlist /* for merging to parent */ int next; /* doubly linked list of actives */ int prev; /* doubly linked list of actives */ } ; static int regionstart; /* start of active list */ static int last_region; /* end of region chain */ /* supernode is collection of equivalent vertices */ /* defined as having same set of regions */ static struct supernode { int rcount; /* number of regions */ int rlist; /* list of regions spot */ int degree; int flag; /* see below */ float height; /* for ordering of elimination */ int verlist; /* list of vertices spot */ int vercount; /* number of vertices */ int rtimestamp; /* for marking on changed region */ int stimestamp; /* for marking touched */ int next; /* doubly linked list of actives */ int prev; /* doubly linked list of actives */ } *slist ; /* flag bits */ #define MD_AUG_CON 1 static int superstart; /* start of active list */ static int margin; /* how high to go above minimum degree, command line -m */ static int total_fill; /* total number of fill in factored matrix */ static REAL total_flops; /* total operation count, counting mul+add as 2 */ static int passes; /* number of times through main loop */ void dsolve(); extern int mindeg_debug_level; /* 0 none 1 print lowest degree 2 print supernode sizes 3 print vertex elim 4 print region absorbing 5 print region merging */ static int minabsorb = 2; /* max size region to try to absorb */ static int vtimestamp; /* for marking vertices */ static int stimestamp; /* for marking supernodes */ static int rtimestamp; /* for marking regions for independence */ static int old_rtimestamp; /* for saving previous value */ extern int mindeg_margin; /* how high to go above minimum degree, user var */ extern int mindeg_min_region_size; /* merge smaller regions with parent */ static int total_fill; /* total number of fill in factored matrix */ static int L_total; /* allocated spaces for final trianuglar matrix */ static int elim_count; /* number of variables eliminated so far */ /* macros for conveniently accessing working storage */ #define REG(spot) ((struct region *)(S->ISP+(spot))) #define SNODE(spot) ((struct supernode *)(S->ISP+(spot))) #define INT(spot) (S->ISP+(spot)) #define CHAR(spot) ((char*)(S->ISP+(spot))) /* trial stuff */ int K; /* current row number */ int *xIL; /* next uneliminated vertex */ int *xJL; /* lists of rows to be added to uneliminated rows: i > K => xJL(i) is first row to be added to row i, i < K => xJL(i) is row following row i in some list of rows. xJL(i) = -1 indicates end of list */ int md_alloc ARGS((struct linsys *, int)); int region_alloc ARGS((struct linsys *)); int supernode_compare ARGS((struct supernode **,struct supernode **)); int degree_compare ARGS((struct supernode *,struct supernode *)); void do_region_absorb ARGS((struct linsys *,int,int)); int exact_degree ARGS((struct linsys *,struct supernode *)); int exact_sdegree ARGS((struct linsys *,struct supernode *)); void traverse_recur ARGS((struct linsys *,struct region *,int*,int*)); void permute_recur ARGS((struct linsys *,struct region *)); void factor_recur ARGS((struct linsys *,struct region *)); void sparse_permute ARGS((struct linsys *)); void md_vertex_setup ARGS((struct linsys *)); void md_supernode_setup ARGS((struct linsys *)); void md_region_string ARGS((struct linsys *)); void md_supernode_regions ARGS((struct linsys *)); void degree_sort ARGS((struct linsys *)); void multiple_eliminate ARGS((struct linsys *)); void region_absorb ARGS((struct linsys *)); void clean_supernodes ARGS((struct linsys *)); void merge_supernodes ARGS((struct linsys *)); void traverse_region_tree ARGS((struct linsys *)); void mass_eliminate ARGS((struct linsys *,struct supernode*)); void show_mindeg_state ARGS((struct linsys *)); struct supernode **superheap; /* for heap ordering */ static int heapcount; /* how many in heap */ static struct supernode sentinel; /************************************************************************** * * function: show_mindeg_state() * * purpose: Do graphical display of current factorization state. * Sets all edges between non-eliminated vertices to red, * others to black. Pauses for user response. */ void show_mindeg_state(S) struct linsys *S; { char *marks; /* unelim node numbers */ edge_id e_id; int j; int rcount; struct region *r; int rnum; int snum; struct supernode *s; /* count regions */ for ( rnum = regionstart, rcount=0 ; rnum >= 0 ; rnum = r->next ) { r = REG(rnum); rcount++; } /* figure out which vertices are not eliminated */ marks = temp_calloc(S->N,1); for ( snum = superstart ; snum >= 0 ; snum = s->next ) { s = slist + snum; for ( j = 0 ; j < s->vercount ; j++ ) marks[INT(s->verlist)[j]] = 1; } FOR_ALL_EDGES(e_id) { struct hess_verlist *vh = get_vertex_vhead(get_edge_headv(e_id)); struct hess_verlist *vt = get_vertex_vhead(get_edge_tailv(e_id)); if ( marks[vh->rownum] && marks[vt->rownum] ) set_edge_color(e_id,RED); else set_edge_color(e_id,WHITE); } update_display(); sprintf(msg,"Elimcount %d of %d; regions %d\n",elim_count,S->N,rcount); prompt(msg,errmsg,100); temp_free(marks); } /***************************************************************************** * * function: md_alloc() * * purpose: allocate workspace in linsys ISP. ISP[0] is amount allocated in ints. * No deallocation. No initialization. NSP is arena size in ints. * User must allocate ISP and set NSP before first call. * return: offset from start */ int md_alloc(S,bytes) struct linsys *S; int bytes; /* bytes wanted */ { int spot; int size; /* in ints */ size = (bytes+3)/sizeof(int); if ( S->ISP[0] + size + 1 > S->NSP ) { int newsize = (int)(1.5*S->NSP)+bytes; S->ISP = (int*)temp_realloc((char*)S->ISP,newsize*sizeof(int)); S->NSP = newsize; } spot = S->ISP[0] + 1; S->ISP[0] += size; return spot; } /*************************************************************************** * * function: region_alloc() * * purpose: allocate new region structure in workspace * */ int region_alloc(S) struct linsys *S; { int spot; struct region *r; spot = md_alloc(S,sizeof(struct region)); if ( last_region >= 0 ) REG(last_region)->next = spot; else regionstart = spot; r = REG(spot); r->prev = last_region; r->next = -1; r->merged = spot; /* self */ r->son = -1; last_region = spot; return spot; } /***************************************************************************** * * function: md_vertex_setup() * * purpose: initialize matrix vertex list, one vertex per matrix row, * and establish mapping between surface elements and rows. * */ void md_vertex_setup(S) struct linsys *S; { int i,k; struct hess_verlist *vh; vertex_id v_id; body_id b_id; vlist = (struct md_vertex *)temp_calloc(S->N,sizeof(struct md_vertex)); /* set up reverse indexing from vlist to vheads */ FOR_ALL_VERTICES(v_id) { vh = get_vertex_vhead(v_id); for ( k = 0 ; k < vh->freedom ; k++ ) vlist[vh->rownum+k].newspot = vh - vhead; } for ( i = 0 ; i < optparamcount ; i++ ) { vh = vhead + optparam[i].vhead_index; vlist[vh->rownum].newspot = vh - vhead; } if ( augmented_hessian_mode ) { if ( !everything_quantities_flag ) /* bodies */ { FOR_ALL_BODIES(b_id) { vh = get_body_vhead(b_id); if ( vh->freedom ) vlist[vh->rownum].newspot = vh - vhead; } } for ( i=0 ; i < gen_quant_count ; i++ ) { vh = vhead + GEN_QUANT(i)->vhead_index; if ( vh->freedom ) vlist[vh->rownum].newspot = vh - vhead; } } } /***************************************************************************** * * function: md_region_string() * * purpose: initialize region list with one region per edge, */ void md_region_string(S) struct linsys *S; { int j,k,p,q; size_t n1,n2; int rcount; struct region *r; char *entrymark; /* for tagging done entries of A */ int size; int newspot,spot; /* index into workspace */ size_t n1_end; entrymark = temp_calloc(S->IA[S->N],sizeof(char)); /* estimate regions needed */ regionstart = md_alloc(S,0); /* since may need realloc for vertices */ last_region = -1; for ( j = 0 ; j < vhead_count ; j++ ) vhead[j].flags &= ~HV_DONE; /* fill in one region per edge detected in array */ rcount = 0; for ( p = 0 ; p < S->N ; p++ ) { for ( q = S->IA[p]-A_OFF ; q < S->IA[p+1]-A_OFF ; q++ ) { int s1,s2; if ( entrymark[q] ) continue; n1 = vlist[p].newspot; n2 = vlist[S->JA[q]-A_OFF].newspot; if ( n1 == n2 ) continue; /* don't do just one vertex here */ if ( n1 > n2 ) { size_t tmp = n1; n1 = n2; n2 = tmp; } /* need new region */ spot = region_alloc(S); r = REG(spot); /* find its supernodes */ s1 = vlist[vhead[n1].rownum].supernode; s2 = vlist[vhead[n2].rownum].supernode; r->supercount = (s1==s2) ? 1 : 2; newspot = md_alloc(S,r->supercount*sizeof(int)); r = REG(spot); /* in case of reallocation of workspace */ r->superlist = newspot; INT(r->superlist)[0] = s1; if ( s1 != s2 ) INT(r->superlist)[1] = s2; size = vhead[n1].freedom + vhead[n2].freedom; r->size = r->vercount = size; newspot = md_alloc(S,r->vercount*sizeof(int)); r = REG(spot); /* in case of reallocation of workspace */ r->verlist = newspot; for ( k = 0 ; k < vhead[n1].freedom ; k++ ) INT(r->verlist)[k] = vhead[n1].rownum + k; for ( k = 0 ; k < vhead[n2].freedom ; k++ ) INT(r->verlist)[vhead[n1].freedom+k] = vhead[n2].rownum + k; /* the edge */ for ( k = 0 ; k < vhead[n1].freedom ; k++ ) { int row; row = vhead[n1].rownum + k; for ( spot = S->IA[row]-A_OFF ; spot < S->IA[row+1]-A_OFF ; spot++ ) if ( S->JA[spot]-A_OFF >= vhead[n2].rownum ) break; /* entry may not exist if value 0 */ for ( j = 0 ; (spotIA[row+1]-A_OFF) && (jJA[spot]-A_OFF > vhead[n2].rownum+j ) continue; entrymark[spot] = 'x'; spot++; } } /* the vertices */ if ( !(vhead[n1].flags & HV_DONE) ) { for ( k = 0 ; k < vhead[n1].freedom ; k++ ) { int row; row = vhead[n1].rownum + k; for ( spot = S->IA[row]-A_OFF ; spot < S->IA[row+1]-A_OFF ; spot++ ) if ( S->JA[spot]-A_OFF >= vhead[n1].rownum ) break; for ( j=0 ; (spotIA[row+1]-A_OFF) && (jJA[spot]-A_OFF > vhead[n1].rownum+j ) continue; entrymark[spot] = 'x'; spot++; } } vhead[n1].flags |= HV_DONE; } if ( !(vhead[n2].flags & HV_DONE) ) { for ( k = 0 ; k < vhead[n2].freedom ; k++ ) { int row; row = vhead[n2].rownum + k; for ( spot = S->IA[row]-A_OFF ; spot < S->IA[row+1]-A_OFF ; spot++ ) if ( S->JA[spot]-A_OFF >= vhead[n2].rownum ) break; for ( j = 0 ; (spotIA[row+1]-A_OFF)&&(jJA[spot]-A_OFF > vhead[n2].rownum+j ) continue; entrymark[spot] = 'x'; spot++; } } vhead[n2].flags |= HV_DONE; } rcount++; } } /* now see if any vertices have been missed */ n1_end = augmented_hessian_mode ? vhead_count : web.skel[VERTEX].max_ord+1 + optparamcount; for ( n1 = 0 ; n1 < n1_end ; n1++ ) { if ( vhead[n1].flags & HV_DONE ) continue; if ( vhead[n1].freedom == 0 ) continue; /* need new region */ spot = region_alloc(S); r = REG(spot); r->supercount = 1; newspot = md_alloc(S,r->supercount*sizeof(int)); r = REG(spot); /* in case of reallocation of workspace */ r->superlist = newspot; INT(r->superlist)[0] = vlist[vhead[n1].rownum].supernode; size = vhead[n1].freedom; r->size = r->vercount = size; newspot = md_alloc(S,r->vercount*sizeof(int)); r = REG(spot); /* in case of reallocation of workspace */ r->verlist = newspot; for ( k = 0 ; k < vhead[n1].freedom ; k++ ) INT(r->verlist)[k] = vhead[n1].rownum + k; /* fill matrix */ for ( k = 0 ; k < vhead[n1].freedom ; k++ ) { int row; row = vhead[n1].rownum + k; for ( spot = S->IA[row]-A_OFF ; spot < S->IA[row+1]-A_OFF ; spot++ ) if ( S->JA[spot]-A_OFF >= vhead[n1].rownum ) break; for ( j=0 ; (spotIA[row+1]-A_OFF) && (jJA[spot]-A_OFF > vhead[n1].rownum+j ) continue; entrymark[spot] = 'x'; spot++; } } vhead[n1].flags |= HV_DONE; rcount++; } temp_free(entrymark); } /***************************************************************************** * * function: md_region_soapfilm() * * purpose: initialize region list with one region per facet or edge * Film quadratic mode will need per facet. */ #ifdef FACETREGIONS void md_region_soapfilm(S) struct linsys *S; { int i,j,k,n; int ecount; struct md_vertex *v; struct region *r; } #endif /***************************************************************************** * * function: md_supernode_regions() * * purpose: set up lists of regions per supernode */ void md_supernode_regions(S) struct linsys *S; { int k,rnum,snum; struct region *r; struct supernode *s; /* count number of correspondences needed */ for ( rnum = regionstart ; rnum >= 0 ; rnum = r->next ) { r = REG(rnum); for ( k = 0 ; k < r->supercount ; k++ ) slist[INT(r->superlist)[k]].rcount++; } for ( snum = superstart ; snum >= 0 ; snum = s->next ) { s = slist + snum; s->rlist = md_alloc(S,s->rcount*sizeof(int)); s->rcount = 0; /* reset so can use as index in loading */ } for ( rnum = regionstart; rnum >= 0 ; rnum = r->next ) { r = REG(rnum); for ( k = 0 ; k < r->supercount ; k++ ) { s = slist + INT(r->superlist)[k]; INT(s->rlist)[s->rcount++] = rnum; } } } /*************************************************************************** * * function: md_supernode_setup() * * purpose: Create one supernode per vertex. Does not set regions. */ int supercount; void md_supernode_setup(S) struct linsys *S; { int n,i,j; struct supernode *s; int vhead_hi; struct hess_verlist *vh; vhead_hi = augmented_hessian_mode ? vhead_count : web.skel[VERTEX].max_ord+1 + optparamcount; /* allocate supernode list */ for ( n = 0, supercount = 0; n < vhead_hi ; n++ ) if ( vhead[n].freedom > 0 ) supercount++; slist = (struct supernode *)temp_calloc(supercount,sizeof(struct supernode)); s = slist; supercount = 0; /* Higher-order Lagrange edge and facet innards */ if ( web.lagrange_order >= 3 ) /* do edge innards */ { edge_id e_id; vertex_id *v; FOR_ALL_EDGES(e_id) { int df; /* total degrees of freedom on edge */ v = get_edge_vertices(e_id); for ( j = 1, df = 0 ; j < web.lagrange_order ; j++ ) { vh = get_vertex_vhead(v[j]); df += vh->freedom; vh->flags |= SUPERNODE_DONE; } if ( df == 0 ) continue; s->verlist = md_alloc(S,df*sizeof(int)); for ( j = 1 ; j < web.lagrange_order ; j++ ) { vh = get_vertex_vhead(v[j]); if ( vh->freedom > 0 ) { for ( i = 0 ; i < vh->freedom ; i++ ) { INT(s->verlist)[s->vercount+i] = vh->rownum + i; vlist[INT(s->verlist)[s->vercount+i]].supernode = supercount; } s->vercount += vh->freedom; } } s->next = supercount+1; s->prev = supercount-1; supercount++; s++; } } /* end edge innards */ /* do facet innards */ if ( (web.lagrange_order >= 4) && (web.representation == SOAPFILM) ) { facet_id f_id; vertex_id *v; int ctrl = (web.lagrange_order+1)*(web.lagrange_order+2)/2; FOR_ALL_FACETS(f_id) { int df; /* total degrees of freedom on facet interior */ v = get_facet_vertices(f_id); for ( j = 1, df = 0 ; j < ctrl ; j++ ) { if ( !(get_vattr(v[j]) & Q_MIDFACET) ) continue; vh = get_vertex_vhead(v[j]); df += vh->freedom; vh->flags |= SUPERNODE_DONE; } if ( df == 0 ) continue; s->verlist = md_alloc(S,df*sizeof(int)); for ( j = 1 ; j < ctrl ; j++ ) { if ( !(get_vattr(v[j]) & Q_MIDFACET) ) continue; vh = get_vertex_vhead(v[j]); for ( i = 0 ; i < vh->freedom ; i++ ) { INT(s->verlist)[s->vercount+i] = vh->rownum + i; vlist[INT(s->verlist)[s->vercount+i]].supernode = supercount; } s->vercount += vh->freedom; } s->next = supercount+1; s->prev = supercount-1; supercount++; s++; } } /* end facet innards */ /* now all single vertices and miscellaneous */ for ( n = 0 ; n < vhead_hi ; n++ ) if ( vhead[n].freedom > 0 && !(vhead[n].flags & SUPERNODE_DONE) ) { s->vercount = vhead[n].freedom; s->verlist = md_alloc(S,s->vercount*sizeof(int)); for ( i = 0 ; i < vhead[n].freedom ; i++ ) { INT(s->verlist)[i] = vhead[n].rownum + i; vlist[INT(s->verlist)[i]].supernode = supercount; } /* s->height = get_coord(vhead[n].v_id)[0];*/ /* not so good */ s->height = (float)n; /* pretty good */ if ( (s->vercount == 1) && (S->A[S->IA[s->verlist]-A_OFF] == 0.0) ) s->flag |= MD_AUG_CON; /* so don't eliminate constraints first */ s->next = supercount+1; s->prev = supercount-1; supercount++; s++; } slist[0].prev = -1; slist[supercount-1].next = -1; superstart = 0; } /**************************************************************************** * * function: clean_supernodes() * * purpose: eliminate merged regions from supernode region lists */ void clean_supernodes(S) struct linsys *S; { struct supernode *s; int i,j,k,m; int n; for ( n = superstart ; n >= 0 ; n = s->next ) { s = slist + n; for ( i=0,j=0 ; i < s->rcount ; i++ ) { int rnum = INT(s->rlist)[i]; while ( REG(rnum)->merged != rnum ) rnum = REG(rnum)->merged; if ( REG(rnum)->timestamp > old_rtimestamp ) s->rtimestamp = REG(rnum)->timestamp; /* mark as affected */ for ( k = 0 ; k < j ; k++ ) { if ( INT(s->rlist)[k] == rnum ) break; /* duplicate */ if ( INT(s->rlist)[k] > rnum ) /* insert */ { for ( m = j ; m > k ; m-- ) INT(s->rlist)[m] = INT(s->rlist)[m-1]; INT(s->rlist)[k] = rnum; j++; break; } } if ( k == j ) { INT(s->rlist)[j++] = rnum; } } s->rcount = j; } } /**************************************************************************** * * function: supernode_compare() * * purpose: lexical comparison of supernode region lists * return -1 for a < b, 0 for a == b, 1 for a > b */ struct linsys *SSS; /* for communication with compare routine */ int supernode_compare(aa,bb) struct supernode **aa,**bb; { int k; struct linsys *S; struct supernode *a = *aa, *b = *bb; if ( a->rcount < b->rcount ) return -1; if ( a->rcount > b->rcount ) return 1; S = SSS; for ( k = 0 ; k < a->rcount ; k++ ) { if ( INT(a->rlist)[k] < INT(b->rlist)[k] ) return -1; if ( INT(a->rlist)[k] > INT(b->rlist)[k] ) return 1; } return 0; } /**************************************************************************** * * function: merge_supernodes() * * purpose: merge supernodes with same regions */ void merge_supernodes(S) struct linsys *S; { struct supernode **sslist,*s,*ss; int n,k,j,i; int count; int newverlist; if ( superstart < 0 ) { supercount = 0; return; } /* all done */ /* make list to sort */ sslist = (struct supernode **)temp_calloc(supercount,sizeof(struct supernode *)); /* fill list */ for ( n = superstart, count = 0 ; n >= 0 ; n = s->next ) { s = slist + n; /* only do touched supernodes */ if ( s->rtimestamp > old_rtimestamp ) sslist[count++] = s; } /* sort */ SSS = S; /* for communication with compare routine */ qsort((char*)sslist,count,sizeof(struct supernode *),FCAST supernode_compare); /* merge */ for ( n = 1, k = 0 ; n < count ; n++ ) { s = sslist[k]; if ( supernode_compare(sslist+n ,&s) == 0 ) { /* merge */ ss = sslist[n]; if ( mindeg_debug_level > 7 ) printf("Merging supernode %d into %d.\n",ss-slist,s-slist); newverlist = md_alloc(S,(s->vercount+ss->vercount)*sizeof(int)); memcpy(CHAR(newverlist),CHAR(s->verlist),s->vercount*sizeof(int)); memcpy(CHAR(newverlist+s->vercount),CHAR(ss->verlist), ss->vercount*sizeof(int)); s->verlist = newverlist; s->vercount += ss->vercount; s->flag = 0; if ( s->height < ss->height ) s->height = ss->height; ss->verlist = -1; ss->vercount = 0; ss->flag = 0; if ( ss->next >= 0 ) slist[ss->next].prev = ss->prev; if ( ss->prev >= 0 ) slist[ss->prev].next = ss->next; else superstart = ss->next; supercount--; /* remove from region supernode list */ for ( j = 0 ; j < ss->rcount ; j++ ) { struct region *r = REG(INT(ss->rlist)[j]); for ( i = 0 ; i < r->supercount ; i++ ) if ( INT(r->superlist)[i] == (sslist[n]-slist) ) break; r->supercount--; for ( ; i < r->supercount ; i++ ) INT(r->superlist)[i] = INT(r->superlist)[i+1]; } } else /* keep supernode */ { k = n; } } temp_free((char*)sslist); } /**************************************************************************** * * function do_region_absorb(keeper,goner) * * purpose: absorb one region into another * */ void do_region_absorb(S,keeper,goner) struct linsys *S; int keeper,goner; /* the regions */ { int n; struct region *rk = REG(keeper),*rg = REG(goner); /* find spots in keeper matrix */ for ( n = 0 ; n < rk->vercount ; n++ ) vlist[INT(rk->verlist)[n]].newspot = n; rg->merged = keeper; if ( rg->next >= 0 ) REG(rg->next)->prev = rg->prev; else last_region = rg->prev; if ( rg->prev >= 0 ) REG(rg->prev)->next = rg->next; else regionstart = rg->next; rg->BROTHER = rk->son; rk->son = goner; /* rg->size = rg->vercount; */ } /**************************************************************************** * * function region_absorb() * * purpose: see if any regions entirely contained in others * */ void region_absorb(S) struct linsys *S; { int i,j,k; struct region *r; struct supernode *s; int absorbcount = 0; int maxab = 0; for ( i = regionstart ; i >= 0 ; i = r->next ) { r = REG(i); if ( r->supercount > minabsorb ) continue; /* timestamp supernodes of r */ stimestamp++; for ( j = 0 ; j < r->supercount ; j++ ) slist[INT(r->superlist)[j]].stimestamp = stimestamp; /* pick a supernode */ s = slist + INT(r->superlist)[0]; /* test regions of s */ for ( k = 0 ; k < s->rcount ; k++ ) { int rnum = INT(s->rlist)[k]; struct region *rr; int count; while ( REG(rnum)->merged != rnum ) rnum = REG(rnum)->merged; rr = REG(rnum); if ( rr->supercount <= r->supercount ) continue; for ( j = 0, count = 0 ; j < rr->supercount ; j++ ) count += (slist[INT(rr->superlist)[j]].stimestamp == stimestamp); if ( count == r->supercount ) /* have it */ { if ( r->supercount > maxab ) maxab = r->supercount; do_region_absorb(S,rnum,i); if ( mindeg_debug_level >= 5 ) printf("Absorbing region %d with %d.\n",i,rnum); absorbcount++; break; } } } if ( mindeg_debug_level > 1 ) printf("Absorbed %d; max size %d\n",absorbcount,maxab); } /**************************************************************************** * * function: degree_compare() * * purpose: compare supernode degrees, breaking ties by verlist spot. */ int degree_compare(s1,s2) struct supernode *s1,*s2; { int diff = s1->degree - s2->degree; if ( s1->flag & MD_AUG_CON ) { if ( s2->flag & MD_AUG_CON ) return (s1->height < s2->height) ? -1 : 1; else return 1; } if ( s2->flag & MD_AUG_CON ) return -1; if ( diff ) return diff; return (s1->height < s2->height) ? -1 : 1; /* high order, since high first */ } /************************************************************************** * * function: exact_degree() * * purpose: calculate exact external degree of supernode as union of * component regions. Union is calculated by timestamping * vertices. * return: number of vertices in union. */ int exact_degree(S,s) struct linsys *S; struct supernode *s; { int i,j,degree; struct region *r; struct supernode *ss; stimestamp += 2; /* for current supernode */ SSS = S; for ( i = 0, degree = 0 ; i < s->rcount ; i++ ) { r = REG(INT(s->rlist)[i]); for ( j = 0 ; j < r->supercount ; j++ ) { ss = slist + INT(r->superlist)[j]; if ( ss->stimestamp < stimestamp ) { degree += ss->vercount ; ss->stimestamp = stimestamp; } } } return degree - s->vercount; } /************************************************************************** * * function: exact_sdegree() * * purpose: calculate exact supernode degree of supernode as union of * component regions. Union is calculated by timestamping * supernode. * return: number of supernodes in union, including self. */ int exact_sdegree(S,s) struct linsys *S; struct supernode *s; { int i,j,degree; struct region *r; struct supernode *ss; stimestamp++; /* for current supernode */ for ( i = 0, degree = 0 ; i < s->rcount ; i++ ) { r = REG(INT(s->rlist)[i]); for ( j = 0 ; j < r->supercount ; j++ ) { ss = slist + INT(r->superlist)[j]; if ( ss->stimestamp < stimestamp ) { degree++ ; ss->stimestamp = stimestamp; } } } return degree; } /**************************************************************************** * * function: degree_sort() * * purpose: order supernodes by degree. Degree is external degree, the number * of nodes bordering the region left after removing the supernode. */ void degree_sort(S) struct linsys *S; { int n; struct supernode *s; int spot; heapcount = 0; for ( n = superstart ; n >= 0 ; n = s->next ) { s = slist + n; if ( s->rcount == 0 ) continue; /* empty */ /* calculate degree */ if ( s->rtimestamp > old_rtimestamp ) s->degree = exact_degree(S,s); /* insert in heap */ spot = ++heapcount; /* using heap[1] as root for convenience */ while ( spot>1 ) /* filter down */ { if ( degree_compare(s,superheap[spot>>1]) < 0 ) superheap[spot] = superheap[spot>>1]; else break; spot >>= 1; } superheap[spot] = s; } if ( heapcount != supercount ) kb_error(2467,"Internal error: degree_sort() has heapcount != supercount.\n", RECOVERABLE); } /****************************************************************************** * * function: mass_eliminate() * * purpose: eliminate one supernode, if independent */ void mass_eliminate(S,s) struct linsys *S; struct supernode *s; { int i,k; struct region *r,*rk; struct md_vertex *v; int newvlist; int newslist; int vercount; int size,ssize; int scount; int rkeep; int sontotal = 0; /* of elim nodes of sons */ /* check independence and gather son elim totals */ for ( i = 0 ; i < s->rcount ; i++ ) { int rnum = INT(s->rlist)[i]; r = REG(rnum); if ( r->timestamp == rtimestamp ) return; /* not independent */ if ( r->son < 0 ) sontotal += r->size - r->vercount; } if ( mindeg_debug_level >= 2 ) printf("Eliminating supernode %d, size %d.\n",s-slist,s->vercount); /* mark timestamps */ for ( i = 0 ; i < s->rcount ; i++ ) { r = REG(INT(s->rlist)[i]); r->timestamp = rtimestamp; } /* create merged supernode and vertex lists */ /* with eliminated supernode vertices at end */ if ( sontotal > mindeg_min_region_size ) sontotal = 0; /* don't merge with sons */ size = s->degree + s->vercount + sontotal; newvlist = md_alloc(S,size*sizeof(int)); ssize = exact_sdegree(S,s); newslist = md_alloc(S,ssize*sizeof(int)); /* first, elim supernode at end */ vtimestamp++; /* new round */ stimestamp++; /* new round */ INT(newslist)[ssize-1] = s-slist; s->stimestamp = stimestamp; vercount = s->degree; for ( k = 0 ; k < s->vercount ; k++ ) { INT(newvlist)[vercount++] = INT(s->verlist)[k]; vlist[INT(s->verlist)[k]].timestamp = vtimestamp; S->P[elim_count+k] = INT(s->verlist)[k]; S->IP[INT(s->verlist)[k]] = elim_count+k; } /* and sons, if merging */ if ( sontotal > 0 ) for ( i = 0 ; i < s->rcount ; i++ ) { int j; r = REG(INT(s->rlist)[i]); if ( r->son >= 0 ) continue; for ( j = r->vercount ; j < r->size ; j++ ) INT(newvlist)[vercount++] = INT(r->verlist)[j]; } vercount = 0; scount = 0; for ( i = 0 ; i < s->rcount ; i++ ) { struct supernode *ss; r = REG(INT(s->rlist)[i]); for ( k = 0 ; k < r->supercount ; k++ ) { ss = slist + INT(r->superlist)[k]; if ( ss->stimestamp == stimestamp ) continue; INT(newslist)[scount++] = INT(r->superlist)[k]; ss->stimestamp = stimestamp; ss->rtimestamp = rtimestamp; } for ( k = 0 ; k < r->vercount ; k++ ) { v = vlist + INT(r->verlist)[k]; if ( v->timestamp == vtimestamp ) continue; INT(newvlist)[vercount++] = INT(r->verlist)[k]; v->timestamp = vtimestamp; } } /* set up new region */ rkeep = region_alloc(S); rk = REG(rkeep); rk->verlist = newvlist; rk->vercount = vercount; rk->superlist = newslist; rk->supercount = scount; rk->son = -1; if ( vercount != s->degree ) printf("vercount %d != degree %d, supernode %d\n",vercount,s->degree,s-slist); /* mark all old regions as merged and remove from active list */ for ( i = 0 ; i < s->rcount ; i++ ) { int rnum = INT(s->rlist)[i]; r = REG(rnum); r->merged = rkeep; if ( (r->size > r->vercount) || ( r->son >= 0 ) ) { /* keep nonleaf regions */ r->BROTHER = rk->son; rk->son = rnum; } else if ( mindeg_debug_level >= 5 ) { if (r->size == r->vercount) printf("Dropping nodeless region %d\n",rnum); else printf("Incorporating small region %d into region %d\n",rnum,rkeep); } if ( r->next >= 0 ) REG(r->next)->prev = r->prev; else last_region = r->prev; if ( r->prev >= 0 ) REG(r->prev)->next = r->next; else regionstart = r->next; if ( mindeg_debug_level >= 5 ) printf("Merging region %d with %d.\n",INT(s->rlist)[i],rkeep); } /* eliminate nodes */ rk = REG(rkeep); rk->size = size; if ( mindeg_debug_level >= 5 ) printf("New region %d size %d vercount %d son %d.\n", rkeep,rk->size,rk->vercount,rk->son); if ( mindeg_debug_level >= 7 ) { printf("Border: "); for ( i = 0 ; i < rk->vercount ; i++ ) printf("%d ",INT(rk->verlist)[i]); printf(" elim: "); for ( ; i < rk->size ; i++ ) printf("%d ",INT(rk->verlist)[i]); } elim_count += s->vercount; /* delete this supernode */ if ( s->next >= 0 ) slist[s->next].prev = s->prev; if ( s->prev >= 0 ) slist[s->prev].next = s->next; else superstart = s->next; s->verlist = -1; supercount--; s->rcount = 0; /* inactivate */ } /****************************************************************************** * * function: groom_regions_recur() * * purpose: Traverse newly created region tree, gathering stats * (and maybe compacting) */ static int final_regions; /* number making it to factoring */ static int final_versizes; /* number making it to factoring */ void groom_regions_recur(S,r) struct linsys *S; struct region *r; { struct region *rr; int son,k; for ( son = r->son ; son >= 0 ; son = rr->BROTHER ) { rr = REG(son); groom_regions_recur(S,rr); } /* record fills per vertex for later factoring */ for ( k = r->vercount ; k < r->size ; k++ ) { int v = INT(r->verlist)[k]; S->LIA[v] += r->size - (k - r->vercount); } /* gather stats */ { int vn = r->vercount,vd = r->size; total_fill += (vd*(vd+1) - vn*(vn+1))/2; total_flops += ((vd+1.)*vd*(vd+2.) - (vn+1.)*vn*(vn+2.))/3; } final_regions++; final_versizes += r->size; } /****************************************************************************** * * function: compact_recur() * * purpose: Compact one region information to free up large amounts * of storage before numerical factoring. Recursive. * * return: Offset (in ints) of new location in new_ISP */ static int *new_ISP; /* compacted storage */ static int new_ISP_spot; int compact_recur(S,r) struct linsys *S; struct region *r; { struct region *rr; int son; int *v; int retval; int *rnumptr; int i; rnumptr = &(r->son); for ( son = r->son ; son >= 0 ; son = rr->BROTHER ) { rr = REG(son); *rnumptr = compact_recur(S,rr); rnumptr = &(((struct region*)(new_ISP + *rnumptr))->BROTHER); } /* now do this region */ retval = new_ISP_spot; rr = (struct region *)(new_ISP + new_ISP_spot); *rr = *r; new_ISP_spot += sizeof(struct region)/sizeof(int); v = INT(r->verlist); rr->verlist = new_ISP_spot; for ( i = 0 ; i < r->size ; i++ ) new_ISP[new_ISP_spot++] = v[i]; return retval; } /****************************************************************************** * * function: compact_regions() * * purpose: Compact region information to free up large amounts * of storage before numerical factoring. */ void compact_regions(S) struct linsys *S; { int rnum; int *new_rnum; int new_NSP; struct region *r; new_NSP = final_regions*sizeof(struct region) + final_versizes*sizeof(int); new_ISP = (int*)temp_calloc(new_NSP,1); new_ISP_spot = 0; new_rnum = ®ionstart; for ( rnum = regionstart ; rnum >= 0 ; rnum = r->next ) { r = REG(rnum); *new_rnum = compact_recur(S,r); new_rnum = &(((struct region*)(new_ISP+*new_rnum))->next); } temp_free((char*)S->ISP); S->ISP = new_ISP; S->NSP = new_NSP; } /****************************************************************************** * * function: multiple_eliminate() * * purpose: eliminate independent supernodes of low degree */ void multiple_eliminate(S) struct linsys *S; { int lowdegree; int bounddegree; /* cutoff for multiple elimination */ int spot; old_rtimestamp = rtimestamp; /* save, so know who changed */ rtimestamp++; /* new round of independence */ lowdegree = superheap[1]->degree; /* so we know where we started */ if ( mindeg_debug_level >= 1 ) printf("Low degree %d.\n",lowdegree); /* using higher bounddegree leads to about 1/3 as many passes through main loop, but not much reduction in total time */ bounddegree = lowdegree+margin; /* conservative to start with */ bounddegree = mindeg_margin < lowdegree ? lowdegree+mindeg_margin : 2*lowdegree; sentinel.degree = 2+bounddegree; /* big degree */ sentinel.verlist = 1 << (8*sizeof(int)-2); /* big for degree_compare */ while ( superheap[1]->degree <= bounddegree ) { mass_eliminate(S,superheap[1]); /* now adjust heap */ spot = 1; /* empty spot */ for ( ;; ) { if ( spot*2 > heapcount ) { superheap[spot] = &sentinel; break; } if ( spot*2 == heapcount ) { superheap[spot] = superheap[spot*2]; superheap[spot*2] = &sentinel; break; } if ( degree_compare(superheap[spot*2],superheap[spot*2+1]) < 0 ) { superheap[spot] = superheap[spot*2]; spot *= 2; } else { superheap[spot] = superheap[spot*2+1]; spot = spot*2 + 1; } } } } /************************************************************************* * * function: traverse_region_tree() * * purpose: Traverse region tree after it's constructed, doing factoring. * Uses remaining LA space as scratch space to assemble * current supernode rows, prior to compaction. * Everything stored in permuted order. */ static int treedepth; static int IJA_base; /* number of entries in LIJA */ void traverse_recur(S,r,depth,fill) struct linsys *S; struct region *r; int *depth; /* matrix depth of r and below */ int *fill; /* total fill of subtree */ { int son; struct region *rr; int fillsum = 0; int sondepth; int maxdepth = 0; int sonfill; int thisfill; treedepth++; for ( son = r->son ; son >= 0 ; son = rr->BROTHER ) { rr = REG(son); traverse_recur(S,rr,&sondepth,&sonfill); if ( sondepth > maxdepth ) maxdepth = sondepth; fillsum += sonfill; } thisfill = (r->size*(r->size+1) - r->vercount*(r->vercount+1))/2; *fill = fillsum + thisfill; *depth = maxdepth + (r->size*(r->size+1))/2; { int n; for (n=0;nsize*(r->size+1))/2); } treedepth--; } /********************************************************************************* * * function: permute_recur() * * purpose: traverse region tree to find elimnation order * */ void permute_recur(S,r) struct linsys *S; struct region *r; { int k; struct region *rr; int son; if ( mindeg_debug_level > 5 ) printf("permute_recur region %d, vercount %d rsize %d\n", (int*)r-S->ISP,r->vercount,r->size); for ( son = r->son ; son >= 0 ; son = rr->BROTHER ) { rr = REG(son); permute_recur(S,rr); } for ( k = r->vercount ; k < r->size ; k++ ) { S->P[K] = INT(r->verlist)[k]; S->IP[S->P[K]] = K; K++; } IJA_base += r->size; } /********************************************************************************* * * function: sparse_permute() * * purpose: Permute sparse matrix structure according to permutation S->IP. * Does upper triangular form, with rows in order. * Leaves permuted sparse matrix in S->pIA,pJA,pA. * Uses radix sort. */ void sparse_permute(S) struct linsys *S; { int i,j,end,total; int *cIA; int *cJA; REAL *cA; /* allocate */ S->pIA = (int*)temp_realloc((char*)S->pIA,(S->N+1)*sizeof(int)); S->pJA = (int*)temp_realloc((char*)S->pJA,(S->IA[S->N]-A_OFF)*sizeof(int)); S->pA = (REAL*)temp_realloc((char*)S->pA,(S->IA[S->N]-A_OFF)*sizeof(REAL)); memset((char*)S->pIA,0,(S->N+1)*sizeof(int)); memset((char*)S->pJA,0,(S->IA[S->N]-A_OFF)*sizeof(int)); memset((char*)S->pA,0,(S->IA[S->N]-A_OFF)*sizeof(REAL)); cIA = (int*)temp_calloc(S->N+1,sizeof(int)); cJA = (int*)temp_calloc(S->IA[S->N]-A_OFF,sizeof(int)); cA = (REAL*)temp_calloc(S->IA[S->N]-A_OFF,sizeof(REAL)); /* sort on permuted column */ for ( i = 0 ; i < S->N ; i++ ) { int ii = S->IP[i]; end = S->IA[i+1] - A_OFF; for ( j = S->IA[i] - A_OFF ; j < end ; j++ ) { int m = S->IP[S->JA[j]-A_OFF]; if ( ii < m ) cIA[m]++; else cIA[ii]++; } } for ( i = 0, total = 0 ; i < S->N ; i++ ) { int tmp = cIA[i]; cIA[i] = total; total += tmp; } cIA[S->N] = total; for ( i = 0 ; i < S->N ; i++ ) { int ii = S->IP[i]; end = S->IA[i+1] - A_OFF; for ( j = S->IA[i] - A_OFF ; j < end ; j++ ) { int m = S->IP[S->JA[j]-A_OFF]; if ( ii < m ) { cA[cIA[m]] = S->A[j]; cJA[cIA[m]++] = ii; } else { cA[cIA[ii]] = S->A[j]; cJA[cIA[ii]++] = m; } } } for ( i = S->N-1 ; i > 0 ; i-- ) cIA[i] = cIA[i-1]; cIA[0] = 0; /* now sort on permuted row */ for ( i = 0 ; i < S->N ; i++ ) { end = cIA[i+1]; for ( j = cIA[i] ; j < end ; j++ ) S->pIA[cJA[j]]++; } for ( i = 0, total = 0 ; i < S->N ; i++ ) { int tmp = S->pIA[i]; S->pIA[i] = total; total += tmp; } S->pIA[S->N] = total; for ( i = 0 ; i < S->N ; i++ ) { end = cIA[i+1]; for ( j = cIA[i] ; j < end ; j++ ) { S->pA[S->pIA[cJA[j]]] = cA[j]; S->pJA[S->pIA[cJA[j]]++] = i; } } for ( i = S->N-1 ; i > 0 ; i-- ) S->pIA[i] = S->pIA[i-1]; S->pIA[0] = 0; temp_free((char*)cIA); temp_free((char*)cJA); temp_free((char*)cA); } /******************************************************************************** * * function: factor_recur() * * purpose: Traverse region tree, factoring supernode at each. */ int vcompare ARGS((int*,int*)); int vcompare(a,b) int *a,*b; { return *a-*b; } void factor_recur(S,r) struct linsys *S; struct region *r; { int to_elim = r->size - r->vercount; REAL *base; /* scratch row start in LA */ int i,j,m,ii,ii_next; int end; REAL pivot; int *jspot; struct region *reg; int son; /* first, do sons */ for ( son = r->son ; son >= 0 ; son = reg->BROTHER ) { reg = REG(son); factor_recur(S,reg); } if ( to_elim == 0 ) return; /* get verlist in proper order with permuted numbers */ for ( j = 0 ; j < r->size ; j++ ) INT(r->verlist)[j] = S->IP[INT(r->verlist)[j]]; qsort((char*)(INT(r->verlist)),r->size,sizeof(int), FCAST vcompare); /* fill in full rows in scratch space */ for ( i = 0, base = S->LA + S->LIA[K] ; i < to_elim ; i++ ) { REAL pa,pb,p11,p12,p21,p22; int jj; /* first, fill from original matrix */ end = S->pIA[K+i+1]; for ( j = S->pIA[K+i] ; j < end ; j++ ) base[S->pJA[j]-(K+i)] = S->pA[j]; /* incorporate the shift */ if ( S->lambda != 0.0 ) { if ( hessian_linear_metric_flag ) { if ( S->lambda != 0.0 ) { end = Met.pIA[K+i+1]; for ( j = Met.pIA[K+i] ; j < end ; j++ ) base[Met.pJA[j]-(K+i)] -= S->lambda*Met.pA[j]; } } else if ( web.area_norm_flag ) base[0] -= S->lambda*Met.A[S->P[K+i]]; /* special metric matrix */ else if ( augmented_hessian_mode ) { if ( S->P[K+i] < S->A_rows ) /* do not shift constraints! */ base[0] -= S->lambda; } else base[0] -= S->lambda; } /* next, add in previous rows that have entry in col K */ ii_next = 0; for ( ii = xJL[K+i] ; ii >= 0 ; ii = ii_next ) { REAL *subbase = base - (K+i); REAL *la; int *ja; int IJdiff = S->LIA[ii] - S->LIJA[ii]; switch ( S->psize[ii] ) { case ONEBYONE: pivot = S->LA[S->LIA[ii]]*S->LA[xIL[ii]]; end = S->LIA[ii+1]; for ( j = xIL[ii],ja = S->LJA+j-IJdiff,la=S->LA+j ; j < end ; j++,la++,ja++ ) subbase[*ja] -= pivot*(*la); /* big improvement */ ii_next = xJL[ii]; if ( ++(xIL[ii]) < end ) { m = S->LJA[xIL[ii]-IJdiff]; xJL[ii] = xJL[m]; xJL[m] = ii; } break; case ZEROPIVOT: /* nothing to add */ ii_next = xJL[ii]; if ( ++(xIL[ii]) < end ) { m = S->LJA[xIL[ii]-IJdiff]; xJL[ii] = xJL[m]; xJL[m] = ii; } break; case SECONDOFPAIR: /* will handle under FIRSTOFPAIR */ ii_next = xJL[ii]; break; case FIRSTOFPAIR: p11 = S->LA[S->LIA[ii]]; p21 = p12 = S->LA[S->LIA[ii]+1]; p22 = S->LA[S->LIA[ii+1]]; pa = p11*S->LA[xIL[ii]] + p12*S->LA[xIL[ii+1]]; pb = p21*S->LA[xIL[ii]] + p22*S->LA[xIL[ii+1]]; end = S->LIA[ii+1]; for ( j = xIL[ii], jj = xIL[ii+1] ; j < end ; j++,jj++ ) base[S->LJA[j-IJdiff]-(K+i)] -= pa*S->LA[j] + pb*S->LA[jj]; ii_next = xJL[ii]; if ( ++(xIL[ii]) < end ) { m = S->LJA[xIL[ii]-IJdiff]; xJL[ii] = xJL[m]; xJL[m] = ii; } if ( ii_next == ii+1 ) ii_next = xJL[ii+1]; if ( ++(xIL[ii+1]) < end ) { m = S->LJA[xIL[ii+1]-(S->LIA[ii+1]-S->LIJA[ii+1])]; xJL[ii+1] = xJL[m]; xJL[m] = ii+1; } break; default: kb_error(1835,"Internal error: Illegal case of psize\n", RECOVERABLE); } } /* compress to dense form */ for ( j = to_elim ; j < r->size ; j++ ) { REAL *to = base + j - i; REAL *from = base + INT(r->verlist)[j] - (K+i); if ( from != to ) { *to = *from; *from = 0.0; /* clear for next round */ } } S->LIA[K+i+1] = S->LIA[K+i] + r->size - i; base += r->size - i; } /* fill in LJA */ for ( i = 0 ; i < to_elim ; i++ ) S->LIJA[K+i] = IJA_base + i; jspot = S->LJA + S->LIJA[K]; for ( m = 0 ; m < r->size ; m++,jspot++ ) *jspot = INT(r->verlist)[m]; IJA_base += r->size; /* factor */ for ( i = 0 ; i < to_elim ; i++ ) /* pivot row */ { REAL *pivrow = S->LA + S->LIA[K+i]; int p; REAL big,sigma,critsize; int br; /* first, decide on 1x1 or 2x2 pivot */ p = ONEBYONE; /* default pivot size 1 */ /* find max element in pivot row (same as pivot col) */ /* but only considering current supernode */ for ( j=i+1,big=-1.0,br = 0 ; j < to_elim ; j++ ) if ( fabs(pivrow[j-i]) > big ) { big = fabs(pivrow[j-i]); br = j; } critsize = hessian_epsilon * S->rowmag[S->P[K+i]]; if ( ((big <= critsize)||(!BK_flag)) && (fabs(pivrow[0]) <= critsize) ) p = ZEROPIVOT; else if ( (big*BKalpha > fabs(pivrow[0])) && BK_flag ) { /* find max in row/col r */ REAL *rr; for ( j = i, sigma = -1.0; j
LA + S->LIA[K+j] + br - j; if ( fabs(*rr) > sigma ) sigma = fabs(*rr); } for ( rr = S->LA + S->LIA[K+br], j = br ; j < to_elim; rr++, j++ ) if ( fabs(*rr) > sigma ) sigma = fabs(*rr); if ( BKalpha*big*big > sigma*fabs(pivrow[0]) ) { p = FIRSTOFPAIR; } } if ( p == ZEROPIVOT ) { S->psize[K+i] = ZEROPIVOT; S->zero++; if ( S->P[K+i] >= S->A_rows ) S->degencon++; memset((char*)pivrow,0,(r->size-i)*sizeof(REAL)); xIL[K+i] = -1; } else if ( p == ONEBYONE ) { S->psize[K+i] = ONEBYONE; pivot = 1/pivrow[0]; if ( pivot > 0.0 ) S->pos++; else S->neg++; for ( j = i+1; j < to_elim ; j++ ) /* row down */ { REAL *spot = S->LA + S->LIA[K+j]; REAL pp = pivrow[j-i]*pivot; REAL *pr = pivrow + j - i; /* this is the time-consuming inner loop */ for ( m = j ; m < r->size ; m++ , spot++, pr++) /* semiheavy */ /* *spot -= pivrow[m-i]*pp; */ *spot -= (*pr)*pp; /* no improvement */ } for ( m = i+1, pivrow++ ; m < r->size ; m++,pivrow++ ) *pivrow *= pivot; /* pivot row */ xIL[K+i] = S->LIA[K+i]+(to_elim-i); if ( xIL[K+i] < S->LIA[K+i+1] ) { m = S->LJA[xIL[K+i]-(S->LIA[K+i]-S->LIJA[K+i])]; xJL[K+i] = xJL[m]; xJL[m] = K+i; } S->psize[K+i] = ONEBYONE; } else /* 2x2 pivot */ { REAL *pivrow1 = pivrow; REAL *pivrow2 = S->LA + S->LIA[K+i+1]; REAL p11,p12,p22,detinv,*yy1,*yy2,pa1,pa2; int k; REAL *x; S->psize[K+i] = FIRSTOFPAIR; S->psize[K+i+1] = SECONDOFPAIR; if ( br != i+1 ) /* swap rows to get adjacent */ { REAL dtmp,*rii,*rr; int c; /* swap in matrix, both A and JA */ #define DSWAP(a,b) {dtmp=(a);(a)=(b);(b)=dtmp;} for ( j = 0 ; j <= i ; j++ ) /* swap columns */ { rr = S->LA + S->LIA[K+j] - j; DSWAP(rr[i+1],rr[br]); } for ( j = i+2 ; j < br ; j++ ) /* across to down */ { DSWAP(pivrow2[j-(i+1)],S->LA[S->LIA[K+j]+br-j]); } for ( j = br+1, rr=pivrow2+(br-i), rii=S->LA+S->LIA[K+br]+1 ; j < r->size ; j++, rr++, rii++ ) { DSWAP(*rr,*rii); } DSWAP(pivrow2[0],S->LA[S->LIA[K+br]]); /* diag elements */ /* fix up LJA in swapped columns */ c = S->LJA[S->LIJA[K+i+1]]; S->LJA[S->LIJA[K+i+1]] = S->LJA[S->LIJA[K+br]]; S->LJA[S->LIJA[K+br]] = c; } /* now, actual 2x2 pivot */ p11 = pivrow1[0]; p12 = pivrow1[1]; p22 = pivrow2[0]; detinv = 1/(p11*p22 - p12*p12); if ( detinv > 0.0 ) { if ( p11+p22 > 0.0 ) S->pos += 2; else S->neg += 2; } else { S->pos++; S->neg++; } /* sweep through matrix */ for ( k = i+2, x = S->LA+S->LIA[K+i+2] ; k < to_elim ; k++ ) /* row */ { pa1 = (pivrow1[k-i]*p22 - pivrow2[k-i-1]*p12)*detinv; pa2 = (pivrow2[k-i-1]*p11 - pivrow1[k-i]*p12)*detinv; for ( j=k,yy1=pivrow1+k-i,yy2=pivrow2+k-i-1 ; jsize; j++ ,x++,yy1++,yy2++) /*col*/ { *x -= pa1*(*yy1) + pa2*(*yy2); } pivrow1[k-i] = pa1; pivrow2[k-i-1] = pa2; } for ( k = to_elim ; k < r->size ; k++ ) /* finish pivot rows */ { pa1 = (pivrow1[k-i]*p22 - pivrow2[k-i-1]*p12)*detinv; pa2 = (pivrow2[k-i-1]*p11 - pivrow1[k-i]*p12)*detinv; pivrow1[k-i] = pa1; pivrow2[k-i-1] = pa2; } xIL[K+i] = S->LIA[K+i]+(to_elim-i); if ( xIL[K+i] < S->LIA[K+i+1] ) { m = S->LJA[xIL[K+i]-(S->LIA[K+i]-S->LIJA[K+i])]; xJL[K+i] = xJL[m]; xJL[m] = K+i; } i++; /* since just did 2 rows */ xIL[K+i] = S->LIA[K+i]+(to_elim-i); if ( xIL[K+i] < S->LIA[K+i+1] ) { m = S->LJA[xIL[K+i]-(S->LIA[K+i]-S->LIJA[K+i])]; xJL[K+i] = xJL[m]; xJL[m] = K+i; } } } K += to_elim; } /************************************************************************** * * function: traverse_region_tree() * * purpose: do numerical factoring. * Permutes nodes to factoring order according to region tree. * Calls factor_recur(). */ void traverse_region_tree(S) struct linsys *S; { struct region *r; int rnum,n; int fill; /* allocate space */ for ( fill = 0, n = 0 ; n < S->N ; n++ ) { int tmp = S->LIA[n]; S->LIA[n] = fill; fill += tmp; } S->LIA[S->N] = fill; if ( fill != total_fill ) { sprintf(errmsg,"Internal error: fill %d != total_fill %d\n",fill,total_fill); kb_error(1836,errmsg,RECOVERABLE); } xIL = (int*)temp_calloc(S->N,sizeof(int)); xJL = (int*)temp_calloc(S->N,sizeof(int)); for ( n = 0 ; n < S->N ; n++ ) xJL[n] = -1; /* list terminators */ /* get tree traverse permutation order */ K = 0; IJA_base = 0; /* for total LIJA entries */ for ( rnum = regionstart ; rnum >= 0 ; rnum = r->next ) { r = REG(rnum); permute_recur(S,r); } sparse_permute(S); if ( hessian_linear_metric_flag && (S->lambda != 0.0) ) { memcpy((char*)Met.P,(char*)S->P,S->N*sizeof(int)); memcpy((char*)Met.IP,(char*)S->IP,S->N*sizeof(int)); sparse_permute(&Met); } /* allocate L space */ S->Lsize = total_fill; S->LJA = (int*)temp_realloc((char*)S->LJA,IJA_base*sizeof(int)); S->LIJA = (int*)temp_realloc((char*)S->LIJA,(S->N+1)*sizeof(int)); S->LA = (REAL*)temp_realloc((char*)S->LA,S->Lsize*sizeof(REAL)); memset(S->LJA,0,IJA_base*sizeof(int)); memset(S->LIJA,0,(S->N+1)*sizeof(int)); memset(S->LA,0,S->Lsize*sizeof(REAL)); K = 0; /* current row */ IJA_base = 0 ; for ( rnum = regionstart ; rnum >= 0 ; rnum = r->next ) { r = REG(rnum); factor_recur(S,r); } temp_free((char*)xIL); xIL = NULL; temp_free((char*)xJL); xJL = NULL; /* test_print(S,0); */ /*debug */ /* dsolve(S); */ } /************************************************************************* * * function: xmd_solve() * * purpose: solve factored system for given right hand side * Factor stored as U, permuted order * LJA indices in LIJA, LA indices in LIA * */ void xmd_solve(S,B,x) struct linsys *S; /* factored system */ REAL *B; /* incoming right hand side */ REAL *x; /* solution, may be rhs */ { int n; /* row index */ int i; int *jp; /* pointer into LIJA */ REAL *BB,*Y,*e; if ( S->psize == NULL ) kb_error(1837,"Internal error: Must call xmd_factor before xmd_solve.\n", RECOVERABLE); /* Use existing working storage if possible */ if ( S->NSP*sizeof(int) < 2*S->N*sizeof(REAL)) { S->ISP = (int*)temp_realloc((char*)S->ISP,2*S->N*sizeof(REAL)); S->NSP = (2*S->N*sizeof(REAL))/sizeof(int); } BB = (REAL*)S->ISP; /* intermediate solutions */ Y = ((REAL*)S->ISP) + S->N; /* intermediate solutions */ /* solve U^T Y = B */ for ( n = 0 ; n < S->N ; n++ ) BB[n] = B[S->P[n]]; /* permute */ for ( n = 0 ; n < S->N ; n++ ) { int start,end; Y[n] = BB[S->LJA[S->LIJA[n]]]; /* for BK inner permutation */ if ( Y[n] == 0.0 ) continue; if ( S->psize[n] == FIRSTOFPAIR ) start = 2; else start = 1; end = S->LIA[n+1]; for ( i=S->LIA[n]+start, e=S->LA+i , jp=S->LJA+S->LIJA[n]+start ; i < end ; i++,e++,jp++ ) BB[*jp] -= (*e)*Y[n]; } /* solve D V = Y (will use Y to store V) */ for ( n = 0 ; n < S->N ; n++ ) { if ( S->psize[n] == ONEBYONE ) Y[n] /= S->LA[S->LIA[n]]; else if ( S->psize[n] == ZEROPIVOT ) Y[n] = 0.0; /* generalized inverse */ else { REAL piv[2][2]; REAL pinv[2][2]; REAL det,yy; piv[0][0] = S->LA[S->LIA[n]]; piv[0][1] = piv[1][0] = S->LA[S->LIA[n]+1]; piv[1][1] = S->LA[S->LIA[n+1]]; det = piv[0][0]*piv[1][1] - piv[0][1]*piv[1][0]; pinv[0][0] = piv[1][1]/det; pinv[1][0] = pinv[0][1] = -piv[0][1]/det; pinv[1][1] = piv[0][0]/det; yy = Y[n]*pinv[0][0] + Y[n+1]*pinv[1][0]; Y[n+1] = Y[n]*pinv[0][1] + Y[n+1]*pinv[1][1]; Y[n] = yy; n++; } } /* solve U X = V */ for ( n = S->N-1 ; n >= 0 ; n-- ) { int start,end; if ( S->psize[n] == FIRSTOFPAIR ) start = 2; else start = 1; end = S->LIA[n+1]; for ( i=S->LIA[n]+start, e=S->LA+i, jp=S->LJA+S->LIJA[n]+start ; i < end ; i++,e++,jp++ ) Y[n] -= (*e)*BB[*jp]; BB[S->LJA[S->LIJA[n]]] = Y[n]; } /* unpermute */ for ( n = 0 ; n < S->N ; n++ ) x[S->P[n]] = BB[n]; } /************************************************************************* * * function: xmd_solve_multi() * * purpose: solve factored system for multiple right hand sides * Factor stored as U, permuted order * LJA indices in LIJA, LA indices in LIA * */ void xmd_solve_multi(S,B,x,rk) struct linsys *S; /* factored system */ REAL **B; /* incoming right hand side */ REAL **x; /* solution, may be rhs */ int rk; /* number of right sides */ { int k; /* rhs column index */ for ( k = 0 ; k < rk ; k++ ) xmd_solve(S,B[k],x[k]); return; #ifdef OLDXMDMULTI /* something wrong with this */ { int n; /* row index */ int i; int *jp; /* pointer into LIJA */ REAL **BB,**Y,*e; if ( S->psize == NULL ) kb_error(2531,"Internal error: Must call BK_factor before BK_solve.\n",RECOVERABLE); BB = dmatrix(0,S->N-1,0,rk-1); /* intermediate solutions */ Y = dmatrix(0,S->N-1,0,rk-1); /* intermediate solutions */ /* solve U^T Y = B */ for ( n = 0 ; n < S->N ; n++ ) for ( k = 0 ; k < rk ; k++ ) BB[n][k] = B[k][S->P[n]]; /* permute */ for ( n = 0 ; n < S->N ; n++ ) { int start,end; REAL *yy,*bb,ee; for ( yy = Y[n], k = 0 , bb = BB[S->LJA[S->LIJA[n]]]; k < rk ; k++ ) *(yy++) = *(bb++); if ( S->psize[n] == FIRSTOFPAIR ) start = 2; else start = 1; end = S->LIA[n+1]; for ( i=S->LIA[n]+start, e=S->LA+i , jp=S->LJA+S->LIJA[n]+start ; i < end ; i++,e++,jp++ ) { ee = *e; for ( bb = BB[*jp], yy = Y[n], k = 0 ; k < rk ; k++ ) *(bb++) -= ee*(*(yy++)); } } /* solve D V = Y (will use Y to store V) */ for ( n = 0 ; n < S->N ; n++ ) { if ( S->psize[n] == ONEBYONE ) { REAL *y,pinv; pinv = 1.0/S->LA[S->LIA[n]]; for ( y = Y[n], k = 0 ; k < rk ; k++ ) *(y++) *= pinv; } else if ( S->psize[n] == ZEROPIVOT ) for ( k = 0 ; k < rk ; k++ ) Y[n][k] = 0.0; /* generalized inverse */ else { REAL piv[2][2]; REAL pinv[2][2]; REAL det,yy; piv[0][0] = S->LA[S->LIA[n]]; piv[0][1] = piv[1][0] = S->LA[S->LIA[n]+1]; piv[1][1] = S->LA[S->LIA[n+1]]; det = piv[0][0]*piv[1][1] - piv[0][1]*piv[1][0]; pinv[0][0] = piv[1][1]/det; pinv[1][0] = pinv[0][1] = -piv[0][1]/det; pinv[1][1] = piv[0][0]/det; for ( k = 0 ; k < rk ; k++ ) { yy = Y[n][k]*pinv[0][0] + Y[n+1][k]*pinv[1][0]; Y[n+1][k] = Y[n][k]*pinv[0][1] + Y[n+1][k]*pinv[1][1]; Y[n][k] = yy; } n++; } } /* solve U X = V */ for ( n = S->N-1 ; n >= 0 ; n-- ) { int start,end; REAL *yy,*bb,ee; if ( S->psize[n] == FIRSTOFPAIR ) start = 2; else start = 1; end = S->LIA[n+1]; for ( i=S->LIA[n]+start, e=S->LA+i, jp=S->LJA+S->LIJA[n]+start ; i < end ; i++,e++,jp++ ) { ee = *e; for ( yy = Y[n], bb = BB[*jp], k = 0 ; k < rk ; k++ ) *(yy++) -= ee*(*(bb++)); } for ( yy = Y[n], bb = BB[S->LJA[S->LIJA[n]]], k = 0 ; k < rk ; k++ ) *(bb++) = *(yy++); } /* unpermute */ for ( n = 0 ; n < S->N ; n++ ) for ( k = 0 ; k < rk ; k++ ) x[k][S->P[n]] = BB[n][k]; free_matrix(Y); free_matrix(BB); } #endif } /*************************************************************************/ void dsolve(S) /* solve as dense matrix */ struct linsys *S; { REAL **a,**u; int i,j,k,m,jj; int *xIP = (int*)temp_calloc(S->N,sizeof(int)); for ( k = 0 ; k < S->N ; k++ ) xIP[S->LJA[S->LIJA[k]]] = k; a = dmatrix(0,S->N-1,0,S->N-1); for ( i = 0 ; i < S->N ; i++ ) { for ( j = S->IA[i]-A_OFF ; j < S->IA[i+1] - A_OFF ; j++ ) { k = xIP[S->IP[i]]; m = xIP[S->IP[S->JA[j]-A_OFF]]; a[k][m] = a[m][k] = S->A[j]; } } for ( i = 0 ; i < S->N ; i++ ) { if ( S->psize[i] == ONEBYONE ) { for ( j = i+1 ; j < S->N ; j++ ) for ( k = i+1 ; k < S->N ; k++ ) a[j][k] -= a[i][j]*a[i][k]/a[i][i]; for ( k = i+1 ; k < S->N ; k++ ) a[i][k] /= a[i][i]; } else if ( S->psize[i] == ZEROPIVOT ) { a[i][i] = 0.0; } else { REAL p11,p12,p22,detinv,pa1,pa2; p11 = a[i][i]; p12 = a[i][i+1]; p22 = a[i+1][i+1]; detinv = 1/(p11*p22 - p12*p12); /* sweep through matrix */ for ( k = i+2 ; k < S->N ; k++ ) /* row */ { pa1 = (a[i][k]*p22 - a[i+1][k]*p12)*detinv; pa2 = (a[i+1][k]*p11 - a[i][k]*p12)*detinv; for ( j=k ; j < S->N ; j++ ) /*col*/ { a[k][j] -= pa1*a[i][j] + pa2*a[i+1][j]; } a[i][k] = pa1; a[i+1][k] = pa2; } i++; /* since did 2 rows */ } } u = dmatrix(0,S->N-1,0,S->N-1); for ( i = 0 ; i < S->N ; i++ ) for ( j = S->LIA[i], jj = S->LIJA[i]; j < S->LIA[i+1] ; j++ ) u[i][S->LJA[jj]] = S->LA[j]; for ( i = 0 ; i < S->N ; i++ ) for ( j = i ; j < S->N ; j++ ) if ( fabs(a[i][j]-u[i][j]) > 100*machine_eps ) { printf("u[%d][%d] = %20.15f a[%d][%d] = %20.15f\n", i,j,(DOUBLE)u[i][j],i,j,(DOUBLE)a[i][j]); break; } free_matrix(a); free_matrix(u); temp_free((char*)xIP); } /*************************************************************************** * * function: xmd_factor() * * purpose: experimental minimal degree factoring of system * */ void xmd_factor(S) struct linsys *S; { int i,j; int rnum; struct region *r; if ( S->N <= 0 ) { kb_error(1839,"Internal error: Empty linear system.\n",WARNING); return; } total_flops = total_fill = 0; rtimestamp = 0; stimestamp = 0; old_rtimestamp = -1; /* less than rtimestamp first time around */ vtimestamp = 0; S->neg = S->zero = S->pos = 0; S->degencon = 0; /* working storage */ S->NSP = 4*S->IA[S->N]; S->ISP = (int*)temp_realloc((char*)S->ISP,S->NSP*sizeof(int)); memset(S->ISP,0,S->NSP*sizeof(int)); /* row magnitudes for nullity detection */ S->rowmag = (REAL *)temp_realloc((char*)S->rowmag,S->N*sizeof(REAL)); memset(S->rowmag,0,S->N*sizeof(REAL)); for ( i = 0 ; i < S->N ; i++ ) { int end = S->IA[i+1] - A_OFF; for ( j = S->IA[i]-A_OFF ; j < end ; j++ ) { S->rowmag[i] += S->A[j]*S->A[j]; S->rowmag[S->JA[j]-A_OFF] += S->A[j]*S->A[j]; } } for ( i = 0 ; i < S->N ; i++ ) S->rowmag[i] = sqrt(S->rowmag[i]); md_vertex_setup(S); md_supernode_setup(S); md_region_string(S); md_supernode_regions(S); /* space for lists */ superheap = (struct supernode**)temp_calloc(supercount+1, sizeof(struct supernode*)); /* final lower triangular matrix storage */ S->psize = (int*)temp_realloc((char*)S->psize,S->N*sizeof(int)); S->LIA = (int*)temp_realloc((char*)S->LIA,(S->N+1)*sizeof(int)); memset((char*)S->psize,0,S->N*sizeof(int)); memset((char*)S->LIA,0,(S->N+1)*sizeof(int)); L_total = 0; elim_count = 0; for(passes=0;;passes++) { if ( supercount == 0 ) break; degree_sort(S); multiple_eliminate(S); region_absorb(S); /* needed to have few passes through main loop */ clean_supernodes(S); /* supernodes stamped */ merge_supernodes(S); if ( mindeg_debug_level == -1 ) show_mindeg_state(S); } S->LIA[elim_count] = L_total; /* final sentinel */ /* free temp storage */ temp_free((char*)slist); temp_free((char*)vlist); vlist = NULL; temp_free((char*)superheap); superheap = NULL; /* gather stats */ final_regions = 0; final_versizes = 0; for ( rnum = regionstart ; rnum >= 0 ; rnum = r->next ) { r = REG(rnum); groom_regions_recur(S,r); } compact_regions(S); traverse_region_tree(S); eigen_pos = S->pos; eigen_neg = S->neg; eigen_zero = S->zero; if ( !hessian_quiet_flag ) { printf("Variables: %d Original fill: %d\n",S->N,S->IA[S->N]); printf("Workspace: %d bytes\n",S->ISP[0]*sizeof(int)); printf("Passes through main loop: %d\n",passes); printf("Total_fill: %d\n",total_fill); printf("Total_flops: %g\n",total_flops); } } evolver-2.30c.dfsg/src/boundary.c0000644000175300017530000004554211410765113017216 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************** * * File: boundary.c * * Purpose: Handle boundary computations. */ #include "include.h" /*************************************************************** * * Function: b_proj() * * Purpose: Construct projection matrices from R^n vectors * to parameter tangent space or to boundary tangent * space. * * Mathematical background: * Let T[i] be the boundary tangent along parameter i, and * let V be the vector in R^n. Let C be the linear combination * coefficients that give the projection of V: * (proj V)[j] = C[i]*T[i][j] (sum on repeated indices) * The projection is defined by having identical dot products with * the tangents as does V: * C[i]*T[i][j]*T[k][j] = V[j]*T[k][j] for all k. * Define the matrix A by A[i][k] = T[i][j]*T[k][j], and let B be * its inverse. Note A and B are symmetric. So * A[k][i]*C[i] = T[k][j]*V[j] * and * C[i] = B[i][k]*T[k][j]*V[j]. * So the coefficient projection matrix is D[i][j] = B[i][k]*T[k][j]. * (which is returned for PARAMPROJ) * And * (proj V)[m] = C[i]*T[i][m] = T[i][m]*B[i][k]*T[k][j]*V[j] * so the tangent projection matrix is * E[m][j] = T[i][m]*B[i][k]*T[k][j]. * (which is returned for TANJPROJ) */ void b_proj(bdry,param,a,type,v_id) struct boundary *bdry; /* boundary involved */ REAL *param; /* parameter values to use */ REAL **a; /* returned matrix */ int type; /* PARAMPROJ or TANGPROJ */ vertex_id v_id; /* so can use extra attributes */ { int pcount = bdry->pcount; int i,j,k,m; REAL dummy; /* for eval_all function value */ REAL temp[MAXCOORD]; MAT2D(B,3,3); /* both A and B above */ MAT2D(T,MAXCOORD,MAXCOORD); for ( j = 0 ; j < SDIM ; j++ ) { eval_all(bdry->coordf[j],param,pcount,&dummy,temp,v_id); for ( i = 0 ; i < pcount ; i++ ) T[i][j] = temp[i]; } for ( i = 0 ; i < pcount ; i++ ) for ( j = 0 ; j < pcount ; j++ ) B[i][j] = SDIM_dot(T[i],T[j]); mat_inv(B,pcount); if ( type == PARAMPROJ ) { for ( i = 0 ; i < pcount ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { a[i][j] = 0.0; for ( k = 0 ; k < pcount ; k++ ) a[i][j] += B[i][k]*T[k][j]; } } else if ( type == TANGPROJ ) { for ( m = 0 ; m < SDIM ; m++ ) for ( j = 0 ; j < SDIM ; j++ ) { a[m][j] = 0.0; for ( i = 0 ; i < pcount ; i++ ) for ( k = 0 ; k < pcount ; k++ ) a[m][j] += T[i][m]*B[i][k]*T[k][j]; } } else if ( type == GRADPROJ ) { for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < pcount ; j++ ) { a[i][j] = 0.0; for ( k = 0 ; k < pcount ; k++ ) a[i][j] += T[k][i]*B[k][j]; } } } /* end b_proj() */ /********************************************************************** * * Function: b_extrapolate() * * Purpose: Find projection of point on boundary, starting at * given boundary location and projecting several * times on tangent. */ void b_extrapolate(bdry,base_x,point_x,new_x,base_param,new_param,v_id) struct boundary *bdry; /* boundary involved (in) */ REAL *base_x; /* coordinates of base point (in) */ REAL *point_x; /* coordinates of point to project (in) */ REAL *new_x; /* projected coordinates (out) */ REAL *base_param; /* base point parameters (in) */ REAL *new_param; /* projected parameters (out) */ vertex_id v_id; /* so can use vertex attributes */ { int pcount; REAL co[MAXCOORD]; int i,k; REAL x[MAXCOORD],s[MAXCOORD]; MAT2D(a,MAXPARAM,MAXCOORD); pcount = bdry->pcount; for ( k = 0 ; k < SDIM ; k++ ) { x[k] = base_x[k]; /* start at base point */ new_param[k] = base_param[k]; } #ifdef OLDWAY /* iterate projection on tangent till get close to desired point */ for ( i = 0 ; i < 5 ; i++ ) { for ( k = 0 ; k < SDIM ; k++ ) s[k] = point_x[k] - x[k]; b_proj(bdry,new_param,a,PARAMPROJ,v_id); matvec_mul(a,s,co,pcount,SDIM); for ( k = 0 ; k < pcount ; k++ ) new_param[k] += co[k]; for ( k = 0 ; k < SDIM ; k++ ) x[k] = eval(bdry->coordf[k],new_param,NULLID,NULL); } #else /* from Laszlo Csirmaz, being much more careful */ for ( i=0; i<5; i++) { REAL lambda, oldsq,newsq,newx; REAL pp[MAXPARAM]; int iter; lambda=1.0; oldsq=0.0; for(k=0;k 1e-10 ) { b_proj(bdry,new_param,a,PARAMPROJ,v_id); matvec_mul(a,s,co,pcount,SDIM); /* the correcting vector is in co */ for(iter=0;iter<10;iter++) { // the goal is x + lambda*(point_x - x) // the distance is lambda*sqrt(oldsq); for( k=0;kcoordf[k],pp,NULLID,NULL); tmp = x[k]-s[k]; newx += tmp*tmp; tmp += lambda*(point_x[k]-x[k]); newsq+= tmp*tmp; } // is it close enough? if( newsq <= 0.5*oldsq || (newsq <= oldsq+1e-10 && (newx <= 0.5*oldsq || newsq <= 1e-10 || newx <= 1e-10 ))) break; lambda *= 0.5; oldsq *= 0.25; } if(iter== 10 ) { kb_error(9000,"Extrapolate does not converge in 10 steps: boundary is not smooth enough.\n",RECOVERABLE); } for(k=0;kattr & B_CONVEX) ) return; tail = get_edge_tailv(e_id); if ( bdry != get_boundary(tail) ) { invert(e_id); tail = get_edge_tailv(e_id); if ( bdry != get_boundary(tail) ) return; } head = get_edge_headv(e_id); /* now the calculation */ get_edge_side(e_id,s); ss = SDIM_dot(s,s); if ( bdry->pcount == 1 ) { REAL t1[MAXCOORD],t2[MAXCOORD], ac,t1t1,t2t2,st1,st2,t1u1,t2u2,ar1,ar2,ep; REAL *accel = &ac,*params,t1t2,val,su1,su2; /* tail vertex */ params = get_param(tail); su1 = t1t1 = st1 = t1u1 = 0.0; for ( i = 0 ; i < SDIM ; i++ ) { eval_second(bdry->coordf[i],params,bdry->pcount,&val,t1+i,&accel,tail); t1t1 += t1[i]*t1[i]; st1 += s[i]*t1[i]; t1u1 += t1[i]*ac; su1 += s[i]*ac; } ar1 = sqrt(ss*t1t1/st1/st1 - 1.0); /* head vertex */ params = get_param(head); su2 = t2t2 = st2 = t2u2 = t1t2 = 0.0; for ( i = 0 ; i < SDIM ; i++ ) { eval_second(bdry->coordf[i],params,bdry->pcount,&val,t2+i,&accel,tail); t2t2 += t2[i]*t2[i]; t1t2 += t1[i]*t2[i]; st2 += s[i]*t2[i]; t2u2 += t2[i]*ac; su2 += s[i]*ac; } ar2 = sqrt(ss*t2t2/st2/st2 - 1.0); ep = -st1/6*(ar1+ar2); ep += ss/12/ar1*((-st1*t1t1+ss*t1u1) - ss*t1t1*(-t1t1+su1)/st1)/st1/st1; ep += ss/12/ar2*(-st1*t2t2 + ss*t2t2*t1t2/st2)/st2/st2; f = get_force(tail); for ( i = 0 ; i < SDIM ; i++ ) f[i] -= ep*t1[i]/t1t1; ep = st2/6*(ar1+ar2); ep += ss/12/ar2*((st2*t2t2+ss*t2u2) - ss*t2t2*(t2t2+su2)/st2)/st2/st2; ep += ss/12/ar1*(st2*t1t1 - ss*t1t1*t1t2/st1)/st1/st1; f = get_force(head); for ( i = 0 ; i < SDIM ; i++ ) f[i] -= ep*t2[i]/t2t2; } else /* 2 or more parameters */ { b_proj(bdry,get_param(tail),a,TANGPROJ,tail); matvec_mul(a,s,q,SDIM,SDIM); qq = SDIM_dot(q,q); norm = web.spring_constant*(1 + (ss-qq)/3/qq)*sqrt(fabs(ss-qq)/qq)/2; f = get_force(tail); for ( i = 0 ; i < SDIM ; i++ ) f[i] += norm*q[i]; b_proj(bdry,get_param(head),a,TANGPROJ,head); matvec_mul(a,s,q,SDIM,SDIM); qq = SDIM_dot(q,q); norm = web.spring_constant*(1 + (ss-qq)/3/qq)*sqrt(fabs(ss-qq)/qq)/2; f = get_force(head); for ( i = 0 ; i < SDIM ; i++ ) f[i] -= norm*q[i]; } } /* end bdry_force */ /***************************************************************** * * Function: bdry_spring_energy() * * Purpose: Calculate energy of kludge boundary force. */ void bdry_spring_energy(e_id) edge_id e_id; { REAL s[MAXCOORD],q[MAXCOORD]; struct boundary *bdry; vertex_id tail,head; REAL ss,qq,sprenergy; MAT2D(a,3,3); if ( get_eattr(e_id) & FIXED ) return; if ( !(get_eattr(e_id) & BOUNDARY) ) return; /* see if edge and tail are on same boundary */ bdry = get_edge_boundary(e_id); if ( !bdry ) return; if ( !(bdry->attr & B_CONVEX) ) return; tail = get_edge_tailv(e_id); head = get_edge_headv(e_id); if ( bdry != get_boundary(tail) ) { invert(e_id); tail = get_edge_tailv(e_id); head = get_edge_headv(e_id); if ( bdry != get_boundary(tail) ) return; } /* now the calculation */ get_edge_side(e_id,s); ss = SDIM_dot(s,s); b_proj(bdry,get_param(tail),a,TANGPROJ,tail); matvec_mul(a,s,q,SDIM,SDIM); qq = SDIM_dot(q,q); sprenergy = web.spring_constant*ss*sqrt((ss - qq)/qq)/12; b_proj(bdry,get_param(head),a,TANGPROJ,head); matvec_mul(a,s,q,SDIM,SDIM); qq = SDIM_dot(q,q); sprenergy += web.spring_constant*ss*sqrt((ss - qq)/qq)/12; binary_tree_add(web.total_energy_addends,sprenergy); web.spring_energy += sprenergy; } /* end bdry_spring_energy */ /***************************************************************** * * Function: bdry_basis() * * Purpose: calculate basis of boundary tangent plane. */ int bdry_basis(v_id,basis) vertex_id v_id; REAL **basis; /* for return */ { struct boundary *b = get_boundary(v_id); int j,i; REAL dummy; /* for eval_all function value */ REAL temp[MAXCOORD]; if ( !b ) return 0; for ( j = 0 ; j < SDIM ; j++ ) { eval_all(b->coordf[j],get_param(v_id),b->pcount,&dummy,temp,v_id); for ( i = 0 ; i < b->pcount ; i++ ) basis[i][j] = temp[i]; } return b->pcount; } /* end bdry_basis() */ /***************************************************************************** * * VERTEX-VERTEX HITTING ON ONE-PARAMETER BOUNDARY * ******************************************************************************/ #define BDRYHIT_PARTNER_NAME "bdryhit_partner" /****************************************************************************** * * function: detect_bdry_hits() * * purpose: detect when two vertices along a BOUNDARY_HITTING boundary * collide and setting positions to average thereof. */ void detect_bdry_hits() { vertex_id v_id,vv_id; struct boundary *bdry; int i,exnum,partner; REAL p1,pp1; int eltype; exnum = find_extra(BDRYHIT_PARTNER_NAME,&eltype); if ( exnum < 0 ) return; FOR_ALL_VERTICES(v_id) { if ( !(get_vattr(v_id) & BOUNDARY) ) continue; bdry = get_boundary(v_id); if ( !(bdry->attr & PARTNER_HITTING) ) continue; partner = *VINT(v_id,exnum); if ( partner == 0 ) continue; vv_id = get_ordinal_id(VERTEX,abs(partner)-1); if ( !valid_id(vv_id) ) continue; if ( abs(*VINT(vv_id,exnum)) != loc_ordinal(v_id)+1 ) { sprintf(errmsg,"Partner of %s is %d, but partner of %d is %d.\n", ELNAME(v_id),partner,partner,abs(*VINT(vv_id,exnum))); kb_error(3378,errmsg,RECOVERABLE); } p1 = get_param(v_id)[0]; pp1 = get_param(vv_id)[0]; if ( ((partner > 0) && (pp1 < p1 )) || ((partner < 0) && (pp1 > p1)) ) { REAL *param = get_param(v_id); REAL *x = get_coord(v_id); param[0] = (p1+pp1)/2; for ( i = 0 ; i < SDIM ; i++ ) x[i] = eval(bdry->coordf[i],param,v_id,NULL); param = get_param(vv_id); x = get_coord(vv_id); param[0] = (p1+pp1)/2; for ( i = 0 ; i < SDIM ; i++ ) x[i] = eval(bdry->coordf[i],param,vv_id,NULL); set_attr(v_id,HIT_PARTNER); set_attr(vv_id,HIT_PARTNER); } } } /****************************************************************************** * * function: partner_hit_velocity_fix() * * purpose: For vertices hitting on a PARTNER_HITTING boundary * set force on each to average. */ void partner_hit_velocity_fix() { vertex_id v_id,vv_id; struct boundary *bdry; int i,exnum,partner,eltype; REAL *v,*vv; exnum = find_extra(BDRYHIT_PARTNER_NAME,&eltype); if ( exnum < 0 ) return; FOR_ALL_VERTICES(v_id) { if ( !(get_vattr(v_id) & HIT_PARTNER) ) continue; bdry = get_boundary(v_id); if ( !(bdry->attr & PARTNER_HITTING) ) continue; partner = *VINT(v_id,exnum); if ( partner == 0 ) continue; vv_id = get_ordinal_id(VERTEX,abs(partner)-1); v = get_velocity(v_id); vv = get_velocity(vv_id); for ( i = 0 ; i < SDIM ; i++ ) { REAL avev = (v[i]+vv[i])/2; v[i] = vv[i] = avev; } } } /****************************************************************************** * * function: partner_hit_volgrad_fix() * * purpose: For vertices hitting on a PARTNER_HITTING boundary * set volgrad velocity on each to average. */ void partner_hit_volgrad_fix() { vertex_id v_id,vv_id; struct boundary *bdry; int i,exnum,partner,eltype; exnum = find_extra(BDRYHIT_PARTNER_NAME,&eltype); if ( exnum < 0 ) return; FOR_ALL_VERTICES(v_id) { struct volgrad *vgptr,*vvgptr; if ( !(get_vattr(v_id) & HIT_PARTNER) ) continue; bdry = get_boundary(v_id); if ( !(bdry->attr & PARTNER_HITTING) ) continue; partner = *VINT(v_id,exnum); if ( partner == 0 ) continue; vv_id = get_ordinal_id(VERTEX,abs(partner)-1); for ( vgptr = get_vertex_vgrad(v_id) ; vgptr ; vgptr = vgptr->chain ) for ( vvgptr = get_vertex_vgrad(vv_id) ; vvgptr ; vvgptr = vvgptr->chain ) if ( vgptr->fixnum == vvgptr->fixnum ) { for ( i = 0 ; i < SDIM ; i++ ) { REAL avev = (vgptr->velocity[i]+vvgptr->velocity[i])/2; vgptr->velocity[i] = vvgptr->velocity[i] = avev; } } } } /*************************************************************************** * * function: partner_shift_grads() * * purpose: For vertices with partners, shift all grads to vertex with * lower id. */ void partner_shift_grads(mode) int mode; /* bits for CALC_FORCE and CALC_VOLGRADS */ { vertex_id v_id,vv_id; struct boundary *bdry; int i,exnum,partner,eltype; exnum = find_extra(BDRYHIT_PARTNER_NAME,&eltype); if ( exnum < 0 ) return; FOR_ALL_VERTICES(v_id) { struct volgrad *vgptr,*vvgptr,*prev,temp; REAL *x,*xx; if ( !(get_vattr(v_id) & HIT_PARTNER) ) continue; bdry = get_boundary(v_id); if ( !(bdry->attr & PARTNER_HITTING) ) continue; partner = *VINT(v_id,exnum); if ( partner == 0 ) continue; if ( abs(partner) >= ordinal(v_id)+1 ) continue; vv_id = get_ordinal_id(VERTEX,abs(partner)-1); /* destination */ if ( abs(*VINT(vv_id,exnum)) != ordinal(v_id)+1 ) { sprintf(errmsg,"Partner of %d is %d, but partner of %d is %d.\n", ordinal(v_id)+1,partner,partner,abs(*VINT(vv_id,exnum))); kb_error(3383,errmsg,RECOVERABLE); } if ( mode & CALC_FORCE ) { x = get_force(v_id); xx = get_force(vv_id); for ( i = 0 ; i < SDIM ; i++ ) { xx[i] += x[i]; x[i] = 0.0; } } if ( (mode & CALC_VOLGRADS) && vgradbase ) { prev = NULL; for ( vgptr = get_vertex_vgrad(v_id) ; vgptr ; prev = vgptr, vgptr = vgptr->chain ) { int found = 0; for ( vvgptr = get_vertex_vgrad(vv_id) ; vvgptr ; vvgptr = vvgptr->chain ) if ( vgptr->fixnum == vvgptr->fixnum ) { for ( i = 0 ; i < SDIM ; i++ ) { vvgptr->grad[i] += vgptr->grad[i]; vgptr->grad[i] = 0.0; } found = 1; break; } if ( !found ) /* move whole structure over to vv_id */ { vvgptr = get_vertex_vgrad(vv_id); temp.chain = vgptr->chain; set_vertex_vgrad(vv_id,vgptr); if ( prev ) prev->chain = vgptr->chain; else set_vertex_vgrad(v_id,vgptr->chain); vgptr->chain = vvgptr; vgptr = &temp; } } } } } /*************************************************************************** * * function: partner_move() * * purpose: For vertices with partners, shift position to vertex with * lower id. */ void partner_move() { vertex_id v_id,vv_id; struct boundary *bdry; int i,exnum,partner,eltype; exnum = find_extra(BDRYHIT_PARTNER_NAME,&eltype); if ( exnum < 0 ) return; FOR_ALL_VERTICES(v_id) { REAL *x,*xx,*p,*pp; if ( !(get_vattr(v_id) & HIT_PARTNER) ) continue; bdry = get_boundary(v_id); if ( !(bdry->attr & PARTNER_HITTING) ) continue; partner = *VINT(v_id,exnum); if ( partner == 0 ) continue; if ( abs(partner) >= ordinal(v_id)+1 ) continue; vv_id = get_ordinal_id(VERTEX,abs(partner)-1); /* destination */ if ( !valid_id(vv_id) ) continue; if ( abs(*VINT(vv_id,exnum)) != ordinal(v_id)+1 ) { sprintf(errmsg,"Partner of %d is %d, but partner of %d is %d.\n", ordinal(v_id)+1,partner,partner,abs(*VINT(vv_id,exnum))); kb_error(3006,errmsg,RECOVERABLE); } p = get_param(v_id); pp = get_param(vv_id); x = get_coord(v_id); xx = get_coord(vv_id); for ( i = 0 ; i < SDIM ; i++ ) x[i] = xx[i]; for ( i = 0 ; i < bdry->pcount ; i++ ) p[i] = pp[i]; } } evolver-2.30c.dfsg/src/gauss.c0000644000175300017530000012020311410765113016501 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************** * * File: gauss.c * * Purpose: Does calculations needed for including gauss curvature * integral named method. Linear model only. * Implemented as total angle deficit of boundary vertices. * Uses general quantity interface. */ #include "include.h" void sqgauss_method_cleanup ARGS((void)); REAL levine_energy_all ARGS(( struct qinfo *, int )); /********************************************************************* * * function: gauss_integral_init() * * purpose: Check illegalities, and sees if attributes defined for * designating boundaries. * */ #define GAUSS_BDRY_V_NAME "gauss_bdry_v" #define GAUSS_BDRY_E_NAME "gauss_bdry_e" int gauss_bdry_v_attr,gauss_bdry_e_attr; void gauss_integral_init(mode,mi) int mode; struct method_instance *mi; { if ( web.dimension != 2 ) kb_error(1590,"gauss_integral method only for 2D facets.\n",RECOVERABLE); if ( SDIM != 3 ) kb_error(1591,"gauss_integral method only for 3D space.\n",RECOVERABLE); if ( web.modeltype != LINEAR ) kb_error(1592,"gauss_integral method only for LINEAR model.\n",RECOVERABLE); gauss_bdry_v_attr = find_attribute(VERTEX,GAUSS_BDRY_V_NAME); gauss_bdry_e_attr = find_attribute(EDGE,GAUSS_BDRY_E_NAME); } /******************************************************************** * * function: gauss_int_energy() * * purpose: single facet contribution to total angle of boundary * vertices. * */ REAL gauss_int_energy(f_info) struct qinfo *f_info; { REAL s1[MAXCOORD],s2[MAXCOORD]; facetedge_id fe; vertex_id v_id; int k; REAL energy = 0.0; REAL a; fe = get_facet_fe(f_info->id); for ( k = 0 ; k < FACET_VERTS ; k++, fe = get_next_edge(fe) ) { edge_id e_id = get_fe_edge(fe); if ( gauss_bdry_e_attr >= 0 ) { if ( *(int*)get_extra(e_id,gauss_bdry_e_attr) ) energy -= M_PI; /* normalization */ } else if ( get_eattr(e_id) & (BOUNDARY|FIXED) ) energy -= M_PI; /* normalization */ v_id = get_fe_tailv(fe); if ( gauss_bdry_v_attr >= 0 ) { if ( *(int*)get_extra(v_id,gauss_bdry_v_attr) == 0 ) continue; } else if ( !(get_vattr(v_id) & (BOUNDARY|FIXED) ) ) continue; get_fe_side(fe,s1); get_fe_side(get_prev_edge(fe),s2); a = -SDIM_dot(s1,s2)/sqrt(SDIM_dot(s1,s1)*SDIM_dot(s2,s2)); energy += acos(a); } return energy; } /******************************************************************** * * function: gauss_int_gradient() * * purpose: single facet contribution to gradient of total angle of * boundary vertices. * */ REAL gauss_int_gradient(f_info) struct qinfo *f_info; { REAL s1[MAXCOORD],s2[MAXCOORD]; facetedge_id fe; vertex_id v_id; int j,k; REAL energy = 0.0; REAL ss1,ss2,ss12; REAL a,b; fe = get_facet_fe(f_info->id); for ( k = 0 ; k < FACET_VERTS ; k++ ) memset((char*)(f_info->grad[k]),0,SDIM*sizeof(REAL)); for ( k = 0 ; k < FACET_VERTS ; k++, fe = get_next_edge(fe) ) { edge_id e_id = get_fe_edge(fe); if ( gauss_bdry_e_attr >= 0 ) { if ( *(int*)get_extra(e_id,gauss_bdry_e_attr) ) energy -= M_PI; /* normalization */ } else if ( get_eattr(e_id) & (BOUNDARY|FIXED) ) energy -= M_PI; /* normalization */ v_id = get_fe_tailv(fe); if ( gauss_bdry_v_attr >= 0 ) { if ( *(int*)get_extra(v_id,gauss_bdry_v_attr) == 0 ) continue; } else if ( !(get_vattr(v_id) & (BOUNDARY|FIXED) ) ) continue; get_fe_side(fe,s1); get_fe_side(get_prev_edge(fe),s2); ss1 = SDIM_dot(s1,s1); ss2 = SDIM_dot(s2,s2); ss12 = SDIM_dot(s1,s2); a = -ss12/sqrt(ss1*ss2); energy += acos(a); b = sqrt(1-a*a)*ss1*sqrt(ss1*ss2); for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[(k+1)%3][j] += (ss1*s2[j] - ss12*s1[j])/b; b = sqrt(1-a*a)*ss2*sqrt(ss1*ss2); for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[(k+2)%3][j] += -(ss2*s1[j] - ss12*s2[j])/b; } return energy; } /******************************************************************** * * Function: sqgauss_energy() * * Purpose: Does square gauss curvature energy calculation for vertices. * */ void sqgauss_energy() { REAL modulus = globals(sqgauss_param)->value.real; vertex_id v_id,v[3]; edge_id e_id; facet_id f_id; facetedge_id fe; int fixcount; int i; REAL area; REAL side[3][MAXCOORD]; REAL ss[3]; REAL st[3]; REAL angle; REAL c; REAL gc; /* gaussian curvarture */ struct gvert { REAL angle; REAL area; REAL star_area; } *gverts,*gv; gverts = (struct gvert*)temp_calloc(web.skel[VERTEX].max_ord+1, sizeof(struct gvert)); /* accumulate angles around vertices */ FOR_ALL_FACETS(f_id) { fe = get_facet_fe(f_id); fixcount = 0; for ( i = 0; i < FACET_VERTS ; i++,fe=get_next_edge(fe) ) { e_id = get_fe_edge(fe); get_edge_side(e_id,side[i]); v[i] = get_edge_tailv(e_id); if ( get_vattr(v[i]) & (FIXED|BOUNDARY) ) fixcount++; } for ( i = 0 ; i < FACET_VERTS ; i++ ) { ss[i] = SDIM_dot(side[i],side[i]); st[i] = SDIM_dot(side[i],side[(i+2)%3]); } area = 0.5*sqrt(ss[0]*ss[1]-st[1]*st[1]); for ( i = 0 ; i < FACET_VERTS ; i++ ) { c = -st[i]/sqrt(ss[i]*ss[(i+2)%3]); angle = acos(c); gv = gverts + loc_ordinal(v[i]); gv->angle += angle; gv->area += area/3; gv->star_area += area/(3-fixcount); } } /* calc square gauss curvature at each vertex */ FOR_ALL_VERTICES(v_id) { struct gvert *vg = gverts + loc_ordinal(v_id); if ( get_vattr(v_id) & (FIXED|BOUNDARY) ) continue; gc = (wedge_angle(v_id) - vg->angle)/vg->area; binary_tree_add(web.total_energy_addends,modulus*gc*gc*vg->star_area); } temp_free((char*)gverts); } /************************************************************************* * * function: wedge_angle() * * purpose: find topological angle around vertex: * 2*pi for nonconstrained vertex * pi for one constraint * calculate angle between 2 constraints */ REAL wedge_angle(v_id) vertex_id v_id; { int concount; conmap_t *conmap = get_v_constraint_map(v_id); REAL normal[2][MAXCOORD]; REAL c; int j; struct constraint *con; REAL fval; concount = conmap[0]; if ( concount == 0 ) return 2*M_PI; if ( concount == 1 ) return M_PI; /* two constraints, so find angle between */ if ( concount > 2 ) { sprintf(errmsg, "gauss_curvature_integral: More than two constraints on vertex %s.\n", ELNAME(v_id)); kb_error(1593,errmsg,RECOVERABLE); } for ( j = 1, concount = 0 ; j <= (int)conmap[0] ; j++ ) { con = get_constraint(conmap[j]); eval_all(con->formula,get_coord(v_id),SDIM,&fval,normal[concount],v_id); concount++; } c = SDIM_dot(normal[0],normal[1]) /sqrt(SDIM_dot(normal[0],normal[0])) /sqrt(SDIM_dot(normal[1],normal[1])); return M_PI - acos(c); } /******************************************************************** * * Function: sqgauss_force() * * Purpose: Does square gauss curvature force calculation for vertices. * */ void sqgauss_force() { REAL modulus = globals(sqgauss_param)->value.real; vertex_id v_id,v[3]; edge_id e_id,e[3]; facet_id f_id; facetedge_id fe; int fixcount; REAL side[3][MAXCOORD]; REAL ss[3]; REAL st[3]; int j,i; REAL area,angle,c,c1,c2; REAL *f; REAL gc; /* gaussian curvarture */ struct gvert { REAL angle; REAL angle_grad[MAXCOORD]; REAL area; REAL area_grad[MAXCOORD]; REAL star_area; REAL star_area_grad[MAXCOORD]; REAL gc; /* gaussian curvature */ } *gverts; struct gedge { REAL area_grad[2][MAXCOORD]; REAL star_area_grad[2][MAXCOORD]; REAL angle_grad[2][MAXCOORD]; /* dhead/dtail, dtail/dhead */ } *gedges; gverts = (struct gvert*)temp_calloc(web.skel[VERTEX].max_ord+1, sizeof(struct gvert)); gedges = (struct gedge*)temp_calloc(web.skel[EDGE].max_ord+1, sizeof(struct gedge)); /* accumulate angles around vertices */ FOR_ALL_FACETS(f_id) { fe = get_facet_fe(f_id); fixcount = 0; for ( i = 0; i < FACET_VERTS ; i++,fe=get_next_edge(fe) ) { e[i] = get_fe_edge(fe); get_edge_side(e[i],side[i]); v[i] = get_edge_tailv(e[i]); if ( get_vattr(v[i]) & (FIXED|BOUNDARY) ) fixcount++; } for ( i = 0 ; i < FACET_VERTS ; i++ ) { ss[i] = SDIM_dot(side[i],side[i]); st[i] = SDIM_dot(side[i],side[(i+2)%3]); } area = 0.5*sqrt(ss[0]*ss[1]-st[1]*st[1]); for ( i = 0 ; i < FACET_VERTS ; i++ ) { int i1 = (i+1)%3; int i2 = (i+2)%3; struct gedge *ge1,*ge2; struct gvert *gv; int jj1,jj2; gv = gverts + loc_ordinal(v[i]); ge1 = gedges + loc_ordinal(e[i]); ge2 = gedges + loc_ordinal(e[i2]); jj1 = inverted(e[i]) ? 1 : 0; jj2 = inverted(e[i2]) ? 0 : 1; c = -st[i]/sqrt(ss[i]*ss[i2]); angle = acos(c); gv->angle += angle; c1 = (1+st[i]/ss[i])/area/2; c2 = (1+st[i]/ss[i2])/area/2; for ( j = 0 ; j < SDIM ; j++ ) gv->angle_grad[j] += c1*side[i][j] - c2*side[i2][j]; c1 = st[i]/ss[i]/area/2; c2 = 1/area/2; for ( j = 0 ; j < SDIM ; j++ ) ge1->angle_grad[jj1][j] -= c1*side[i][j] - c2*side[i2][j]; c1 = 1/area/2; c2 = st[i]/ss[i2]/area/2; for ( j = 0 ; j < SDIM ; j++ ) ge2->angle_grad[jj2][j] -= c1*side[i][j] - c2*side[i2][j]; gv->area += area/3; gv->star_area += area/(3-fixcount); c1 = ss[i1]/area/4; c2 = st[i1]/area/4; for ( j = 0 ; j < SDIM ; j++ ) { c = c2*side[i1][j] - c1*side[i][j]; gv->area_grad[j] += c/3; gv->star_area_grad[j] += c/(3-fixcount); ge1->area_grad[jj1][j] += c/3; ge2->area_grad[jj2][j] += c/3; ge1->star_area_grad[jj1][j] += c/(3-fixcount); ge2->star_area_grad[jj2][j] += c/(3-fixcount); } } } /* calc square gauss curvature at each vertex */ FOR_ALL_VERTICES(v_id) { struct gvert *gv = gverts + loc_ordinal(v_id); if ( get_vattr(v_id) & (FIXED|BOUNDARY) ) continue; gc = (wedge_angle(v_id) - gv->angle)/gv->area; binary_tree_add(web.total_energy_addends,modulus*gc*gc*gv->star_area); gv->gc = gc; /* now self terms in derivative */ f = get_force(v_id); for ( j = 0 ; j < SDIM ; j++ ) f[j] -= (2*gc*gv->star_area*(-gv->angle_grad[j]/gv->area - gc*gv->area_grad[j]/gv->area) + gc*gc*gv->star_area_grad[j])*modulus; } /* now cross terms from edges */ FOR_ALL_EDGES(e_id) { struct gvert *gv1 = gverts + loc_ordinal(get_edge_tailv(e_id)); struct gvert *gv2 = gverts + loc_ordinal(get_edge_headv(e_id)); struct gedge *ge = gedges + loc_ordinal(e_id); /* head energy as function of tail */ if ( !(get_vattr(get_edge_headv(e_id)) & (FIXED|BOUNDARY) ) ) { f = get_force(get_edge_tailv(e_id)); for ( j = 0 ; j < SDIM ; j++ ) f[j] -= (2*gv2->gc*gv2->star_area*(-ge->angle_grad[0][j]/gv2->area - gv2->gc*ge->area_grad[0][j]/gv2->area) + gv2->gc*gv2->gc*ge->star_area_grad[0][j])*modulus; } /* tail energy as function of head */ if ( !(get_vattr(get_edge_tailv(e_id)) & (FIXED|BOUNDARY) ) ) { f = get_force(get_edge_headv(e_id)); for ( j = 0 ; j < SDIM ; j++ ) f[j] -= (2*gv1->gc*gv1->star_area*(-ge->angle_grad[1][j]/gv1->area - gv1->gc*ge->area_grad[1][j]/gv1->area) + gv1->gc*gv1->gc*ge->star_area_grad[1][j])*modulus; } } temp_free((char*)gverts); temp_free((char*)gedges); } /******************************************************************** sqgauss integral as method *********************************************************************/ static struct gvert { REAL angle; REAL angle_grad[MAXCOORD]; REAL area; REAL area_grad[MAXCOORD]; REAL star_area; REAL star_area_grad[MAXCOORD]; REAL gc; /* gaussian curvature */ } *gverts,*gv; static struct gedge { REAL area_grad[2][MAXCOORD]; REAL star_area_grad[2][MAXCOORD]; REAL angle_grad[2][MAXCOORD]; /* dhead/dtail, dtail/dhead */ } *gedges; /************************************************************************* * * function: sqgauss_method_init() * * purpose: gather data for sqgauss method. * Should really be replaced by local gathering. */ void sqgauss_method_init(mode,mi) int mode; struct method_instance *mi; { vertex_id v[3]; edge_id e_id,e[3]; facet_id f_id; facetedge_id fe; int fixcount; REAL side[3][MAXCOORD]; REAL ss[3]; REAL st[3]; int j,i; REAL area,angle,c,c1,c2; if ( web.modeltype != LINEAR ) kb_error(2866,"Method sq_gauss_curvature only for LINEAR model.\n", RECOVERABLE); gverts = (struct gvert*)temp_calloc(web.skel[VERTEX].max_ord+1, sizeof(struct gvert)); /* accumulate angles around vertices */ if ( mode == METHOD_VALUE ) FOR_ALL_FACETS(f_id) { fe = get_facet_fe(f_id); fixcount = 0; for ( i = 0; i < FACET_VERTS ; i++,fe=get_next_edge(fe) ) { e_id = get_fe_edge(fe); get_edge_side(e_id,side[i]); v[i] = get_edge_tailv(e_id); if ( get_vattr(v[i]) & (FIXED|BOUNDARY) ) fixcount++; } for ( i = 0 ; i < FACET_VERTS ; i++ ) { ss[i] = SDIM_dot(side[i],side[i]); st[i] = SDIM_dot(side[i],side[(i+2)%3]); } area = 0.5*sqrt(ss[0]*ss[1]-st[1]*st[1]); for ( i = 0 ; i < FACET_VERTS ; i++ ) { c = -st[i]/sqrt(ss[i]*ss[(i+2)%3]); angle = acos(c); gv = gverts + loc_ordinal(v[i]); gv->angle += angle; gv->area += area/3; gv->star_area += area/(3-fixcount); } } else if ( mode == METHOD_GRADIENT ) { gedges = (struct gedge*)temp_calloc(web.skel[EDGE].max_ord+1, sizeof(struct gedge)); /* accumulate angles around vertices */ FOR_ALL_FACETS(f_id) { fe = get_facet_fe(f_id); fixcount = 0; for ( i = 0; i < FACET_VERTS ; i++,fe=get_next_edge(fe) ) { e[i] = get_fe_edge(fe); get_edge_side(e[i],side[i]); v[i] = get_edge_tailv(e[i]); if ( get_vattr(v[i]) & (FIXED|BOUNDARY) ) fixcount++; } for ( i = 0 ; i < FACET_VERTS ; i++ ) { ss[i] = SDIM_dot(side[i],side[i]); st[i] = SDIM_dot(side[i],side[(i+2)%3]); } area = 0.5*sqrt(ss[0]*ss[1]-st[1]*st[1]); for ( i = 0 ; i < FACET_VERTS ; i++ ) { int i1 = (i+1)%3; int i2 = (i+2)%3; struct gedge *ge1,*ge2; int jj1,j2; gv = gverts + loc_ordinal(v[i]); ge1 = gedges + loc_ordinal(e[i]); ge2 = gedges + loc_ordinal(e[i2]); jj1 = inverted(e[i]) ? 1 : 0; j2 = inverted(e[i2]) ? 0 : 1; c = -st[i]/sqrt(ss[i]*ss[i2]); angle = acos(c); gv->angle += angle; c1 = (1+st[i]/ss[i])/area/2; c2 = (1+st[i]/ss[i2])/area/2; for ( j = 0 ; j < SDIM ; j++ ) gv->angle_grad[j] += c1*side[i][j] - c2*side[i2][j]; c1 = st[i]/ss[i]/area/2; c2 = 1/area/2; for ( j = 0 ; j < SDIM ; j++ ) ge1->angle_grad[jj1][j] -= c1*side[i][j] - c2*side[i2][j]; c1 = 1/area/2; c2 = st[i]/ss[i2]/area/2; for ( j = 0 ; j < SDIM ; j++ ) ge2->angle_grad[j2][j] -= c1*side[i][j] - c2*side[i2][j]; gv->area += area/3; gv->star_area += area/(3-fixcount); c1 = ss[i1]/area/4; c2 = st[i1]/area/4; for ( j = 0 ; j < SDIM ; j++ ) { c = c2*side[i1][j] - c1*side[i][j]; gv->area_grad[j] += c/3; gv->star_area_grad[j] += c/(3-fixcount); ge1->area_grad[jj1][j] += c/3; ge2->area_grad[j2][j] += c/3; ge1->star_area_grad[jj1][j] += c/(3-fixcount); ge2->star_area_grad[j2][j] += c/(3-fixcount); } } } } } /************************************************************************ * * function: sqgauss_method_cleanup() * * purpose: free memory allocated in initialization. */ void sqgauss_method_cleanup() { temp_free((char*)gverts); temp_free((char*)gedges); } /******************************************************************** * * Function: sqgauss_method_value() * * Purpose: Does square gauss curvature energy calculation for vertices. * */ REAL sqgauss_method_value(v_info) struct qinfo *v_info; { REAL modulus = sqgauss_flag ? globals(sqgauss_param)->value.real : 0.0; REAL gc; /* gaussian curvarture */ /* calc square gauss curvature at each vertex */ struct gvert *vg = gverts + loc_ordinal(v_info->id); if ( get_vattr(v_info->id) & (FIXED|BOUNDARY) ) return 0.0; gc = (wedge_angle(v_info->id) - vg->angle)/vg->area; return modulus*gc*gc*vg->star_area; } /******************************************************************** * * Function: sqgauss_method_grad() * * Purpose: Does square gauss curvature force calculation for vertices. * */ REAL sqgauss_method_grad(v_info) struct qinfo *v_info; { REAL modulus = sqgauss_flag ? globals(sqgauss_param)->value.real : 0.0; vertex_id v_id; edge_id e_id,start_e; int j; REAL gc; /* gaussian curvarture */ REAL energy = 0.0; /* calc square gauss curvature at each vertex */ v_id = v_info->id; gv = gverts + loc_ordinal(v_id); if ( get_vattr(v_id) & (FIXED|BOUNDARY) ) return 0.0; gc = (wedge_angle(v_id) - gv->angle)/gv->area; energy += modulus*gc*gc*gv->star_area; gv->gc = gc; /* now self terms in derivative */ for ( j = 0 ; j < SDIM ; j++ ) v_info->grad[0][j] += (2*gc*gv->star_area*(-gv->angle_grad[j]/gv->area - gc*gv->area_grad[j]/gv->area) + gc*gc*gv->star_area_grad[j])*modulus; /* now cross terms from edges */ start_e = e_id = get_vertex_edge(v_id); if ( !(get_vattr(v_id) & (FIXED|BOUNDARY) ) ) do { struct gvert *gv2 = gverts + loc_ordinal(get_edge_headv(e_id)); struct gedge *ge = gedges + loc_ordinal(e_id); /* head energy as function of tail */ for ( j = 0 ; j < SDIM ; j++ ) v_info->grad[0][j] += (2*gv2->gc*gv2->star_area*(-ge->angle_grad[0][j]/gv2->area - gv2->gc*ge->area_grad[0][j]/gv2->area) + gv2->gc*gv2->gc*ge->star_area_grad[0][j])*modulus; e_id = get_next_tail_edge(e_id); } while ( !equal_element(start_e,e_id) ); return energy; } /***************************************************************************** Gaussian curvature at vertices (star model) ******************************************************************************/ void star_gauss_method_init(mode,mi) int mode; struct method_instance *mi; { if ( web.modeltype != LINEAR ) kb_error(2869,"Method gauss_curvature only for LINEAR model.\n", RECOVERABLE); } REAL star_gauss_method_all(v_info,mode) struct qinfo *v_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ { REAL deficit = 2*M_PI; int k,i,j; int fudge = (v_info->flags & INCOMPLETE_STAR) ? 1 : 0; if ( v_info->vcount <= 1 ) return 0.0; for ( k = 1 ; k < v_info->vcount-fudge ; k++ ) { REAL ss1,ss2,s1s2; int kk = (!fudge && k==(v_info->vcount-1)) ? 1 : k+1; REAL *s1 = v_info->sides[0][k-1],*s2 = v_info->sides[0][kk-1]; REAL denom; ss1 = SDIM_dot(s1,s1); ss2 = SDIM_dot(s2,s2); s1s2 = SDIM_dot(s1,s2); deficit -= acos(s1s2/sqrt(ss1*ss2)); if ( mode == METHOD_VALUE ) continue; denom = sqrt(ss1*ss2 - s1s2*s1s2); for ( i = 0 ; i < SDIM ; i++ ) { REAL ddefds1,ddefds2; ddefds1 = (s2[i] - s1s2/ss1*s1[i])/denom; ddefds2 = (s1[i] - s1s2/ss2*s2[i])/denom; v_info->grad[k][i] += ddefds1; v_info->grad[0][i] -= ddefds1; v_info->grad[kk][i] += ddefds2; v_info->grad[0][i] -= ddefds2; if ( mode == METHOD_GRADIENT ) continue; for ( j = 0 ; j < SDIM ; j++ ) { REAL dddefds1ds1,dddefds1ds2,dddefds2ds1,dddefds2ds2; dddefds1ds1 = (-s2[j]/ss1*s1[i] + s1s2/ss1/ss1*2*s1[j]*s1[i] - (i==j ? s1s2/ss1 : 0))/denom - ddefds1/denom*0.5/denom*(2*ss2*s1[j] - 2*s1s2*s2[j]); dddefds1ds2 = ((i==j ? 1.0 : 0.0) - s1[j]/ss1*s1[i])/denom - ddefds1/denom*0.5/denom*(2*ss1*s2[j] - 2*s1s2*s1[j]); dddefds2ds1 = ((i==j ? 1.0 : 0.0) - s2[j]/ss2*s2[i])/denom - ddefds2/denom*0.5/denom*(2*ss2*s1[j] - 2*s1s2*s2[j]); dddefds2ds2 = (-s1[j]/ss2*s2[i] + s1s2/ss2/ss2*2*s2[j]*s2[i] - (i==j ? s1s2/ss2 : 0))/denom - ddefds2/denom*0.5/denom*(2*ss1*s2[j] - 2*s1s2*s1[j]); v_info->hess[k][k][i][j] += dddefds1ds1; v_info->hess[0][k][i][j] -= dddefds1ds1; v_info->hess[k][0][i][j] -= dddefds1ds1; v_info->hess[0][0][i][j] += dddefds1ds1; v_info->hess[k][kk][i][j] += dddefds1ds2; v_info->hess[0][kk][i][j] -= dddefds1ds2; v_info->hess[k][0][i][j] -= dddefds1ds2; v_info->hess[0][0][i][j] += dddefds1ds2; v_info->hess[kk][k][i][j] += dddefds2ds1; v_info->hess[0][k][i][j] -= dddefds2ds1; v_info->hess[kk][0][i][j] -= dddefds2ds1; v_info->hess[0][0][i][j] += dddefds2ds1; v_info->hess[kk][kk][i][j] += dddefds2ds2; v_info->hess[0][kk][i][j] -= dddefds2ds2; v_info->hess[kk][0][i][j] -= dddefds2ds2; v_info->hess[0][0][i][j] += dddefds2ds2; } } } return deficit; } REAL star_gauss_method_value(v_info) struct qinfo *v_info; { return star_gauss_method_all(v_info,METHOD_VALUE); } REAL star_gauss_method_grad(v_info) struct qinfo *v_info; { return star_gauss_method_all(v_info,METHOD_GRADIENT); } REAL star_gauss_method_hess(v_info) struct qinfo *v_info; { return star_gauss_method_all(v_info,METHOD_HESSIAN); } /***************************************************************************** Square Gaussian curvature integral at vertices (star model) ******************************************************************************/ void star_sqgauss_method_init(mode,mi) int mode; struct method_instance *mi; { if ( web.modeltype != LINEAR ) kb_error(2867,"Method sq_gauss_curvature only for LINEAR model.\n", RECOVERABLE); } REAL star_sqgauss_method_all(v_info,mode) struct qinfo *v_info; int mode; /* METHOD_VALUE, etc. */ { REAL deficit = 2*M_PI; REAL area = 0.0; int k,kk,i,j; REAL **dadv=NULL,**ddefdv=NULL; /* gradient storage */ int pairs,final_edge; if ( v_info->vcount <= 1 ) return 0.0; pairs = (v_info->vcount - 1); if ( pairs <= 0 ) return 0.0; if ( v_info->flags & INCOMPLETE_STAR ) { pairs--; final_edge = pairs; } else final_edge = 0; if ( mode == METHOD_HESSIAN ) { dadv = temp_dmatrix(0,v_info->vcount,0,SDIM-1); ddefdv = temp_dmatrix(0,v_info->vcount,0,SDIM-1); } for ( k = 1 ; k < v_info->vcount ; k++ ) { REAL ss1,ss2,s1s2; int kk = (k==(v_info->vcount-1)) ? 0 : k; ss1 = SDIM_dot(v_info->sides[0][k-1],v_info->sides[0][k-1]); ss2 = SDIM_dot(v_info->sides[0][kk],v_info->sides[0][kk]); s1s2 = SDIM_dot(v_info->sides[0][k-1],v_info->sides[0][kk]); deficit -= acos(s1s2/sqrt(ss1*ss2)); area += sqrt(ss1*ss2 - s1s2*s1s2)/6; } if ( mode == METHOD_VALUE ) return deficit*deficit/area; /* now individual vertex gradients */ for ( k = 1 ; k < v_info->vcount ; k++ ) { REAL ss1,ss2,s1s2; int kk = (k==(v_info->vcount-1)) ? 1 : k+1; REAL *s1 = v_info->sides[0][k-1],*s2 = v_info->sides[0][kk-1]; REAL denom; ss1 = SDIM_dot(s1,s1); ss2 = SDIM_dot(s2,s2); s1s2 = SDIM_dot(s1,s2); denom = sqrt(ss1*ss2 - s1s2*s1s2); for ( i = 0 ; i < SDIM ; i++ ) { REAL ddefds1,ddefds2,dads1,dads2,dsqds1,dsqds2; int j; ddefds1 = (s2[i] - s1s2/ss1*s1[i])/denom; ddefds2 = (s1[i] - s1s2/ss2*s2[i])/denom; dads1 = (s1[i]*ss2 - s1s2*s2[i])/denom/6; dads2 = (s2[i]*ss1 - s1s2*s1[i])/denom/6; dsqds1 = 2*deficit*ddefds1/area - deficit*deficit/area/area*dads1; dsqds2 = 2*deficit*ddefds2/area - deficit*deficit/area/area*dads2; v_info->grad[k][i] += dsqds1; v_info->grad[0][i] -= dsqds1; v_info->grad[kk][i] += dsqds2; v_info->grad[0][i] -= dsqds2; if ( mode == METHOD_GRADIENT ) continue; /* record gradients for later */ if ( dadv ) { ddefdv[k][i] += ddefds1; ddefdv[kk][i] += ddefds2; dadv[k][i] += dads1; dadv[kk][i] += dads2; } /* add in pure second partial terms */ for ( j = 0 ; j < SDIM ; j++ ) { REAL dddefds1ds1,dddefds1ds2,dddefds2ds1,dddefds2ds2; REAL ddads1ds1,ddads1ds2,ddads2ds1,ddads2ds2; REAL ddenomds1j,ddenomds2j; REAL ddsqds1ds1,ddsqds1ds2,ddsqds2ds1,ddsqds2ds2; /* denom = sqrt(ss1*ss2 - s1s2*s1s2); */ ddenomds1j = 1.0/denom*(s1[j]*ss2 - s1s2*s2[j]); ddenomds2j = 1.0/denom*(s2[j]*ss1 - s1s2*s1[j]); ddads1ds1 = ((i==j ? 1:0)*ss2 - s2[j]*s2[i])/denom/6 - (s1[i]*ss2 - s1s2*s2[i])/denom/6/denom*ddenomds1j; ddads1ds2 = (s1[i]*2*s2[j] - s1[j]*s2[i] - s1s2*(i==j?1.0:0.))/denom/6 - (s1[i]*ss2 - s1s2*s2[i])/denom/6/denom*ddenomds2j; ddads2ds1 = (s2[i]*2*s1[j] - s2[j]*s1[i] - s1s2*(i==j?1:0))/denom/6 - (s2[i]*ss1 - s1s2*s1[i])/denom/6/denom*ddenomds1j; ddads2ds2 = ((i==j?1.0:0.0)*ss1 - s1[j]*s1[i])/denom/6 - (s2[i]*ss1 - s1s2*s1[i])/denom/6/denom*ddenomds2j; dddefds1ds1 = (-s2[j]/ss1*s1[i] + s1s2/ss1/ss1*2*s1[j]*s1[i] - (i==j ? s1s2/ss1 : 0))/denom - ddefds1/denom*ddenomds1j; dddefds1ds2 = ((i==j ? 1.0 : 0.0) - s1[j]/ss1*s1[i])/denom - ddefds1/denom*ddenomds2j; dddefds2ds1 = ((i==j ? 1.0 : 0.0) - s2[j]/ss2*s2[i])/denom - ddefds2/denom*ddenomds1j; dddefds2ds2 = (-s1[j]/ss2*s2[i] + s1s2/ss2/ss2*2*s2[j]*s2[i] - (i==j ? s1s2/ss2 : 0))/denom - ddefds2/denom*ddenomds2j; ddsqds1ds1 = 2*deficit*dddefds1ds1/area - deficit*deficit/area/area*ddads1ds1; ddsqds1ds2 = 2*deficit*dddefds1ds2/area - deficit*deficit/area/area*ddads1ds2; ddsqds2ds1 = 2*deficit*dddefds2ds1/area - deficit*deficit/area/area*ddads2ds1; ddsqds2ds2 = 2*deficit*dddefds2ds2/area - deficit*deficit/area/area*ddads2ds2; v_info->hess[k][k][i][j] += ddsqds1ds1; v_info->hess[0][k][i][j] -= ddsqds1ds1; v_info->hess[k][0][i][j] -= ddsqds1ds1; v_info->hess[0][0][i][j] += ddsqds1ds1; v_info->hess[k][kk][i][j] += ddsqds1ds2; v_info->hess[0][kk][i][j] -= ddsqds1ds2; v_info->hess[k][0][i][j] -= ddsqds1ds2; v_info->hess[0][0][i][j] += ddsqds1ds2; v_info->hess[kk][k][i][j] += ddsqds2ds1; v_info->hess[0][k][i][j] -= ddsqds2ds1; v_info->hess[kk][0][i][j] -= ddsqds2ds1; v_info->hess[0][0][i][j] += ddsqds2ds1; v_info->hess[kk][kk][i][j] += ddsqds2ds2; v_info->hess[0][kk][i][j] -= ddsqds2ds2; v_info->hess[kk][0][i][j] -= ddsqds2ds2; v_info->hess[0][0][i][j] += ddsqds2ds2; } } } if ( mode == METHOD_HESSIAN ) { /* add in product of gradient terms */ for ( k = 1 ; k < v_info->vcount ; k++ ) for ( kk = 1 ; kk < v_info->vcount ; kk++ ) for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { REAL term; term = 2/area*ddefdv[k][i]*ddefdv[kk][j] - 2*deficit/area/area*dadv[k][i]*ddefdv[kk][j] - 2*deficit/area/area*ddefdv[k][i]*dadv[kk][j] + 2*deficit*deficit/area/area/area*dadv[k][i]*dadv[kk][j]; v_info->hess[k][kk][i][j] += term; v_info->hess[0][kk][i][j] -= term; v_info->hess[k][0][i][j] -= term; v_info->hess[0][0][i][j] += term; } free_temp_matrix(dadv); free_temp_matrix(ddefdv); } return deficit*deficit/area; } REAL star_sqgauss_method_value(v_info) struct qinfo *v_info; { return star_sqgauss_method_all(v_info,METHOD_VALUE); } REAL star_sqgauss_method_grad(v_info) struct qinfo *v_info; { return star_sqgauss_method_all(v_info,METHOD_GRADIENT); } REAL star_sqgauss_method_hess(v_info) struct qinfo *v_info; { return star_sqgauss_method_all(v_info,METHOD_HESSIAN); } /************************************************************************** levine_energy A curvature-dependent energy requested by Dov Levine, levine@techunix.technion.ac.il Depends on two variables, levine_a and levine_t, to be set by user. E = Integral ( K1*K2*t/a + 1/4/a*(K1 - K2)*log((1-K2*t)/(1-K1*t)) dA where K1 and K2 are the principal curvatures. ****************************************************************************/ static REAL levine_a=1.0,levine_t=1.0,levine_c=1.0; void levine_energy_init(mode,meth) int mode; struct method_instance *meth; { int k; /* find the two variables */ k = lookup_global("levine_a"); if ( k >= 0 ) levine_a = globals(k)->value.real; else levine_a = 1.0; k = lookup_global("levine_t"); if ( k >= 0 ) levine_t = globals(k)->value.real; else levine_t = 1.0; k = lookup_global("levine_c"); /* coeff of log part, for test */ if ( k >= 0 ) levine_c = globals(k)->value.real; else levine_c = 1.0; } REAL levine_energy_all(v_info,mode) struct qinfo *v_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ { REAL deficit = 2*M_PI; REAL area = 0.0; REAL coeff = levine_c*levine_t/levine_a; int variety; /* PLAIN_SQ, EFF_SQ, NORMAL_SQ */ int pairs; int i,j,k; REAL energy,ff,fn,g; REAL dAdv[MAXCOORD], *a, *d, *s1s1, *s1s2, *s2s2; REAL vnorm[MAXCOORD]; REAL **dAdv1,**dAdv2,***dvnorm1,***dvnorm2,**ds2; REAL ***ddAdv1dv1,***ddAdv2dv1,***ddAdv1dv2,***ddAdv2dv2; REAL ***ddss12,***ddss21,***ddss22; REAL **dfndv1,**dfndv2,**dffdv1,**dffdv2,**dnndv1,**dnndv2; REAL ***dfdv1,***dfdv2; REAL *s1,*s2; REAL **s = v_info->sides[0]; REAL temp[MAXCOORD]; #define MAXV 10 REAL aa[MAXV*5]; MAT2D(ds1,12*MAXV,MAXCOORD); MAT3D(ddss11,16*MAXV,MAXCOORD,MAXCOORD); REAL hh0; /* curvature adjusted for h-h0 */ REAL gc,root,kappa1,kappa2; REAL dgc[MAXV][MAXCOORD]; REAL dhh0[MAXV][MAXCOORD]; REAL darea[MAXV][MAXCOORD]; REAL ddef[MAXV][MAXCOORD]; variety = NORMAL_SQ; if ( v_info->vcount <= 1 ) return 0.0; /* gaussian curvature */ for ( k = 1 ; k < v_info->vcount ; k++ ) { REAL ss1,ss2,s1s2; int kk = (k==(v_info->vcount-1)) ? 0 : k; ss1 = SDIM_dot(v_info->sides[0][k-1],v_info->sides[0][k-1]); ss2 = SDIM_dot(v_info->sides[0][kk],v_info->sides[0][kk]); s1s2 = SDIM_dot(v_info->sides[0][k-1],v_info->sides[0][kk]); deficit -= acos(s1s2/sqrt(ss1*ss2)); area += sqrt(ss1*ss2 - s1s2*s1s2)/6; } gc = deficit/area; /* mean curvature */ pairs = (v_info->vcount - 1); if ( pairs <= 0 ) return 0.0; if ( v_info->vcount > MAXV ) { a = (REAL*)mycalloc(5*pairs,sizeof(REAL)); ds1 = dmatrix(0,12*v_info->vcount,0,SDIM-1); } else { memset((char*)aa,0,sizeof(REAL)*5*pairs); a = aa; memset((char*)ds1[0],0,sizeof(REAL)*12*v_info->vcount*MAXCOORD); } d = a+pairs; s1s1 = d + pairs; s1s2 = s1s1 + pairs; s2s2 = s1s2 + pairs; ds2 = ds1 + pairs; dAdv1 = ds2 + pairs; dAdv2 = dAdv1 + pairs; dfndv1 = dAdv2 + pairs; dfndv2 = dfndv1 + pairs; dffdv1 = dfndv2 + pairs; dffdv2 = dffdv1 + pairs; dnndv1 = dffdv2 + pairs; dnndv2 = dnndv1 + pairs; /* basic dot products */ /* area = 0.0; */ for ( j = 0 ; j < SDIM ; j++ ) dAdv[j] = vnorm[j] = 0.0; for ( k = 0 ; k < pairs ; k++ ) { s1 = s[k]; s2 = s[(k+1==pairs)?0:k+1]; s1s1[k] = SDIM_dot(s1,s1); s1s2[k] = SDIM_dot(s1,s2); s2s2[k] = SDIM_dot(s2,s2); d[k] = s1s1[k]*s2s2[k] - s1s2[k]*s1s2[k]; a[k] = 0.5*sqrt(d[k]); for ( j = 0 ; j < SDIM ; j++ ) { ds1[k][j] = 2*(s2s2[k]*s1[j] - s1s2[k]*s2[j]); ds2[k][j] = 2*(s1s1[k]*s2[j] - s1s2[k]*s1[j]); dAdv1[k][j] = 0.125/a[k]*ds1[k][j]; dAdv2[k][j] = 0.125/a[k]*ds2[k][j]; dAdv[j] -= dAdv1[k][j] + dAdv2[k][j]; } if ( variety != PLAIN_SQ ) { cross_prod(s1,s2,temp); for ( j = 0 ; j < SDIM ; j++ ) vnorm[j] += 0.5*temp[j]/3; } } /* energy */ ff = SDIM_dot(dAdv,dAdv); fn = SDIM_dot(dAdv,vnorm); if ( fn == 0.0 ) { ff = 0.0; fn = 1e-6; } hh0 = ff/fn/2; /* mean curvature, so divide by 2 */ if ( hh0*hh0 < gc ) root = 0.0; else root = sqrt(hh0*hh0 - gc); kappa1 = hh0 + root; kappa2 = hh0 - root; #define APPROX_LEVINE_XX #ifdef APPROX_LEVINE /* express in terms of hh0 and gc without sqrt to keep smooth */ energy = coeff*deficit + 4*(hh0*hh0 - gc)/4/levine_a*levine_t*(1 + 2*hh0*levine_t)*area; #else energy = coeff*deficit + (2*root)/4/levine_a* log((1-kappa2*levine_t)/(1-kappa1*levine_t))*area; #endif if ( !is_finite(energy) ) kb_error(2586,"Levine energy infinite.\n",WARNING); if ( mode == METHOD_VALUE ) return energy; /* now individual vertex gradients */ for ( k = 0 ; k < v_info->vcount ; k++ ) for ( i = 0 ; i < SDIM ; i++ ) { dgc[k][i] = dhh0[k][i] = darea[k][i] = ddef[k][i] = 0.0; } /* gaussian part */ for ( k = 1 ; k < v_info->vcount ; k++ ) { REAL ss1,ss2,s1s2; int kk = (k==(v_info->vcount-1)) ? 0 : k; REAL *s1 = v_info->sides[0][k-1],*s2 = v_info->sides[0][kk]; REAL denom; ss1 = SDIM_dot(s1,s1); ss2 = SDIM_dot(s2,s2); s1s2 = SDIM_dot(s1,s2); denom = sqrt(ss1*ss2 - s1s2*s1s2); for ( i = 0 ; i < SDIM ; i++ ) { REAL ddefds1,ddefds2,dads1,dads2; ddefds1 = (s2[i] - s1s2/ss1*s1[i])/denom; ddefds2 = (s1[i] - s1s2/ss2*s2[i])/denom; dads1 = (s1[i]*ss2 - s1s2*s2[i])/denom/6; dads2 = (s2[i]*ss1 - s1s2*s1[i])/denom/6; dgc[k][i] += ddefds1/area - deficit/area/area*dads1; dgc[0][i] -= ddefds1/area - deficit/area/area*dads1; dgc[kk?k+1:1][i] += ddefds2/area - deficit/area/area*dads2; dgc[0][i] -= ddefds2/area - deficit/area/area*dads2; ddef[k][i] += ddefds1; ddef[0][i] -= ddefds1; ddef[kk?k+1:1][i] += ddefds2; ddef[0][i] -= ddefds2; darea[k][i] += dads1; darea[0][i] -= dads1; darea[kk?k+1:1][i] += dads2; darea[0][i] -= dads2; } } /* other part */ if ( v_info->vcount > MAXV ) ddss11 = dmatrix3(16*pairs,SDIM,SDIM); else memset((char*)ddss11[0][0],0,sizeof(REAL)*16*pairs*MAXCOORD*MAXCOORD); ddss12 = ddss11 + pairs; ddss21 = ddss12 + pairs; ddss22 = ddss21 + pairs; ddAdv1dv1 = ddss22 + pairs; ddAdv1dv2 = ddAdv1dv1 + pairs; ddAdv2dv1 = ddAdv1dv2 + pairs; ddAdv2dv2 = ddAdv2dv1 + pairs; dvnorm1 = ddAdv2dv2 + pairs; dvnorm2 = dvnorm1 + pairs; dfdv1 = dvnorm2 + pairs; dfdv2 = dfdv1 + pairs; /* first, some more common terms */ for ( k = 0 ; k < pairs ; k++ ) { s1 = s[k]; s2 = s[(k+1==pairs)?0:k+1]; for ( i = 0 ; i < SDIM ; i++ ) { ddss11[k][i][i] = 2*s2s2[k]; ddss12[k][i][i] = -2*s1s2[k]; ddss21[k][i][i] = -2*s1s2[k]; ddss22[k][i][i] = 2*s1s1[k]; for ( j = 0 ; j < SDIM ; j++ ) { ddss11[k][i][j] -= 2*s2[i]*s2[j]; ddss12[k][i][j] += 4*s1[i]*s2[j] - 2*s2[i]*s1[j]; ddss21[k][i][j] += 4*s2[i]*s1[j] - 2*s1[i]*s2[j]; ddss22[k][i][j] -= 2*s1[i]*s1[j]; ddAdv1dv1[k][i][j] = -0.125/a[k]/a[k]*dAdv1[k][i]*ds1[k][j] + 0.125/a[k]*ddss11[k][i][j]; ddAdv1dv2[k][i][j] = -0.125/a[k]/a[k]*dAdv1[k][i]*ds2[k][j] + 0.125/a[k]*ddss12[k][i][j]; dfdv1[k][i][j] = -ddAdv1dv1[k][i][j] - ddAdv1dv2[k][i][j]; ddAdv2dv1[k][i][j] = -0.125/a[k]/a[k]*dAdv2[k][i]*ds1[k][j] + 0.125/a[k]*ddss21[k][i][j]; ddAdv2dv2[k][i][j] = -0.125/a[k]/a[k]*dAdv2[k][i]*ds2[k][j] + 0.125/a[k]*ddss22[k][i][j]; dfdv2[k][i][j] = -ddAdv2dv1[k][i][j] - ddAdv2dv2[k][i][j]; } dffdv1[k][i] = 2*SDIM_dot(dfdv1[k][i],dAdv); dffdv2[k][i] = 2*SDIM_dot(dfdv2[k][i],dAdv); } if ( variety == PLAIN_SQ ) continue; dvnorm1[k][0][1] = -0.5*s2[2]/3; dvnorm1[k][0][2] = 0.5*s2[1]/3; dvnorm1[k][1][0] = 0.5*s2[2]/3; dvnorm1[k][1][2] = -0.5*s2[0]/3; dvnorm1[k][2][0] = -0.5*s2[1]/3; dvnorm1[k][2][1] = 0.5*s2[0]/3; dvnorm2[k][0][1] = 0.5*s1[2]/3; dvnorm2[k][0][2] = -0.5*s1[1]/3; dvnorm2[k][1][0] = -0.5*s1[2]/3; dvnorm2[k][1][2] = 0.5*s1[0]/3; dvnorm2[k][2][0] = 0.5*s1[1]/3; dvnorm2[k][2][1] = -0.5*s1[0]/3; for ( i = 0 ; i < SDIM ; i++ ) { dfndv1[k][i] = SDIM_dot(dfdv1[k][i],vnorm); dfndv1[k][i] += SDIM_dot(dAdv,dvnorm1[k][i]); dfndv2[k][i] = SDIM_dot(dfdv2[k][i],vnorm); dfndv2[k][i] += SDIM_dot(dAdv,dvnorm2[k][i]); dnndv1[k][i] = 2*SDIM_dot(vnorm,dvnorm1[k][i]); dnndv2[k][i] = 2*SDIM_dot(vnorm,dvnorm2[k][i]); } } /* now, the actual gradients */ if ( fn != 0.0 ) for ( k = 0 ; k < pairs ; k++ ) { for ( i = 0 ; i < SDIM ; i++ ) { g = dffdv1[k][i]/fn - ff/fn/fn*dfndv1[k][i]; dhh0[k+1][i] += g/2; dhh0[0][i] -= g/2; g = dffdv2[k][i]/fn - ff/fn/fn*dfndv2[k][i]; dhh0[(k+1==pairs)?1:k+2][i] += g/2; dhh0[0][i] -= g/2; } } #ifdef APPROX_LEVINE for ( k = 0 ; k < v_info->vcount ; k++ ) for ( i = 0 ; i < SDIM ; i++ ) { REAL droot = root==0.0 ? 0.0 : 0.5/root*(2*hh0*dhh0[k][i] - dgc[k][i]); REAL dkappa1 = dhh0[k][i] + droot; REAL dkappa2 = dhh0[k][i] - droot; v_info->grad[k][i] += coeff*ddef[k][i] + (2*hh0*dhh0[k][i]-dgc[k][i])/levine_a*levine_t*(1+2*hh0*levine_t)*area + (hh0*hh0 - gc)/levine_a*levine_t*(2*dhh0[k][i]*levine_t)*area + (hh0*hh0 - gc)/levine_a*levine_t*(1 + 2*hh0*levine_t)*darea[k][i]; } #else for ( k = 0 ; k < v_info->vcount ; k++ ) for ( i = 0 ; i < SDIM ; i++ ) { REAL droot = root==0.0 ? 0.0 : 0.5/root*(2*hh0*dhh0[k][i] - dgc[k][i]); REAL dkappa1 = dhh0[k][i] + droot; REAL dkappa2 = dhh0[k][i] - droot; v_info->grad[k][i] += coeff*ddef[k][i] + ( droot/2/levine_a* (log(1-kappa2*levine_t)-log(1-kappa1*levine_t))*area + root/2/levine_a* (1/(1-kappa2*levine_t)*(-dkappa2*levine_t) -1/(1-kappa1*levine_t)*(-dkappa1*levine_t))*area + root/2/levine_a* (log(1-kappa2*levine_t)-log(1-kappa1*levine_t))*darea[k][i]); } #endif if ( mode == METHOD_GRADIENT ) return energy; /* No hessian yet */ return energy; } REAL levine_energy_value(v_info) struct qinfo *v_info; { return levine_energy_all(v_info,METHOD_VALUE); } REAL levine_energy_grad(v_info) struct qinfo *v_info; { return levine_energy_all(v_info,METHOD_GRADIENT); } evolver-2.30c.dfsg/src/nulgraph.c0000644000175300017530000000173611410765113017210 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /* Null graphics module for evolver */ #include "include.h" /* callable null functions of the right types */ void null_start ARGS((void)) {} void null_edge(struct graphdata * g,edge_id id) {} void null_facet(struct graphdata * g,facet_id id) {} void display_null(struct tsort * t) {} void graph_new_surface() {} void display() { if ( init_graphics == NULL ) kb_error(1252,"No screen display available. This Evolver compiled with nulgraph.c.\n",WARNING); ENTER_GRAPH_MUTEX init_graphics = null_function; finish_graphics = null_function; graph_edge = null_edge; graph_start = null_start; graph_facet = null_facet; graph_end = null_function; display_facet = display_null; graphgen(); LEAVE_GRAPH_MUTEX } evolver-2.30c.dfsg/src/tmain.c0000644000175300017530000013600111410765113016472 0ustar hazelscthazelsct /************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ int crit_count; /* debugging */ /********************************************************** * * File: tmain.c * * Contents: main() for Surface Evolver */ #if defined(WIN32) && !defined(WINVER) #define _WIN32_WINNT 0x0500 #define _WIN32_WINDOWS 0x0410 #define WINVER 0x0400 #endif #include "include.h" int just_lex_flag = 0; /* for -l option */ /***********************************************************************/ /* System-specific thread stuff is all in this file. */ #ifdef WINTHREADS /* MS Windows multithreading */ DWORD WINAPI winthread_worker( void * arg ); CRITICAL_SECTION thread_cs; /* main thread critical section */ CRITICAL_SECTION element_cs; /* per element critical section */ CRITICAL_SECTION web_cs; /* web structure critical section */ LPCRITICAL_SECTION element_mutex_ptr = &element_cs; /* for use in other files */ LPCRITICAL_SECTION web_mutex_ptr = &web_cs; /* for use in other files */ HANDLE workthread_wakeup; /* worker thread wake-up event */ HANDLE mainthread_wakeup; /* resume main thread event */ HANDLE barrier_event; /* for synchronizing worker threads */ volatile LONG barrier_count; HANDLE *threadlist; /* thread handles */ DWORD thread_data_key; #endif #ifdef PTHREADS /* portable Unix multithreading */ #include /*#include */ pthread_mutex_t thread_mutex; /* main thread critical section */ pthread_mutex_t element_mutex; /* element modify critical section */ pthread_mutex_t web_mutex; /* web modify critical section */ void *element_mutex_ptr = (void*)&element_mutex; /* for use in other files */ void *web_mutex_ptr = (void*)&web_mutex; /* for use in other files */ pthread_cond_t workthread_wakeup; /* worker thread wake-up event */ pthread_cond_t mainthread_wakeup; /* resume main thread event */ pthread_t *threadlist; /* thread ids */ void * pthread_worker ( void *); pthread_key_t thread_data_key; int task_serial_number; /* global */ #endif int spincount = 0; /* times to try spinlock before sleeping */ /* End of thread stuff. */ /***********************************************************************/ /* Separate thread for periodically printing state; useful when deadlocked */ int web_locks; int thread_locks; int element_locks; int web_unlocks; int thread_unlocks; int element_unlocks; void * reporter_thread ARGS((void * )); void * reporter_thread(void * arg) { puts("Reporter thread running."); for (;;) { #ifdef WIN32 Sleep(10000); #else sleep(10); #endif printf("Reporter: global_id %08LX\n",global_id); printf("web_locks %d element_locks %d thread_locks %d\n", web_locks,element_locks,thread_locks); printf("web_unlocks %d element_unlocks %d thread_unlocks %d\n", web_unlocks,element_unlocks,thread_unlocks); #ifdef XXXX if ( pthread_mutex_trylock(&thread_mutex) == EBUSY ) puts("thread_mutex locked"); else { pthread_mutex_unlock(&thread_mutex); puts("thread_mutex unlocked"); } if ( pthread_mutex_trylock(&web_mutex) == EBUSY ) puts("web_mutex locked"); else { pthread_mutex_unlock(&web_mutex); puts("web_mutex unlocked"); } if ( pthread_mutex_trylock(&element_mutex) == EBUSY ) puts("element_mutex locked"); else { pthread_mutex_unlock(&element_mutex); puts("element_mutex unlocked"); } #endif } } int tty_flag; /* to do tty interface instead of menus */ #if defined(MOTIF) || defined(MAC_APP) || defined(WIN32S) || defined(MAC_CW) #define main old_main #endif #ifdef SHARED_MEMORY void m_set_idlist() { proc_ids[GET_THREAD_ID] = getpid(); } #endif #if defined(MPI_EVOLVER) #define main old_main #endif int main ARGS2((argc,argv), int argc, char *argv[]) { int pause_flag=0; /* whether to pause after banner message */ msgmax = 2000; if ( !msg ) msg = my_list_calloc(1,msgmax,ETERNAL_BLOCK); set_ctypes(); outfd = stdout; sprintf(msg,"Surface Evolver %s\n\n",VERSION); outstring(msg); #ifdef LONGDOUBLE sprintf(msg,"Compiled for %d byte long double.\n\n",sizeof(REAL)); outstring(msg); #endif #ifdef MPI_EVOLVER MPI_Barrier(MPI_COMM_WORLD); /* wait for everybody to print */ #endif if ( sizeof(element_id) > sizeof(REAL) ) kb_error(3102,"Bad datatype sizes: element_id is %d bytes, but REAL is %d.\n", UNRECOVERABLE); #ifdef _HISTORY_H_ /* readline history initialization */ using_history(); #endif #ifdef SGI_MULTI procs_requested = 1; /* default at John's request. m_get_numprocs(); */ #endif #ifdef WIN32 /* Mutex for use between graph thread and main thread. */ /* Nameless to prevent interference between separate processes. */ graphmutex = CreateMutex(NULL,0,NULL); mem_mutex = CreateMutex(NULL,0,NULL); #if defined(PROF_EVALS) || defined(PROFILING) SetThreadAffinityMask(GetCurrentThread(),1); #endif #ifdef MPI_EVOLVER mpi_mutex = CreateMutex(NULL,0,NULL); #endif #elif defined(PTHREADS) pthread_mutex_init(&graphmutex,NULL); pthread_mutex_init(&mem_mutex,NULL); #ifdef MPI_EVOLVER pthread_mutex_init(&mpi_mutex,NULL); #endif #endif print_express(NULL,0); /* just to initialize string allocation */ /* find machine resolution */ { REAL eps,one = 1.0; for ( eps = 1.0 ; one + eps != one ; eps /= 2.0 ) ; machine_eps = 2.0*eps; } find_cpu_speed(); initialize_perm_globals(); /* put some internal variable names in permanent symbol table */ /* parse command line options */ if ( argc > 0 ) { argv++; argc--; while ( argc && (argv[0] != NULL) && (argv[0][0] == '-') ) { switch ( argv[0][1] ) { case 'E': err_tok_gen_flag = 1; break; case 'a': auto_convert_flag = (argv[0][2]=='-') ? 0 : 1; break; case 'q': option_q = (argv[0][2]=='-') ? 0 : 1; break; case 'Q': quiet_load_flag = 1; break; case 'e': echo_flag = 1; break; case 'Z': pause_flag = 1; break; /* chance to attach debugger */ #ifdef MPI_EVOLVER case 'z': mpi_debug = 1; break; #endif case 't': tty_flag = 1; break; case 'u': tty_flag = 1; break; case 'f' : /* commands from file */ cmdfilename = argv[0]+2; if ( cmdfilename[0] == 0 ) /* probably a space inserted */ { cmdfilename = *++argv; argc--; } break; case 'd' : /* parser debug */ yydebug = 1; break; case 'i' : match_id_flag = 1; break; case 'I' : sparse_ibase_flag = 1; break; case 'p' : procs_requested = atoi(argv[0]+2); #if defined(SGI_MULTI) || defined (THREADS) if ( procs_requested < 1 ) { kb_error(1321, "-p with nonpositive number. Threads set to 1.\n", WARNING); procs_requested = 1; } if ( procs_requested > MAXPROCS ) { sprintf(errmsg, "This Evolver only compiled for a maximum of %d threads.\n",MAXPROCS); kb_error(2551,errmsg,WARNING); sprintf(errmsg, "Threads set to %d. Recompile with -DMAXPROCS=%d if you want more.\n", MAXPROCS,procs_requested); erroutstring(errmsg); procs_requested = MAXPROCS; } #ifdef THREADS threadflag = 1; #endif #else kb_error(1322,"-p option not effective. This Evolver not compiled for multithreading.\n", WARNING); #endif break; case 'x' : exit_after_error = 1; break; case 'w' : exit_after_warning = exit_after_error = 1; break; case 'y' : break_after_warning = 1; break; case 'm' : memdebug = 1; break; case 'l' : just_lex_flag = 1; break; default: sprintf(msg,"Illegal option: %s\n",argv[0]); outstring(msg); case 'h' : outstring("Legal options: \n"); outstring(" -ffilename take commands from file\n"); outstring(" -i use id numbers as in datafile\n"); outstring(" -q convert to named quantities\n"); outstring(" -a auto convert to named quantities when needed\n"); outstring(" -x exit after error\n"); outstring(" -w exit after warning\n"); outstring(" -y break after warning\n"); outstring(" -d parser debugging on\n"); outstring(" -m memory debugging on\n"); #ifdef SGI_MULTI outstring(" -pn use n processes \n"); #endif #if defined(THREADS) outstring(" -pn use n worker threads \n"); #endif outstring(" -h print this help\n"); break; } argv++; argc--; } } if ( pause_flag ) { prompt("Hit ENTER to continue.\n",msg,sizeof(msg)); } #ifdef SGI_MULTI sprintf(msg,"Using %d processes on %d processors.\n\n", procs_requested,m_get_numprocs()); outstring(msg); m_set_procs(procs_requested); if ( m_get_numprocs() > 1 ) { int n; /* set up list of locks available for critical sections */ usconfig(CONF_INITSIZE,200*_MAXLOCKS); usconfig(CONF_ARENATYPE,US_SHAREDONLY); usconfig(CONF_INITUSERS,4+m_get_numprocs()); lock_arena = usinit(lock_arena_name); if ( lock_arena == NULL ) { perror(lock_arena_name); exit(2); } for ( n = 0 ; n < _MAXLOCKS ; n++ ) { locklist[n] = usnewlock(lock_arena); if ( locklist[n] == NULL ) { fprintf(stderr,"lock allocation failure on lock %d.\n",n); perror("usnewlock"); exit(2); } } m_fork(m_set_idlist); m_park_procs(); mpflag = M_INACTIVE; } #endif nprocs = procs_requested; #ifdef WINTHREADS /* Set up worker threads for MS-Windows multiprocessor machines */ thread_data_key = TlsAlloc(); TlsSetValue(thread_data_key,(void*)&default_thread_data); // for main thread if ( threadflag ) { int i; int retval; SYSTEM_INFO si; GetSystemInfo(&si); workthread_wakeup = CreateEvent(NULL,TRUE,FALSE,NULL); mainthread_wakeup = CreateEvent(NULL,TRUE,TRUE,NULL); barrier_event = CreateEvent(NULL,TRUE,TRUE,NULL); #if _WIN32_WINNT >= 0x0500 { retval = InitializeCriticalSectionAndSpinCount(&thread_cs,spincount); retval = InitializeCriticalSectionAndSpinCount(&element_cs,spincount); retval = InitializeCriticalSectionAndSpinCount(&web_cs,spincount); } #else { InitializeCriticalSection(&thread_cs); InitializeCriticalSection(&element_cs); InitializeCriticalSection(&web_cs); } #endif threadlist = (HANDLE*)my_list_calloc(procs_requested,sizeof(HANDLE), ETERNAL_BLOCK); thread_data_ptrs = (struct thread_data**)my_list_calloc(procs_requested, sizeof(struct thread_data*),ETERNAL_BLOCK); busythreads = procs_requested; barrier_count = procs_requested; ResetEvent(barrier_event); for ( i = 0 ; i < procs_requested ; i++ ) { DWORD tid; /* dummy for thread id */ thread_data_ptrs[i] = (struct thread_data *)my_list_calloc(1,sizeof(struct thread_data), ETERNAL_BLOCK); thread_data_ptrs[i]->worker_id = i; threadlist[i] = CreateThread(NULL,0,winthread_worker, thread_data_ptrs[i],0,&tid); if ( threadlist[i] == NULL ) { FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ (LPTSTR) &errmsg, ERRMSGSIZE, NULL ); erroutstring("Cannot create worker threads.\n"); kb_error(2190,errmsg,UNRECOVERABLE); } } WaitForSingleObject(mainthread_wakeup,INFINITE); sprintf(msg,"Created %d worker threads on %d processor machine.\n", procs_requested,si.dwNumberOfProcessors); if ( si.dwNumberOfProcessors == 1 ) spincount = 1; /* spin worthless on 1 cpu */ else spincount = 4000; outstring(msg); } #endif #ifdef PTHREADS /* Set up worker threads for Unix multiprocessor machines with pthreads */ pthread_key_create(&thread_data_key,NULL); pthread_setspecific(thread_data_key,(void*)&default_thread_data); // for main thread if ( threadflag ) { int i; pthread_cond_init(&mainthread_wakeup,NULL); pthread_cond_init(&workthread_wakeup,NULL); pthread_mutex_init(&thread_mutex, NULL); pthread_mutex_init(&element_mutex, NULL); threadlist = (pthread_t *)my_list_calloc(procs_requested+10,sizeof(int), ETERNAL_BLOCK); thread_data_ptrs = (struct thread_data**)my_list_calloc(procs_requested, sizeof(struct thread_data *),ETERNAL_BLOCK); busythreads = procs_requested; /* countdown barrier */ pthread_mutex_lock(&thread_mutex); for ( i = 0 ; i < procs_requested ; i++ ) { int ret; /* dummy for thread id */ thread_data_ptrs[i] = (struct thread_data *)my_list_calloc(1, sizeof(struct thread_data),ETERNAL_BLOCK); thread_data_ptrs[i]->worker_id = i; ret = pthread_create(threadlist+i,NULL,pthread_worker, (void*)(thread_data_ptrs[i])); if ( ret != 0 ) { kb_error(2191,"Cannot create worker threads.\n",UNRECOVERABLE); } } /* wait for workers to begin waiting; */ pthread_cond_wait(&mainthread_wakeup,&thread_mutex); pthread_mutex_unlock(&thread_mutex); sprintf(msg,"Created %d worker threads.\n", procs_requested); outstring(msg); } #endif /* If not doing multiple processes then have a thread data structure so we don't have to write different code for threading and nonthreading various places */ if ( thread_data_ptrs == NULL ) { thread_data_ptrs = &default_thread_data_ptr; thread_data_ptrs[0] = &default_thread_data; #ifdef WINTHREADS /* set per-thread data */ TlsSetValue(thread_data_key,(void*)&default_thread_data); #endif #ifdef PTHREADS /* set per-thread data */ pthread_setspecific(thread_data_key,(void*)&default_thread_data); #endif } signal(SIGINT,catcher); /* to catch user interrupt */ #ifdef SIGUSR1 signal(SIGUSR1,catcher); /* to catch user interrupt */ #endif #ifdef SIGALRM signal(SIGALRM,catcher); /* to catch alarm clock */ #endif #ifdef SIGTERM signal(SIGTERM,catcher); /* to catch user interrupt, dump and kill */ #endif #ifdef SIGHUP signal(SIGHUP,catcher); /* to catch user interrupt, dump and kill */ #endif #ifdef SIGPIPE signal(SIGPIPE,catcher); /* to catch broken pipe */ #endif ENTER_GRAPH_MUTEX; scoeff_init(); vcoeff_init(); reset_web(); /* in case no datafile on command line */ init_view(); LEAVE_GRAPH_MUTEX; if ( argc && argv && argv[0] && argv[1] ) kb_error(1323,"Extra command line arguments ignored.\n",WARNING); /* command sources stack */ push_commandfd(stdin,"stdin"); subshell_depth = 0; if ( cmdfilename ) /* trap back to here if error and skip cmdfilename */ { if ( !setjmp(jumpbuf[subshell_depth]) ) push_commandfd(NULL,cmdfilename); } #ifdef __cplusplus char *to_load = (argc > 0) ? argv[0] : NULL; for ( ;; ) { try { startup(to_load); datafile_flag = 0; /* use C++ exception mechanism instead of setjmp/longjmp */ for (;;) { try { exec_commands(NULL,"Enter command: "); /* command read/execute loop */ } catch ( int k ) { } } } catch ( loadexcep k ) /* corresponds to loadjumpbuf */ { /* LOAD command returns here */ if ( list && (list != permlist)) { myfree((char*)list); list = NULL; }/* plug memory leak */ to_load = loadfilename[0] ? loadfilename : NULL; } catch ( int j ) /* corresponds to jumpbuf */ { to_load = NULL; } } #else if ( setjmp(loadjumpbuf) ) { /* LOAD command returns here */ if ( list && (list != permlist)) { myfree((char*)list); list = NULL; }/* plug memory leak */ #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) mpi_loadfile(); #endif startup(loadfilename); } else { if ( setjmp(jumpbuf[subshell_depth]) ) /* return here after datafile errors */ { startup(NULL); } else { if ( argc > 0 ) startup(argv[0]); else { #ifdef MPI_EVOLVER if ( this_task != MASTER_TASK ) return 0; /* let master task ask for datafile */ #endif startup(NULL); } } } datafile_flag = 0; #endif #ifdef MPI_EVOLVER // MPI_Barrier(MPI_COMM_WORLD); if ( this_task > 0 ) return 0; /* return to mpi main() */ calc_energy(); /* initial energy */ #endif subshell_depth = 0; #ifdef __cplusplus #else /* return here after commandfile errors */ while ( setjmp(jumpbuf[subshell_depth]) != 0 ); exec_commands(NULL,"Enter command: "); /* command read and execute loop */ #endif my_exit(0); return 0; /* success return code */ } #ifdef __cplusplus void loadstub() { struct loadexcep something; throw something; } #endif /******************************************************************** * * function: my_exit() * * purpose: graceful exit from program */ void my_exit ARGS1((code), int code) { if ( OOGL_flag ) End_OOGL(); #ifdef SIGTERM signal(SIGTERM,SIG_DFL); #endif #ifdef SGI_MULTI if ( nprocs > 1) { m_rele_procs(); m_kill_procs(); /* kill any parallel threads */ } #endif #if defined(MPI_EVOLVER) mpi_my_exit(); #endif #ifdef USE_READLINE /* CSL */ save_readline_history(); #endif exit(code); } /******************************************************************** * * function: exec_commands() * * purpose: reads and executes commands from input. * pops command file stack whenever end of file. */ int exec_commands ARGS2((basefd,promptstring), FILE *basefd, /* stop when get back to this input source */ char *promptstring) /* prompt to use */ { /* main event loop of program */ while ( commandfd && (commandfd != basefd)) { char response[200]; datafile_flag = 0; /* in case of error bailout sometimes */ if ( !subshell_depth ) { temp_free_all(); /* stray memory blocks */ free_discards(DISCARDS_ALL); /* from previous cycle */ memset(response,0,sizeof(response)); } if ( prompt(promptstring,response,sizeof(response)) == EOF ) pop_commandfd(); else if ( old_menu(response) == END_COMMANDS ) return END_COMMANDS; } return 0; } #ifdef MAC_OS_X /******************************************************************** * * function: mac_exec_commands() * * purpose: wrapper for exec_commands so Mac main thread (which is * the draw thread) can call with one argument. */ struct thread_data mac_exec_thread_data; void *mac_exec_commands ARGS1((arg), FILE *arg) { #ifdef __cplusplus char *to_load = NULL; int flag; /* so doesn't prompt user for input file first time, if have */ pthread_setspecific(thread_data_key,(void*)&mac_exec_thread_data); flag = !datafilename[0]; for ( ;; ) { if ( flag ) startup(to_load); flag = 1; try { /* use C++ exception mechanism instead of setjmp/longjmp */ for (;;) { try { chdir(curdir); exec_commands(NULL,"Enter command: "); /* command read/execute loop */ } catch ( int k ) { } } } catch ( loadexcep k ) /* corresponds to loadjmpbuf */ { /* LOAD command returns here */ if ( list && (list != permlist)) { myfree((char*)list); list = NULL; }/* plug memory leak */ to_load = loadfilename[0] ? loadfilename : NULL; } catch ( int j ) /* corresponds to jumpbuf */ { to_load = NULL; } } #else pthread_setspecific(thread_data_key,(void*)&mac_exec_thread_data); if ( setjmp(loadjumpbuf) ) { /* LOAD command returns here */ if ( list && (list != permlist)) { myfree((char*)list); list = NULL; }/* plug memory leak */ startup(loadfilename); } else { if ( setjmp(jumpbuf[subshell_depth]) ) /* return here after datafile errors */ { exec_commands(NULL,"Enter command: "); /* command read/execute loop */ // startup(NULL); } else { if ( do_show_flag == 1 ) do_show(); chdir(curdir); /* so command thread gets proper directory */ exec_commands(NULL,"Enter command: "); /* command read/execute loop */ // startup(NULL); } } datafile_flag = 0; #endif #ifdef MPI_EVOLVER MPI_Barrier(MPI_COMM_WORLD); if ( this_task > 0 ) return 0; /* return to mpi main() */ calc_energy(); /* initial energy */ #endif #ifdef __cplusplus #else while ( setjmp(jumpbuf[subshell_depth]) != 0 ); /* return here after commandfile errors */ exec_commands(NULL,"Enter command: "); /* command read and execute loop */ #endif return NULL; } #endif /******************************************************************** * * function: exec_file() * * purpose: reads and executes commands from a file. * to be used by read "filename" command. * can be used in the middle of executing a command. */ void exec_file ARGS2((fd,name), FILE *fd, /* file, if already opened, like stdin */ char *name) /* file name, if not already opened */ { int old_read_depth = read_depth; push_commandfd(fd,name); do /* main event loop of program */ { char response[200]; temp_free_all(); /* stray memory blocks */ free_discards(DISCARDS_SOME); /* from previous cycle */ memset(response,0,sizeof(response)); if ( prompt("Enter command: ",response,sizeof(response)) == EOF ) pop_commandfd(); else old_menu(response); } while ( read_depth > old_read_depth ); } /**************************************************************** * * Function: startup() * * Purpose: Start new datafile. * *****************************************************************/ void startup ARGS1((file_name), char *file_name) /* NULL if need to ask for name */ { char *name = file_name; char response[100]; FILE *newfd; /* be sure graphics thread is ok before loading new file */ ENTER_GRAPH_MUTEX; LEAVE_GRAPH_MUTEX; datafile_flag = 0; /* close leftover input files */ for ( ; read_depth > (cmdfilename ? 2 : 1) ; read_depth-- ) fclose(cmdfile_stack[read_depth-1].fd); file_retry: if ( name == NULL ) { char *c; #ifdef MPI_EVOLVER if ( this_task != MASTER_TASK ) return; /* wait for master to get new name */ #endif prompt("Enter new datafile name (none to continue, q to quit): ", response,sizeof(response)); c = strchr(response,'\n'); if ( c ) *c = 0; c = strchr(response,' '); if ( c ) *c = 0; if ( (strcmp(response,"q") == 0) || (strcmp(response,"quit")==0) || (strcmp(response,"bye")==0) || (strcmp(response,"exit")==0)) my_exit(0); else if ( !response[0] ) return; /* continue same */ name = response; #ifdef MPI_EVOLVER strcpy(loadfilename,name); mpi_loadfile(); #endif } newfd = path_open(name,SETDATAFILENAME); if (newfd == NULL) { if ( name[0] ) { sprintf(msg,"Cannot open datafile %s.\n",name); erroutstring(msg); if ( exit_after_error ) my_exit(1); name = NULL; while ( read_depth > (cmdfilename ? 2 : 1) ) pop_commandfd(); goto file_retry; } return; /* continue with old */ } #ifdef WIN32 #ifdef MPI_EVOLVER sprintf(msg,"Surface Evolver MPI - %s",name); #else sprintf(msg,"Surface Evolver - %s",name); #endif SetConsoleTitle(msg); #endif ENTER_GRAPH_MUTEX; reset_web(); init_view(); if (memdebug) memory_report(); push_commandfd(newfd,name); /* start #include stack */ #ifdef __WIN32__ if ( heapcheck() < 0 ) kb_error(1324,"Internal error: Corrupt heap.\n",UNRECOVERABLE); #endif datafile_flag = 1; /* so parser knows */ datafile_input_flag = 1; /* so lex input knows */ cmdptr = 0; initialize(); LEAVE_GRAPH_MUTEX; datafile_flag = 0; if ( read_depth > 0 ) cmdfile_stack[read_depth-1].line = line_no; if ( datafile_view_flag ) { int i,j; REAL sum; for ( sum = 0.0, i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < 3 ; j++ ) sum += fabs(view[i][j]); overall_size = 3/sum; dont_resize_flag = 1; } else { resize(); } graph_new_surface(); /* tell graphics we have new one */ if ( parse_errors ) { while ( (read_depth > 1) ) pop_commandfd(); kb_error(1325,"Invalid datafile. Surface may not be in a consistent state.\n",RECOVERABLE); } run_checks(); update_display(); #ifndef MPI_EVOLVER calc_content(Q_ENERGY|Q_FIXED|Q_INFO|Q_RENORMALIZE); if ( web.torus_flag ) fix_volconst(); calc_pressure(); calc_energy(); /* just to get initial total area */ target_length = web.total_area; /* for square curvature string model */ if ( OOGL_flag ) ask_wrap_display(); #endif } #ifdef THREADS /**************************************************************************** ***************************************************************************** Multi-threading for multi-processing with shared memory. ****************************************************************************/ void thread_stage_setup ARGS((void)); /***************************************************************************** * * function: task_caller() * * purpose: provide common place for various type worker threads to * call the desired task. */ void task_caller ARGS1((thread_tasknum), int thread_tasknum) { struct thread_data *data = GET_THREAD_DATA; __int32 task_caller_elapsed_time[2]; __int32 now[2]; task_caller_elapsed_time[0] = 0; task_caller_elapsed_time[1] = 0; data->task_state = 1; PROF_NOW(now); #ifdef _MSC_VER data->stagestart[0] = *(__int64*)now; #endif PROF_START(task_caller) switch ( thread_tasknum ) { case TH_PROJECT_ALL_TEST: thread_project_all(TEST_MOVE); break; case TH_PROJECT_ALL_ACTUAL: thread_project_all(ACTUAL_MOVE); break; case TH_CALC_FACET_ENERGY: data->total_energy = data->total_area = 0.0; thread_calc_facet_energy(); break; case TH_CALC_FACET_FORCES: thread_calc_facet_forces(); break; case TH_MULTI_CALC_QUANT: multi_calc_quants(m_type); break; case TH_MULTI_QUANT_GRADS: m_calc_quant_grads(m_type); break; case TH_FIX_GRADS: m_fix_grads(); break; case TH_MULTI_QUANT_HESS: m_calc_quant_hess(m_type,m_mode,m_rhs); break; case TH_MOVE_VERTICES: thread_move_vertices(); break; default: sprintf(errmsg,"Invalid thread task: %d\n",thread_tasknum); kb_error(2192,errmsg,UNRECOVERABLE); } PROF_FINISH(task_caller) #ifdef ZZZZZ if(verbose_flag) { char *taskname; switch ( thread_tasknum ) { case TH_PROJECT_ALL_TEST: taskname="thread_project_all"; break; case TH_PROJECT_ALL_ACTUAL: taskname="thread_project_all"; break; case TH_CALC_FACET_ENERGY: taskname="thread_calc_facet_energy"; break; case TH_CALC_FACET_FORCES: taskname="thread_calc_facet_forces"; break; case TH_MULTI_CALC_QUANT: taskname="multi_calc_quants"; break; case TH_MULTI_QUANT_GRADS: taskname="m_calc_quant_grads"; break; case TH_FIX_GRADS: taskname="m_fix_grads"; break; case TH_MULTI_QUANT_HESS: taskname="m_calc_quant_hess"; break; default: taskname="default"; } printf("%30s",taskname); PROF_PRINT(task_caller) } #endif data->task_state = 2; } #endif #ifdef WINTHREADS /***************************************************************************** * * function: winthread_worker() * * purpose: Main winthread worker function. Waits for wakeup call and * then executes task. * */ DWORD WINAPI winthread_worker ( void * arg ) { struct thread_data *data = (struct thread_data *)arg; /* set per-thread data */ TlsSetValue(thread_data_key,(void*)data); /* Set affinity mask. Actually seems to hurt. */ /* SetThreadAffinityMask(threadlist[data->worker_id],1<worker_id); */ /* Loop waiting for wakeup calls. Use single event rather than event */ /* for each type of task since MAXIMUM_WAIT_OBJECTS is 64. */ for (;;) { InterlockedDecrement(&busythreads); if ( busythreads == 0 ) { ResetEvent(workthread_wakeup); SetEvent(barrier_event); } WaitForSingleObject(barrier_event,INFINITE); InterlockedDecrement(&barrier_count); if ( barrier_count == 0 ) SetEvent(mainthread_wakeup); WaitForSingleObject(workthread_wakeup,INFINITE); task_caller(thread_task); } } #endif #ifdef PTHREADS /***************************************************************************** * * function: pthread_worker() * * purpose: Main pthreads worker function. Waits for wakeup call and * then executes task. * arg is thread data area. */ void *pthread_worker( void * arg ) { struct thread_data *data = (struct thread_data *)arg; /* set per-thread data */ pthread_setspecific(thread_data_key,(void*)data); for (;;) { /* barrier song and dance to let main thread know workers are waiting */ pthread_mutex_lock(&thread_mutex); busythreads--; if ( busythreads == 0 ) pthread_cond_signal(&mainthread_wakeup); pthread_cond_wait(&workthread_wakeup,&thread_mutex); pthread_mutex_unlock(&thread_mutex); #ifdef PTHREAD_LOG { /* log thread event */ rdtsc(data->events[data->eventcount].time_low, data->events[data->eventcount].time_high); data->events[data->eventcount].type = thread_task; data->eventcount++; } #endif task_caller(thread_task); #ifdef PTHREAD_LOG { /* log thread event */ rdtsc(data->events[data->eventcount].time_low, data->events[data->eventcount].time_high); data->events[data->eventcount].type = -thread_task; data->eventcount++; } #endif } return NULL; } /* end pthread_worker() */ #endif #ifdef THREADS /************************************************************************** * * function: pcomp() * * purpose: comparison function for sorting partition coordinates. */ struct pcoord { REAL p_coord; vertex_id v_id; }; int pcomp ARGS((struct pcoord*,struct pcoord*)); int pcomp ARGS2((a,b), struct pcoord *a, struct pcoord *b) { if ( a->p_coord < b->p_coord ) return -1; if ( a->p_coord > b->p_coord ) return 1; return 0; } /************************************************************************** * * function: thread_stage_setup() * * purpose: Initialize element lists for thread stages. */ void thread_stage_setup() { int i,pcount,pspot,perstage; vertex_id v_id; edge_id e_id; facet_id f_id; int proc,stage; struct pcoord *pcoords; struct thread_stages_data *th; int one = 1; max_thread_stages = 2; /* simple 1-D partitioning */ /* Make sure vertex partitioning coord exists */ v_partition_coord_attr = find_attribute(VERTEX,"v_partition_coord"); if ( v_partition_coord_attr < 0 ) { v_partition_coord_attr = add_attribute(VERTEX,"v_partition_coord", REAL_TYPE,0,&one,0,NULL); } /* default for now: use x coordinate as partition coordinate */ FOR_ALL_VERTICES(v_id) { *((REAL*)(get_extra(v_id,v_partition_coord_attr))) = get_coord(v_id)[0]; } v_partition_stage_attr = find_attribute(VERTEX,"v_partition_stage"); if ( v_partition_stage_attr < 0 ) { v_partition_stage_attr = add_attribute(VERTEX,"v_partition_stage", INTEGER_TYPE,0,&one,0,NULL); } v_partition_proc_attr = find_attribute(VERTEX,"v_partition_proc"); if ( v_partition_proc_attr < 0 ) { v_partition_proc_attr = add_attribute(VERTEX,"v_partition_proc", INTEGER_TYPE,0,&one,0,NULL); } e_partition_stage_attr = find_attribute(EDGE,"e_partition_stage"); if ( e_partition_stage_attr < 0 ) { e_partition_stage_attr = add_attribute(EDGE,"e_partition_stage", INTEGER_TYPE,0,&one,0,NULL); } e_partition_proc_attr = find_attribute(EDGE,"e_partition_proc"); if ( e_partition_proc_attr < 0 ) { e_partition_proc_attr = add_attribute(EDGE,"e_partition_proc", INTEGER_TYPE,0,&one,0,NULL); } f_partition_stage_attr = find_attribute(FACET,"f_partition_stage"); if ( f_partition_stage_attr < 0 ) { f_partition_stage_attr = add_attribute(FACET,"f_partition_stage", INTEGER_TYPE,0,&one,0,NULL); } f_partition_proc_attr = find_attribute(FACET,"f_partition_proc"); if ( f_partition_proc_attr < 0 ) { f_partition_proc_attr = add_attribute(FACET,"f_partition_proc", INTEGER_TYPE,0,&one,0,NULL); } /* Find percentiles, for load balancing */ pcoords = (struct pcoord*)temp_calloc(web.skel[VERTEX].count, sizeof(struct pcoord)); pcount = 0; FOR_ALL_VERTICES(v_id) { pcoords[pcount].p_coord = *((REAL*)(get_extra(v_id,v_partition_coord_attr))); pcoords[pcount].v_id = v_id; pcount++; } qsort((char*)pcoords,pcount,sizeof(struct pcoord),FCAST pcomp); pspot = 0; perstage = pcount/(nprocs*max_thread_stages) + 1; for ( proc = 0 ; proc < nprocs ; proc++ ) { for ( stage = 0 ; stage < max_thread_stages ; stage++ ) { for ( i = 0 ; (i < perstage) && (pspot < pcount) ; i++,pspot++ ) { *((int *)(get_extra(pcoords[pspot].v_id,v_partition_proc_attr))) = proc; *((int *)(get_extra(pcoords[pspot].v_id,v_partition_stage_attr))) = stage; } } } temp_free((char*)pcoords); if ( thread_stages == NULL ) thread_stages = (struct thread_stages_data*)mycalloc(nprocs, sizeof(struct thread_stages_data)); /* set up all element's stage lists */ for ( proc = 0 ; proc < nprocs ; proc++ ) for ( stage = 0 ; stage < max_thread_stages ; stage++ ) thread_stages[proc].counts[VERTEX][stage] = 0; thread_stages[0].counts[VERTEX][max_thread_stages] = 0; FOR_ALL_VERTICES(v_id) { proc = *(int*)get_extra(v_id,v_partition_proc_attr); stage = *(int*)get_extra(v_id,v_partition_stage_attr); th = thread_stages+proc; if ( th->counts[VERTEX][stage] >= th->allocated[VERTEX][stage] ) { int newalloc = th->allocated[VERTEX][stage] + 10 + 2*web.skel[VERTEX].count/(nprocs*max_thread_stages); th->blocks[VERTEX][stage] = (element_id *)kb_realloc( (char*)(th->blocks[VERTEX][stage]),newalloc*sizeof(element_id)); th->allocated[VERTEX][stage] = newalloc; } th->blocks[VERTEX][stage][th->counts[VERTEX][stage]++] = v_id; } for ( proc = 0 ; proc < nprocs ; proc++ ) for ( stage = 0 ; stage < max_thread_stages ; stage++ ) thread_stages[proc].counts[EDGE][stage] = 0; thread_stages[0].counts[EDGE][max_thread_stages] = 0; FOR_ALL_EDGES(e_id) { vertex_id head = get_edge_headv(e_id); vertex_id tail = get_edge_tailv(e_id); int hproc = *(int*)get_extra(head,v_partition_proc_attr); int tproc = *(int*)get_extra(tail,v_partition_proc_attr); int hstage = *(int*)get_extra(head,v_partition_stage_attr); int tstage = *(int*)get_extra(tail,v_partition_stage_attr); int hband = hstage + max_thread_stages*hproc; int tband = tstage + max_thread_stages*tproc; if ( abs(hband-tband) <= 1 ) { if ( hband < tband ) { proc = hproc; stage = hstage; } else { proc = tproc; stage = tstage;} } else if ( web.symmetry_flag && (hband == 0) && (tband == nprocs*max_thread_stages-1 ) ) { proc = tproc; stage = tstage;} else if ( web.symmetry_flag && (tband == 0) && (hband == nprocs*max_thread_stages-1 ) ) { proc = hproc; stage = hstage;} else /* big element */ { proc = 0; stage = max_thread_stages; } th = thread_stages+proc; if ( th->counts[EDGE][stage] >= th->allocated[EDGE][stage] ) { int newalloc = th->allocated[EDGE][stage] + 10 + 2*web.skel[EDGE].count/(nprocs*max_thread_stages); th->blocks[EDGE][stage] = (element_id *)kb_realloc( (char*)(th->blocks[EDGE][stage]),newalloc*sizeof(element_id)); th->allocated[EDGE][stage] = newalloc; } th->blocks[EDGE][stage][th->counts[EDGE][stage]++] = e_id; *((int *)(get_extra(e_id,e_partition_proc_attr))) = proc; *((int *)(get_extra(e_id,e_partition_stage_attr))) = stage; } for ( proc = 0 ; proc < nprocs ; proc++ ) for ( stage = 0 ; stage < max_thread_stages ; stage++ ) thread_stages[proc].counts[FACET][stage] = 0; thread_stages[0].counts[FACET][max_thread_stages] = 0; FOR_ALL_FACETS(f_id) { facetedge_id fe = get_facet_fe(f_id); int topband = -1; int lowband = 1000000; int vproc,vstage,vband; for ( i = 0 ; i < FACET_VERTS ; i++ ) { v_id = get_fe_headv(fe); vproc = *(int*)get_extra(v_id,v_partition_proc_attr); vstage = *(int*)get_extra(v_id,v_partition_stage_attr); vband = vstage + max_thread_stages*vproc; if ( vband < lowband ) lowband = vband; if ( vband > topband ) topband = vband; fe = get_next_edge(fe); } if ( topband-lowband <= 1 ) { proc = lowband/max_thread_stages; stage = lowband % max_thread_stages; } else if ( web.symmetry_flag && (lowband == 0) && (topband == nprocs*max_thread_stages-1 ) ) { proc = topband/max_thread_stages; stage = topband % max_thread_stages; } else /* big element */ { proc = 0; stage = max_thread_stages; } th = thread_stages+proc; if ( th->counts[FACET][stage] >= th->allocated[FACET][stage] ) { int newalloc = th->allocated[FACET][stage] + 10 + 2*web.skel[FACET].count/(nprocs*max_thread_stages); th->blocks[FACET][stage] = (element_id *)kb_realloc( (char*)(th->blocks[FACET][stage]),newalloc*sizeof(element_id)); th->allocated[FACET][stage] = newalloc; } th->blocks[FACET][stage][th->counts[FACET][stage]++] = f_id; *((int *)(get_extra(f_id,f_partition_proc_attr))) = proc; *((int *)(get_extra(f_id,f_partition_stage_attr))) = stage; } partition_timestamp = global_timestamp; } /* end thread_stage_setup */ #endif #ifdef WINTHREADS /************************************************************************** * * function: thread_launch() * * purpose: Send signal to launch worker threads, after initializing * global iteration variable. */ void thread_launch(int task, int element_type) { int i,j,proc; __int32 now[2]; PROF_NOW(now); thread_launch_start = *(__int64*)now; global_id = web.skel[element_type].used; /* in case no threads */ if ( partition_timestamp < top_timestamp ) thread_stage_setup(); for ( proc = 0 ; proc < nprocs ; proc++ ) thread_stages[proc].stage = thread_stages[proc].spot = 0; thread_task = task; ResetEvent(mainthread_wakeup); /* just to be sure */ busythreads = procs_requested; ResetEvent(barrier_event); barrier_count = procs_requested; SetEvent(workthread_wakeup); WaitForSingleObject(mainthread_wakeup,INFINITE); /* Cleanup and amalgamation of data */ switch ( task ) { case TH_CALC_FACET_ENERGY: for ( i = 0 ; i < nprocs ; i++ ) { web.total_energy += thread_data_ptrs[i]->total_energy; web.total_area += thread_data_ptrs[i]->total_area; } break; } PROF_NOW(now); thread_launch_end = *(__int64*)now; if ( verbose_flag ) { /* print detailed clock timing */ printf("thread_launch_start %12I64X\n",thread_launch_start-thread_launch_start); for (i = 0 ; i <= max_thread_stages ; i++ ) printf("task 0 stage %d start: %12I64X end: %12I64X\n",i, thread_data_ptrs[0]->stagestart[i]-thread_launch_start, thread_data_ptrs[0]->stageend[i]-thread_launch_start); for ( j = 1; j < nprocs ; j++ ) { for (i = 0 ; i < max_thread_stages ; i++ ) printf("task 1 stage %d start: %12I64X end: %12I64X\n",i, thread_data_ptrs[j]->stagestart[i]-thread_launch_start, thread_data_ptrs[j]->stageend[i]-thread_launch_start); } printf("thread_launch_end %12I64X\n",thread_launch_end-thread_launch_start); } } #endif #ifdef PTHREADS /************************************************************************** * * function: thread_launch() * * purpose: Send signal to launch worker threads, after initializing * global iteration variable. * Note: Have to be very careful with timing, since wake up happens only * if thread is actually waiting when signal made; signal ignored if * made before wait starts. */ void thread_launch(int task, int element_type) { int i,proc; global_id = web.skel[element_type].used; for ( i = 0 ; i < nprocs ; i++ ) { /* make sure we are starting with an actual element */ while ( valid_id(global_id) && !valid_element(global_id) ) global_id = elptr(global_id)->forechain; thread_data_ptrs[i]->iteration_id = global_id; if ( valid_id(global_id) ) global_id = elptr(global_id)->forechain; } if ( partition_timestamp < top_timestamp ) thread_stage_setup(); for ( proc = 0 ; proc < nprocs ; proc++ ) thread_stages[proc].stage = thread_stages[proc].spot = 0; thread_task = task; pthread_mutex_lock(&thread_mutex); /* wait for last thread to wait */ busythreads = procs_requested; pthread_cond_broadcast(&workthread_wakeup); /* wait for workers to begin waiting; */ pthread_cond_wait(&mainthread_wakeup,&thread_mutex); pthread_mutex_unlock(&thread_mutex); #ifdef PTHREAD_LOG { /* log thread event */ rdtsc(main_events[main_eventcount].time_low, main_events[main_eventcount].time_high); main_events[main_eventcount].type = 0; main_eventcount++; } #endif /* Cleanup and amalgamation of data */ switch ( task ) { case TH_CALC_FACET_ENERGY: for ( i = 0 ; i < nprocs ; i++ ) { web.total_energy += thread_data_ptrs[i]->total_energy; web.total_area += thread_data_ptrs[i]->total_area; } break; } } #endif #ifndef THREADS void thread_launch(int task, int element_type) { sprintf(errmsg,"Internal error. Function thread_launch called in non-threaded Evolver. Task %d, type %d.\n",task,element_type); kb_error(2193,errmsg,RECOVERABLE); } #endif #ifdef THREADS /************************************************************************** * * function: thread_next_element() * * purpose: Get next element on task list for a calling thread. * Assumes global_id initialized to first element of * appropriate element list. Goes until end of list, not * sentinel, so to be used for functions that don't modify * the element list. * * Performance note: with two processors, get twice as many thread_locks * as thread_unlocks!! Get about half as many web_locks as web_unlocks. */ element_id thread_next_element() { element_id id; int proc,nextproc; int eltype; struct thread_stages_data *th; struct thread_data *data; __int32 now[2]; /* for timing */ /* First, in case multithreading not being used */ if ( threadflag == 0 ) { while ( valid_id(global_id) & !valid_element(global_id) ) global_id = elptr(global_id)->forechain; if ( !valid_id(global_id) ) return NULLID; id = global_id; global_id = elptr(id)->forechain; return id; } data = GET_THREAD_DATA; proc = data->worker_id; th = thread_stages + proc; eltype = id_type(global_id); for (;;) /* loop just for case when starting new stage */ { if ( th->spot < th->counts[eltype][th->stage] ) { id = th->blocks[eltype][th->stage][th->spot++]; break; } else { th->stage++; /* signify done */ #ifdef _MSC_VER PROF_NOW(now); data->stageend[th->stage-1] = *(__int64*)now; #endif th->spot = 0; if ( th->stage == ((proc==0) ? max_thread_stages+1 : max_thread_stages) ) { id = NULLID; break; } else /* look for next stage */ { if ( th->stage < max_thread_stages ) { nextproc = (proc == nprocs - 1) ? 0 : proc+1; while ( thread_stages[nextproc].stage < th->stage ) ; /* wait */ } else /* proc 0 to do big elements */ { for ( nextproc = 1 ; nextproc < nprocs ; nextproc++ ) while ( thread_stages[nextproc].stage < th->stage ) ; /* wait */ } #ifdef _MSC_VER PROF_NOW(now); data->stagestart[th->stage] = *(__int64*)now; #endif } } } return id; } #endif /*************************************************************************** * */ #ifdef WINTHREADS /* Indirection to lock functions to avoid including windows.h everywhere */ int mylock_element(element_id id) { if ( TryEnterCriticalSection(element_mutex_ptr)==0 ) {EnterCriticalSection(element_mutex_ptr); element_locks++; } else element_unlocks++; return 0; /* so can be used in conditionals */ } int myunlock_element(element_id id) { LeaveCriticalSection(element_mutex_ptr); return 0; } int mylock_web(void) { if ( TryEnterCriticalSection(web_mutex_ptr)==0 ) {EnterCriticalSection(web_mutex_ptr); web_locks++; } else web_unlocks++; return 0; } int myunlock_web(void) { LeaveCriticalSection(web_mutex_ptr); return 0; } struct thread_data *win_get_thread_data(DWORD key) { return (struct thread_data *)TlsGetValue(key); } #endif #ifdef PTHREADS int mylock_element(element_id id) { /* Using lock field in element structure. */ /* using lock count semantics, i.e. lock=1 is locked. */ /* Note we have to save ebx, since caller assumes saved. */ #ifdef TIMING __asm__ ( " pushl %ebx\n" " pushl 8(%ebp)\n" " call elptr\n" " addl $4,%esp\n" " movl %eax,%ebx\n" " addl $16,%ebx\n" ".L8889j: \n" " movl $1,%eax\n" "LOCK cmpxchgl %eax,0(%ebx)\n" " jz .L8889j \n" " popl %ebx\n" ); #endif return 0; } #endif /************************************************************************** * * function: find_cpu_speed() * * purpose: tries to find out current CPU speed for profiling reports. */ void find_cpu_speed() { #if defined(_MSC_VER) && defined(PROFILING) { /* use high-performance counter to estimate frequency */ LARGE_INTEGER freq,start,finish; __int32 profstart[2],profnow[2]; QueryPerformanceFrequency(&freq); PROF_NOW(profstart); PROF_NOW(profnow); QueryPerformanceCounter(&start); do { if ( *(__int64*)profnow - *(__int64*)profstart <= 0 ) { /* something wrong; maybe hibernated in middle of loop! */ return; } PROF_NOW(profnow); } while ( *(__int64*)profnow - *(__int64*)profstart < 100000000 ); QueryPerformanceCounter(&finish); cpu_speed = (*(__int64*)profnow-*(__int64*)profstart)/ ((finish.QuadPart-start.QuadPart)/(double)freq.QuadPart); } #else cpu_speed = 3e9; /* rough guess */ #endif } evolver-2.30c.dfsg/src/filmq.c0000644000175300017530000006750511410765113016506 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************************ * * file: filmq.c * * contents: Functions for calculating energy, volume, * and their gradients for the QUADRATIC * SOAPFILM model. Also utility functions * for quadratic interpolation and Gaussian * integration. */ /************************************************************************ * * Functions to calculate area, volume, and energy according to * the tessellation model. * * Quadratic patch version, using n-point facet cubature of integral_order. */ #include "include.h" /* volume coefficients, used only for quadratic volume */ REAL vcoeff[FACET_CTRL][FACET_CTRL][FACET_CTRL]; /* interpolation polynomial second partials */ REAL poly2partial[FACET_CTRL][2][2] = { { {1.0, 1.0}, {1.0, 1.0} }, {{-2.0, -1.0}, {-1.0, 0.0} },{ {1.0,0.0},{0.0,0.0} }, { {0.0,1.0},{1.0,0.0} }, { {0.0,0.0},{0.0,1.0} }, { {0.0,-1.0},{-1.0,2.0} } }; /************************************************************************ * * Calculates all forces on control points due to facet and * accumulates them at each control point. * Quadratic version. */ void facet_force_q(f_id) facet_id f_id; { vertex_id v_id[FACET_CTRL]; MAT2D(x,FACET_CTRL,MAXCOORD); REAL normal[MAXCOORD]; int i,j,k,n; REAL norm,summer[2][MAXCOORD]; facetedge_id fe_id; REAL density = get_facet_density(f_id); REAL area = 0.0; REAL gdensity = 0.0; REAL z; body_id b_id; REAL forces[FACET_CTRL][MAXCOORD]; /* total forces from this facet */ REAL *forceptr[FACET_CTRL]; /* pointers to forces */ WRAPTYPE wraps[FACET_CTRL]; MAT2D(tang,MAXCOORD,MAXCOORD); memset((char*)forces,0,sizeof(forces)); /* set to 0 */ /* get control points */ fe_id = get_facet_fe(f_id); for ( i = 0, j = 0 ; i < FACET_EDGES ; i++ ) { v_id[j++] = get_fe_tailv(fe_id); v_id[j++] = get_fe_midv(fe_id); fe_id = get_next_edge(fe_id); } for ( j = 0 ; j < FACET_CTRL ; j++ ) forceptr[j] = forces[j]; get_facet_verts(f_id,x,wraps); if ( web.metric_flag ) { simplex_force_metric(v_id,x,density,forceptr); goto cumforces; /* assume no gravity */ } if ( web.gravflag && !(get_fattr(f_id) & NONCONTENT) ) { b_id = get_facet_body(f_id); if ( valid_id(b_id) ) gdensity += get_body_density(b_id)*web.grav_const; b_id = get_facet_body(facet_inverse(f_id)); if ( valid_id(b_id) ) gdensity -= get_body_density(b_id)*web.grav_const; } for ( i = 0 ; i < gauss2D_num ; i++ ) /* integration point number */ { /* calculate tangents and normals */ mat_mult(gpolypartial[i],x,tang,web.dimension,ctrl_num,SDIM); cross_prod(tang[0],tang[1],normal); norm = sqrt(SDIM_dot(normal,normal)); for ( n = 0 ; n < 2 ; n++ ) /* parameter number */ cross_prod(normal,tang[n],summer[n]); area += density*gauss2Dwt[i]/2*norm; for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) forces[k][j] -= density*gauss2Dwt[i]/2/norm* (gpolypartial[i][1][k]*summer[0][j] - gpolypartial[i][0][k]*summer[1][j]); /* add gravitational force */ if ( web.gravflag && !(get_fattr(f_id) & NONCONTENT) ) { z = 0.0; for ( j = 0 ; j < FACET_CTRL ; j++ ) z += gpoly[i][j]*x[j][2]; for ( j = 0 ; j < FACET_CTRL ; j++ ) { forces[j][0] -= 0.5*z*z*gauss2Dwt[i]/2*gdensity *(gpolypartial[i][0][j]*tang[1][1] - tang[0][1]*gpolypartial[i][1][j]); forces[j][1] -= 0.5*z*z*gauss2Dwt[i]/2*gdensity *(tang[0][0]*gpolypartial[i][1][j] - gpolypartial[i][0][j]*tang[1][0]); forces[j][2] -= z*gpoly[i][j]*gauss2Dwt[i]/2*gdensity *(tang[0][0]*tang[1][1] - tang[0][1]*tang[1][0]); } } } cumforces: /* add to totals, unwrapping if necessary */ for ( i = 0 ; i < FACET_CTRL ; i++ ) /* vertex loop */ { REAL *f; REAL wforce[MAXCOORD]; /* unwrapped forces */ f= get_force(v_id[i]); if ( web.symmetry_flag ) { (*sym_form_pullback)(get_coord(v_id[i]),wforce,forces[i],wraps[i]); for ( j = 0 ; j < SDIM ; j++ ) f[j] += wforce[j]; } else for ( j = 0 ; j < SDIM ; j++ ) f[j] += forces[i][j]; } set_facet_area(f_id,area); } /* end facet_force_q() */ /********************************************************************* * * Function: facet_energy_q() * * Purpose: Returns energy due to facet. Quadratic version. */ void facet_energy_q(f_id,mode) facet_id f_id; int mode; /* AREA_ONLY or ALL_ENERGY */ { REAL energy = 0.0; body_id b_id; MAT2D(x,FACET_CTRL,MAXCOORD); vertex_id v_id[FACET_CTRL]; REAL normal[MAXCOORD]; int i,j; REAL norm; facetedge_id fe_id; REAL u = 0.0; /* gravitational integral */ REAL z; WRAPTYPE wraps[FACET_CTRL]; MAT2D(tang,MAXCOORD,MAXCOORD); /* get control points */ get_facet_verts(f_id,x,wraps); if ( web.metric_flag ) { energy = simplex_energy_metric(v_id,x); goto skip_from_metric; } for ( i = 0 ; i < gauss2D_num ; i++ ) /* integration point number */ { /* calculate tangents and normals */ mat_mult(gpolypartial[i],x,tang,web.dimension,ctrl_num,SDIM); cross_prod(tang[0],tang[1],normal); norm = sqrt(SDIM_dot(normal,normal)); energy += gauss2Dwt[i]*norm/2; if ( web.gravflag && !(get_fattr(f_id) & NONCONTENT) ) { z = 0.0; for ( j = 0 ; j < FACET_CTRL ; j++ ) z += gpoly[i][j]*x[j][2]; u += 0.5*z*z*gauss2Dwt[i]/2*(tang[0][0]*tang[1][1] - tang[0][1]*tang[1][0]); } } skip_from_metric: set_facet_area(f_id,energy); if ( mode == AREA_ONLY ) return; binary_tree_add(web.total_area_addends,energy); /* apportion area to vertices and midpoints to scale motion */ /* Note apportionment ratios depend on equivalent test */ /* function used to get curvature; here linear used */ { fe_id = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++, fe_id = get_next_edge(fe_id) ) { vertex_id vv_id; edge_id e_id = get_fe_edge(fe_id); vv_id = get_edge_headv(e_id); add_vertex_star(vv_id,energy); vv_id = get_edge_midv(e_id); add_vertex_star(vv_id,energy); } } /* calculate surface energy */ energy *= get_facet_density(f_id); /* add gravitational energy, vector potential z*z/2*k */ if ( web.gravflag && !(get_fattr(f_id) & NONCONTENT) ) { b_id = get_facet_body(f_id); if ( valid_id(b_id) ) energy += u*get_body_density(b_id)*web.grav_const; b_id = get_facet_body(facet_inverse(f_id)); if ( valid_id(b_id) ) energy -= u*get_body_density(b_id)*web.grav_const; } web.total_energy = web.total_energy + energy; } /* end facet_energy_q() */ /********************************************************************** * * Function: facet_volume_q() * * Purpose: Find triangle's contribution to volumes of neighboring bodies. */ void facet_volume_q(f_id) facet_id f_id; { body_id b_id0,b_id1; REAL vol = 0.0; REAL *x[FACET_CTRL]; vertex_id v_id[FACET_CTRL]; int i,j,k; facetedge_id fe_id; if ( get_fattr(f_id) & NONCONTENT ) return; b_id0 = get_facet_body(f_id); b_id1 = get_facet_body(facet_inverse(f_id)); if ( !valid_id(b_id0) && !valid_id(b_id1) ) return; if ( web.symmetric_content && !everything_quantities_flag ) kb_error(1038, "Do convert_to_quantities for symmetric content for quadratic model.\n", RECOVERABLE); /* get control points */ fe_id = get_facet_fe(f_id); for ( i = 0, j = 0 ; i < FACET_EDGES ; i++ ) { v_id[j++] = get_fe_tailv(fe_id); v_id[j++] = get_fe_midv(fe_id); fe_id = get_next_edge(fe_id); } for ( j = 0 ; j < FACET_CTRL ; j++ ) x[j] = get_coord(v_id[j]); /* volume, integral of z dx dy */ for ( i = 0 ; i < FACET_CTRL ; i++ ) for ( j = 0 ; j < FACET_CTRL ; j++ ) for ( k = 0 ; k < FACET_CTRL ; k++ ) vol += vcoeff[i][j][k]*x[i][2]*(x[j][0]*x[k][1]-x[j][1]*x[k][0]); /* add to body volumes */ if ( valid_id(b_id0) ) add_body_volume(b_id0,vol); if ( valid_id(b_id1) ) add_body_volume(b_id1,-vol); } /* end facet_volume_q() */ /**************************************************************** * * Function: film_grad_q() * * Purpose: Calculate volume gradients in SOAPFILM quadratic * model. */ void film_grad_q() { body_id bi_id; /* identifier for body i */ body_id bj_id; /* identifier for body j */ facetedge_id fe_id; facet_id f_id; REAL g[MAXCOORD]; vertex_id v_id[FACET_CTRL]; REAL *x[FACET_CTRL]; volgrad *vgptr; int i,j,k,n; FOR_ALL_FACETS(f_id) { if ( get_fattr(f_id) & NONCONTENT ) continue; bi_id = get_facet_body(f_id); bj_id = get_facet_body(facet_inverse(f_id)); /* get control points */ fe_id = get_facet_fe(f_id); for ( i = 0, j = 0 ; i < FACET_EDGES ; i++ ) { v_id[j++] = get_fe_tailv(fe_id); v_id[j++] = get_fe_midv(fe_id); fe_id = get_next_edge(fe_id); } for ( j = 0 ; j < FACET_CTRL ; j++ ) x[j] = get_coord(v_id[j]); for ( i = 0 ; i < FACET_CTRL ; i++ ) { g[0] = g[1] = g[2] = 0.0; for ( j = 0 ; j < FACET_CTRL ; j++ ) for ( k = 0 ; k < FACET_CTRL ; k++ ) { g[0] += vcoeff[k][i][j]*x[j][1]*x[k][2] - vcoeff[j][k][i]*x[j][2]*x[k][1]; g[1] += vcoeff[j][k][i]*x[j][2]*x[k][0] - vcoeff[k][i][j]*x[j][0]*x[k][2]; g[2] += vcoeff[i][j][k]*(x[j][0]*x[k][1] - x[j][1]*x[k][0]); } if ( valid_id(bi_id) && (get_battr(bi_id) & (PRESSURE|FIXEDVOL)) ) { vgptr = get_bv_new_vgrad(get_body_fixnum(bi_id),v_id[i]); vgptr->bb_id = bi_id; for ( n = 0 ; n < SDIM ; n++ ) vgptr->grad[n] += g[n]; } if ( valid_id(bj_id) && (get_battr(bj_id) & (FIXEDVOL|PRESSURE)) ) { vgptr = get_bv_new_vgrad(get_body_fixnum(bj_id),v_id[i]); vgptr->bb_id = bj_id; for ( n = 0 ; n < SDIM ; n++ ) vgptr->grad[n] -= g[n]; } } } } /* end film_grad_q() */ /************************************************************************* * * Function: triangle_integral() * * Purpose: Used by vcoeff_init() to calculate volume coefficients. * */ REAL triangle_integral ARGS(( REAL(*)(REAL,REAL))); REAL triangle_integral(f) /* on side 2 triangle, 5th degree */ #ifdef NOPROTO REAL (*f)(); #else REAL (*f)(REAL,REAL); #endif { int i; REAL sum = 0.0; for ( i = 0 ; i < 7 ; i++ ) sum += gauss2Dwt5[i]*(*f)(2*gauss2Dpt5[i][0],2*gauss2Dpt5[i][1]); return 2*sum; } /* Interpolation polynomials for 6 point interpolation on triangle. Triangle is: u > 0, v > 0, u + v < 2. Point indexing (counterclockwise): 4 5 3 0 1 2 */ /********************************************************************** * * Function: intpoly6() * * Purpose: interpolation polynomial evaluation */ REAL intpoly6(k,u,v) int k; /* polynomial number */ REAL u,v; /* evaluation point */ { switch ( k ) { case 0: return (1 - (u + v))*(2 - (u + v))/2.0; case 1: return u*(2 - (u + v)); case 2: return u*(u - 1)/2.0; case 3: return u*v; case 4: return v*(v - 1)/2.0; case 5: return v*(2 - (u + v)); } /* bad index */ return 0.0; } /************************************************************************ * * Function: inpoly6part() * * Purpose: partials of interpolation polynomials */ REAL intpoly6part(k,i,u,v) int k; /* which polynomial (control point index) */ int i; /* partial number: 0 for u, 1 for v */ REAL u,v; /* evaluation point */ { if ( i == 0 ) switch ( k ) { case 0: return u + v - 1.5; case 1: return 2 - 2*u - v; case 2: return u - 0.5; case 3: return v; case 4: return 0.0; case 5: return -v; } else if ( i == 1 ) switch ( k ) { case 0: return u + v - 1.5; case 1: return -u; case 2: return 0.0; case 3: return u; case 4: return v - 0.5; case 5: return 2 - u - 2*v; } /* bad index */ return 0.0; } /********************************************************************** * * Function: vcoeff_init() * * Purpose: initializing coefficients for quadratic volume calculations. */ /* integrand function */ static int al,be,ga; REAL vintzf(u,v) REAL u,v; { return intpoly6(al,u,v)*intpoly6part(be,0,u,v)*intpoly6part(ga,1,u,v); } void vcoeff_init() { REAL t; for ( al = 0 ; al < FACET_CTRL ; al++ ) for ( be = 0; be < FACET_CTRL ; be++ ) for ( ga = 0 ; ga < FACET_CTRL ; ga++ ) { t = triangle_integral(vintzf); /* round to nearest 90th */ t = (int)(t*90.001)/90.0; vcoeff[al][be][ga] = t; } } /********************************************************************* ********************************************************************** Quadratic film quantities ********************************************************************** **********************************************************************/ /********************************************************************* Film area quantity Uses Gaussian integration. Not known to be upper bound on area. **********************************************************************/ /********************************************************************* * * Function: q_facet_tension_q() * * Purpose: Returns energy due to facet. * Quadratic version. */ REAL q_facet_tension_q(f_info) struct qinfo *f_info; { REAL value = 0.0; int m; REAL det; REAL st,ss,tt; REAL **tang; for ( m = 0 ; m < gauss2D_num ; m++ ) /* integration point number */ { tang = f_info->sides[m]; ss = SDIM_dot(tang[0],tang[0]); st = SDIM_dot(tang[0],tang[1]); tt = SDIM_dot(tang[1],tang[1]); det = ss*tt - st*st; value += gauss2Dwt[m]*sqrt(det); } value /= 2; /* triangle factor */ if ( quantities_only_flag ) { set_facet_area(f_info->id,value); #ifdef SHARED_MEMORY if ( nprocs > 1 ) proc_total_area[GET_THREAD_ID] += value; else #endif binary_tree_add(web.total_area_addends,value); } if ( METH_INSTANCE(f_info->method)->flags & USE_DENSITY ) value *= get_facet_density(f_info->id); return value; } /* end q_facet_tension_q() */ /********************************************************************* * * Function: q_facet_tension_grad_q() * * Returns gradient and energy due to facet. * Quadratic version. */ REAL q_facet_tension_q_grad(f_info) struct qinfo *f_info; { REAL value = 0.0; int m,j,k; REAL det,norm; REAL st,ss,tt; REAL density,fudge; if ( METH_INSTANCE(f_info->method)->flags & USE_DENSITY ) density = get_facet_density(f_info->id); else density = 1.0; for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[k][j] = 0.0; for ( m = 0 ; m < gauss2D_num ; m++ ) /* integration point number */ { REAL **tang = f_info->sides[m]; /* calculate tangents and det */ ss = SDIM_dot(tang[0],tang[0]); st = SDIM_dot(tang[0],tang[1]); tt = SDIM_dot(tang[1],tang[1]); det = ss*tt - st*st; norm = sqrt(det); value += gauss2Dwt[m]*norm/2; /* gradients */ fudge = density*gauss2Dwt[m]/norm/2; for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[k][j] += fudge* ( tang[0][j]*gpolypartial[m][0][k]*tt - tang[0][j]*gpolypartial[m][1][k]*st - tang[1][j]*gpolypartial[m][0][k]*st + tang[1][j]*gpolypartial[m][1][k]*ss); } return density*value; } /* end q_facet_tnesion_grad_q() */ /********************************************************************* * * Function: q_facet_tension_q_hess() * * Purpose: Returns hessian, gradient and energy due to facet. * Quadratic version. */ REAL q_facet_tension_q_hess(f_info) struct qinfo *f_info; { REAL value = 0.0; int m,j,jj,k,kk,p,q; REAL det,norm; REAL st,ss,tt; REAL tr[FACET_CTRL][MAXCOORD]; /* traces */ MAT2D(at,2,2); MAT2D(ainv,2,2); MAT4D(aa,FACET_CTRL,MAXCOORD,2,2); REAL density,fudge; if ( METH_INSTANCE(f_info->method)->flags & USE_DENSITY ) density = get_facet_density(f_info->id); else density = 1.0; for ( m = 0 ; m < gauss2D_num ; m++ ) /* integration point number */ { REAL **tang = f_info->sides[m]; /* calculate tangents and det */ ss = SDIM_dot(tang[0],tang[0]); st = SDIM_dot(tang[0],tang[1]); tt = SDIM_dot(tang[1],tang[1]); det = ss*tt - st*st; if ( det <= 0.0 ) continue; norm = sqrt(det); value += gauss2Dwt[m]*norm/2; ainv[0][0] = tt/det; ainv[0][1] = ainv[1][0] = -st/det; ainv[1][1] = ss/det; /* gradients */ fudge = density*gauss2Dwt[m]*norm/4; for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) { for ( p = 0 ; p < 2 ; p++ ) for ( q = 0 ; q < 2; q++ ) at[p][q] = tang[p][j]*gpolypartial[m][q][k] + tang[q][j]*gpolypartial[m][p][k]; mat_mult(at,ainv,aa[k][j],2,2,2); tr[k][j] = aa[k][j][0][0] + aa[k][j][1][1]; f_info->grad[k][j] += fudge*tr[k][j]; } /* hessians */ for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( kk = 0 ; kk < FACET_CTRL ; kk++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) f_info->hess[k][kk][j][jj] += fudge* ( 0.5*tr[k][j]*tr[kk][jj] - (aa[k][j][0][0]*aa[kk][jj][0][0] + aa[k][j][0][1]*aa[kk][jj][1][0] + aa[k][j][1][0]*aa[kk][jj][0][1] + aa[k][j][1][1]*aa[kk][jj][1][1]) + ( (j==jj)? 2*(gpolypartial[m][0][k]*gpolypartial[m][0][kk]*ainv[0][0] + gpolypartial[m][0][k]*gpolypartial[m][1][kk]*ainv[0][1] + gpolypartial[m][1][k]*gpolypartial[m][0][kk]*ainv[1][0] + gpolypartial[m][1][k]*gpolypartial[m][1][kk]*ainv[1][1]) : 0.0) ); } /* end gauss pt loop */ return density*value; } /* end q_facet_tension_q_hess() */ /********************************************************************* Upper bound on facet quadratic area Good way is to do sqrt(gaussian int of det) which gives upper bound by Cauchy-Schwarz inequality for 7-pt integration, since det is order 4 polynomial. ***********************************************************************/ /********************************************************************* * * Returns energy due to facet. * Quadratic version. */ REAL q_facet_tension_uq(f_info) struct qinfo *f_info; { REAL value = 0.0; int m; REAL det; REAL st,ss,tt; for ( m = 0 ; m < gauss2D_num ; m++ ) /* integration point number */ { REAL **tang = f_info->sides[m]; ss = SDIM_dot(tang[0],tang[0]); st = SDIM_dot(tang[0],tang[1]); tt = SDIM_dot(tang[1],tang[1]); det = ss*tt - st*st; value += gauss2Dwt[m]*det; } value = sqrt(value)/2; if ( quantities_only_flag ) { set_facet_area(f_info->id,value); #ifdef SHARED_MEMORY if ( nprocs > 1 ) proc_total_area[GET_THREAD_ID] += value; else #endif binary_tree_add(web.total_area_addends,value); } if ( METH_INSTANCE(f_info->method)->flags & USE_DENSITY ) value *= get_facet_density(f_info->id); return value; } /********************************************************************* * * Returns gradient and energy due to facet. * Quadratic version. */ REAL q_facet_tension_uq_grad(f_info) struct qinfo *f_info; { REAL value = 0.0; int m,j,k; REAL det; REAL norm; REAL st,ss,tt; REAL detgrad[FACET_CTRL][MAXCOORD]; REAL density = 1.0; if ( METH_INSTANCE(f_info->method)->flags & USE_DENSITY ) density = get_facet_density(f_info->id); for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) detgrad[k][j] = 0.0; for ( m = 0 ; m < gauss2D_num ; m++ ) /* integration point number */ { REAL **tang = f_info->sides[m]; /* calculate tangents and det */ ss = SDIM_dot(tang[0],tang[0]); st = SDIM_dot(tang[0],tang[1]); tt = SDIM_dot(tang[1],tang[1]); det = ss*tt - st*st; value += gauss2Dwt[m]*det; /* gradients */ for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) detgrad[k][j] += gauss2Dwt[m]* ( tang[0][j]*gpolypartial[m][0][k]*tt - tang[0][j]*gpolypartial[m][1][k]*st - tang[1][j]*gpolypartial[m][0][k]*st + tang[1][j]*gpolypartial[m][1][k]*ss); } norm = density/sqrt(value*4); for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[k][j] = norm*detgrad[k][j]; return density*sqrt(value)/2; } /********************************************************************* * * Returns hessian, gradient and energy due to facet. * Quadratic version. */ REAL q_facet_tension_uq_hess(f_info) struct qinfo *f_info; { REAL value = 0.0; int m,j,jj,k,kk,r,q; REAL det,norm; REAL st,ss,tt; MAT2D(at,2,2); MAT2D(ainv,2,2); MAT4D(aa,FACET_CTRL,MAXCOORD,2,2); REAL detgrad[FACET_CTRL][MAXCOORD]; REAL dethess[FACET_CTRL][FACET_CTRL][MAXCOORD][MAXCOORD]; REAL density = 1.0; if ( METH_INSTANCE(f_info->method)->flags & USE_DENSITY ) density = get_facet_density(f_info->id); for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) detgrad[k][j] = 0.0; for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( kk = 0 ; kk < FACET_CTRL ; kk++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) dethess[k][kk][j][jj] = 0.0; for ( m = 0 ; m < gauss2D_num ; m++ ) /* integration point number */ { REAL **p = gpolypartial[m]; REAL **tang = f_info->sides[m]; /* calculate tangents and det */ ss = SDIM_dot(tang[0],tang[0]); st = SDIM_dot(tang[0],tang[1]); tt = SDIM_dot(tang[1],tang[1]); det = ss*tt - st*st; value += gauss2Dwt[m]*det; ainv[0][0] = tt/det; ainv[0][1] = ainv[1][0] = -st/det; ainv[1][1] = ss/det; /* gradients */ for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) { for ( r = 0 ; r < 2 ; r++ ) for ( q = 0 ; q < 2; q++ ) at[r][q] = tang[r][j]*gpolypartial[m][q][k] + tang[q][j]*gpolypartial[m][r][k]; mat_mult(at,ainv,aa[k][j],2,2,2); detgrad[k][j] += gauss2Dwt[m] *(p[0][k]*tang[0][j]*tt + ss*p[1][k]*tang[1][j] - st*(p[0][k]*tang[1][j] + tang[0][j]*p[1][k])); } /* hessians */ for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( kk = 0 ; kk < FACET_CTRL ; kk++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) { dethess[k][kk][j][jj] += gauss2Dwt[m]* ( 4*p[0][k]*tang[0][j]*tang[1][jj]*p[1][kk] + 4*tang[0][jj]*p[0][kk]*p[1][k]*tang[1][j] - 2*(p[0][kk]*tang[1][jj]+tang[0][jj]*p[1][kk]) *(p[0][k]*tang[1][j]+tang[0][j]*p[1][k])); if ( j == jj ) dethess[k][kk][j][jj] += gauss2Dwt[m]* ( 2*p[0][k]*p[0][kk]*tt + 2*ss*p[1][k]*p[1][kk] - 2*st*(p[0][k]*p[1][kk] + p[0][kk]*p[1][k]) ); } } /* end gauss pt loop */ /* gradients */ norm = density/sqrt(value*4); for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[k][j] = norm*detgrad[k][j]; /* hessians */ for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( kk = 0 ; kk < FACET_CTRL ; kk++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) f_info->hess[k][kk][j][jj] = norm* (0.5*dethess[k][kk][j][jj] - detgrad[k][j]*detgrad[kk][jj]/value); return density*sqrt(value)/2; } /********************************************************************** Quadratic volume quantity **********************************************************************/ /********************************************************************** * * function: q_facet_volume_q() * * purpose: value of volume integral on quadratic facet */ REAL q_facet_volume_q(f_info) struct qinfo *f_info; { REAL vol = 0.0; int m,i; /* volume, integral of z dx dy */ for ( m = 0 ; m < gauss2D_num ; m++ ) { REAL **t = f_info->sides[m]; REAL z; for ( z = 0., i = 0 ; i < FACET_CTRL ; i++ ) z += gpoly[m][i]*f_info->x[i][2]; vol += gauss2Dwt[m]*z*(t[0][0]*t[1][1]-t[0][1]*t[1][0]); } return vol/2; } /* end q_facet_volume_q() */ /********************************************************************** * * function: q_facet_volume_q_grad() * * purpose: value and gradient of volume integral on quadratic facet */ REAL q_facet_volume_q_grad(f_info) struct qinfo *f_info; { REAL vol = 0.0; int i,j,k; REAL **x = f_info->x; REAL v; /* volume, integral of z dx dy */ for ( i = 0 ; i < FACET_CTRL ; i++ ) for ( j = 0 ; j < FACET_CTRL ; j++ ) for ( k = 0 ; k < FACET_CTRL ; k++ ) { v = vcoeff[i][j][k]; if ( v == 0.0 ) continue; vol += v*x[i][2]*(x[j][0]*x[k][1]-x[j][1]*x[k][0]); } /* gradients */ for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[k][j] = 0.0; for ( i = 0 ; i < FACET_CTRL ; i++ ) for ( j = 0 ; j < FACET_CTRL ; j++ ) for ( k = 0 ; k < FACET_CTRL ; k++ ) { v = vcoeff[i][j][k]; if ( v == 0.0 ) continue; f_info->grad[i][2] += v*(x[j][0]*x[k][1]-x[j][1]*x[k][0]); f_info->grad[j][0] += v*x[i][2]*x[k][1]; f_info->grad[k][1] += v*x[i][2]*x[j][0]; f_info->grad[j][1] -= v*x[i][2]*x[k][0]; f_info->grad[k][0] -= v*x[i][2]*x[j][1]; } return vol; } /* end q_facet_volume_grad_q() */ /********************************************************************** * * function: q_facet_volume_q_hess() * * purpose: hessian, value, and gradof volume integral on quadratic facet */ REAL q_facet_volume_q_hess(f_info) struct qinfo *f_info; { REAL vol = 0.0; int i,j,k; REAL v; REAL **x; REAL ****h; x = f_info->x; /* volume, integral of z dx dy */ for ( i = 0 ; i < FACET_CTRL ; i++ ) for ( j = 0 ; j < FACET_CTRL ; j++ ) for ( k = 0 ; k < FACET_CTRL ; k++ ) vol += vcoeff[i][j][k]*x[i][2]*(x[j][0]*x[k][1]-x[j][1]*x[k][0]); /* gradients */ for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[k][j] = 0.0; for ( i = 0 ; i < FACET_CTRL ; i++ ) for ( j = 0 ; j < FACET_CTRL ; j++ ) for ( k = 0 ; k < FACET_CTRL ; k++ ) { v = vcoeff[i][j][k]; f_info->grad[i][2] += v*(x[j][0]*x[k][1]-x[j][1]*x[k][0]); f_info->grad[j][0] += v*x[i][2]*x[k][1]; f_info->grad[k][1] += v*x[i][2]*x[j][0]; f_info->grad[j][1] -= v*x[i][2]*x[k][0]; f_info->grad[k][0] -= v*x[i][2]*x[j][1]; } /* hessian */ h = f_info->hess; for ( i = 0 ; i < FACET_CTRL ; i++ ) for ( j = 0 ; j < FACET_CTRL ; j++ ) for ( k = 0 ; k < FACET_CTRL ; k++ ) { v = vcoeff[i][j][k]; h[i][j][2][0] += v*x[k][1]; h[j][i][0][2] += v*x[k][1]; h[i][k][2][1] += v*x[j][0]; h[k][i][1][2] += v*x[j][0]; h[j][k][0][1] += v*x[i][2]; h[k][j][1][0] += v*x[i][2]; h[i][j][2][1] -= v*x[k][0]; h[j][i][1][2] -= v*x[k][0]; h[i][k][2][0] -= v*x[j][1]; h[k][i][0][2] -= v*x[j][1]; h[j][k][1][0] -= v*x[i][2]; h[k][j][0][1] -= v*x[i][2]; } return vol; } /* end q_facet_volume_q_hess() */ evolver-2.30c.dfsg/src/psgraph.c0000644000175300017530000006543711410765113017044 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /***************************************************************** * * File: psgraph.c * * Purpose: PostScript output. */ /******************************************************************* * * Notes on usage. * * The depth sort is slowed down by having long unrefined edges. * Example: 245760 facets only: 102 sec (starfish31.fe) * with long bare edges: 2151 sec * refined bare edges: 175 sec */ #include "include.h" static FILE *fd; int gridflag; int ps_colorflag; int crossingflag; char ps_file_name[1000]; int ps_widthattr; /* for redrawing edges on top of subsequent facets */ int edgeredrawflag; /* for extra drawing of edges on top of facets */ int *edgeredrawhead; int edgeredrawcount; int edgeredrawmax; struct erl {double x[2]; /* edge vector */ float v[2]; /* vertex position */ int color; int next; int etype; }; struct erl *edgeredrawlist; /***************************************************************** * * Function: ps_init() * * Purpose: Get file name from user, write file header. */ void ps_init() { char response[100]; int xlo,xhi,ylo,yhi; /* int according to PostScript manual */ REAL extra; time_t t; REAL xsize,ysize,scale; /* for clipping box */ int dummy; int n; if ( (web.representation != STRING) && (gridflag < 0) ) { prompt("Show grid lines? ",response,sizeof(response)); gridflag = (toupper(response[0]) == 'Y'); edgeredrawflag = !gridflag; /* default */ if (toupper(response[1]) == 'Y') edgeredrawflag = 1; if (toupper(response[1]) == 'N') edgeredrawflag = 0; } if ( ps_colorflag < 0 ) { prompt("Do colors? ",response,sizeof(response)); ps_colorflag = (toupper(response[0]) == 'Y'); } if ( (web.representation == STRING) && (SDIM > 2) && (crossingflag < 0) ) { prompt("Do crossings? ",response,sizeof(response)); crossingflag = (toupper(response[0]) == 'Y'); } if ( labelflag < 0 ) { prompt("Do labels? (i for ids, o for originals) ",response,sizeof(response)); switch ( toupper(response[0]) ) { case 'I' : case 'Y' : labelflag = LABEL_ID; break; case 'O' : labelflag = LABEL_ORIG; break; default : labelflag = NOLABELS; } } if ( strlen(ps_file_name) == 0 ) { prompt("Enter file name (.ps will be added): ",ps_file_name,sizeof(ps_file_name)); } if ( (strcmp(ps_file_name+strlen(ps_file_name)-3,".ps")!=0) && (strcmp(ps_file_name+strlen(ps_file_name)-4,".eps")!=0) ) strcat(ps_file_name,".ps"); fd = fopen(ps_file_name,"w"); if ( fd == NULL ) { sprintf(errmsg,"Cannot open %s.\n",ps_file_name); ps_file_name[0] = 0; kb_error(1649,errmsg,RECOVERABLE); } edgeredrawcount = 1; edgeredrawhead = (int*)temp_calloc(web.skel[VERTEX].max_ord+5,sizeof(int)); edgeredrawmax = web.skel[EDGE].count; edgeredrawlist = (struct erl *)temp_calloc(edgeredrawmax,sizeof(struct erl)); ps_widthattr = find_extra(PS_WIDTHNAME,&dummy); fputs("%!PS-Adobe-3.0 EPSF-3.0\n",fd); /* bounding box, with a little extra for line widths and labels */ extra = (labelflag>0) ? .066 : .002; xsize = maxclipx - minclipx; ysize = maxclipy - minclipy; if ( xsize/ysize > 8/10.5 ) scale = 8*72/xsize; else scale = 10.5*72/ysize; if ( full_bounding_box_flag ) { xlo = 0; ylo = 0; xhi = (int)(xsize*scale); yhi = (int)(ysize*scale); } else /* as defined by surface */ { xlo = (int)floor((bbox_minx - extra - minclipx)*scale); if ( xlo < 0 ) xlo = 0; xhi = (int)ceil((bbox_maxx + extra - minclipx)*scale); if ( xhi > xsize*scale ) xhi = (int)(xsize*scale); ylo = (int)floor((bbox_miny - extra - minclipy)*scale); if ( ylo < 0 ) ylo = 0; yhi = (int)ceil((bbox_maxy + extra - minclipy )*scale); if ( yhi > ysize*scale ) yhi = (int)(ysize*scale); } fprintf(fd,"%%%%BoundingBox: %d %d %d %d\n",xlo,ylo,xhi,yhi); fprintf(fd,"%%%%Title: (%s)\n",ps_file_name); fprintf(fd,"%%%%Creator: Surface Evolver\n"); time(&t); fprintf(fd,"%%%%CreationDate: %s\n",asctime(localtime(&t))); fprintf(fd,"%%%%EndComments\n"); fprintf(fd,"%% Image is in %g\" x %g\" box aligned lower left on paper.\n", (DOUBLE)(xhi-xlo)/72.,(DOUBLE)(yhi-ylo)/72.); fputs("% Change relsize to alter relative size of labels and linewidths.\n",fd); fprintf(fd,"/relsize %f def\n",ps_labelsize); fprintf(fd,"%f %f scale\n",(DOUBLE)scale,(DOUBLE)scale); fprintf(fd,"%f %f translate\n",(DOUBLE)(-minclipx),(DOUBLE)(-minclipy)); fprintf(fd,"newpath %f %f moveto %f %f lineto %f %f \n", (DOUBLE)minclipx,(DOUBLE)minclipy,(DOUBLE)maxclipx,(DOUBLE)minclipy, (DOUBLE)maxclipx,(DOUBLE)maxclipy); fprintf(fd," lineto %f %f lineto closepath clip\n", (DOUBLE)minclipx,(DOUBLE)maxclipy); fputs("1 setlinecap 1 setlinejoin\n",fd); if ( ps_colorflag ) { fputs("/fa { newpath setrgbcolor moveto lineto\n",fd); fputs(" lineto closepath fill } def % filled facet without edges\n\n",fd); fputs("/fb {setrgbcolor 6 copy newpath moveto lineto\n",fd); fputs(" lineto closepath fill 6 2 roll 6 copy} def % filled facet with edges\n\n",fd); fputs("/fc {setrgbcolor .001 relsize mul setlinewidth 6 2 roll 6 copy} def % outline only\n\n",fd); fputs("/edge {relsize mul setlinewidth setrgbcolor newpath moveto lineto stroke} def\n",fd); fputs("/arcedge {relsize mul setlinewidth setrgbcolor newpath moveto arc stroke} def\n",fd); } else { fputs("/fa { newpath setgray moveto lineto\n",fd); fputs(" lineto closepath fill } def % filled facet without edges \n\n",fd); fputs("/fb {setgray 6 copy newpath moveto lineto\n",fd); fputs(" lineto closepath fill 6 2 roll 6 copy} def % filled facet with edges\n\n",fd); fputs("/fc {setgray 0 setgray .001 relsize mul setlinewidth 6 2 roll 6 copy} def % outline only\n\n",fd); fputs("/edge {relsize mul setlinewidth 0 setgray newpath moveto lineto stroke} def\n",fd); fputs("/arcedge {relsize mul setlinewidth 0 setgray newpath moveto arc stroke} def\n",fd); } if ( labelflag>0 ) { fputs("/vorad .022 relsize mul def\n",fd); fputs("/arrowrad vorad def\n",fd); if ( ps_colorflag ) fputs("/edge { relsize mul setlinewidth setrgbcolor 4 copy newpath",fd); else fputs("/edge { relsize mul setlinewidth 0 setgray 4 copy newpath",fd); fputs(" moveto lineto stroke\n",fd); fputs(" /y2 exch def /x2 exch def /y1 exch def /x1 exch def\n",fd); fputs(" /dy y2 y1 sub def /dx x2 x1 sub def\n",fd); fputs(" /mag dx dx mul dy dy mul add sqrt def\n",fd); fputs(" mag 0 ne { \n",fd); fputs(" /xu dx mag div def /yu dy mag div def\n",fd); fputs(" /angle xu yu neg atan def\n",fd); fputs(" newpath x2 xu vorad mul sub y2 yu vorad mul sub moveto\n",fd); fputs(" x2 xu vorad mul sub arrowrad yu mul add\n",fd); fputs(" y2 yu vorad mul sub arrowrad xu mul sub\n",fd); fputs(" arrowrad angle angle 45 add arc\n",fd); fputs(" stroke\n",fd); fputs(" newpath x2 xu vorad mul sub y2 yu vorad mul sub moveto\n",fd); fputs(" x2 xu vorad mul sub arrowrad yu mul sub\n",fd); fputs(" y2 yu vorad mul sub arrowrad xu mul add\n",fd); fputs(" arrowrad angle 180 sub angle 225 sub arcn\n",fd); fputs(" stroke\n",fd); fputs(" } if \n",fd); fputs(" } def\n",fd); fputs("/Helvetica findfont .03 scalefont setfont\n",fd); for ( n = 1 ; n <= 5 ; n++ ) { fprintf(fd,"/vertex%d { gsave translate relsize relsize scale\n",n); fprintf(fd," newpath 0 setgray 0 0 .022 0 360 arc fill\n"); fprintf(fd," newpath 1 setgray 0 0 .020 0 360 arc fill\n"); if ( n > 2 ) fprintf(fd," 2 %d div 2 %d div scale\n",n,n); fprintf(fd," %6.4f -.01 moveto 0 setgray show grestore } def\n", .004-.01*n); fprintf(fd,"/edgenum%d { gsave translate relsize relsize scale\n",n); fprintf(fd," newpath %5.3f -.016 moveto\n",-(.008+.004*n)); fprintf(fd," 0 setgray %5.3f 0 rlineto 0 .032 rlineto %6.3f 0\n", 0.022+.013*n,-(0.022+0.013*n)); fprintf(fd," rlineto fill newpath %5.3f -.014 moveto\n",-(.0078+.004*n)); fprintf(fd," 1 setgray %5.3f 0 rlineto 0 .03 rlineto %6.3f 0\n", 0.020+.013*n,-(0.020+.013*n)); fprintf(fd, " rlineto fill 0 setgray %6.3f -.01 moveto show grestore } def\n", 0.001-0.006*n); fprintf(fd,"/facenum%d { gsave translate relsize relsize scale\n",n); fputs(" newpath 1 setgray -.02 -.015 moveto\n",fd); fprintf(fd," %5.3f 0 rlineto 0 .03 rlineto %5.3f 0 rlineto fill 0 setgray \n",(n+1)*0.025,-(n+1)*0.025); fputs(" newpath -.019 -.01 moveto show grestore } def \n",fd); } } fprintf(fd,"/ew {%7.5f edge} def %% normal string edge width\n", ps_stringwidth); fprintf(fd,"/fw {%7.5f edge} def %% fixed edge width\n", ps_fixededgewidth); fprintf(fd,"/tw {%7.5f edge} def %% triple edge width\n",ps_tripleedgewidth); fprintf(fd,"/ww {%7.5f edge} def %% edges on walls\n",ps_conedgewidth); fprintf(fd,"/bw {%7.5f edge} def %% bare edge width\n",ps_bareedgewidth); fprintf(fd,"/gw {%7.5f edge} def %% grid edge width\n",ps_gridedgewidth); fprintf(fd,"/no {pop pop pop pop} def %% no edge \n"); if (crossingflag > 0) /* redefine edge and gw */ { if ( ps_colorflag ) fputs("/edge { 8 copy .002 add relsize mul setlinewidth 0 setlinecap 1 setgray pop pop pop\n",fd); else fputs("/edge { 5 copy .002 add relsize mul setlinewidth 0 setlinecap 1 setgray\n",fd); fputs(" newpath moveto lineto stroke setlinewidth 2 setlinecap\n",fd); if ( ps_colorflag ) fputs(" setrgbcolor newpath moveto lineto stroke} def\n",fd); else fputs(" 0 setgray newpath moveto lineto stroke} def\n",fd); fputs("/gw {0.002 edge} def\n",fd); } } /************************************************************ * * Function: ps_edge() * * Purpose: Graphs one edge, already transformed. */ void ps_edge(t) struct tsort *t; { int i; if ( t->color == CLEAR ) return; if ( t->flag & EDGE_ARC ) { REAL w1[MAXCOORD],w2[MAXCOORD],mag1,mag2,w1w2,center[2],radius; REAL angle1,angle2,det; for (i = 0 ; i < SDIM ; i++ ) { w1[i] = t->x[1][i] - t->x[0][i]; w2[i] = t->x[2][i] - t->x[0][i]; } det = w1[0]*w2[1] - w1[1]*w2[0]; mag1 = SDIM_dot(w1,w1); mag2 = SDIM_dot(w2,w2); w1w2 = w1[0]*w2[0] + w1[1]*w2[1]; if ( 4000*det*det <= mag1*mag1*mag2 + mag1*mag2*mag2 - 2*mag1*w1w2*mag2 ) { /* practically straight line */ for ( i = 0 ; i < SDIM ; i++ ) t->x[1][i] = t->x[2][i]; fprintf(fd," %7.4f %7.4f ",(DOUBLE)t->x[0][0],(DOUBLE)t->x[0][1]); fprintf(fd," %7.4f %7.4f ",(DOUBLE)t->x[1][0],(DOUBLE)t->x[1][1]); t->flag &= ~EDGE_ARC; } else { /* circle */ center[0] = t->x[0][0] + 0.5*(w2[1]*mag1 - w1[1]*mag2)/det; center[1] = t->x[0][1] + 0.5*(-w2[0]*mag1 + w1[0]*mag2)/det; radius = sqrt((mag1*mag1*mag2+mag1*mag2*mag2-2*mag1*w1w2*mag2)/4/det/det); angle1 = 180/M_PI*atan2(t->x[0][1]-center[1],t->x[0][0]-center[0]); angle2 = 180/M_PI*atan2(t->x[2][1]-center[1],t->x[2][0]-center[0]); if ( det > 0 ) { fprintf(fd," %7.4f %7.4f %7.4f %7.4f %7.4f %7.4f %7.4f ", center[0],center[1], radius,angle1,angle2,(DOUBLE)t->x[0][0],(DOUBLE)t->x[0][1]); } else { fprintf(fd," %7.4f %7.4f %7.4f %7.4f %7.4f %7.4f %7.4f ", center[0],center[1], radius,angle2,angle1,(DOUBLE)t->x[2][0],(DOUBLE)t->x[2][1]); } } } /* else if ( t->flag & SPHERE_ARC ) { } */ else { fprintf(fd," %7.4f %7.4f ",(DOUBLE)t->x[0][0],(DOUBLE)t->x[0][1]); fprintf(fd," %7.4f %7.4f ",(DOUBLE)t->x[1][0],(DOUBLE)t->x[1][1]); } /* orientation for arrow label */ if ( labelflag && (t->etype[0] & LABEL_REVERSED) ) fprintf(fd," 4 2 roll "); /* color, if wanted */ if ( ps_colorflag > 0 ) for ( i = 0 ; i < 3 ; i++ ) fprintf(fd," %5.3f",(DOUBLE)rgb_colors[t->color][i]); /* width of edge */ if ( t->flag & EDGE_ARC ) { if ( ps_widthattr >= 0 ) fprintf(fd," %5.3f arcedge ",*EREAL(t->f_id,ps_widthattr)); else fprintf(fd," %5.3f arcedge\n",ps_stringwidth); } else { if ( ps_widthattr >= 0 ) fprintf(fd," %5.3f edge ",*EREAL(t->f_id,ps_widthattr)); else if ( web.representation == STRING ) fprintf(fd," ew\n"); else if ( t->etype[0] & BARE_EDGE ) fprintf(fd," bw\n"); else if ( t->etype[0] & FIXED_EDGE ) fprintf(fd," fw\n"); else if ( t->etype[0] & CONSTRAINT_EDGE ) fprintf(fd," ww\n"); else if ( t->etype[0] & BOUNDARY_EDGE ) fprintf(fd," ww\n"); else if ( t->etype[0] & SINGLE_EDGE ) fprintf(fd," ww\n"); else if ( t->etype[0] & TRIPLE_EDGE ) fprintf(fd," tw\n"); else fprintf(fd," gw\n"); /* regular grid interior edge */ } if ( labelflag>0 ) { char *hv=NULL,*tv=NULL,*en=NULL; switch ( labelflag ) { case LABEL_ID: { hv = ELNAME(get_edge_headv(t->f_id)); tv = ELNAME1(get_edge_tailv(t->f_id)); en = ELNAME2(t->f_id); break; } case LABEL_ORIG: { hv = ELNAME(get_original(get_edge_headv(t->f_id))); tv = ELNAME1(get_original(get_edge_tailv(t->f_id))); en = ELNAME2(get_original(t->f_id)); break; } } if ( strlen(tv) > 5 ) tv[5] = 0; if ( strlen(hv) > 5 ) hv[5] = 0; if ( strlen(en) > 5 ) en[5] = 0; /* max 5 chars in labels */ if ( (t->flag & LABEL_TAIL) && (strlen(tv) > 0) ) fprintf(fd," (%s) %f %f vertex%1d\n",tv, (DOUBLE)t->x[0][0],(DOUBLE)t->x[0][1],strlen(tv)); if ( (t->flag & LABEL_HEAD) && (strlen(hv) > 0) ) fprintf(fd," (%s) %f %f vertex%1d\n",hv, (DOUBLE)t->x[1][0],(DOUBLE)t->x[1][1],strlen(hv)); if ( (t->flag & LABEL_EDGE) && (strlen(en) > 0) ) { if ( inverted(t->f_id) ) fprintf(fd," (%s) %f %f edgenum%1d\n",en, (DOUBLE)((3*t->x[0][0]+2*t->x[1][0])/5), (DOUBLE)(3*t->x[0][1]+2*t->x[1][1])/5,strlen(en)); else fprintf(fd," (%s) %f %f edgenum%1d\n",en, (DOUBLE)((2*t->x[0][0]+3*t->x[1][0])/5), (DOUBLE)(2*t->x[0][1]+3*t->x[1][1])/5,strlen(en)); } } } /*************************************************************************** * * Function: ps_facet() * * Purpose: Graphs one facet, already transformed. */ void ps_facet(t) struct tsort *t; { int i,j; char line[200]; char *ptr = line; int do_flag = 0; /* whether to actually print this line */ /* paint facet */ /* if ( (t->color != CLEAR) && (t->color != UNSHOWN) ) */ { REAL gray; REAL denom; denom = sqrt(dotf(t->normal,t->normal,3)); if ( denom == 0.0 ) return; sprintf(ptr," %7.4f %7.4f",(DOUBLE)t->x[0][0],(DOUBLE)t->x[0][1]); ptr+=strlen(ptr); sprintf(ptr," %7.4f %7.4f",(DOUBLE)t->x[1][0],(DOUBLE)t->x[1][1]); ptr+=strlen(ptr); sprintf(ptr," %7.4f %7.4f",(DOUBLE)t->x[2][0],(DOUBLE)t->x[2][1]); ptr+=strlen(ptr); if ( shading_flag ) gray = gray_level(t->normal); else gray = 1.0; if ( ps_colorflag > 0 ) { int c = t->color; if ( facet_rgb_color_attr > 0 ) sprintf(ptr," %5.3f %5.3f %5.3f ",((c>>24)&0xFF)/255.*gray, ((c>>16)&0xFF)/255.*gray, ((c>>8)&0xFF)/255.*gray); else for ( i = 0 ; i < 3 ; i++ ) { sprintf(ptr," %5.3f", (DOUBLE)(rgb_colors[t->color>=0?t->color:0][i]*gray)); ptr+=strlen(ptr); } } else { sprintf(ptr," %f ",(DOUBLE)gray); ptr += strlen(ptr); } } if ( web.hide_flag && (t->color != CLEAR) && (t->color != UNSHOWN) ) { strcat(ptr," fb "); do_flag = 1; } else strcat(ptr," fc "); /* designated edges */ for ( i = 0 ; i < FACET_VERTS ; i++ ) if ( (t->ecolor[i] == CLEAR) || (((gridflag<=0) || (t->color==UNSHOWN)) && ((t->etype[i]&EBITS) == INVISIBLE_EDGE) ) || ((t->etype[i]&EBITS) & SPLITTING_EDGE) ) strcat(ptr," no"); else { int ii; do_flag = 1; /* see if need to reverse edge for arrow labelling */ if ( labelflag && (t->etype[i] & LABEL_REVERSED) ) strcat(line," 4 2 roll "); if ( ps_colorflag > 0 ) { int c = t->ecolor[i]>=0 ? t->ecolor[i] : 0; strcat(ptr,"\n "); ptr += strlen(ptr); if ( edge_rgb_color_attr > 0 ) sprintf(ptr," %5.3f %5.3f %5.3f ",((c>>24)&0xFF)/255., ((c>>16)&0xFF)/255., ((c>>8)&0xFF)/255.); else for ( j = 0 ; j < 3 ; j++ ) { sprintf(ptr," %5.3f",(DOUBLE)rgb_colors[c][j]); ptr += strlen(ptr); } } /* width of edge, in descending order of thickness */ ii = (i==2)?0:(i+1); if ( (t->x[ii][0]-t->x[i][0])*(t->x[ii][0]-t->x[i][0]) + (t->x[ii][1]-t->x[i][1])*(t->x[ii][1]-t->x[i][1]) < 1e-10 ) strcat(line," no"); else if ( (ps_widthattr >= 0) && valid_id(t->f_id) ) { facetedge_id fe = get_facet_fe(t->f_id); for ( j = 0 ; j < i ; j++ ) fe = get_next_edge(fe); sprintf(line+strlen(line)," %7.5f edge ", *EREAL(get_fe_edge(fe),ps_widthattr)); } else if ( t->etype[i] & BARE_EDGE ) strcat(line," bw"); else if ( t->etype[i] & FIXED_EDGE ) strcat(line," fw"); else if ( t->etype[i] & CONSTRAINT_EDGE ) strcat(line," ww"); else if ( t->etype[i] & BOUNDARY_EDGE ) strcat(line," ww"); else if ( t->etype[i] & SINGLE_EDGE ) strcat(line," ww"); else if ( t->etype[i] & TRIPLE_EDGE ) strcat(line," tw"); else strcat(line," gw"); /* regular grid interior edge */ } strcat(line,"\n"); if ( do_flag ) fputs(line,fd); /* draw previous edge fragments */ if ( edgeredrawflag ) { for ( i = 0 ; i < FACET_VERTS ; i++ ) { vertex_id v_id = t->v_id[i]; if ( valid_id(v_id) ) { int erlp = edgeredrawhead[loc_ordinal(v_id)]; for ( ; erlp != 0 ; erlp = edgeredrawlist[erlp].next ) { struct erl *ep = edgeredrawlist + erlp; if ( fabs(t->x[i][0]-ep->v[0])+fabs(t->x[i][1]-ep->v[1]) > .00001 ) continue; if ( ep->x[0]*ep->x[0] + ep->x[1]*ep->x[1] < (labelflag?0.01:1e-10) ) continue; if ( ps_colorflag > 0 ) { int c = ep->color; if ( edge_rgb_color_attr > 0 ) sprintf(line,"%10.6f %10.6f %10.6f %10.6f %5.3f %5.3f %5.3f ", t->x[i][0],t->x[i][1],t->x[i][0]+ep->x[0],t->x[i][1]+ep->x[1], ((c>>24)&0xFF)/255., ((c>>16)&0xFF)/255., ((c>>8)&0xFF)/255.); else sprintf(line,"%10.6f %10.6f %10.6f %10.6f %5.3f %5.3f %5.3f ", t->x[i][0],t->x[i][1],t->x[i][0]+ep->x[0],t->x[i][1]+ep->x[1], (DOUBLE)rgb_colors[c][0],(DOUBLE)rgb_colors[c][1], (DOUBLE)rgb_colors[c][2]); } else sprintf(line,"%10.6f %10.6f %10.6f %10.6f ", t->x[i][0], t->x[i][1],t->x[i][0]+ep->x[0],t->x[i][1]+ep->x[1]); /* see if need to reverse edge for arrow labelling */ if ( labelflag && (ep->etype & LABEL_REVERSED) ) strcat(line," 4 2 roll "); if ( ep->etype & BARE_EDGE ) strcat(line," bw"); else if ( ep->etype & FIXED_EDGE ) strcat(line," fw"); else if ( ep->etype & CONSTRAINT_EDGE ) strcat(line," ww"); else if ( ep->etype & BOUNDARY_EDGE ) strcat(line," ww"); else if ( ep->etype & SINGLE_EDGE ) strcat(line," ww"); else if ( ep->etype & TRIPLE_EDGE ) strcat(line," tw"); else strcat(line," gw"); /* regular grid interior edge */ fputs(line,fd); fputs("\n",fd); } } } } /* save edge end fragments for later redrawing on top of later facets for smoother edge line */ if ( edgeredrawflag ) { for ( i = 0 ; i < FACET_VERTS ; i++ ) { vertex_id v_id; double len,fudge; int ii = i >= FACET_VERTS-1 ? 0 : i+1; if ( (t->ecolor[i] == CLEAR) || (((gridflag<=0) || (t->color==UNSHOWN)) && ((t->etype[i]&EBITS) == INVISIBLE_EDGE) ) || ((t->etype[i]&EBITS) & SPLITTING_EDGE) ) continue; len = sqrt((t->x[ii][0]-t->x[i][0])*(t->x[ii][0]-t->x[i][0]) + (t->x[ii][1]-t->x[i][1])*(t->x[ii][1]-t->x[i][1])); fudge = (len > .005) ? .005/len : 1.0; /* max length .005 */ if ( edgeredrawcount > edgeredrawmax-2 ) { edgeredrawlist = (struct erl *)temp_realloc((char*)edgeredrawlist, 2*edgeredrawmax*sizeof(struct erl)); edgeredrawmax *= 2; } v_id = t->v_id[i]; if ( valid_id(v_id) ) { int didflag = 0,erlp; for ( erlp = edgeredrawhead[loc_ordinal(v_id)] ; erlp ; erlp = edgeredrawlist[erlp].next ) { struct erl *ep = edgeredrawlist + erlp; if ( fabs(t->x[i][0]-ep->v[0])+fabs(t->x[i][1]-ep->v[1]) + fabs(ep->x[0]-fudge*(t->x[ii][0]-t->x[i][0])) + fabs(ep->x[1]-fudge*(t->x[ii][1]-t->x[i][1])) < .0001 ) { didflag = 1; break; } } if ( didflag == 0 ) { edgeredrawlist[edgeredrawcount].x[0] = fudge*(t->x[ii][0]-t->x[i][0]); edgeredrawlist[edgeredrawcount].x[1] = fudge*(t->x[ii][1]-t->x[i][1]); edgeredrawlist[edgeredrawcount].v[0] = t->x[i][0]; edgeredrawlist[edgeredrawcount].v[1] = t->x[i][1]; edgeredrawlist[edgeredrawcount].next = edgeredrawhead[loc_ordinal(v_id)]; edgeredrawlist[edgeredrawcount].etype = t->etype[i]; edgeredrawlist[edgeredrawcount].color = t->ecolor[i]; edgeredrawhead[loc_ordinal(v_id)] = edgeredrawcount; edgeredrawcount++; } } v_id = t->v_id[ii]; if ( valid_id(v_id) ) { int didflag = 0,erlp; for ( erlp = edgeredrawhead[loc_ordinal(v_id)] ; erlp ; erlp = edgeredrawlist[erlp].next ) { struct erl *ep = edgeredrawlist + erlp; if ( fabs(t->x[ii][0]-ep->v[0])+fabs(t->x[ii][1]-ep->v[1]) + fabs(ep->x[0]-fudge*(t->x[i][0]-t->x[ii][0])) + fabs(ep->x[1]-fudge*(t->x[i][1]-t->x[ii][1])) < .0001 ) { didflag = 1; break; } } if ( didflag == 0 ) { edgeredrawlist[edgeredrawcount].x[0] = fudge*(t->x[i][0]-t->x[ii][0]); edgeredrawlist[edgeredrawcount].x[1] = fudge*(t->x[i][1]-t->x[ii][1]); edgeredrawlist[edgeredrawcount].v[0] = t->x[ii][0]; edgeredrawlist[edgeredrawcount].v[1] = t->x[ii][1]; edgeredrawlist[edgeredrawcount].next = edgeredrawhead[loc_ordinal(v_id)]; edgeredrawlist[edgeredrawcount].etype = t->etype[i]; edgeredrawlist[edgeredrawcount].color = t->ecolor[i]; edgeredrawhead[loc_ordinal(v_id)] = edgeredrawcount; edgeredrawcount++; } } } } if ( labelflag>0 ) { facetedge_id fe; char * fn=NULL; switch ( labelflag ) { case LABEL_ID: fn = ELNAME(t->f_id); break; case LABEL_ORIG: fn = ELNAME(get_original(t->f_id)); break; } if ( fn ) fn[5] = 0; if ( (t->color != CLEAR) && (t->color != UNSHOWN) && (t->flag&LABEL_FACET) ) if ( strlen(fn) > 0 ) fprintf(fd," (%c%s) %f %f facenum%d\n", (t->flag&FLIPPED_FACET)?'+':'-',fn, (DOUBLE)(t->x[0][0]+t->x[1][0]+t->x[2][0])/3, (DOUBLE)(t->x[0][1]+t->x[1][1]+t->x[2][1])/3,strlen(fn)); fe = get_facet_fe(t->f_id); if ( valid_id(fe) ) for ( i = 0 ; i < FACET_EDGES ; i++ , fe = get_next_edge(fe) ) { edge_id e_id = get_fe_edge(fe); char *tv=NULL,*hv=NULL; char *en=NULL; REAL a; int ii,jj,kk; if ( t->flag & FLIPPED_FACET ) { kk = 2 - i; ii = (3-i)%3 ; jj = (5-i)%3; } else { kk = i; ii = i; jj = (i+1)%3; } if (t->ecolor[kk] == CLEAR) continue; if ( (gridflag<0) && (((t->etype[kk]&EBITS) == INVISIBLE_EDGE) || (t->etype[kk] & SPLITTING_EDGE))) continue; switch ( labelflag ) { case LABEL_ID: { tv = ELNAME(get_edge_tailv(e_id)); hv = ELNAME1(get_edge_headv(e_id)); en = ELNAME2(e_id); break; } case LABEL_ORIG: { tv = ELNAME(get_original(get_edge_tailv(e_id))); hv = ELNAME1(get_original(get_edge_headv(e_id))); en = ELNAME2(get_original(e_id)); break; } } if ( strlen(tv) > 5 ) tv[5] = 0; if ( strlen(hv) > 5 ) hv[5] = 0; if ( strlen(en) > 5 ) en[5] = 0; /* max 5 chars in labels */ if ( tv && (strlen(tv) > 0) ) fprintf(fd,"(%s) %f %f vertex%1d\n",tv,(DOUBLE)t->x[ii][0], (DOUBLE)t->x[ii][1],strlen(tv)); if ( hv && (strlen(hv) > 0) ) fprintf(fd,"(%s) %f %f vertex%1d\n",hv,(DOUBLE)t->x[jj][0], (DOUBLE)t->x[jj][1],strlen(hv)); a = .35; if ( en && (strlen(en) > 0) ) { if ( inverted(e_id) ) fprintf(fd," (%s) %f %f edgenum%1d\n",en, (DOUBLE)((1-a)*t->x[ii][0]+a*t->x[jj][0]), (DOUBLE)((1-a)*t->x[ii][1]+a*t->x[jj][1]),strlen(en)); else fprintf(fd," (%s) %f %f edgenum%1d\n",en, (DOUBLE)(a*t->x[ii][0]+(1-a)*t->x[jj][0]), (DOUBLE)(a*t->x[ii][1]+(1-a)*t->x[jj][1]),strlen(en)); } } } } /************************************************************* * * Function: ps_finish() * * Purpose: End PostScript output. */ void ps_finish() { int i; /* do text */ fputs("\n/Helvetica findfont .10 scalefont setfont\n",fd); fputs("0 setgray\n",fd); for ( i = 0 ; i < MAXTEXTS ; i++ ) { REAL xspot = -1.4+2.8*text_chunks[i].start_x; REAL yspot = -1.4+2.8*text_chunks[i].start_y; if ( text_chunks[i].text ) { char *c; fprintf(fd,"%f %f moveto (",xspot,yspot); for ( c = text_chunks[i].text ; *c ; c++ ) { if ( *c == '\n' ) { fprintf(fd,") show\n"); yspot -= .10; fprintf(fd,"%f %f moveto (",xspot,yspot); } else fprintf(fd,"%c",*c); } fprintf(fd,") show\n"); } } fputs("\nshowpage\n\n",fd); fputs("%%EOF\n",fd); fclose(fd); ps_file_name[0] = 0; temp_free((char*)edgeredrawhead); temp_free((char*)edgeredrawlist); } evolver-2.30c.dfsg/src/command.yac0000644000175300017530000045232611410765113017345 0ustar hazelscthazelsct /************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************* * * File: command.yac * YACC specification for expression and command parser for evolver. * Does algebraic form of expressions and builds parse tree * Accepts longest complete expression, so coordinates can * be listed as expressions on same line */ /* Notes: vcommand denotes commands that can be recognized as such from their first token; only such commands can be assigned to procedure variables. backquotes used to surround commands at start of comma-separated expression list. On semicolons: Semicolons are command separators and optional command terminators. Note that 'command' by itself does not have a terminating semicolon. Semicolons are therefore associated with the outermost part of a nested command, so don't work inside if-then. Blocks in { } are commands in order to work inside structured commands, so need semicolon separator afterwards. */ %{ #include "include.h" #include "lex.h" #define YYSTYPE yystype #define gettxt(a,b) (b) #define yylex kb_yylex int assignbacktrack ARGS((void)); #ifndef __GNUC__ #ifdef YYBISON /* for Bison */ #ifndef __yy_memcpy static void __yy_memcpy ARGS((char *, char *, int )); #endif #endif #endif /* for bison version 2.1 output */ #define __STDC__ 1 /* for non-ANSI compilers */ #define const #ifndef NO_YACC_DEBUG #define YYDEBUG 1 #endif int help_flag; /* avoid error message while doing help */ /* Backtrack to previous := in inputbuffer */ int assignbacktrack () { int spot; for ( spot = inputbufferspot - 1; spot > 0 ; spot-- ) if ( inputbuffer[spot-1] == ':' && inputbuffer[spot] == '=' ) return spot+1; return 0; } %} %pure_parser %token EXPRESSION_START_ COMMAND_START_ HISTORY_ GEOMVIEW_ VIEW_MATRIX_ %token LEAD_INTEGER_ INTEGER_ REAL_ SIGNED_NUMBER_ NEWIDENT_ REDEFINE_ %token MATHFUNC_ MATHFUNC2_ POW_ USERFUNC_ MIDV_ DATAFILENAME_ LOGFILE_ %token PI_ E_ G_ PARAM_ SYMBOL_ TOTAL_ EXTRA_ATTRIBUTE_ FIXEDVOL_ %token IDENT_ UMINUS_ SHELL_ COLOR_ HESSIAN_ VOLCONST_ TORUS_PERIODS_ %token VERTICES_ EDGES_ FACETS_ BODIES_ HESSIAN_MENU_ POSTSCRIPT_ %token LENGTH_ AREA_ VOLUME_ ID_ OID_ TAG_ ORIGINAL_ FACETEDGES_ WRAP_ %token QUOTATION_ UNSET_ TOPINFO_ OPACITY_ VALENCE_ HESSIAN_SADDLE_ %token SET_ FIXED_ DENSITY_ PRESSURE_ CONSTRAINT_ COORD_ DISSOLVE_ %token WHERE_ LIST_ SHOW_ DELETE_ REFINE_ RECALC_ SHOWQ_ EDGESWAP_ %token FIX_ UNFIX_ TOGGLENAME_ TOGGLEVALUE_ STAR_ QUANTITY_NAME_ PAUSE_ %token GO_ SHOW_VOL_ CHECK_ READ_ ZOOM_ ON_ OFF_ GEOMPIPE_ SELF_ %token SINGLE_LETTER_ LONG_JIGGLE_ RAW_VERAVG_ COUNTS_ CHDIR_ %token ALICE_ STABILITY_TEST_ DEFINE_ UPLUS_ DATATYPE_ FLUSH_COUNTS_ %token AUTOCHOP_ UTEST_ ATTRIBUTE_ RITZ_ MOVE_ VERTEXNORMAL_ POP_ %token SYSTEM_ TETRA_POINT_ TRIPLE_POINT_ LANCZOS_ EIGENPROBE_ EXEC_ %token AREAWEED_ EDGEWEED_ GRAVITY_ EDGEDIVIDE_ LINEAR_ QUADRATIC_ %token DIFFUSION_ EXTRAPOLATE_ TRANSFORM_DEPTH_ PRINTF_ ERRPRINTF_ %token PRINT_ MAX_ MIN_ COUNT_ SUM_ AVG_ BREAK_ CONTINUE_ SIZEOF_ %token TRANSFORM_EXPR_ BARE_ BOTTOMINFO_ METIS_ KMETIS_ KEYLOGFILE_ %token SCALE_ BURCHARD_ REBODY_ BOUNDARY_ ORIENTATION_ OMETIS_ %token SQ_MEAN_CURV_ FRONTCOLOR_ SINGLE_REDEFD_ METHOD_NAME_ TASK_EXEC_ %token RAWEST_VERAVG_ SINGLE_LETTER_ARG_ BACKCOLOR_ LAGRANGE_ RETURN_ %token TRANSFORM_EXPR_VERB_ OOGLFILE_ PARALLEL_EXEC_ BINARY_OFF_FILE_ %token SPRINTF_ CONVERT_TO_QUANTS_ METIS_FACTOR_ FUNCTION_ EXPRINT_ %token DIHEDRAL_ WRAP_VERTEX_ ARRAYIDENT_ DATE_AND_TIME_ LOCAL_ %token SHOW_EXPR_ SHOW_TRANS_ AXIAL_POINT_ ENERGY_ CONSERVED_ INFO_ONLY_ %token ASSIGN_ PROCEDURE_ FOREACH_ STRINGGLOBAL_ EQUIANGULATE_ %token HISTOGRAM_ LOGHISTOGRAM_ AREA_FIXED_ QUIT_ WARNING_MESSAGES_ %token IF_ WHILE_ DO_ NO_REFINE_ STRING_ NONCONTENT_ FOR_ HIT_PARTNER_ %token FRONTBODY_ BACKBODY_ COLORFILE_ PERM_STRINGGLOBAL_ FUNCTION_IDENT_ %token THICKEN_ COLORMAP_ REDIRECT_ NEWVERTEX_ NEWEDGE_ NEWFACET_ %token MODULUS_ TARGET_ VALUE_ INVERSE_PERIODS_ NEWBODY_ DELTA_ %token GAP_CONSTANT_ DUMP_ NOTCH_ QUANTITY_ LOAD_ PERM_PROCEDURE_ %token PROCEDURE_WORD_ DYNAMIC_LOAD_FUNC_ PERM_IDENT_ PERMLOAD_ %token HELP_ VERTEX_AVERAGE_ METHOD_INSTANCE_ RAW_VERTEX_AVERAGE_ %token OPTIMIZE_ REDIRECTOVER_ TOLERANCE_ RAWEST_VERTEX_AVERAGE_ %token JIGGLE_ VIEW_TRANSFORMS_ CLOSE_SHOW_ IS_DEFINED_ NODISPLAY_ %token PERM_ASSIGN_ PHASE_ VIEW_TRANSFORM_SWAP_COLORS_ BACKQUOTE_COMMA_ %token INTERNAL_VARIABLE_ DIRICHLET_ SOBOLEV_ VIEW_TRANSFORM_PARITY_ %token SOBOLEV_SEEK_ DIRICHLET_SEEK_ HESSIAN_SEEK_ REORDER_STORAGE_ %token RENUMBER_ALL_ CONSTRAINT_NAME_ BOUNDARY_NAME_ PROCEDURE_IDENT_ %token POP_TRI_TO_EDGE_ POP_EDGE_TO_TRI_ POP_QUAD_TO_QUAD_ SHOWVERB_ %token PROCEDURES_ MPI_TASK_ATTR_ T1_EDGESWAP_ MERGE_EDGE_ MERGE_FACET_ %token MERGE_VERTEX_ RESET_COUNTS_ VALID_ELEMENT_ MID_EDGE_ MID_FACET_ %token GO_COUNT_ ELEMENT_IDENT_ BODY_METIS_ REVERSE_ORIENTATION_ %token MATRIX_MULTIPLY_ MATRIX_INVERSE_ BINARY_PRINTF_ DUMP_MEMLIST_ %token FREE_DISCARDS_ REPARTITION_ METIS_READJUST_ MEAN_CURVATURE_ %token GLOBAL_ LEAD_INTEGER_AT_ INTEGER_AT_ MATRIX_DETERMINANT_ %token SUBCOMMAND_ ABORT_ BREAKPOINT_ WHEREAMI_ ADDLOAD_ SIMPLEX_TO_FE_ %token DISPLAY_TEXT_ DELETE_TEXT_ SUPPRESS_WARNING_ UNSUPPRESS_WARNING_ %token RESET_PROFILING_ VALID_CONSTRAINT_ VALID_BOUNDARY_ %token ARRAY_ATTRIBUTE_ PROFILING_ %token ',' '.' ';' '[' ']' '{' '}' '(' ')' '`' %start whole %right PERM_ASSIGN_ ASSIGN_ ASSIGNOP_ %left PIPE_ %nonassoc THEN_ %nonassoc ELSE_ %left '=' %right '?' ':' %left OR_ %left AND_ %right NOT_ %right EQ_ '>' '<' LE_ GE_ NE_ %nonassoc ON_CONSTRAINT_ %nonassoc HIT_CONSTRAINT_ %nonassoc ON_BOUNDARY_ %nonassoc ON_QUANTITY_ %nonassoc ON_METHOD_INSTANCE_ %left '+' '-' %left '*' '/' '%' IMOD_ IDIV_ DOT_ %nonassoc UMINUS_ UPLUS_ %left '^' %nonassoc EPRINT_ %% /* pseudo-variable values are node indices in node list */ /**************************************************************************/ whole : COMMAND_START_ /* empty line */ whole : COMMAND_START_ { begin_local_scope(); } commandline { end_local_scope(); } commandline : commands { $$.i = makenode(CMDLIST_,$1.i,0); } command : vcommand { $$.i = $1.i; /* for commands distinguishable by First tok */} /**************************************************************************/ command : command PIPE_ stringexpr { int p = makenode(PIPE_,$3.i,0); subtree_swap(&$1.i,&p); /* so pipe executed first */ $$.i = makenode(PIPE_END_,p,$1.i); } command : command PIPE_ error {kb_error(2330,"Piping must be to quoted string or string expression.\n",Q_ERROR);} /**************************************************************************/ command : command REDIRECT_ stringexpr { int p = makenode(REDIRECT_,$3.i,0); subtree_swap(&$1.i,&p); /* so file openedfirst */ $$.i = makenode(REDIRECT_END_,p,$1.i); } command : command REDIRECT_ error { kb_error(2331, "Redirection must be to quoted string or string expression.\n",Q_ERROR);} /**************************************************************************/ command : command REDIRECTOVER_ stringexpr { int p = makenode(REDIRECTOVER_,$3.i,0); subtree_swap(&$1.i,&p); /* so file openedfirst */ $$.i = makenode(REDIRECT_END_,p,$1.i); } command : command REDIRECTOVER_ error { kb_error(2332, "Redirection must be to quoted string or string expression.\n",Q_ERROR);} /**************************************************************************/ vcommand : BREAKPOINT_ PROCEDURE_ rexpr { $$.i = makenode(SET_BREAKPOINT_,$2.i,$3.i); } vcommand : BREAKPOINT_ FUNCTION_IDENT_ rexpr { $$.i = makenode(SET_BREAKPOINT_,$2.i,$3.i); } vcommand : BREAKPOINT_ PROCEDURE_IDENT_ rexpr { $$.i = makenode(SET_BREAKPOINT_,$2.i,$3.i); } vcommand : BREAKPOINT_ error { kb_error(5981,"Syntax: breakpoint procedurename linenumber\n", Q_ERROR); } vcommand : UNSET_ BREAKPOINT_ { $$.i = makenode(UNSET_BREAKPOINT_,0,0); } vcommand : WHEREAMI_ { $$.i = makenode(WHEREAMI_COMMAND_,0,0); } /**************************************************************************/ vcommand : error { kb_error(3988,"Illegal command syntax.\n", Q_ERROR); } vcommand : PROCEDURE_ { $$.i = makenode(PROCEDURE_,$1.i,0); } vcommand : PROCEDURE_ rexpr { int init = makenode(REPEAT_INIT_,$2.i,0); $$.i = makenode(PROCEDURE_,$1.i,0); $$.i = makenode(REPEAT_,init,$$.i); } vcommand : PROCEDURE_ error { kb_error(3600,"Missing semicolon?",Q_ERROR); } /**************************************************************************/ vcommand : PERM_PROCEDURE_ { $$.i = makenode(PERM_PROCEDURE_,$1.i,0); } vcommand : PERM_PROCEDURE_ rexpr { int init = makenode(REPEAT_INIT_,$2.i,0); $$.i = makenode(PERM_PROCEDURE_,$1.i,0); $$.i = makenode(REPEAT_,init,$$.i); } vcommand : PERM_PROCEDURE_ error { kb_error(3601,"Procedure has no arguments; can be followed by repetition count.",Q_ERROR); } /**************************************************************************/ command : command error { kb_error(2333,"Missing semicolon?\n",Q_ERROR); } commands: commandlist { $$.i = $1.i; } commands: commandlistterm { $$.i = $1.i; } commandblock: '{' { begin_local_scope(); } commands '}' { end_local_scope(); $$.i = makenode(COMMAND_BLOCK_,$3.i,0); } commandblock: '{' '}' { $$.i = makenode(NULLBLOCK_,0,0); } commandblock: '{' error '}' { kb_error(3602,"Error following '{'",Q_ERROR); } /**************************************************************************/ onecommand : commandsemic { $$.i = $1.i; } onecommand : command { $$.i = $1.i; } command : commandblock { $$.i = $1.i; } command : commandblock rexpr { int init = makenode(REPEAT_INIT_,$2.i,0); subtree_swap(&$1.i,&init); $$.i = makenode(REPEAT_,init,$1.i); } command : commandblock error { kb_error(3603, "Error following command block; expected ';' or repetition count or nothing.", Q_ERROR); } /**************************************************************************/ /* want to do left recursion */ commandsemic: ';' { $$.i = makenode(NULLCMD_,0,0); } commandsemic: command ';' { $$.i = $1.i; } commandterm: commandsemic { $$.i = $1.i; } commandlist: command { $$.i = $1.i; } commandlistterm: commandterm { $$.i = $1.i; } commandlist: commandlistterm command { $$.i = makenode(CMDLIST_,$1.i,$2.i); } commandlistterm: commandlistterm commandterm { $$.i = makenode(CMDLIST_,$1.i,$2.i); } ifhead: IF_ rexpr { $$.i = makenode(IFTEST_,$2.i,0); } THEN_ command { $$.i = makenode(IF_,$3.i,$5.i); } vcommand: IF_ error { kb_error(2334,"Syntax: IF rexpr THEN command [ ELSE command ]\n",Q_ERROR);} vcommand: ifhead {$$.i = makenode(ELSE_,$1.i,0); } vcommand: ifhead ELSE_ command { $$.i = makenode(ELSE_,$1.i,$3.i); } vcommand: ELSE_ error { kb_error(2335,"Illegal ELSE. Don't use semicolon before ELSE.\n",Q_ERROR); } vcommand: '?' { $$.i = makenode(SINGLE_LETTER_,'?',0); } /* commandsemic: ';' { $$.i = makenode(NOP_,0,0); } */ /**************************************************************************/ command: GEOMVIEW_ stringexpr { $$.i = makenode(GEOMVIEW_,$2.i,0); } command: GEOMVIEW_ toggle { $$.i = makenode(GEOMVIEW_TOGGLE_,$2.i,0); } command: GEOMVIEW_ { $$.i = makenode(GEOMVIEW_TOGGLE_,ON_,0); } command: GEOMVIEW_ error { kb_error(2336,"Syntax: GEOMVIEW ON|OFF or GEOMVIEW \"geomview command\"\n",Q_ERROR); } /**************************************************************************/ command: GEOMPIPE_ stringexpr { $$.i = makenode(GEOMPIPE_,$2.i,0); } command: GEOMPIPE_ toggle { $$.i = makenode(GEOMPIPE_TOGGLE_,$2.i,0); } command: GEOMPIPE_ { $$.i = makenode(GEOMPIPE_TOGGLE_,ON_,0); } command: GEOMPIPE_ error { kb_error(2337,"Syntax: GEOMPIPE ON|OFF or GEOMPIPE \"shell command\"\n",Q_ERROR); } /**************************************************************************/ command: LOGFILE_ stringexpr { $$.i = makenode(LOGFILE_,$2.i,0); } command: LOGFILE_ toggle { $$.i = makenode(LOGFILE_TOGGLE_,$2.i,0); } command: LOGFILE_ { $$.i = makenode(LOGFILE_TOGGLE_,ON_,0); } command: LOGFILE_ error { kb_error(2338,"Syntax: LOGFILE ON|OFF or LOGFILE \"filename\"\n",Q_ERROR); } /**************************************************************************/ command: KEYLOGFILE_ stringexpr { $$.i = makenode(KEYLOGFILE_,$2.i,0); } command: KEYLOGFILE_ toggle { $$.i = makenode(KEYLOGFILE_TOGGLE_,$2.i,0); } command: KEYLOGFILE_ { $$.i = makenode(KEYLOGFILE_TOGGLE_,ON_,0); } command: KEYLOGFILE_ error { kb_error(2419,"Syntax: KEYLOGFILE ON|OFF or KEYLOGFILE \"filename\"\n",Q_ERROR); } /**************************************************************************/ vcommand: POSTSCRIPT_ stringexpr { $$.i = makenode( POSTSCRIPT_,$2.i,0); } vcommand: POSTSCRIPT_ error { kb_error(3361,"Syntax: POSTSCRIPT \"filename\"\n",Q_ERROR); } /**************************************************************************/ vcommand: BINARY_OFF_FILE_ stringexpr { $$.i = makenode( BINARY_OFF_FILE_,$2.i,0); } vcommand: BINARY_OFF_FILE_ error { kb_error(4339,"Syntax: BINARY_OFF_FILE \"filename\"\n",Q_ERROR); } /**************************************************************************/ vcommand: OOGLFILE_ stringexpr { $$.i = makenode( OOGLFILE_,$2.i,0); } vcommand: OOGLFILE_ error { kb_error(2339,"Syntax: OOGLFILE \"filename\"\n",Q_ERROR); } /**************************************************************************/ vcommand: HISTORY_ { $$.i = makenode(HISTORY_,0,0); } vcommand: HISTORY_ error { kb_error(2340,"Syntax: HISTORY (no arguments)\n",Q_ERROR); } /**************************************************************************/ vcommand: RETURN_ { $$.i = makenode(RETURN_,0,0); } vcommand: RETURN_ rexpr { $$.i = makenode(RETURN_,$2.i,0); } vcommand: RETURN_ error { kb_error(2341,"Syntax: RETURN [expr] \n",Q_ERROR); } /**************************************************************************/ vcommand: BREAK_ { $$.i = makenode(BREAK_,1,0); } vcommand: BREAK_ INTEGER_ { $$.i = makenode(BREAK_,$2.i,0); } vcommand: BREAK_ error { kb_error(2342,"Syntax: BREAK or BREAK integer (to break multiple levels)\n",Q_ERROR); } /**************************************************************************/ vcommand: CONTINUE_ { $$.i = makenode(CONTINUE_,1,0); } vcommand: CONTINUE_ INTEGER_ { $$.i = makenode(CONTINUE_,$2.i,0); } vcommand: CONTINUE_ error { kb_error(2343,"Syntax: CONTINUE or CONTINUE integer (to continue in higher level loop)\n",Q_ERROR); } /**************************************************************************/ vcommand: GO_ { $$.i = makenode(SINGLE_LETTER_,'g',0); } vcommand: GO_COUNT_ { int init,count,g; real_val = $1.i; count = makenode(PUSHCONST,0,0); init = makenode(REPEAT_INIT_,count,0); g = makenode(SINGLE_LETTER_,'g',0); $$.i = makenode(REPEAT_,init,g); } vcommand: GO_ rexpr { int init,g; init = makenode(REPEAT_INIT_,$2.i,0); g = makenode(SINGLE_LETTER_,'g',0); $$.i = makenode(REPEAT_,init,g); } vcommand: GO_ error { kb_error(3666,"Syntax: GO count\n",Q_ERROR); } /**************************************************************************/ whilehead: WHILE_ rexpr { $$.i = makenode(WHILE_TOP_,$2.i,0); } DO_ { $$.i = $3.i; } vcommand: whilehead command { $$.i = makenode(WHILE_END_,$1.i,$2.i); } vcommand: WHILE_ error { kb_error(2344,"Syntax: WHILE rexpr DO command\n",Q_ERROR); } /**************************************************************************/ dohead: DO_ { $$.i = makenode(DO_ENTRY_,0,0);} onecommand { $$.i = makenode(DO_TOP_,$2.i,$3.i); } vcommand: dohead WHILE_ rexpr { $$.i = makenode(DO_END_,$1.i,$3.i); } vcommand: dohead error { kb_error(4345,"Missing WHILE at end of DO statement.\n",Q_ERROR); } command: DO_ error { kb_error(2345,"Syntax: DO command WHILE expr\n",Q_ERROR); } /**************************************************************************/ forentry: FOR_ '(' commandsemic { $$.i = makenode(FOR_ENTRY_,$3.i,0); } forhead: forentry rexpr ';' { $$.i = makenode(FOR_HEAD_,$1.i,$2.i); } forhead: forentry ';' { int tmp; real_val = 1; tmp = makenode(PUSHCONST,0,0); $$.i = makenode(FOR_HEAD_,$1.i,tmp); } fortop: forhead command ')' { $$.i = makenode(FOR_TOP_,$1.i,$2.i); } fortop: forhead ')' { int tmp = makenode(NULLCMD_,0,0); $$.i = makenode(FOR_TOP_,$1.i,tmp); } vcommand: fortop command { $$.i = makenode(FOR_END_,$1.i,$2.i); } vcommand: FOR_ error { kb_error(2514, "Syntax: FOR ( command ; rexpr ; command ) command\n",Q_ERROR); } vcommand: FOR_ '(' error { kb_error(3668, "Error in initializer of FOR loop.\n",Q_ERROR); } forhead: forentry error { kb_error(3669, "Error in test expression of FOR loop.\n",Q_ERROR); } fortop: forhead error { kb_error(3670, "Error in increment part of FOR loop.\n",Q_ERROR); } vcommand: fortop error { kb_error(2844, "Bad FOR loop body. Try starting body on same line as FOR header.\n", Q_ERROR); } /**************************************************************************/ vcommand: SINGLE_LETTER_ { $$.i = makenode(SINGLE_LETTER_,$1.i,0); } vcommand: SINGLE_REDEFD_ { $$.i = makenode(SINGLE_REDEFD_,$1.i,0); } vcommand: SINGLE_REDEFD_ rexpr { int init = makenode(REPEAT_INIT_,$2.i,0); $$.i = makenode(SINGLE_REDEFD_,$1.i,0); $$.i = makenode(REPEAT_,init,$$.i); } vcommand: SINGLE_REDEFD_ error { kb_error(3671, "Expected repetition count after redefined single letter.\n",Q_ERROR); } vcommand: SINGLE_LETTER_ARG_ { $$.i = makenode(SINGLE_LETTER_,$1.i,0); } vcommand: SINGLE_LETTER_ rexpr { int init = makenode(REPEAT_INIT_,$2.i,0); $$.i = makenode(SINGLE_LETTER_,$1.i,0); $$.i = makenode(REPEAT_,init,$$.i); } vcommand: SINGLE_LETTER_ error { kb_error(3672, "Expected repetition count after single letter command.\n",Q_ERROR); } vcommand: SINGLE_LETTER_ARG_ rexpr { assigntype = ASSIGN_; switch ($1.i) { case 't': $$.i = makenode(EDGEWEED_,$2.i,0); break; case 'w': $$.i = makenode(AREAWEED_,$2.i,0); break; case 'l': $$.i = makenode(EDGEDIVIDE_,$2.i,0); break; case 'm': $$.i = makenode(SET_SCALE_,$2.i,0); break; case 'n': $$.i = makenode(NOTCH_,$2.i,0); break; case 'j': $$.i = makenode(JIGGLE_,$2.i,0); break; case 'G': $$.i = makenode(SET_GRAVITY_,$2.i,0); break; case 'P': $$.i = makenode(INVOKE_P_MENU_,$2.i,0); break; case 'M': $$.i = makenode(SET_MODEL_,$2.i,0); break; case 'y': $$.i = makenode(TORDUP_,$2.i,0); break; case 'K': $$.i = makenode(SKINNY_,$2.i,0); break; case 'k': $$.i = makenode(SET_GAP_CONSTANT_,$2.i,0); break; case 'p': $$.i = makenode(SET_AMBIENT_PRESSURE_,$2.i,0); break; default: kb_error(1884,"Extra expression after single letter command.\n",Q_ERROR); } } vcommand: SINGLE_LETTER_ARG_ error { kb_error(3660, "Expected argument after single letter command.\n",Q_ERROR); } /**************************************************************************/ vcommand: READ_ { $$.i = makenode(NOP_,0,0); } vcommand: READ_ stringexpr { $$.i = makenode(READ_,$2.i,0); } vcommand : READ_ error { kb_error(2346,"Syntax: READ \"filename\" (need quoted string or string expression)\n",Q_ERROR);} /**************************************************************************/ command: TRANSFORM_DEPTH_ rexpr { $$.i = makenode(TRANSFORM_DEPTH_,$2.i,0);} command: TRANSFORM_DEPTH_ ASSIGN_ rexpr { $$.i = makenode(TRANSFORM_DEPTH_,$3.i,0);} command: TRANSFORM_DEPTH_ error { kb_error(2348,"Syntax: TRANSFORM_DEPTH := integer\n",Q_ERROR);} /**************************************************************************/ vcommand: TRANSFORM_EXPR_VERB_ stringexpr { $$.i = makenode(TRANSFORM_EXPR_,$2.i,0);} vcommand: TRANSFORM_EXPR_VERB_ ASSIGN_ stringexpr { $$.i = makenode(TRANSFORM_EXPR_,$3.i,0);} vcommand: TRANSFORM_EXPR_VERB_ error { kb_error(2349,"Syntax: TRANSFORM_EXPR := string (quoted string or string expression) \n",Q_ERROR); } /**************************************************************************/ vcommand: SYSTEM_ stringexpr { $$.i = makenode(SYSTEM_,$2.i,0); } vcommand: SYSTEM_ error { kb_error(2350,"Syntax: SYSTEM \"command\" (need quoted string or string expression)\n",Q_ERROR);} /**************************************************************************/ vcommand: EXEC_ stringexpr { $$.i = makenode(EXEC_,$2.i,0); } vcommand: EXEC_ error { kb_error(2351,"Syntax: EXEC string (need quoted string or string expression)\n",Q_ERROR);} /**************************************************************************/ vcommand: PARALLEL_EXEC_ stringexpr { $$.i = makenode(PARALLEL_EXEC_,$2.i,0); } vcommand: PARALLEL_EXEC_ error { kb_error(3115,"Syntax: PARALLEL_EXEC string (need quoted string or string expression)\n",Q_ERROR);} /**************************************************************************/ vcommand: TASK_EXEC_ rexpr ',' stringexpr { $$.i = makenode(TASK_EXEC_,$2.i,$4.i); } vcommand: TASK_EXEC_ error { kb_error(3119,"Syntax: TASK_EXEC nodenumber, string (need quoted string or string expression)\n",Q_ERROR);} /**************************************************************************/ vcommand: CHDIR_ stringexpr { $$.i = makenode(CHDIR_,$2.i,0); } vcommand: CHDIR_ error { kb_error(2352,"Syntax: CHDIR \"command\" (need quoted string or string expression)\n",Q_ERROR);} /**************************************************************************/ vcommand: METIS_ rexpr { $$.i = makenode(METIS_,$2.i,0); } vcommand: METIS_ error { kb_error(3236,"Syntax: METIS numparts\n",Q_ERROR); } vcommand: KMETIS_ rexpr { $$.i = makenode(KMETIS_,$2.i,0); } vcommand: KMETIS_ error { kb_error(2354,"Syntax: KMETIS numparts\n",Q_ERROR); } vcommand: METIS_READJUST_ rexpr { $$.i = makenode(METIS_READJUST_,$2.i,0); } vcommand: METIS_READJUST_ error { kb_error(3237,"Syntax: METIS_READJUST numparts\n",Q_ERROR); } vcommand: BODY_METIS_ rexpr { $$.i = makenode(BODY_METIS_,$2.i,0); } vcommand: BODY_METIS_ error { kb_error(3775,"Syntax: BODY_METIS numparts\n",Q_ERROR); } command: OMETIS_ rexpr { $$.i = makenode(OMETIS_,$2.i,0); } command: OMETIS_ { $$.i = makenode(OMETIS_,0,0); } command: OMETIS_ error { kb_error(2355,"Syntax: OMETIS or OMETIS expr\n",Q_ERROR); } /**************************************************************************/ vcommand: EDGEWEED_ rexpr { $$.i = makenode(EDGEWEED_,$2.i,0); } vcommand: EDGEWEED_ error { kb_error(2356,"Syntax: EDGEWEED minlength\n",Q_ERROR);} /**************************************************************************/ vcommand: AREAWEED_ rexpr { $$.i = makenode(AREAWEED_,$2.i,0); } vcommand: AREAWEED_ error { kb_error(2357,"Syntax: AREAWEED minarea\n",Q_ERROR);} /**************************************************************************/ vcommand: EDGEDIVIDE_ rexpr { $$.i = makenode(EDGEDIVIDE_,$2.i,0); } vcommand: EDGEDIVIDE_ error { kb_error(2358,"Syntax: EDGE_DIVIDE maxlength\n",Q_ERROR);} /**************************************************************************/ vcommand: LANCZOS_ '(' rexpr ',' rexpr ')' { $$.i = makenode(LANCZOS_,$3.i,$5.i); } vcommand: LANCZOS_ rexpr { $$.i = makenode(LANCZOS_,$2.i,0); } vcommand: LANCZOS_ error { kb_error(2359,"Syntax: lanczos rexpr or lanczos(expr,count) \n",Q_ERROR);} /**************************************************************************/ vcommand: RITZ_ '(' rexpr ',' rexpr ')' { $$.i = makenode(RITZ_,$3.i,$5.i); } vcommand: RITZ_ error { kb_error(2360,"Syntax: RITZ(probe_value, number_of_eigenvalues)\n",Q_ERROR); } /**************************************************************************/ vcommand: EIGENPROBE_ '(' rexpr ',' rexpr ')' { $$.i = makenode(EIGENPROBE_,$3.i,$5.i); } vcommand: EIGENPROBE_ rexpr { $$.i = makenode(EIGENPROBE_,$2.i,0); } vcommand: EIGENPROBE_ error { kb_error(2361, "Syntax: EIGENPROBE probe_value or EIGENPROBE(probe_value, iterationmax)\n",Q_ERROR); } /**************************************************************************/ vcommand: MOVE_ rexpr { $$.i = makenode(MOVE_,$2.i,0); } vcommand: MOVE_ error { kb_error(2362,"Syntax: MOVE stepsize\n",Q_ERROR); } /**************************************************************************/ vcommand: HESSIAN_SADDLE_ { $$.i = makenode(HESSIAN_SADDLE_,0,0); } vcommand: HESSIAN_SADDLE_ rexpr { $$.i = makenode(HESSIAN_SADDLE_,$2.i,0); } vcommand: HESSIAN_SADDLE_ error { kb_error(2363,"Syntax: SADDLE or SADDLE maxstepsize\n", Q_ERROR); } /**************************************************************************/ vcommand: HESSIAN_SEEK_ { $$.i = makenode(HESSIAN_SEEK_,0,0); } vcommand: HESSIAN_SEEK_ rexpr { $$.i = makenode(HESSIAN_SEEK_,$2.i,0); } vcommand: HESSIAN_SEEK_ error { kb_error(2364, "Syntax: HESSIAN_SEEK or HESSIAN_SEEK maxstepsize\n", Q_ERROR); } /**************************************************************************/ vcommand: COUNTS_ { $$.i = makenode(COUNTS_,0,0); } vcommand: QUIT_ { $$.i = makenode(SINGLE_LETTER_,'q',0); } vcommand: SUBCOMMAND_ { $$.i = makenode(SUBCOMMAND_,'q',0); } vcommand: ABORT_ { $$.i = makenode(ABORT_,'q',0); } vcommand: SIMPLEX_TO_FE_ { $$.i = makenode(SIMPLEX_TO_FE_,'q',0); } vcommand: REORDER_STORAGE_ { $$.i = makenode(REORDER_STORAGE_,0,0); } vcommand: RENUMBER_ALL_ { $$.i = makenode(RENUMBER_ALL_,0,0); } vcommand: DUMP_MEMLIST_ { $$.i = makenode(DUMP_MEMLIST_,0,0); } vcommand: FREE_DISCARDS_ { $$.i = makenode(FREE_DISCARDS_,0,0); } vcommand: REPARTITION_ { $$.i = makenode(REPARTITION_,0,0); } vcommand: EXTRAPOLATE_ { $$.i = makenode(EXTRAPOLATE_,0,0); } vcommand: REBODY_ { $$.i = makenode(REBODY_,0,0); } /**************************************************************************/ vcommand: ZOOM_ { $$.i = makenode(ZOOM_,0,0); } vcommand: ZOOM_ rexpr rexpr { $$.i = makenode(ZOOM_,$2.i,$3.i); } vcommand: ZOOM_ error { kb_error(2365,"Syntax: ZOOM [ vertex_id radius ]\n",Q_ERROR);} /**************************************************************************/ vcommand: BURCHARD_ INTEGER_ { $$.i = makenode(BURCHARD_,$2.i,0); } /**************************************************************************/ vcommand: LAGRANGE_ rexpr { $$.i = makenode(LAGRANGE_,$2.i,0); } vcommand: LAGRANGE_ error { kb_error(2366,"Syntax: LAGRANGE order\n",Q_ERROR); } /**************************************************************************/ vcommand: SHOW_VOL_ { $$.i = makenode(tok,0,0); } | PAUSE_ { $$.i = makenode(tok,0,0); } | PRINT_ PROFILING_ { $$.i = makenode(PRINT_PROFILING_,0,0); } | RESET_PROFILING_ { $$.i = makenode(tok,0,0); } | FLUSH_COUNTS_ { $$.i = makenode(tok,0,0); } | RESET_COUNTS_ { $$.i = makenode(tok,0,0); } | CHECK_ { $$.i = makenode(tok,0,0); } | SHOWQ_ { $$.i = makenode(tok,0,0); } | LONG_JIGGLE_ { $$.i = makenode(tok,0,0); } | RAW_VERAVG_ { $$.i = makenode(tok,0,0); } | RAWEST_VERAVG_ { $$.i = makenode(tok,0,0); } | ALICE_ { $$.i = makenode(tok,0,0); } | LINEAR_ { $$.i = makenode(tok,0,0); } | QUADRATIC_ { $$.i = makenode(tok,0,0); } | STABILITY_TEST_ { $$.i = makenode(tok,0,0); } | UTEST_ { $$.i = makenode(tok,0,0); } | SHELL_ { $$.i = makenode(tok,0,0); } | CONVERT_TO_QUANTS_ { $$.i = makenode(tok,0,0); } | METIS_FACTOR_ { $$.i = makenode(tok,0,0); } | DIRICHLET_ { $$.i = makenode(tok,0,0); } | DIRICHLET_SEEK_ { $$.i = makenode(tok,0,0); } | SOBOLEV_ { $$.i = makenode(tok,0,0); } | SOBOLEV_SEEK_ { $$.i = makenode(tok,0,0); } | HESSIAN_ { $$.i = makenode(tok,0,0); } | HESSIAN_MENU_ { $$.i = makenode(tok,0,0); } | HELP_ { $$.i = makenode(SINGLE_LETTER_,'h',0); } | RECALC_ { $$.i = makenode(tok,0,0); } | LIST_ TOPINFO_ { $$.i = makenode(TOPINFO_,0,0); } | LIST_ BOTTOMINFO_ { $$.i = makenode(BOTTOMINFO_,0,0); } | LIST_ ATTRIBUTE_ { $$.i = makenode(LIST_ATTRIBUTES_,0,0); } | LIST_ PROCEDURES_ { $$.i = makenode(LIST_PROCS_,0,0); } | LIST_ BOUNDARY_ rexpr { $$.i = makenode(LIST_BOUNDARY_,$3.i,0);} | LIST_ BOUNDARY_NAME_ { int k = makenode(PUSHCONST,$2.i,0); $$.i = makenode(LIST_BOUNDARY_,k,0); list[$$.i].op1.bdry_id = $2.i; } | LIST_ CONSTRAINT_ rexpr { $$.i = makenode(LIST_CONSTRAINT_,$3.i,0);} | LIST_ CONSTRAINT_NAME_ { int k = makenode(PUSHCONST,$2.i,0); $$.i = makenode(LIST_CONSTRAINT_,k,0); list[$$.i].op1.con_id = $2.i; } | LIST_ QUANTITY_ QUANTITY_NAME_ { $$.i = makenode(LIST_QUANTITY_,$3.i,0);} | LIST_ QUANTITY_NAME_ { $$.i = makenode(LIST_QUANTITY_,$2.i,0);} | LIST_ METHOD_INSTANCE_ METHOD_NAME_ { $$.i = makenode(LIST_METHOD_INSTANCE_,$3.i,0);} | LIST_ METHOD_NAME_ { $$.i = makenode(LIST_METHOD_INSTANCE_,$2.i,0);} | CLOSE_SHOW_ { $$.i = makenode(CLOSE_SHOW_,0,0); } | TOGGLENAME_ toggle { $$.i = makenode($1.i,$2.i,0); } | TOGGLENAME_ { $$.i = makenode($1.i,ON_,0); } | OPTIMIZE_ toggle { $$.i = makenode($1.i,$2.i,0); } | OPTIMIZE_ { $$.i = makenode($1.i,ON_,0); } | AUTOCHOP_ toggle { $$.i = makenode($1.i,$2.i,0); } | AUTOCHOP_ { $$.i = makenode($1.i,ON_,0); } | JIGGLE_ { $$.i = makenode($1.i,ON_,0); } /**************************************************************************/ estart :EXPRESSION_START_ {verb_flag=0;} whole : estart rexpr { YYACCEPT; } whole : estart rexpr ',' { YYACCEPT; } /**************************************************************************/ command : INTERNAL_VARIABLE_ assignop {verb_flag=0;} rexpr { assigntype = $2.i; $$.i = makenode(SET_INTERNAL_,$1.i,$4.i); } command : INTERNAL_VARIABLE_ assignop error { kb_error(3673,"Expected expression to assign to internal variable.\n",Q_ERROR);} vcommand : SET_ INTERNAL_VARIABLE_ {verb_flag=0;} rexpr { assigntype = ASSIGN_; $$.i = makenode(SET_INTERNAL_,$2.i,$4.i); } vcommand : SET_ INTERNAL_VARIABLE_ error { kb_error(3661,"Expected expression for setting internal variable.\n",Q_ERROR);} /**************************************************************************/ vcommand : SET_ GRAVITY_ rexpr { $$.i = makenode(SET_GRAVITY_,$3.i,0); } vcommand : SET_ GRAVITY_ error { kb_error(3675,"Expected expression for setting gravity.\n",Q_ERROR);} command : GRAVITY_ assignop rexpr {assigntype = $2.i; $$.i = makenode(SET_GRAVITY_,$3.i,0); } command : GRAVITY_ error { kb_error(2367,"Syntax: GRAVITY := rexpr \n GRAVITY ON|OFF\n",Q_ERROR);} /***************************************************************************/ vcommand : SET_ CONSTRAINT_ rexpr GLOBAL_ { $$.i = makenode(SET_CONSTRAINT_GLOBAL,$3.i,0); } vcommand : UNSET_ CONSTRAINT_ rexpr GLOBAL_ { $$.i = makenode(UNSET_CONSTRAINT_GLOBAL,$3.i,0); } vcommand : SET_ CONSTRAINT_ CONSTRAINT_NAME_ GLOBAL_ { $$.i = makenode(SET_CONSTRAINT_NAME_GLOBAL,$3.i,0); } vcommand : UNSET_ CONSTRAINT_ CONSTRAINT_NAME_ GLOBAL_ { $$.i = makenode(SET_CONSTRAINT_NAME_GLOBAL,$3.i,0); } vcommand : SET_ CONSTRAINT_NAME_ GLOBAL_ { $$.i = makenode(SET_CONSTRAINT_NAME_GLOBAL,$2.i,0); } vcommand : UNSET_ CONSTRAINT_NAME_ GLOBAL_ { $$.i = makenode(SET_CONSTRAINT_NAME_GLOBAL,$2.i,0); } /**************************************************************************/ vcommand : SET_ SCALE_ rexpr { assigntype = ASSIGN_; $$.i = makenode(SET_INTERNAL_,V_SCALE,$3.i); } vcommand : SET_ SCALE_ error { kb_error(3676,"Syntax: SET SCALE expr\n",Q_ERROR);} command : SCALE_ assignop rexpr { assigntype = $2.i; $$.i = makenode(SET_INTERNAL_,V_SCALE,$3.i); } command : SCALE_ assignop error { kb_error(3677,"Syntax: SCALE := expr\n",Q_ERROR);} vcommand : SET_ DIFFUSION_ rexpr { assigntype = ASSIGN_; $$.i = makenode(SET_INTERNAL_,V_DIFFUSION,$3.i); } vcommand : SET_ DIFFUSION_ error { kb_error(3662,"Syntax: SET DIFFUSION expr\n",Q_ERROR);} /**************************************************************************/ command : GAP_CONSTANT_ assignop rexpr { assigntype = $2.i; $$.i = makenode(SET_INTERNAL_,V_GAP_CONSTANT,$3.i); } command : GAP_CONSTANT_ error { kb_error(2369,"Syntax: GAP_CONSTANT := expr\n",Q_ERROR);} command : AREA_FIXED_ ASSIGN_ rexpr { $$.i = makenode(SET_FIXED_AREA_,$3.i,0); } /**************************************************************************/ vcommand : NOTCH_ rexpr { $$.i = makenode(NOTCH_,$2.i,0); } vcommand : NOTCH_ error { kb_error(2371,"Syntax: NOTCH maxangle\n",Q_ERROR);} /**************************************************************************/ command : AUTOCHOP_ rexpr { $$.i = makenode(SET_AUTOCHOP_,$2.i,0); } command : AUTOCHOP_ ASSIGN_ rexpr { $$.i = makenode(SET_AUTOCHOP_,$3.i,0); } command : AUTOCHOP_ error { kb_error(2372, "Syntax: AUTOCHOP ON|OFF or AUTOCHOP choplength\n", Q_ERROR); } /**************************************************************************/ command : QUANTITY_NAME_ '.' TARGET_ assignop rexpr { assigntype = $4.i; $$.i = makenode(SET_QTARGET_,$5.i,$1.i); } command : QUANTITY_NAME_ '.' MODULUS_ assignop rexpr { assigntype = $4.i; $$.i = makenode(SET_QMODULUS_,$5.i,$1.i); } command : QUANTITY_NAME_ '.' TOLERANCE_ assignop rexpr { assigntype = $4.i; $$.i = makenode(SET_QTOLERANCE_,$5.i,$1.i); } command : METHOD_NAME_ '.' MODULUS_ assignop rexpr { assigntype = $4.i; $$.i = makenode(SET_MMODULUS_,$5.i,$1.i); } command : QUANTITY_NAME_ '.' VOLCONST_ assignop rexpr { assigntype = $4.i; $$.i = makenode(SET_QVOLCONST_,$5.i,$1.i); } command : QUANTITY_NAME_ '.' error { kb_error(3372, "Syntax: QUANTITY_NAME . TARGET|MODULUS|TOLERANCE|VOLCONST := expr\n", Q_ERROR); } command : METHOD_NAME_ '.' error { kb_error(3379, "Syntax: METHOD_NAME . MODULUS\n", Q_ERROR); } /**************************************************************************/ command : SET_ QUANTITY_NAME_ TARGET_ rexpr { assigntype = ASSIGN_; $$.i = makenode(SET_QTARGET_,$4.i,$2.i); } command : SET_ QUANTITY_NAME_ MODULUS_ rexpr { assigntype = ASSIGN_; $$.i = makenode(SET_QMODULUS_,$4.i,$2.i); } command : SET_ QUANTITY_NAME_ TOLERANCE_ rexpr { assigntype = ASSIGN_; $$.i = makenode(SET_QTOLERANCE_,$4.i,$2.i); } command : SET_ METHOD_NAME_ MODULUS_ rexpr { assigntype = ASSIGN_; $$.i = makenode(SET_MMODULUS_,$4.i,$2.i); } command : SET_ QUANTITY_NAME_ VOLCONST_ rexpr { assigntype = ASSIGN_; $$.i = makenode(SET_QVOLCONST_,$4.i,$2.i); } command : SET_ QUANTITY_NAME_ FIXED_ { $$.i = makenode(SET_Q_FIXED_,$2.i,0); } command : SET_ QUANTITY_NAME_ INFO_ONLY_ { $$.i = makenode(SET_Q_INFO_,$2.i,0); } command : SET_ QUANTITY_NAME_ ENERGY_ { $$.i = makenode(SET_Q_ENERGY_,$2.i,0); } command : SET_ QUANTITY_NAME_ CONSERVED_ { $$.i = makenode(SET_Q_CONSERVED_,$2.i,0); } command : SET_ QUANTITY_NAME_ error { strcpy(errmsg,"Syntax:\n"); strcat(errmsg," SET quantityname TARGET expr\n"); strcat(errmsg," SET quantityname MODULUS expr\n"); strcat(errmsg," SET quantityname TOLERANCE expr\n"); strcat(errmsg," SET quantityname VOLCONST expr\n"); strcat(errmsg," SET quantityname FIXED\n"); strcat(errmsg," SET quantityname INFO_ONLY\n"); strcat(errmsg," SET quantityname ENERGY\n"); strcat(errmsg," SET quantityname CONSERVED\n"); kb_error(3663,errmsg,Q_ERROR); } /**************************************************************************/ command : SUPPRESS_WARNING_ rexpr { $$.i = makenode(SUPPRESS_WARNING_,$2.i,0); } command : SUPPRESS_WARNING_ error { kb_error(3456, "Syntax: SUPPRESS_WARNING number\n",Q_ERROR) } command : UNSUPPRESS_WARNING_ rexpr { $$.i = makenode(UNSUPPRESS_WARNING_,$2.i,0); } command : UNSUPPRESS_WARNING_ error { kb_error(3457, "Syntax: UNSUPPRESS_WARNING number\n",Q_ERROR) } /**************************************************************************/ vcommand : LOAD_ stringexpr { $$.i = makenode(LOAD_,$2.i,0); } vcommand : LOAD_ error { kb_error(2373,"Syntax: LOAD \"filename\" (need quoted string or string expression)\n",Q_ERROR);} /**************************************************************************/ vcommand : ADDLOAD_ stringexpr { $$.i = makenode(ADDLOAD_,$2.i,0); } vcommand : ADDLOAD_ error { kb_error(3544,"Syntax: ADDLOAD \"filename\" (need quoted string or string expression)\n",Q_ERROR);} /**************************************************************************/ vcommand : PERMLOAD_ stringexpr { $$.i = makenode(PERMLOAD_,$2.i,0); } vcommand : PERMLOAD_ error { kb_error(2544,"Syntax: PERMLOAD \"filename\" (need quoted string or string expression)\n",Q_ERROR);} /**************************************************************************/ vcommand : DUMP_ stringexpr { $$.i = makenode(DUMP_,$2.i,0); } vcommand : DUMP_ { $$.i = makenode(DUMP_,0,0); } vcommand : DUMP_ error { kb_error(2374,"Syntax: DUMP \"filename\" (need quoted string or string expression)\n",Q_ERROR);} /**************************************************************************/ command : COLORFILE_ ASSIGN_ stringexpr { $$.i = makenode(SET_COLORMAP_,$3.i,0); } /**************************************************************************/ vcommand : OPTIMIZE_ rexpr {$$.i = makenode(SET_OPTIMIZE_,$2.i,0);} command : OPTIMIZE_ error { kb_error(2375,"Syntax: OPTIMIZE maxscale\n",Q_ERROR); } /**************************************************************************/ identassign : IDENT_ ASSIGN_ { $$.i = $1.i; } permidentassign : PERM_IDENT_ PERM_ASSIGN_ { $$.i = $1.i; } command : STRINGGLOBAL_ ASSIGN_ stringexpr { $$.i = makenode(SET_SGLOBAL_,$1.i,$3.i); } command : identassign stringexpr { $$.i = makenode(SET_SGLOBAL_,$1.i,$2.i); } command : STRINGGLOBAL_ ASSIGN_ rexpr { $$.i = makenode(SET_GLOBAL_,$1.i,$3.i); } command : PERM_STRINGGLOBAL_ PERM_ASSIGN_ stringexpr { $$.i = makenode(SET_PERM_SGLOBAL_,$1.i,$3.i); } command : STRINGGLOBAL_ PERM_ASSIGN_ { kb_error(2604,"Cannot make permanent assigment to nonpermanent variable.\n",Q_ERROR) }; command : PERM_STRINGGLOBAL_ ASSIGN_ { kb_error(2603,"Cannot make nonpermanent assigment to permanent variable.\n",Q_ERROR) }; command : identassign rexpr { $$.i = makenode(SET_GLOBAL_,$1.i,$2.i); } command : permidentassign rexpr { $$.i = makenode(SET_PERM_GLOBAL_,$1.i,$2.i); } elidassign : ELEMENT_IDENT_ ASSIGN_ { $$ = $1; } command : elidassign rexpr { $$.i = makenode(SET_ELEMENT_GLOBAL_,$1.i,$2.i); } lvalue : IDENT_ '.' DELTA_ { $$.i = makenode(PDELTA_LVALUE_,$1.i,0); } lvalue : IDENT_ '.' SCALE_ { $$.i = makenode(PSCALE_LVALUE_,$1.i,0); } command : lvalue assignop rexpr { subtree_swap(&$1.i,&$3.i); switch ( list[$1.i].type ) { case PDELTA_LVALUE_: list[$1.i].type = SET_DELTA_; list[$1.i].left = $3.i - $1.i; break; case PSCALE_LVALUE_: list[$1.i].type = SET_PARAM_SCALE; list[$1.i].left = $3.i - $1.i; break; default: sprintf(errmsg,"Internal error: lvalue type %d\n", list[$1.i].type); kb_error(2882,errmsg,COMMAND_ERROR); } list[$1.i].op2.assigntype = $2.i; list[$1.i].stack_delta = -1; $$ = $1; } rexpr : IDENT_ '.' FIXED_ { $$.i = makenode(PUSH_PARAM_FIXED,$1.i,0); } rexpr : lvalue { switch ( list[$1.i].type ) { case PDELTA_LVALUE_: list[$1.i].type = PUSHDELTA_; list[$1.i].datatype = REAL_TYPE; break; case PSCALE_LVALUE_: list[$1.i].type = PUSH_PARAM_SCALE; list[$1.i].datatype = REAL_TYPE; break; default: sprintf(errmsg,"Internal error: lvalue type %d\n", list[$1.i].type); kb_error(2883,errmsg,COMMAND_ERROR); } list[$1.i].stack_delta = 1; $$ = $1; } command : IDENT_ '.' error { kb_error(3380, "Syntax: VARIABLE . PDELTA|PSCALE \n", Q_ERROR); } command : IDENT_ ASSIGNOP_ rexpr { $$.i = makenode($2.i,$1.i,$3.i); } /* command : arrayhead ASSIGNOP_ rexpr { assigntype = $2.i; $$.i = makenode(ARRAYASSIGN,$1.i,$3.i); } command : arrayhead ASSIGN_ rexpr { assigntype = ASSIGN_; $$.i = makenode(ARRAYASSIGN,$1.i,$3.i); } command : arrayhead EQ_ { kb_error(3423,"Got '=' instead of the assignment operator ':='\n", Q_ERROR); } */ command : IDENT_ ASSIGN_ error { kb_error(2376,"Syntax: variable := expr\n",Q_ERROR);} command : IDENT_ EQ_ { kb_error(3422,"Got '=' instead of the assignment operator ':='\n", Q_ERROR); } command : NEWIDENT_ EQ_ { kb_error(3424,"Got '=' instead of the assignment operator ':='\n", Q_ERROR); } command : IDENT_ error { kb_error(2377,"Syntax: variable := expr\n",Q_ERROR);} /**************************************************************************/ datatype : DATATYPE_ { $$.i = $1.datatype; } datatype : STRING_ { $$.i = STRING_TYPE; } argident : NEWIDENT_ { $$.i = add_local_var($1.lexeme,1); } argident : IDENT_ { $$.i = add_local_var($1.lexeme,1); if ( shadow_warn_flag ) { sprintf(errmsg, "Argument \"%s\" shadows already declared variable.\n", $1.lexeme); kb_error(2635,errmsg,WARNING); } } argident : ARRAYIDENT_ { $$.i = add_local_var($1.lexeme,1); if ( shadow_warn_flag ) { sprintf(errmsg, "Argument \"%s\" shadows already declared variable.\n", $1.lexeme); kb_error(2636,errmsg,WARNING); } } argident : PROCEDURE_ { $$.i = add_local_var($1.lexeme,1); if ( shadow_warn_flag ) { sprintf(errmsg, "Argument \"%s\" shadows already declared procedure.\n", $1.lexeme); kb_error(2637,errmsg,WARNING); } } argident : FUNCTION_IDENT_ { $$.i = add_local_var($1.lexeme,1); if ( shadow_warn_flag ) { sprintf(errmsg, "Argument \"%s\" shadows already declared function.\n", $1.lexeme); kb_error(2638,errmsg,WARNING); } } argident : STRINGGLOBAL_ { $$.i = add_local_var($1.lexeme,1); if ( shadow_warn_flag ) { sprintf(errmsg, "Argument \"%s\" shadows already declared string variable.\n", $1.lexeme); kb_error(2639,errmsg,WARNING); } } argident : QUANTITY_NAME_ { $$.i = add_local_var($1.lexeme,1); if ( shadow_warn_flag ) { sprintf(errmsg, "Argument \"%s\" shadows already declared quantity name.\n", $1.lexeme); kb_error(2640,errmsg,WARNING); } } argident : METHOD_NAME_ {$$.i = add_local_var($1.lexeme,1); if ( shadow_warn_flag ) { sprintf(errmsg, "Argument \"%s\" shadows already declared method name.\n", $1.lexeme); kb_error(2641,errmsg,WARNING); } } argident : CONSTRAINT_NAME_ {$$.i = add_local_var($1.lexeme,1); if ( shadow_warn_flag ) { sprintf(errmsg, "Argument \"%s\" shadows already declared constraint.\n", $1.lexeme); kb_error(2642,errmsg,WARNING); } } argident : BOUNDARY_NAME_ { $$.i = add_local_var($1.lexeme,1); if ( shadow_warn_flag ) { sprintf(errmsg, "Argument \"%s\" shadows already declared boundary.\n", $1.lexeme); kb_error(2643,errmsg,WARNING); } } arglist : '(' ')' { $$.i = makenode(ARGLIST_,0,0); } argliststart : '(' datatype argident { int_val = $2.i; $$.i = makenode(ARGLIST_,0,$3.i); } argliststart : argliststart ',' datatype argident { int_val = $3.i; $$.i = makenode(ARGLIST_,$1.i,$4.i); } arglist : '(' error { if ( strcmp(yytext,"int") == 0 ) kb_error(3604,"Expecting datatype or ')' after '('\n ('integer' is the Evolver datatype, not 'int'.\n",Q_ERROR); else kb_error(3636,"Expecting datatype or ')' after '('\n",Q_ERROR); } argliststart : '(' datatype { kb_error(3605,"Expecting identifier after datatype.\n",Q_ERROR); } argliststart : argliststart ',' error { kb_error(3606,"Expecting datatype after ','\n",Q_ERROR); } argliststart : argliststart ',' datatype error { kb_error(3625,"Expecting identifier after datatype.\n",Q_ERROR); } argliststart : argliststart error { kb_error(3525,"Expecting comma or right parenthesis after argument.\n",Q_ERROR); } arglist : argliststart ')' { $$.i = $1.i }; /**************************************************************************/ protobody : commandblock { $$.i = $1.i; } protobody : ';' { $$.i = 0; } protobody : { kb_error(2624,"Missing function body, or ';' after prototype.\n", Q_ERROR); } /**************************************************************************/ functionname : NEWIDENT_ { $$.i = $1.i; } functionname : FUNCTION_IDENT_ { $$.i = $1.i; } vcommand : FUNCTION_ datatype functionname { in_function = 1; if ( $3.i == 0 ) $3.i = add_global($3.lexeme); init_local_scope($3.i); begin_local_scope(); $$.i = makenode(FUNCTION_DEF_START_,$3.i,$2.i); } arglist { $$.i = makenode(FUNCTION_HEAD_,$4.i,$5.i); } protobody { int insize = inputbufferspot - $1.qnum; in_function = 0; globals($3.i)->attr.procstuff.proc_text = mycalloc(insize+1,1); strncpy(globals($3.i)->attr.procstuff.proc_text,inputbuffer+$3.qnum,insize); globals($3.i)->attr.procstuff.proc_text[insize] = 0; list[$4.i].op5.locals = globals($3.i)->attr.procstuff.locals = localbase; if ( localbase ) localbase->flags |= LL_IN_USE; int_val = $2.i; if ( $7.i ) $$.i = makenode(SET_FUNCTION_,$6.i,$7.i); else { makenode(FUNCTION_PROTO_,$6.i,0); $$.i = 0; } exit_local_scope(); } procedurename : NEWIDENT_ { $$.i = $1.i; } procedurename : PROCEDURE_IDENT_ { $$.i = $1.i; } vcommand : PROCEDURE_WORD_ procedurename { in_function = 1; /* for lex*/ if ( $2.i == 0 ) $2.i = add_global($2.lexeme); init_local_scope($2.i); begin_local_scope(); $$.i = makenode(PROCEDURE_DEF_START_,$2.i,0); } arglist { $$.i = makenode(PROCEDURE_HEAD_,$3.i,$4.i); } protobody { int insize = inputbufferspot - $1.qnum; in_function = 0; globals($2.i)->attr.procstuff.proc_text = mycalloc(insize+1,1); strncpy(globals($2.i)->attr.procstuff.proc_text,inputbuffer+$2.qnum,insize); globals($2.i)->attr.procstuff.proc_text[insize] = 0; list[$3.i].op5.locals = globals($2.i)->attr.procstuff.locals = localbase; if ( localbase ) localbase->flags |= LL_IN_USE; if ( $6.i ) $$.i = makenode(SET_ARGSPROC_,$5.i,$6.i); else { makenode(PROCEDURE_PROTO_,$5.i,0); $$.i = 0; } exit_local_scope(); } vcommand : FUNCTION_ datatype error { kb_error(3704,"Expected function name after datatype.\n",Q_ERROR); } vcommand : FUNCTION_ error { kb_error(3705,"Expected datatype for function.\n",Q_ERROR); } vcommand : PROCEDURE_WORD_ error { kb_error(3706,"Expected name of procedure.\n",Q_ERROR); } vcommand : FUNCTION_ { kb_error(3496,"Function returns a value; it's not a stand-alone command.\n",Q_ERROR); } /**************************************************************************/ vcommand : DEFINE_ IDENT_ datatype { $$.i = makenode(DEFINE_IDENT_,$2.i,$3.i); } vcommand : DEFINE_ NEWIDENT_ datatype {int g; if ( $2.i == 0 )g = add_global($2.lexeme); else g = $2.i; /* local */ $$.i = makenode(DEFINE_IDENT_,g,$3.i); } vcommand : DEFINE_ IDENT_ { $$.i = makenode(DEFINE_IDENT_,$2.i,REAL_TYPE); } vcommand : DEFINE_ NEWIDENT_ {int g = $2.i ? $2.i : add_global($2.lexeme); $$.i = makenode(DEFINE_IDENT_,g,REAL_TYPE); } vcommand : DEFINE_ STRINGGLOBAL_ { $$.i = makenode(DEFINE_IDENT_,$2.i,STRING_TYPE); } indexset : '[' rexpr ']' { $$.i = makenode(INDEXSET_,0,$2.i); } indexset : indexset '[' rexpr ']' { $$.i = makenode(INDEXSET_,$1.i,$3.i); } dimensionset : '[' rexpr ']' { $$.i = makenode(DIMENSIONSET_,0,$2.i); } dimensionset : dimensionset '[' rexpr ']' { $$.i = makenode(DIMENSIONSET_,$1.i,$3.i); } arraydecl : DEFINE_ ARRAYIDENT_ datatype dimensionset { $$.qnum = $2.i ; $$.datatype = $3.i; int_val= $$.datatype; $$.i = makenode(DEFINE_ARRAY_,$2.i,$4.i); } arraydecl : DEFINE_ NEWIDENT_ datatype dimensionset { $$.qnum = $2.i ? $2.i : add_global($2.lexeme); $$.datatype = $3.i; int_val= $$.datatype; $$.i = makenode(DEFINE_ARRAY_,$$.qnum,$4.i); } arraydecl : DEFINE_ IDENT_ datatype dimensionset { $$.qnum = $2.i ; $$.datatype = $3.i; int_val= $$.datatype; $$.i = makenode(DEFINE_ARRAY_,$2.i,$4.i); } vcommand : arraydecl { $$.i = $1.i; } /* arrayhead : ARRAYIDENT_ indexset { $$.i = makenode(ARRAY_HEAD_,$2.i,$1.i); $$.qnum = $2.i ; } arrayhead : ARRAYIDENT_ error { sprintf(errmsg,"%s is an array; needs indexing.\n", globals($1.i)->name); kb_error(2378,errmsg,Q_ERROR); } */ /************************************************************** Some whole-array arithmetic syntax. To handle both stand-alone and attribute arrays, datastart of array is pushed on stack for each operand. ***************************************************************/ arraylvalue : ARRAYIDENT_ { $$.i = makenode(ARRAYIDENT_,$1.i,0); } arraylvalue : VERTEXNORMAL_ { /* for implicit generator */ $$.i = makenode(ATTRIB_LVALUE_,0,0); list[$$.i].op1.localnum = 0; list[$$.i].op2.name_id = set_name_eltype(V_NORMAL_ATTR,VERTEX); list[$$.i].type = ARRAY_VERTEX_NORMAL_; list[$$.i].op3.localnum = add_local_var(NULL,SDIM); } arraylvalue : ARRAY_ATTRIBUTE_ { /* for implicit generator */ $$.i = makenode(ATTRIB_LVALUE_,0,0); list[$$.i].op1.localnum = 0; list[$$.i].op2.name_id = set_name_eltype($1.qnum,$1.etype); if ( ($1.etype == VERTEX) && ($1.qnum == V_NORMAL_ATTR) ) { list[$$.i].type = ARRAY_VERTEX_NORMAL_; list[$$.i].op3.localnum = add_local_var(NULL,SDIM); list[$$.i].flags |= IS_VIRTUAL_ATTR; } else if ( ($1.etype == EDGE) && ($1.qnum == E_VECTOR_ATTR) ) { list[$$.i].type = ARRAY_EDGE_VECTOR_; list[$$.i].op3.localnum = add_local_var(NULL,SDIM); list[$$.i].flags |= IS_VIRTUAL_ATTR; } else if ( ($1.etype == FACET) && ($1.qnum == F_NORMAL_ATTR) ) { list[$$.i].type = ARRAY_FACET_NORMAL_; list[$$.i].op3.localnum = add_local_var(NULL,SDIM); list[$$.i].flags |= IS_VIRTUAL_ATTR; } } arraylvalue : singlep ARRAY_ATTRIBUTE_ { if ( $2.etype != $1.etype ) { sprintf(errmsg, "\"%s\" is a %s attribute, not %s.\n", EXTRAS($2.etype)[$2.qnum].name,typenames[$2.etype], typenames[$1.etype]); kb_error(3678,errmsg,Q_ERROR); } $$.i = makenode(ATTRIB_LVALUE_,$1.i,0); list[$$.i].op1.localnum = list[$1.i].op2.localnum; list[$$.i].op2.name_id = set_name_eltype($2.qnum,$2.etype); if ( ($2.etype == VERTEX) && ($2.qnum == V_NORMAL_ATTR) ) { list[$$.i].type = ARRAY_VERTEX_NORMAL_; list[$$.i].op3.localnum = add_local_var(NULL,SDIM); list[$$.i].flags |= IS_VIRTUAL_ATTR; } else if ( ($2.etype == EDGE) && ($2.qnum == E_VECTOR_ATTR) ) { list[$$.i].type = ARRAY_EDGE_VECTOR_; list[$$.i].op3.localnum = add_local_var(NULL,SDIM); list[$$.i].flags |= IS_VIRTUAL_ATTR; } else if ( ($2.etype == FACET) && ($2.qnum == F_NORMAL_ATTR) ) { list[$$.i].type = ARRAY_FACET_NORMAL_; list[$$.i].op3.localnum = add_local_var(NULL,SDIM); list[$$.i].flags |= IS_VIRTUAL_ATTR; } } arraylvalue : singlep NEWIDENT_ { sprintf(errmsg,"\"%s\" is not an attribute name.\n",$2.lexeme); kb_error(3573,errmsg,Q_ERROR); } arraylvalue : singlep error { sprintf(errmsg,"Missing attribute.\n"); kb_error(3574,errmsg,Q_ERROR); } arraylvalueindexset : arraylvalue indexset { $$.i = makenode(ARRAY_LVALUE_INDEXED_,$1.i,$2.i); } vcommand : arraylvalueindexset assignop rexpr { $$.i = makenode(ARRAY_ASSIGNOP_SINGLE_,$1.i,$3.i); list[$$.i].op1.assigntype = $2.i; list[$$.i].op2.name_id = list[$1.i].op2.name_id; list[$$.i].stack_delta = list[$1.i].op1.indexcount + 2; } vcommand : arraylvalue assignop arraylvalue { $$.i = makenode(ARRAY_ASSIGNOP_ARRAY_,$1.i,$3.i); list[$$.i].op1.assigntype = $2.i; list[$3.i].flags |= IS_RVALUE; } vcommand : arraylvalue assignop rexpr { $$.i = makenode(ARRAY_ASSIGNOP_SCALAR_,$1.i,$3.i); list[$$.i].op1.assigntype = $2.i; } vcommand : arraylvalue assignop rexpr '*' arraylvalue { int k = makenode(ARRAY_RVALUE_,$3.i,$5.i); list[k].op1.intval = '*'; $$.i = makenode(ARRAY_ASSIGNOP_S_X_A_,$1.i,k); list[$$.i].op1.assigntype = $2.i; list[$$.i].op3.name_id = list[$5.i].op2.name_id; list[$5.i].flags |= IS_RVALUE; check_special_attr(list[$5.i].op2.name_id); /* Can do dimension check now */ if ( check_array_dims_same(list[$1.i].op2.name_id, list[$5.i].op2.name_id ) == 0 ) kb_error(4379,"Arrays don't have same number of dimensions or types are different.\n", COMMAND_ERROR); } vcommand : arraylvalue assignop arraylvalue '+' arraylvalue { int k = makenode(ARRAY_RVALUE_,$3.i,$5.i); list[k].op1.intval = '+'; $$.i = makenode(ARRAY_ASSIGNOP_A_P_A_,$1.i,k); list[$$.i].op1.assigntype = $2.i; list[$$.i].op3.name_id = list[$3.i].op2.name_id; list[$$.i].op4.name_id = list[$5.i].op2.name_id; list[$3.i].flags |= IS_RVALUE; list[$5.i].flags |= IS_RVALUE; check_special_attr(list[$3.i].op2.name_id); check_special_attr(list[$5.i].op2.name_id); /* Can do dimension check now */ if ( check_array_dims_same(list[$1.i].op2.name_id, list[$3.i].op2.name_id) == 0 ) kb_error(4380,"Arrays don't have same number of dimensions or types are different.\n", COMMAND_ERROR); if ( check_array_dims_same(list[$1.i].op2.name_id, list[$5.i].op2.name_id) == 0 ) kb_error(4381,"Arrays don't have same number of dimensions or types are different.\n", COMMAND_ERROR); } vcommand : arraylvalue assignop arraylvalue '-' arraylvalue { int k = makenode(ARRAY_RVALUE_,$3.i,$5.i); list[k].op1.intval = '-'; $$.i = makenode(ARRAY_ASSIGNOP_A_S_A_,$1.i,k); list[$$.i].op1.assigntype = $2.i; list[$$.i].op3.name_id = list[$3.i].op2.name_id; list[$$.i].op4.name_id = list[$5.i].op2.name_id; list[$3.i].flags |= IS_RVALUE; list[$5.i].flags |= IS_RVALUE; check_special_attr(list[$3.i].op2.name_id); check_special_attr(list[$5.i].op2.name_id); /* Can do dimension check now */ if ( check_array_dims_same(list[$1.i].op2.name_id, list[$3.i].op2.name_id) == 0 ) kb_error(3030,"Arrays don't have same number of dimensions or types are different.\n", COMMAND_ERROR); if ( check_array_dims_same(list[$1.i].op2.name_id, list[$5.i].op2.name_id) == 0 ) kb_error(4383,"Arrays don't have same number of dimensions or types are different.\n", COMMAND_ERROR); } rexpr : arraylvalue DOT_ arraylvalue { $$.i = makenode(DOT_,$1.i,$3.i); list[$1.i].flags |= IS_RVALUE; list[$3.i].flags |= IS_RVALUE; } rexpr : arraylvalue indexset { $$.i = makenode(ARRAY_EVAL_,$1.i,$2.i); list[$1.i].flags |= IS_RVALUE; } /*********** end whole-array syntax *************************/ extraat : EXTRA_ATTRIBUTE_ { $$.i = $1.i; } extraat : ARRAY_ATTRIBUTE_ { $$.i = $1.i; } defextra : DEFINE_ eltype ATTRIBUTE_ extraat datatype { struct extra *ex; $$.qnum = $4.qnum; $$.etype = $4.etype; if ( $2.i != $$.etype ) kb_error(1885,"This extra attribute already defined on different element type.\n",COMMAND_ERROR); ex = EXTRAS($$.etype) + $$.qnum; if ( ex->type != $5.i ) { sprintf(errmsg, "Attribute %s already defined with different type, %s.\n", ex->name,datatype_name[ex->type]); kb_error(1886,errmsg,COMMAND_ERROR); } $$.i = makenode(DEFINE_EXTRA_,0,$2.i); list[$$.i].op1.extranum = $$.qnum; } defextra : DEFINE_ eltype ATTRIBUTE_ NEWIDENT_ datatype { int attr_type=INTEGER_TYPE; if ( $4.i ) { sprintf(errmsg,"Cannot use local variable \"%s\" as attribute.\n", $4.lexeme); kb_error(2615,errmsg,COMMAND_ERROR); } attr_type = $5.i; $$.qnum = add_attribute($2.i,$4.lexeme,attr_type,0,NULL,DUMP_ATTR,NULL); /* being a declaration, has effect when parsed */ $$.i = makenode(DEFINE_EXTRA_,0,$2.i); list[$$.i].op1.extranum = $$.qnum; } vcommand : defextra dimensionset { $$.i = makenode(DEFINE_EXTRA_INDEX_,$1.i,$2.i); } vcommand : defextra FUNCTION_ { begin_scope(); /* ended right below */ elsym = symbol_add("self",list[$1.i].op2.eltype); $$.i = makenode(ATTR_FUNCTION_,$1.i,0); } '{' { init_local_scope(0); begin_local_scope(); } commands '}' { struct extra *ext; end_local_scope(); $$.i = makenode(ATTR_FUNCTION_END_,$3.i,$6.i); list[$$.i].op1.extranum = list[$1.i].op1.extranum; /* attr number */ list[$$.i].op2.eltype = list[$1.i].op2.eltype; /* element type */ list[$3.i].op1.skipsize = $$.i - $3.i; ext = EXTRAS(list[$$.i].op2.eltype) + list[$$.i].op1.extranum; ext->code.locals = localbase; if ( localbase ) localbase->flags |= LL_IN_USE; exit_local_scope(); ext->flags |= FUNCTION_ATTR; end_scope(); } vcommand : defextra { $$.i = $1.i; } vcommand : DEFINE_ QUANTITY_ { $$.i = makenode(DEFINE_QUANTITY_,0,0); } vcommand : DEFINE_ METHOD_INSTANCE_ { $$.i = makenode(DEFINE_METHOD_INSTANCE_,0,0); } vcommand : DEFINE_ CONSTRAINT_ { $$.i = makenode(DEFINE_CONSTRAINT_,0,0); } vcommand : DEFINE_ BOUNDARY_ { $$.i = makenode(DEFINE_BOUNDARY_,0,0); } vcommand : DEFINE_ error { kb_error(2379, "Syntax: DEFINE name [REAL|INTEGER]\n or: DEFINE elementtype ATTRIBUTE name REAL|INTEGER [ dimension ] \n",Q_ERROR); } newlvalue : NEWIDENT_ ASSIGN_ { strncpy($$.lexeme,$1.lexeme,31); /* if ( $$.i == 0 ) ?? */ $$.i = add_global($1.lexeme); /* else $$.i = $1.i; */ /* local ?? */ $$.qnum = assignbacktrack(); } new_permlvalue : NEWIDENT_ PERM_ASSIGN_ { $$.i = add_perm_global($1.lexeme); perm_globals($$.i)->flags |= PERMANENT; perm_flag++; $$.qnum = assignbacktrack(); } newlvalue : SET_ NEWIDENT_ { /* if ( $$.i == 0 ) ?? */ $$.i = add_global($2.lexeme); /* else $$.i = $2.i; ?? */ /* local */ } ASSIGN_ { $$.i = $2.i;$$.qnum = assignbacktrack(); } newlvalue : SET_ NEWIDENT_ { /* if ( $$.i == 0 ) ?? */ $$.i = add_global($2.lexeme); /* else $$.i = $2.i; ?? */ $$.qnum = assignbacktrack(); strcpy($$.lexeme,$2.lexeme); } command : NEWIDENT_ error { kb_error(2380,"Syntax: variable := rexpr | {command} \n",Q_ERROR);} rexpr : NEWIDENT_ error { sprintf(errmsg,"Syntax error: Unexpected new identifier '%s'.\n",$1.lexeme); kb_error(2381,errmsg, Q_ERROR);} command : NEWIDENT_ ASSIGN_ error { kb_error(2382,"Syntax: variable := rexpr | {command} (braces needed around command) \n",Q_ERROR);} command : newlvalue stringexpr { $$.i = makenode(SET_SGLOBAL_,$1.i,$2.i); } command : newlvalue rexpr { $$.i = makenode(SET_GLOBAL_,$1.i,$2.i); } command : newlvalue error { sprintf(errmsg,"Illegal right side of assignment.\n"); kb_error(3756,errmsg, Q_ERROR);} command : new_permlvalue stringexpr { $$.i = makenode(SET_PERM_SGLOBAL_,$1.i,$2.i); } command : new_permlvalue rexpr { $$.i = makenode(SET_PERM_GLOBAL_,$1.i,$2.i); } command : newlvalue { init_local_scope($1.i); begin_local_scope(); } vcommand { int insize = inputbufferspot - $1.qnum; globals($1.i)->attr.procstuff.proc_text = mycalloc(insize+1,1); strncpy(globals($1.i)->attr.procstuff.proc_text,inputbuffer+$1.qnum,insize); $$.i = makenode(SET_PROCEDURE_,$3.i,$1.i); globals($1.i)->attr.procstuff.proc_text[insize] = 0; exit_local_scope(); } command : newlvalue '{' { init_local_scope($1.i); begin_local_scope(); } commands '}' { int k,insize = inputbufferspot - $1.qnum; globals($1.i)->attr.procstuff.proc_text = mycalloc(insize+1,1); strncpy(globals($1.i)->attr.procstuff.proc_text,inputbuffer+$1.qnum,insize); globals($1.i)->attr.procstuff.proc_text[insize] = 0; k = makenode(COMMAND_BLOCK_,$4.i,0); $$.i = makenode(SET_PROCEDURE_,k,$1.i); exit_local_scope(); } command : newlvalue '{' '}' { int k = makenode(NULLBLOCK_,0,0); localbase = NULL; $$.i = makenode(SET_PROCEDURE_,k,$1.i); } command : new_permlvalue '{' { init_local_scope($1.i); begin_local_scope(); } commands '}' { int k,insize = inputbufferspot - $1.qnum; perm_globals($1.i)->attr.procstuff.proc_text = calloc(insize+1,1); strncpy(perm_globals($1.i)->attr.procstuff.proc_text,inputbuffer+$1.qnum,insize); perm_globals($1.i)->attr.procstuff.proc_text[insize] = 0; k = makenode(COMMAND_BLOCK_,$4.i,0); $$.i = makenode(SET_PERM_PROCEDURE_,k,$1.i); exit_local_scope(); } command : new_permlvalue '{' '}' { int k = makenode(NULLBLOCK_,0,0); localbase = NULL; $$.i = makenode(SET_PERM_PROCEDURE_,k,$1.i); } command : PROCEDURE_ ASSIGN_ { $$.qnum = assignbacktrack(); init_local_scope($1.i); begin_local_scope(); } command { int insize = inputbufferspot - $3.qnum; myfree(globals($1.i)->attr.procstuff.proc_text); globals($1.i)->attr.procstuff.proc_text = mycalloc(insize+1,1); strncpy(globals($1.i)->attr.procstuff.proc_text,inputbuffer+$3.qnum,insize); globals($1.i)->attr.procstuff.proc_text[insize] = 0; $$.i = makenode(SET_PROCEDURE_,$4.i,$1.i); exit_local_scope(); } command : PERM_PROCEDURE_ PERM_ASSIGN_ { $$.i = perm_flag++; $$.qnum = assignbacktrack(); init_local_scope($1.i); begin_local_scope(); } command { int insize = inputbufferspot - $3.qnum; free(perm_globals($1.i)->attr.procstuff.proc_text); perm_globals($1.i)->attr.procstuff.proc_text = calloc(insize+1,1); strncpy(perm_globals($1.i)->attr.procstuff.proc_text,inputbuffer+$3.qnum,insize); perm_globals($1.i)->attr.procstuff.proc_text[insize] = 0; perm_globals($1.i)->flags |= PERMANENT; $$.i = makenode(SET_PERM_PROCEDURE_,$4.i,$1.i); perm_flag = $3.i; exit_local_scope(); } command : PROCEDURE_ ASSIGN_ error { kb_error(2383,"Syntax: procedure_name := {command} \n",Q_ERROR);} command : PROCEDURE_ PERM_ASSIGN_ error { kb_error(2384,"Syntax: procedure_name ::= {command} \n",Q_ERROR);} command : identassign '{' { sprintf(errmsg, "'%s' is a variable; cannot be assigned a procedure.\n", globals($1.i)->name); kb_error(3899,errmsg,Q_ERROR); } /**************************************************************************/ vcommand : LOCAL_ localidlist { $$.i = makenode(LOCAL_LIST_START_,$2.i,0); } localidlist: localid { $$.i = $1.i; } localidlist: localidlist ',' localid { $$.i = $3.i; list[$3.i].left = -1; } localid : NEWIDENT_ { ident_t iid = add_local_var($1.lexeme,1); $$.i = makenode(DECLARE_LOCAL_,iid,0); } localid : IDENT_ { ident_t iid = add_local_var($1.lexeme,1); $$.i = makenode(DECLARE_LOCAL_,iid,0); if ( shadow_warn_flag ) { sprintf(errmsg, "Local name \"%s\" shadows already declared variable.\n", $1.lexeme); kb_error(2625,errmsg,WARNING); } } localid : ARRAYIDENT_ { ident_t iid = add_local_var($1.lexeme,1); $$.i = makenode(DECLARE_LOCAL_,iid,0); if ( shadow_warn_flag ) { sprintf(errmsg, "Local name \"%s\" shadows already declared variable.\n", $1.lexeme); kb_error(2626,errmsg,WARNING); } } localid : PROCEDURE_ { ident_t iid = add_local_var($1.lexeme,1); $$.i = makenode(DECLARE_LOCAL_,iid,0); if ( shadow_warn_flag ) { sprintf(errmsg, "Local name \"%s\" shadows already declared procedure.\n", $1.lexeme); kb_error(2627,errmsg,WARNING); } } localid : FUNCTION_IDENT_ { ident_t iid = add_local_var($1.lexeme,1); $$.i = makenode(DECLARE_LOCAL_,iid,0); if ( shadow_warn_flag ) { sprintf(errmsg, "Local name \"%s\" shadows already declared function.\n", $1.lexeme); kb_error(2628,errmsg,WARNING); } } localid : STRINGGLOBAL_ { ident_t iid = add_local_var($1.lexeme,1); $$.i = makenode(DECLARE_LOCAL_,iid,0); if ( shadow_warn_flag ) { sprintf(errmsg, "Local name \"%s\" shadows already declared string variable.\n", $1.lexeme); kb_error(2629,errmsg,WARNING); } } localid : QUANTITY_NAME_ {ident_t iid = add_local_var($1.lexeme,1); $$.i = makenode(DECLARE_LOCAL_,iid,0); if ( shadow_warn_flag ) { sprintf(errmsg, "Local name \"%s\" shadows already declared quantity name.\n", $1.lexeme); kb_error(2630,errmsg,WARNING); } } localid : METHOD_NAME_ {ident_t iid = add_local_var($1.lexeme,1); $$.i = makenode(DECLARE_LOCAL_,iid,0); if ( shadow_warn_flag ) { sprintf(errmsg, "Local name \"%s\" shadows already declared method name.\n", $1.lexeme); kb_error(2631,errmsg,WARNING); } } localid : CONSTRAINT_NAME_ {ident_t iid = add_local_var($1.lexeme,1); $$.i = makenode(DECLARE_LOCAL_,iid,0); if ( shadow_warn_flag ) { sprintf(errmsg, "Local name \"%s\" shadows already declared constraint.\n", $1.lexeme); kb_error(2632,errmsg,WARNING); } } localid : BOUNDARY_NAME_ {ident_t iid = add_local_var($1.lexeme,1); $$.i = makenode(DECLARE_LOCAL_,iid,0); if ( shadow_warn_flag ) { sprintf(errmsg, "Local name \"%s\" shadows already declared boundary.\n", $1.lexeme); kb_error(2633,errmsg,WARNING); } } localid : error { kb_error(2614,"Syntax: LOCAL varname; \n",Q_ERROR);} /**************************************************************************/ vcommand : SINGLE_LETTER_ REDEFINE_ { init_local_scope(0); begin_local_scope(); } command { $$.i = makenode(REDEFINE_SINGLE_,$4.i,$1.i); exit_local_scope(); } vcommand : SINGLE_LETTER_ARG_ REDEFINE_ { init_local_scope(0); begin_local_scope(); } command { $$.i = makenode(REDEFINE_SINGLE_,$4.i,$1.i); exit_local_scope(); } vcommand : SINGLE_REDEFD_ REDEFINE_ { init_local_scope(0); begin_local_scope(); } command { $$.i = makenode(REDEFINE_SINGLE_,$4.i,$1.i); exit_local_scope(); } vcommand : SINGLE_LETTER_ REDEFINE_ { $$.i = makenode(UNREDEFINE_SINGLE_,0,$1.i); } vcommand : SINGLE_REDEFD_ REDEFINE_ { $$.i = makenode(UNREDEFINE_SINGLE_,0,$1.i); } /**************************************************************************/ exprlist : rexpr ',' exprlist { $$.i = makenode(EXPRLIST_,$1.i,$3.i); } exprlist : rexpr ',' error { kb_error(3801,"Missing expression after ','\n", Q_ERROR); } exprlist : rexpr { $$.i = makenode(EXPRLIST_,$1.i,0); } exprlist : stringexpr ',' exprlist { $$.i = makenode(EXPRLIST_,$1.i,$3.i); } exprlist : stringexpr ',' error { kb_error(3891,"Missing expression after ','\n", Q_ERROR); } exprlist : stringexpr { $$.i = makenode(EXPRLIST_,$1.i,0); } /**************************************************************************/ printfhead : PRINTF_ stringexpr { $$.i = makenode(PRINTFHEAD_,$2.i,0); } printfhead : PRINTF_ error { kb_error(3892,"Missing format string after printf.\n", Q_ERROR); } binaryprintfhead : BINARY_PRINTF_ stringexpr { $$.i = makenode(BINARY_PRINTFHEAD_,$2.i,0); } binaryprintfhead : BINARY_PRINTF_ error { kb_error(4892,"Missing format string after printf.\n", Q_ERROR); } errprintfhead : ERRPRINTF_ stringexpr { $$.i = makenode(ERRPRINTFHEAD_,$2.i,0); } errprintfhead : ERRPRINTF_ error { kb_error(3802,"Missing format string after errprintf.\n", Q_ERROR); } vcommand : printfhead { $$.i = $1.i; } vcommand : printfhead ',' exprlist { $$.i = makenode(PRINTF_,$1.i,$3.i); } vcommand : printfhead ',' error { kb_error(3893,"Missing expression after ','\n", Q_ERROR); } vcommand : binaryprintfhead { $$.i = $1.i; } vcommand : binaryprintfhead ',' exprlist { $$.i = makenode(BINARY_PRINTF_,$1.i,$3.i); } vcommand : binaryprintfhead ',' error { kb_error(4893,"Missing expression after ','\n", Q_ERROR); } vcommand : errprintfhead { $$.i = $1.i; } vcommand : errprintfhead ',' exprlist { $$.i = makenode(ERRPRINTF_,$1.i,$3.i); } vcommand : errprintfhead ',' error { $$.i = makenode(ERRPRINTF_,$1.i,$3.i); } { kb_error(3803,"Missing expression after ','\n", Q_ERROR); } /**************************************************************************/ print : PRINT_ vcommand : print PROCEDURE_WORD_ { $$.i = makenode(LIST_PROCS_,0,0); } vcommand : print stringexpr { $$.i = makenode(STRPRINT_,$2.i,0); } vcommand : print PROCEDURE_ { $$.i = makenode(PRINT_PROCEDURE_,$2.i,0); } vcommand : print FUNCTION_IDENT_ { $$.i = makenode(PRINT_PROCEDURE_,$2.i,0); } vcommand : print PROCEDURE_IDENT_ { $$.i = makenode(PRINT_PROCEDURE_,$2.i,0); } vcommand : print PERM_PROCEDURE_ { $$.i = makenode(PRINT_PERM_PROCEDURE_,$2.i,0); } vcommand : EXPRINT_ PROCEDURE_ { $$.i = makenode(EXPRINT_PROCEDURE_,$2.i,0); } /* vcommand : print arrayhead { $$.i = makenode(PRINT_ARRAYPART_,$2.i,0); } */ vcommand : print rexpr { $$.i = makenode(PRINT_,$2.i,0); } /* the previous two rules give a reduce/reduce conflict, resolved by yacc in favor of the first, which is ok since it prints a fully indexed array entry as a scalar. */ vcommand : print arraylvalue { $$.i = makenode(PRINT_ARRAY_LVALUE_,$2.i,0); list[$2.i].flags |= IS_RVALUE; } vcommand: print singlep ggetattrib { int k; switch ( $3.i ) { case COORD_: switch ( $2.etype ) { case VERTEX: case EDGE: case FACET: k = makenode(ATTRIBUTE,$3.i,$3.qnum); list[k].op1.localnum = list[$2.i].op2.localnum; list[k].op2.coordnum = $3.qnum - 1; /* 1-based indexing to 0 */ k = makenode(QUALIFIED_ATTRIBUTE,$2.i,k); $$.i = makenode(PRINT_,k,0); goto vnexit; default: sprintf(errmsg,"\"x\" is not a %s attribute.\n", typenames[$2.etype]); kb_error(2650,errmsg,COMMAND_ERROR); } break; case GET_VERTEXNORMAL_: if ( $2.etype != VERTEX ) { sprintf(errmsg,"\"vertexnormal\" is vertex attribute; cannot be on %s.\n", typenames[$2.etype]); kb_error(2651,errmsg,COMMAND_ERROR); } $$.i = makenode(PRINT_VERTEXNORMAL_,$2.i,0); list[$$.i].op1.localnum = list[$2.i].op2.localnum; goto vnexit; break; case PARAM_: if ( $2.etype != VERTEX ) { sprintf(errmsg,"\"p\" is %s attribute; cannot be on %s.\n", typenames[VERTEX], typenames[$2.etype]); kb_error(2652,errmsg,COMMAND_ERROR); } int_val = V_PARAM_ATTR; break; case GET_EXTRA_ATTR_: if ( $2.etype != $3.etype ) { sprintf(errmsg,"\"%s\" is %s attribute; cannot be on %s.\n", EXTRAS($3.etype)[$3.qnum & YYSHIFTMASK].name, typenames[$3.etype], typenames[$2.etype]); kb_error(2653,errmsg,COMMAND_ERROR); } int_val = $3.qnum; break; default: k = makenode(ATTRIBUTE,$3.i,$3.qnum); list[k].op1.localnum = list[$2.i].op2.localnum; k = makenode(QUALIFIED_ATTRIBUTE,$2.i,k); $$.i = makenode(PRINT_,k,0); goto vnexit; } int_val |= ($2.etype << YYTYPESHIFT); $$.i = makenode(PRINT_ATTR_ARRAY_,$2.i,0); vnexit: ; } /* rexpr : arrayhead { $$.i = makenode(ARRAYEVAL,$1.i,0); } */ vcommand : print SINGLE_LETTER_ { $$.i = makenode(PRINT_LETTER_,$2.i,0); } vcommand : print SINGLE_LETTER_ARG_ { $$.i = makenode(PRINT_LETTER_,$2.i,0); } vcommand : print SINGLE_REDEFD_ { $$.i = makenode(PRINT_LETTER_,$2.i,0); } vcommand : print error { kb_error(2385, "Syntax: PRINT procedure | expression | stringexpression \n",Q_ERROR ); } /**************************************************************************/ vcommand : SHOW_TRANS_ stringexpr { $$.i = makenode(SHOW_TRANS_,$2.i,0); } vcommand : SHOW_TRANS_ error { kb_error(2386,"Syntax: SHOW_TRANS \"string\"\n",Q_ERROR);} /**************************************************************************/ backquote : '`' { backquote_flag = 1; $$.i = makenode(BACKQUOTE_START_,0,0); if ( local_nest_depth == 0 ) init_local_scope(0); begin_local_scope(); list[listtop++].type = SETUP_FRAME_; } rexpr : backquote commands BACKQUOTE_COMMA_ { verb_flag = 0; backquote_flag = 0; end_local_scope(); $$.i = makenode(BACKQUOTE_END_,$1.i,$2.i);} rexpr { $$.i = makenode(ACOMMANDEXPR_,$4.i,$5.i); } rexpr : backquote error { backquote_flag = 0; kb_error(3805,"Backquote syntax: ` commands ` , expression\n", Q_ERROR); } /**************************************************************************/ rexpr : FUNCTION_IDENT_ '(' ')' { $$.i = makenode(FUNCTION_CALL_,$1.i,0); $$.i = makenode(FUNCTION_CALL_RETURN_,$$.i,0); } rexpr : FUNCTION_IDENT_ '(' exprlist ')' { $$.i = makenode(FUNCTION_CALL_,$1.i,$3.i); makenode(FUNCTION_CALL_RETURN_,$$.i,0); } rexpr : FUNCTION_IDENT_ error { kb_error(2870, "Function call needs argument list.\n",Q_ERROR); } /**************************************************************************/ vcommand : PROCEDURE_IDENT_ '(' ')' { $$.i = makenode(PROCEDURE_CALL_,$1.i,0); $$.i = makenode(PROCEDURE_CALL_RETURN_,$$.i,0); } vcommand : PROCEDURE_IDENT_ '(' exprlist ')' { $$.i = makenode(PROCEDURE_CALL_,$1.i,$3.i); makenode(PROCEDURE_CALL_RETURN_,$$.i,0); } vcommand : PROCEDURE_IDENT_ error { kb_error(2871, "Procedure call needs argument list.\n",Q_ERROR); } /**************************************************************************/ vcommand : WRAP_VERTEX_ '(' rexpr ',' rexpr ')' { $$.i = makenode(WRAP_VERTEX_,$3.i,$5.i); } vcommand : WRAP_VERTEX_ error { kb_error(3808,"Syntax: wrap_vertex(vertex_number,wrap_code_number)\n", Q_ERROR); } /**************************************************************************/ rexpr : VIEW_TRANSFORM_PARITY_ '[' rexpr ']' { $$.i = makenode(VIEW_TRANSFORM_PARITY_,$3.i,0); } rexpr : VIEW_TRANSFORM_PARITY_ error { kb_error(2602, "view_transform_parity needs one index.\n", Q_ERROR); } /**************************************************************************/ rexpr : DISPLAY_TEXT_ '(' rexpr ',' rexpr ',' stringexpr ')' { int nn = makenode(TEXT_SPOT_,$3.i,$5.i); $$.i = makenode(DISPLAY_TEXT_,nn,$7.i); } rexpr : DISPLAY_TEXT_ error { kb_error(4683,"Syntax: text_id := DISPLAY_TEXT(x,y,string)\n",Q_ERROR ); } command : DISPLAY_TEXT_ { kb_error(3254,"Syntax: text_id := DISPLAY_TEXT(x,y,string)\n",Q_ERROR ); } command : DELETE_TEXT_ '(' rexpr ')' { $$.i = makenode(DELETE_TEXT_,$3.i,0); } command : DELETE_TEXT_ error { kb_error(4684,"Syntax: DELETE_TEXT(text_id)\n",Q_ERROR ); } /**************************************************************************/ rexpr : NEWVERTEX_ '(' exprlist ')' { $$.i = makenode(CREATE_VERTEX_,$3.i,0); } rexpr : NEWVERTEX_ error { kb_error(2387,"Syntax: NEW_VERTEX(x,y,...) \n",Q_ERROR); } /**************************************************************************/ rexpr : NEWEDGE_ '(' rexpr ',' rexpr ')' { $$.i = makenode(CREATE_EDGE_,$3.i,$5.i); } rexpr : NEWEDGE_ error { kb_error(2388,"Syntax: NEW_EDGE(tail_id,head_id) \n",Q_ERROR); } /**************************************************************************/ rexpr : NEWFACET_ '(' exprlist ')' { $$.i = makenode(CREATE_FACET_,$3.i,0); } rexpr : NEWFACET_ error { kb_error(2389,"Syntax: NEW_FACET(edge1,edge2,...) \n",Q_ERROR); } /**************************************************************************/ rexpr : NEWBODY_ { $$.i = makenode(CREATE_BODY_,0,0); } rexpr : NEWBODY_ error { kb_error(2390,"Syntax: NEW_BODY \n",Q_ERROR); } /**************************************************************************/ elindex : rexpr { $$.i = makenode(ELINDEX_,$1.i,0); } elindex : rexpr '@' rexpr { $$.i = makenode(ELINDEX_,$1.i,$3.i); } elindex : INTEGER_AT_ { $$.i = makenode(PUSH_ELEMENT_ID_,$1.i,$1.qnum); } elindex : UMINUS_ INTEGER_AT_ { $$.i = makenode(PUSH_ELEMENT_ID_,-$1.i,$1.qnum); } elindex : LEAD_INTEGER_AT_ { $$.i = makenode(PUSH_ELEMENT_ID_,$1.i,$1.qnum); } elindex : UMINUS_ LEAD_INTEGER_AT_ { $$.i = makenode(PUSH_ELEMENT_ID_,-$1.i,$1.qnum); } rexpr : VALID_ELEMENT_ '(' eltype '[' elindex ']' ')' { $$.i = makenode(VALID_ELEMENT_,$3.i,$5.i); } rexpr : VALID_ELEMENT_ error { kb_error(3904,"Syntax: valid_element(element_type[expr]) \n",Q_ERROR); } /**************************************************************************/ rexpr : VALID_CONSTRAINT_ '(' rexpr ')' { $$.i = makenode(VALID_CONSTRAINT_,$3.i,0); } rexpr : VALID_CONSTRAINT_ error { kb_error(1901,"Syntax: valid_constraint(expr) \n",Q_ERROR); } /**************************************************************************/ rexpr : VALID_BOUNDARY_ '(' rexpr ')' { $$.i = makenode(VALID_BOUNDARY_,$3.i,0); } rexpr : VALID_BOUNDARY_ error { kb_error(3029,"Syntax: valid_boundary(expr) \n",Q_ERROR); } /**************************************************************************/ vcommand : MERGE_VERTEX_ '(' rexpr ',' rexpr ')' { $$.i = makenode(MERGE_VERTEX_,$3.i,$5.i); } vcommand : MERGE_VERTEX_ error { kb_error(3885,"Syntax: VERTEX_MERGE(first_id,second_id) \n",Q_ERROR); } /**************************************************************************/ vcommand : MERGE_EDGE_ '(' rexpr ',' rexpr ')' { $$.i = makenode(MERGE_EDGE_,$3.i,$5.i); } vcommand : MERGE_EDGE_ error { kb_error(3637,"Syntax: EDGE_MERGE(first_oid,second_oid) \n",Q_ERROR); } /**************************************************************************/ vcommand : MERGE_FACET_ '(' rexpr ',' rexpr ')' { $$.i = makenode(MERGE_FACET_,$3.i,$5.i); } vcommand : MERGE_FACET_ error { kb_error(3607,"Syntax: FACET_MERGE(first_oid,second_oid) \n",Q_ERROR); } /**************************************************************************/ vcommand : MATRIX_MULTIPLY_ '(' arraylvalue ',' arraylvalue ',' arraylvalue ')' { int_val = $7.i; $$.i = makenode(MATRIX_MULTIPLY_,$3.i,$5.i); } vcommand : MATRIX_MULTIPLY_ error { kb_error(3790,"matrix_multiply syntax: matrix_multiply(mat1,mat2,mat3)\n", COMMAND_ERROR); } vcommand : MATRIX_MULTIPLY_ '(' arraylvalue ',' arraylvalue ',' error { kb_error(3791,"matrix_multiply third argument is not an array.\n", COMMAND_ERROR); } vcommand : MATRIX_MULTIPLY_ '(' arraylvalue ',' error { kb_error(3792,"matrix_multiply second argument is not an array.\n", COMMAND_ERROR); } vcommand : MATRIX_MULTIPLY_ '(' error { kb_error(3793,"matrix_multiply first argument is not an array.\n", COMMAND_ERROR); } rexpr : MATRIX_DETERMINANT_ '(' arraylvalue ')' { $$.i = makenode(MATRIX_DETERMINANT_,$3.i,0); } rexpr : MATRIX_INVERSE_ '(' arraylvalue ',' arraylvalue ')' { $$.i = makenode(MATRIX_INVERSE_,$3.i,$5.i); } rexpr : MATRIX_INVERSE_ error { kb_error(3794,"matrix_inverse syntax: matrix_inverse(mat1,mat2)\n", COMMAND_ERROR); } rexpr : MATRIX_INVERSE_ '(' arraylvalue ',' error { kb_error(3795,"matrix_inverse second argument is not an array.\n", COMMAND_ERROR); } rexpr : MATRIX_INVERSE_ '(' error { kb_error(3796,"matrix_inverse first argument is not an array.\n", COMMAND_ERROR); } /**************************************************************************/ verb : LIST_ { $$.i = LIST_; loopdepth++; } vcommand : LIST_ error { erroutstring("Syntax: LIST element_gen [ name ] [ WHERE rexpr ]\n"); erroutstring(" LIST TOPINFO\n"); erroutstring(" LIST BOTTOMINFO\n"); erroutstring(" LIST ATTRIBUTES\n"); erroutstring(" LIST PROCEDURES\n"); erroutstring(" LIST QUANTITY quantityname\n"); erroutstring(" LIST METHOD_INSTANCE instancename\n"); erroutstring(" LIST CONSTRAINT rexpr or name\n"); erroutstring(" LIST BOUNDARY rexpr or name\n"); kb_error(1899,NULL,Q_ERROR); } /**************************************************************************/ verb : DELETE_ { $$.i = DELETE_; loopdepth++; } vcommand : DELETE_ error { kb_error(2391,"Syntax: DELETE element_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); } /**************************************************************************/ verb : VERTEX_AVERAGE_ { $$.i = VERTEX_AVERAGE_; loopdepth++; } verb : RAW_VERTEX_AVERAGE_ { $$.i = RAW_VERTEX_AVERAGE_; loopdepth++; } verb : RAWEST_VERTEX_AVERAGE_ { $$.i = RAWEST_VERTEX_AVERAGE_; loopdepth++; } vcommand : VERTEX_AVERAGE_ error { kb_error(2392,"Syntax: VERTEX_AVERAGE element_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); } vcommand : RAW_VERTEX_AVERAGE_ error { kb_error(2393,"Syntax: RAW_VERTEX_AVERAGE element_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); } vcommand : RAWEST_VERTEX_AVERAGE_ error { kb_error(2394,"Syntax: RAWEST_VERTEX_AVERAGE element_gen [ name ] [ WHERE expr ]\n",Q_ERROR); } /**************************************************************************/ verb : DISSOLVE_ { $$.i = DISSOLVE_; loopdepth++; } vcommand : DISSOLVE_ error { kb_error(2395,"Syntax: DISSOLVE element_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); } /**************************************************************************/ verb : REVERSE_ORIENTATION_ { $$.i = REVERSE_ORIENTATION_; loopdepth++; } vcommand : REVERSE_ORIENTATION_ error { kb_error(3250,"Syntax: REVERSE_ORIENTATION element_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); } /**************************************************************************/ verb : REFINE_ { $$.i = REFINE_; loopdepth++; } vcommand : REFINE_ error { kb_error(2396,"Syntax: REFINE element_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); } /**************************************************************************/ verb : EDGESWAP_ { $$.i = EDGESWAP_; loopdepth++; } vcommand : EDGESWAP_ error { kb_error(2397,"Syntax: EDGESWAP edge_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); } /**************************************************************************/ verb : T1_EDGESWAP_ { $$.i = T1_EDGESWAP_; loopdepth++; } vcommand : T1_EDGESWAP_ error { kb_error(4009,"Syntax: T1_EDGESWAP edge_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); } /**************************************************************************/ verb : EQUIANGULATE_ { $$.i = EQUIANGULATE_; loopdepth++; } vcommand : EQUIANGULATE_ error { kb_error(2545,"Syntax: EQUIANGULATE_ edge_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); } /**************************************************************************/ verb : POP_ { $$.i = POP_; loopdepth++; } vcommand : POP_ error { kb_error(2431,"Syntax: POP element_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); } /**************************************************************************/ verb : POP_TRI_TO_EDGE_ { $$.i = POP_TRI_TO_EDGE_; loopdepth++; } vcommand : POP_TRI_TO_EDGE_ error { kb_error(2800, "Syntax: POP_TRI_TO_EDGE facet_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); } /**************************************************************************/ verb : POP_EDGE_TO_TRI_ { $$.i = POP_EDGE_TO_TRI_; loopdepth++; } vcommand : POP_EDGE_TO_TRI_ error { kb_error(2801,"Syntax: POP_EDGE_TO_TRI edge_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); } /**************************************************************************/ verb : POP_QUAD_TO_QUAD_ { $$.i = POP_QUAD_TO_QUAD_; loopdepth++; } vcommand : POP_QUAD_TO_QUAD_ error { kb_error(2802,"Syntax: POP_QUAD_TO_QUAD facet_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); } /**************************************************************************/ verb : FIX_ { $$.i = FIX_; loopdepth++; } verb : UNFIX_ { $$.i = UNFIX_; loopdepth++; } vcommand : FIX_ IDENT_ { $$.i = makenode(FIX_PARAMETER_,$2.i,0); } vcommand : UNFIX_ IDENT_ { $$.i = makenode(UNFIX_PARAMETER_,$2.i,0); } vcommand : FIX_ QUANTITY_NAME_ { $$.i = makenode(FIX_QUANTITY_,$2.i,0);} vcommand : UNFIX_ QUANTITY_NAME_ { $$.i = makenode(UNFIX_QUANTITY_,$2.i,0);} vcommand : FIX_ error { kb_error(2398,"Syntax: FIX element_gen [ name ] [ WHERE rexpr ] or FIX quantity_name\n", Q_ERROR); } vcommand : UNFIX_ error { kb_error(2399,"Syntax: UNFIX element_gen [ name ] [ WHERE rexpr ] or UNFIX quantity_name\n", Q_ERROR); } /**************************************************************************/ eltype: VERTICES_ { if ( const_expr_flag ) YYABORT; $$.etype = $$.i = VERTEX; } eltype: EDGES_ { if ( const_expr_flag ) YYABORT; $$.etype = $$.i = EDGE ; } eltype: FACETS_ { if ( const_expr_flag ) YYABORT; $$.etype = $$.i = FACET ; } eltype: BODIES_ { if ( const_expr_flag ) YYABORT; $$.etype = $$.i = BODY ; } eltype: FACETEDGES_ { if ( const_expr_flag ) YYABORT; $$.etype = $$.i = FACETEDGE; } multiple: eltype { int next; next = makenode(INIT_ELEMENT_,$1.i,0); $$.i = makenode(NEXT_ELEMENT_,next,0); $$.etype = $1.i; } multiple: singlep eltype { int next; next = makenode(INIT_SUBELEMENT_,$1.i,$2.i); $$.i = makenode(NEXT_ELEMENT_,next,0); $$.etype = $2.etype; } /* single: rexpr { if ( list[$1].datatype < VERTEX_TYPE || list[$1].datatype > FACETEDGE_TYPE ) kb_error(3712,"Need element here.\n",COMMAND_ERROR); $$ = $1; } */ single: eltype '[' elindex ']' { verb_flag = 0; $$.i = makenode(INDEXED_ELEMENT_,$1.i,$3.i); $$.etype = $1.i; } single: eltype '[' error { kb_error(3809,"Missing index of element.\n",Q_ERROR); } single: eltype '[' rexpr error { kb_error(3832,"Missing right bracket after index expression.\n",Q_ERROR); } single: SYMBOL_ { verb_flag = 0; $$.i = makenode(SYMBOL_ELEMENT_,$1.i,0); $$.etype = list[$$.i].op1.eltype; } single: SELF_ { verb_flag = 0; elsym = symbol_lookup("self"); if ( elsym == NULL ) kb_error(2400,"SELF not defined, since not in attribute function def.\n", COMMAND_ERROR); $$.i = makenode(SELF_ELEMENT_,elsym->type,0); $$.etype = elsym->type; } single: ELEMENT_IDENT_ { verb_flag = 0; $$.i = makenode(ELEMENT_IDENT_,$1.i,0); list[$$.i].op1.eltype = $1.etype; $$.etype = $1.etype; } singlep: single '.' { $$ = $1; } singlep: single { $$ = $1; } single: singlep eltype '[' rexpr ']' { subtype = $2.i; $$.i = makenode(INDEXED_SUBTYPE_,$1.i,$4.i); $$.etype = $2.etype; } single: singlep eltype '[' error { kb_error(3810,"Missing index of element.\n",Q_ERROR); } single: singlep eltype '[' rexpr error { kb_error(3812,"Missing right bracket after index expression.\n",Q_ERROR); } /* turn off element as datatype for now rexpr: single { $$.i = makenode(SINGLE_ELEMENT_EXPR_,$1.i,$1.etype); } */ element_gen: singlep { int type = list[$1.i].op1.eltype; begin_scope(); /* ended at end of aggregate */ elsym = symbol_add(default_name,type); elsym->localnum = list[$1.i].op2.localnum; strcpy(last_name,default_name); $1.symptr = elsym; $$.i = makenode(SINGLE_ELEMENT_,$1.i,0); $$.symptr = elsym; $$.etype = $1.etype; } element_gen: single NEWIDENT_ { int type = list[$1.i].op1.eltype; begin_scope(); /* ended at end of aggregate */ elsym = symbol_add($2.lexeme,type); elsym->localnum = list[$1.i].op2.localnum; strcpy(last_name,$2.lexeme); $$.i = makenode(SINGLE_ELEMENT_,$1.i,0); list[$1.i].op5.string = (char*)mycalloc(strlen(elsym->name)+1,1); list[$1.i].flags |= HAS_STRING_5; strcpy(list[$1.i].op5.string,elsym->name); $1.symptr = elsym; $$.symptr = elsym; elsym = symbol_add(default_name,type); /* current id as default */ elsym->localnum = list[$1.i].op2.localnum; $$.etype = $1.etype; } element_gen: multiple { int type = list[$1.i+list[$1.i].left].op1.eltype; begin_scope(); /* ended at end of aggregate */ elsym = symbol_add(default_name,type); elsym->localnum = list[$1.i].op2.localnum; strcpy(last_name,default_name); list[$1.i].op5.string = (char*)mycalloc(strlen(default_name)+1,1); list[$1.i].flags |= HAS_STRING_5; strcpy(list[$1.i].op5.string,default_name); $1.symptr = elsym; $$.symptr = elsym; $$.i = $1.i; $$.etype = $1.etype; } element_gen: multiple NEWIDENT_ { int type = list[$1.i+list[$1.i].left].op1.eltype; begin_scope(); /* ended at end of aggregate */ elsym = symbol_add($2.lexeme,type); elsym->localnum = list[$1.i].op2.localnum; strcpy(last_name,$2.lexeme); list[$1.i].op5.string = (char*)mycalloc(strlen(elsym->name)+1,1); list[$1.i].flags |= HAS_STRING_5; strcpy(list[$1.i].op5.string,elsym->name); $1.symptr = elsym; $$.symptr = elsym; $$.i = $1.i; elsym = symbol_add(default_name,type); /* current id as default */ elsym->localnum = list[$1.i].op2.localnum; $$.etype = $1.etype; } element_gen: single PROCEDURE_ { kb_error(1890,"Name already in use as procedure name.\n",COMMAND_ERROR); } element_gen: multiple PROCEDURE_ { kb_error(1891,"Name already in use as procedure name.\n",COMMAND_ERROR); } element_gen: single IDENT_ { kb_error(1892,"Name already in use as variable name.\n",COMMAND_ERROR); } element_gen: multiple IDENT_ { kb_error(1893,"Name already in use as variable name.\n",COMMAND_ERROR); } element_gen : element_gen WHERE_ rexpr { $$.i = makenode(WHERE_,$1.i,$3.i); $$.symptr = $1.symptr; } element_gen : element_gen WHERE_ error { kb_error(3901,"Missing boolean expression after WHERE.\n",Q_ERROR); } /*****************************************************************************/ toggle: ON_ { $$.i = ON_; } toggle: OFF_ { $$.i = OFF_; } /**************************************************************************/ quotation_concat : QUOTATION_ { $$.i = makenode(QUOTATION_,0,0); } quotation_concat : quotation_concat QUOTATION_ { int size1 = strlen(list[$$.i].op1.string); int size2 = strlen(yytext); $$.i = $1.i; list[$$.i].op1.string = (char*)kb_realloc(list[$$.i].op1.string, size1+size2+1); strncpy(list[$$.i].op1.string+size1,yytext,size2); } stringexpr : quotation_concat { $$.i = $1.i } stringexpr : STRINGGLOBAL_ { $$.i = makenode(STRINGGLOBAL_,$1.i,0); } stringexpr : PERM_STRINGGLOBAL_ { $$.i = makenode(PERM_STRINGGLOBAL_,$1.i,0); } stringexpr : DATAFILENAME_ { $$.i = makenode(DATAFILENAME_,$1.i,0); } stringexpr : WARNING_MESSAGES_ { $$.i = makenode(WARNING_MESSAGES_,$1.i,0); } stringexpr : DATE_AND_TIME_ { $$.i = makenode(DATE_AND_TIME_,0,0); } stringexpr : TRANSFORM_EXPR_ { $$.i = makenode(GET_TRANSFORM_EXPR_,0,0); } sprintfhead : SPRINTF_ stringexpr { $$.i = makenode(SPRINTFHEAD_,$2.i,0); } sprintfhead : SPRINTF_ error { kb_error(3894,"Missing format string after SPRINTF.\n",Q_ERROR); } stringexpr : sprintfhead { $$.i = $1.i; } stringexpr : sprintfhead ',' exprlist { $$.i = makenode(SPRINTF_,$1.i,$3.i); } stringexpr : sprintfhead ',' error { kb_error(3806,"Error in SPRINTF arguments.\n",Q_ERROR); } /**************************************************************************/ getattrib : COORD_ { $$.qnum = $1.i; $$.i = COORD_; $$.datatype=REAL_TYPE; } getattrib : PARAM_ { $$.qnum = $1.i; $$.i = PARAM_; $$.datatype=REAL_TYPE; } getattrib : LENGTH_ { $$.i = GET_LENGTH_; $$.datatype=REAL_TYPE; } getattrib : MEAN_CURVATURE_ { $$.i = GET_MEANCURV_; $$.datatype=REAL_TYPE; } getattrib : SHOW_ { $$.i = GET_SHOW_; $$.datatype=REAL_TYPE; } getattrib : ORIENTATION_ { $$.i = GET_ORIENTATION_; $$.datatype=REAL_TYPE; } getattrib : STAR_ { $$.i = GET_STAR_; $$.datatype=REAL_TYPE; } getattrib : SQ_MEAN_CURV_ { $$.i = GET_SQ_MEAN_CURV_; $$.datatype=REAL_TYPE; } getattrib : DIHEDRAL_ { $$.i = GET_DIHEDRAL_; $$.datatype=REAL_TYPE; } getattrib : VALENCE_ { $$.i = GET_VALENCE_; $$.datatype=REAL_TYPE; } getattrib : AREA_ { $$.i = GET_AREA_; $$.datatype=REAL_TYPE; } getattrib : VOLUME_ { $$.i = GET_VOLUME_; $$.datatype=REAL_TYPE; } getattrib : VOLCONST_ { $$.i = GET_VOLCONST_; $$.datatype=REAL_TYPE; } getattrib : TARGET_ { $$.i = GET_TARGET_; $$.datatype=REAL_TYPE; } getattrib : MPI_TASK_ATTR_ { $$.i = GET_MPI_TASK_; $$.datatype=REAL_TYPE; } getattrib : DENSITY_ { $$.i = GET_DENSITY_; $$.datatype=REAL_TYPE; } getattrib : PRESSURE_ { $$.i = GET_PRESSURE_; $$.datatype=REAL_TYPE; } getattrib : ID_ { $$.i = GET_ID_; $$.datatype=REAL_TYPE; } getattrib : OID_ { $$.i = GET_OID_; $$.datatype=REAL_TYPE; } getattrib : TAG_ { $$.i = GET_TAG_; $$.datatype=REAL_TYPE; } getattrib : COLOR_ { $$.i = GET_COLOR_; $$.datatype=REAL_TYPE; } getattrib : FRONTCOLOR_ { $$.i = GET_FRONTCOLOR_; $$.datatype=REAL_TYPE; } getattrib : BACKCOLOR_ { $$.i = GET_BACKCOLOR_; $$.datatype=REAL_TYPE; } getattrib : BACKBODY_ { $$.i = GET_BACKBODY_; $$.datatype=REAL_TYPE; } getattrib : FRONTBODY_ { $$.i = GET_FRONTBODY_; $$.datatype=REAL_TYPE; } getattrib : ORIGINAL_ { $$.i = GET_ORIGINAL_; $$.datatype=REAL_TYPE; } getattrib : FIXED_ { $$.i = GET_FIXED_; $$.datatype=REAL_TYPE; } getattrib : NO_REFINE_ { $$.i = GET_NO_REFINE_; $$.datatype=REAL_TYPE; } getattrib : HIT_PARTNER_ { $$.i = GET_HIT_PARTNER_; $$.datatype=REAL_TYPE;} getattrib : NONCONTENT_ { $$.i = GET_NONCONTENT_; $$.datatype=REAL_TYPE; } getattrib : NODISPLAY_ { $$.i = GET_NO_DISPLAY_; $$.datatype=REAL_TYPE; } getattrib : FIXEDVOL_ { $$.i = GET_FIXEDVOL_; $$.datatype=REAL_TYPE; } getattrib : AXIAL_POINT_ { $$.i = GET_AXIAL_POINT_; $$.datatype=REAL_TYPE; } getattrib : TRIPLE_POINT_ { $$.i = GET_TRIPLE_PT_; $$.datatype=REAL_TYPE; } getattrib : TETRA_POINT_ { $$.i = GET_TETRA_PT_; $$.datatype=REAL_TYPE; } getattrib : MIDV_ { $$.i = GET_MIDV_; $$.datatype=REAL_TYPE; } getattrib : WRAP_ { $$.i = GET_WRAP_; $$.datatype=REAL_TYPE; } getattrib : MID_EDGE_ { $$.i = GET_MID_EDGE_; $$.datatype=REAL_TYPE; } getattrib : MID_FACET_ { $$.i = GET_MID_FACET_; $$.datatype=REAL_TYPE; } getattrib : BARE_ { $$.i = GET_BARE_; $$.datatype=REAL_TYPE; } getattrib : PHASE_ { $$.i = GET_PHASE_; $$.datatype=REAL_TYPE; } getattrib : QUANTITY_NAME_ { $$.qnum = $1.i; $$.i = GET_QUANTITY_; $$.datatype=REAL_TYPE; } getattrib : METHOD_NAME_ { $$.qnum = $1.i; $$.i = GET_INSTANCE_; $$.datatype=REAL_TYPE; } getattrib : EXTRA_ATTRIBUTE_ { struct extra *ex; $$.i = GET_EXTRA_ATTR_ ; $$.qnum = $1.qnum + ($1.etype << YYTYPESHIFT); $$.etype = $1.etype; ex = EXTRAS($1.etype) + $1.qnum; $$.datatype= (ex->type <= MAX_NUMERIC_TYPE) ? REAL_TYPE : ex->type; } ggetattrib: getattrib { if ( const_expr_flag ) { YYABORT; /* illegal for const rexpr */ } $$= $1; } fullattrib: ggetattrib { if ( (datafile_flag && boundary_expr_flag && ($1.i==PARAM_)) || ( datafile_flag && ($1.i==COORD_) ) ) { coord_num = $1.qnum; $$.i = makenode(PUSHPARAM,0,0); } else { $$.i = makenode(ATTRIBUTE,$1.i,$1.qnum); $$.datatype = list[$$.i].datatype = $1.datatype; } } fullattrib: ggetattrib indexset { if ( const_expr_flag ) { YYABORT; /* illegal for const rexpr */ } switch ( $1.i ) { case COORD_: $$.i = makenode(INDEXED_COORD_,$2.i,0); break; case GET_VERTEXNORMAL_: $$.i = makenode(GET_VERTEXNORMAL_,$2.i,0); break; case PARAM_: if ( $1.etype == VERTEX ) { $$.qnum = V_PARAM_ATTR; $$.i = makenode(INDEXED_ATTRIBUTE,$2.i,$$.qnum+($1.etype<type); list[$1.i].op1.localnum = elsym->localnum; } else kb_error(1896,"\nMissing element for attribute. (Get quantity value with name.value) \n",COMMAND_ERROR); } list[$1.i].datatype = $1.datatype; } rexpr : singlep fullattrib { $$.i = makenode(QUALIFIED_ATTRIBUTE,$1.i,$2.i); list[$2.i].op1.localnum = list[$1.i].op2.localnum; list[$$.i].datatype = $2.datatype; } rexpr : singlep NEWIDENT_ { sprintf(errmsg,"\"%s\" is not a attribute name.\n",$2.lexeme); kb_error(3458,errmsg,Q_ERROR); } /**************************************************************************/ rexpr : IS_DEFINED_ '(' stringexpr ')' { $$.i = makenode(IS_DEFINED_,$3.i,0); } rexpr : IS_DEFINED_ error { kb_error(3814,"Syntax: IS_DEFINED ( quoted_string )\n",Q_ERROR); } rexpr : IS_DEFINED_ '(' stringexpr { kb_error(3815,"Missing closing parenthesis for IS_DEFINED\n",Q_ERROR); } /**************************************************************************/ rexpr : SIZEOF_ '(' ARRAY_ATTRIBUTE_ ')' { int etype; $$.qnum = $3.qnum; etype = $3.etype; $$.i = makenode(SIZEOF_ATTR_,$$.qnum,etype); } rexpr : SIZEOF_ '(' ARRAYIDENT_ ')' { $$.i = makenode(SIZEOF_ARRAY_,$3.i,0); } rexpr : SIZEOF_ '(' stringexpr ')' { $$.i = makenode(SIZEOF_STRING_,$3.i,0); } rexpr : SIZEOF_ error { strcpy(errmsg,"Syntax: SIZEOF ( extra_attribute )\n"); strcat(errmsg," SIZEOF ( array_name ) \n"); strcat(errmsg," SIZEOF ( string_expr ) \n"); kb_error(3816,errmsg,Q_ERROR); } /**************************************************************************/ rexpr : TOGGLEVALUE_ { $$.i = makenode(TOGGLEVALUE,$1.i,0); } rexpr : AUTOCHOP_ { $$.i = makenode(TOGGLEVALUE,AUTOCHOP_,0); } rexpr : LAGRANGE_ { $$.i = makenode(TOGGLEVALUE,LAGRANGE_,0); } rexpr : EPRINT_ rexpr { $$.i = makenode(EPRINT_,$2.i,0); } rexpr : EPRINT_ error { kb_error(2886,"Syntax: EPRINT expression\n",Q_ERROR); } /**************************************************************************/ rexpr : '(' rexpr ')' { $$.i = $2.i; } rexpr : '(' error { kb_error(2401,"Missing closing parenthesis?\n",Q_ERROR); } /**************************************************************************/ rexpr : INTERNAL_VARIABLE_ { $$.i = makenode(GET_INTERNAL_,$1.i,0); } rexpr : SCALE_ { $$.i = makenode(GET_INTERNAL_,V_SCALE,0); } rexpr : IDENT_ { $$.i = makenode(PUSHGLOBAL_,$1.i,0); } rexpr : PERM_IDENT_ { $$.i = makenode(PUSH_PERM_GLOBAL_,$1.i,0); } /**************************************************************************/ rexpr : IDENT_ '.' EXTRA_ATTRIBUTE_ { $$.i = makenode(PUSH_PARAM_EXTRA_,$1.i,$3.i); } rexpr : IDENT_ '.' error { kb_error(3817,"Permitted optimizing parameter attributes: pdelta pscale\n", Q_ERROR); } /**************************************************************************/ rexpr : DYNAMIC_LOAD_FUNC_ { $$.i = makenode(DYNAMIC_LOAD_FUNC_,$1.i,0); } rexpr : TOTAL_ QUANTITY_NAME_ { $$.i = makenode(PUSHQVALUE_,$2.i,0); } rexpr : QUANTITY_NAME_ '.' PRESSURE_ { $$.i = makenode(PUSHQPRESSURE_,$1.i,0); } rexpr : QUANTITY_NAME_ '.' MODULUS_ { $$.i = makenode(PUSHQMODULUS_,$1.i,0); } rexpr : QUANTITY_NAME_ '.' TOLERANCE_ { $$.i = makenode(PUSHQTOLERANCE_,$1.i,0); } rexpr : METHOD_NAME_ '.' MODULUS_ { $$.i = makenode(PUSHMMODULUS_,$1.i,0); } rexpr : QUANTITY_NAME_ '.' TARGET_ { $$.i = makenode(PUSHQTARGET_,$1.i,0); } rexpr : QUANTITY_NAME_ '.' VALUE_ { $$.i = makenode(PUSHQVALUE_,$1.i,0); } rexpr : METHOD_NAME_ '.' VALUE_ { $$.i = makenode(PUSHMVALUE_,$1.i,0); } rexpr : QUANTITY_NAME_ '.' VOLCONST_ { $$.i = makenode(PUSHQVOLCONST_,$1.i,0); } rexpr : QUANTITY_NAME_ '.' FIXED_ { $$.i = makenode(PUSHQFIXED_,$1.i,0); } rexpr : QUANTITY_NAME_ '.' ENERGY_ { $$.i = makenode(PUSHQENERGY_,$1.i,0); } rexpr : QUANTITY_NAME_ '.' INFO_ONLY_ { $$.i = makenode(PUSHQINFO_ONLY_,$1.i,0); } rexpr : QUANTITY_NAME_ '.' CONSERVED_ { $$.i = makenode(PUSHQCONSERVED_,$1.i,0); } rexpr : QUANTITY_NAME_ error { strcpy(errmsg, "Quantity name needs attribute. Syntax: quantityname.attribute\n"); strcat(errmsg,"Possible quantity attributes: \n"); strcat(errmsg," value, modulus, pressure, target, tolerance, volconst,\n"); strcat(errmsg," fixed, energy, info_only, conserved\n"); kb_error(3818,errmsg,Q_ERROR); } rexpr : QUANTITY_NAME_ '.' error { strcpy(errmsg,"Possible quantity attributes: \n"); strcat(errmsg," value, modulus, pressure, target, tolerance, volconst,\n"); strcat(errmsg," fixed, energy, info_only, conserved\n"); kb_error(3819,errmsg,Q_ERROR); } rexpr : METHOD_NAME_ '.' error { kb_error(3907,"Possible method instance attributes: value, modulus \n", Q_ERROR); } rexpr : METHOD_NAME_ error { strcpy(errmsg, "Method instance name needs attribute. Syntax: instancename.attribute\n"); strcat(errmsg,"Possible method instance attributes: value, modulus \n"); kb_error(3820,errmsg,Q_ERROR); } /**************************************************************************/ rexpr : INTEGER_ { real_val = (REAL)$1.i; $$.i = makenode(PUSHCONST,0,0); } rexpr : LEAD_INTEGER_ { real_val = (REAL)$1.i; $$.i = makenode(PUSHCONST,0,0); } rexpr : REAL_ { real_val = $1.r; $$.i = makenode(PUSHCONST,0,0); } signed_expr : SIGNED_NUMBER_ { real_val = $1.r; $$.i = makenode(PUSHCONST,0,0); } rexpr : signed_expr { $$.i = $1.i; } rexpr : PI_ { $$.i = makenode(PUSHPI,0,0); } rexpr : E_ { $$.i = makenode(PUSHE,0,0); } rexpr : G_ { $$.i = makenode(PUSHG,0,0); } rexpr : USERFUNC_ { int_val = $1.i; $$.i = makenode(USERFUNC,0,0); } rexpr : MATHFUNC_ '(' rexpr ')' { $$.i = makenode($1.i,(NTYPE)$3.i,0); } rexpr : MATHFUNC_ error { sprintf(errmsg,"Syntax: %s ( rexpr )\n",keywordname($1.i)); kb_error(3821,errmsg,Q_ERROR); } rexpr : MATHFUNC2_ '(' rexpr ',' rexpr ')' { $$.i = makenode($1.i,(NTYPE)$3.i,$5.i); } rexpr : MATHFUNC2_ { sprintf(errmsg,"Syntax: %s ( rexpr , rexpr )\n",keywordname($1.i)); kb_error(3822,errmsg,Q_ERROR); } /* binary operations kept separate for precedence purposes */ rexpr : rexpr '+' rexpr { $$.i = makenode('+',(NTYPE)$1.i,(NTYPE)$3.i); } rexpr : rexpr '-' rexpr { $$.i = makenode('-',(NTYPE)$1.i,(NTYPE)$3.i); } rexpr : rexpr '=' rexpr { $$.i = makenode('=',(NTYPE)$1.i,(NTYPE)$3.i); } /* for constraints */ rexpr : rexpr '/' rexpr { $$.i = makenode('/',(NTYPE)$1.i,(NTYPE)$3.i); } rexpr : rexpr '*' rexpr { $$.i = makenode('*',(NTYPE)$1.i,(NTYPE)$3.i); } rexpr : rexpr '%' rexpr { $$.i = makenode('%',(NTYPE)$1.i,(NTYPE)$3.i); } rexpr : rexpr IMOD_ rexpr { $$.i = makenode(IMOD_,(NTYPE)$1.i,(NTYPE)$3.i); } rexpr : rexpr IDIV_ rexpr { $$.i = makenode(IDIV_,(NTYPE)$1.i,(NTYPE)$3.i); } rexpr : rexpr '^' rexpr { $$.i = makenode(POW,(NTYPE)$1.i,(NTYPE)$3.i); } rexpr : rexpr '<' rexpr { $$.i = makenode(LT_,(NTYPE)$1.i,(NTYPE)$3.i); } rexpr : rexpr '>' rexpr { $$.i = makenode(GT_,(NTYPE)$1.i,(NTYPE)$3.i); } rexpr : rexpr NE_ rexpr { $$.i = makenode(NE_,(NTYPE)$1.i,(NTYPE)$3.i); } rexpr : rexpr EQ_ rexpr { $$.i = makenode(EQ_,(NTYPE)$1.i,(NTYPE)$3.i); } rexpr : rexpr LE_ rexpr { $$.i = makenode(LE_,(NTYPE)$1.i,(NTYPE)$3.i); } rexpr : rexpr GE_ rexpr { $$.i = makenode(GE_,(NTYPE)$1.i,(NTYPE)$3.i); } rexpr : rexpr AND_ rexpr { $$.i = makenode(AND_,(NTYPE)$1.i,(NTYPE)$3.i); } rexpr : rexpr OR_ rexpr { $$.i = makenode(OR_,(NTYPE)$1.i,(NTYPE)$3.i); } rexpr : rexpr '+' arraylvalue error { kb_error(3031,"Cannot add scalar and array\n",Q_ERROR); } rexpr : rexpr '-' arraylvalue error { kb_error(3027,"Cannot subtract scalar and array\n",Q_ERROR); } rexpr : rexpr '+' error { kb_error(3823,"Bad second expression after +\n",Q_ERROR); } rexpr : rexpr '-' error { kb_error(3825,"Bad second expression after -\n",Q_ERROR); } rexpr : rexpr '=' error { kb_error(3828,"Bad second expression after =\n",Q_ERROR); } rexpr : rexpr '/' error { kb_error(3829,"Bad second expression after /\n",Q_ERROR); } rexpr : rexpr '*' error { kb_error(3830,"Bad second expression after *\n",Q_ERROR); } rexpr : rexpr '%' error { kb_error(3831,"Bad second expression after %\n",Q_ERROR); } rexpr : rexpr IMOD_ error { kb_error(2887,"Bad second expression after IMOD\n",Q_ERROR); } rexpr : rexpr IDIV_ error { kb_error(3919,"Bad second expression after IDIV\n",Q_ERROR); } rexpr : rexpr '^' error { kb_error(3920,"Bad second expression after ^\n",Q_ERROR); } rexpr : rexpr '<' error { kb_error(3921,"Bad second expression after <\n",Q_ERROR); } rexpr : rexpr '>' error { kb_error(3922,"Bad second expression after >\n",Q_ERROR); } rexpr : rexpr NE_ error { kb_error(3923,"Bad second expression after !=\n",Q_ERROR); } rexpr : rexpr EQ_ error { kb_error(3924,"Bad second expression after ==\n",Q_ERROR); } rexpr : rexpr LE_ error { kb_error(3925,"Bad second expression after <=\n",Q_ERROR); } rexpr : rexpr GE_ error { kb_error(3926,"Bad second expression after >=\n",Q_ERROR); } rexpr : rexpr AND_ error { kb_error(3927,"Bad second expression after AND\n",Q_ERROR); } rexpr : rexpr OR_ error { kb_error(3928,"Bad second expression after OR\n",Q_ERROR); } /**************************************************************************/ rexpr : UMINUS_ rexpr { $$.i = makenode(CHS,(NTYPE)$2.i,0); } rexpr : UMINUS_ error { kb_error(3826,"Bad expression after unary minus.\n",Q_ERROR); } rexpr : UPLUS_ rexpr { $$.i = $2.i; } rexpr : NOT_ rexpr { $$.i = makenode(NOT_,(NTYPE)$2.i,0); } rexpr : NOT_ error { kb_error(3827,"Bad expression after NOT.\n",Q_ERROR); } /**************************************************************************/ rexpr : rexpr '?' { cond_expr_flag++; $$.i = makenode(COND_TEST_,$1.i,0); } rexpr ':' { cond_expr_flag--; $$.i = makenode(COND_EXPR_,$3.i,$4.i); } rexpr { $$.i = makenode(COND_ELSE_,$6.i,$7.i); } rexpr : rexpr '?' error { kb_error(3824,"Conditional expression syntax: expr1 ? expr2 : expr3\n", Q_ERROR); } /**************************************************************************/ aggregate : MAX_ { $$.i = MAX_; } aggregate : MIN_ { $$.i = MIN_; } aggregate : SUM_ { $$.i = SUM_; } aggregate : AVG_ { $$.i = AVG_; } aggregate : COUNT_ { $$.i = COUNT_; } histotype : HISTOGRAM_ { $$.i = HISTOGRAM_; } histotype : LOGHISTOGRAM_ { $$.i = LOGHISTOGRAM_; } vcommand : HISTOGRAM_ error { kb_error(2402,"Syntax: HISTOGRAM(element_gen,expression)\n", Q_ERROR); } vcommand : LOGHISTOGRAM_ error { kb_error(2403,"Syntax: LOGHISTOGRAM(element_gen,expression)\n", Q_ERROR); } /**************************************************************************/ foreachhead : FOREACH_ { aggrtype = FOREACH_; loopdepth++; $$.i = makenode(AGGREGATE_INIT_,0,0); } vcommand: foreachhead element_gen DO_ command { int aggr; aggrtype = FOREACH_; aggr = makenode(AGGREGATE_,$2.i,$4.i); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : FOREACH_ error { kb_error(2404, "Syntax: FOREACH element_gen [ name ] [WHERE expr] DO command\n",Q_ERROR);} /**************************************************************************/ vcommand : SHOWVERB_ { use_given_id = 1; /* in eval() */ } element_gen { $$.i = makenode(SHOW_,$3.i,0); use_given_id = 0; end_scope(); } vcommand: SHOWVERB_ { $$.i = makenode(SINGLE_LETTER_,'s',0); } vcommand: SHOWVERB_ error { kb_error(2405,"Syntax: SHOW element_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR);} /**************************************************************************/ vcommand : SHOW_EXPR_ { use_given_id = 1; /* in eval() */ } element_gen { $$.i = makenode(SHOW_EXPR_,$3.i,0); use_given_id = 0; end_scope(); } vcommand : SHOW_EXPR_ error { kb_error(2406,"Syntax: SHOW_EXPR element_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR);} /**************************************************************************/ vcommand : histotype { loopdepth++; aggrtype = $1.i; $$.i = makenode(AGGREGATE_INIT_,0,0); } '(' element_gen ',' rexpr ')' { int aggr; aggrtype = $1.i; aggr = makenode(AGGREGATE_,$4.i,$6.i); $$.i = makenode(AGGREGATE_END_,$2.i,aggr); end_scope(); } rexpr : aggregate { loopdepth++; aggrtype = $1.i; $$.i = makenode(AGGREGATE_INIT_,0,0); } '(' element_gen ',' rexpr ')' { int aggr; aggrtype = $1.i; aggr = makenode(AGGREGATE_,$4.i,$6.i); $$.i = makenode(AGGREGATE_END_,$2.i,aggr); end_scope(); } vcommand : verb { aggrtype = $1.i; $$.i = makenode(AGGREGATE_INIT_,0,0); } element_gen { int aggr; aggrtype = $1.i; aggr = makenode(AGGREGATE_,$3.i,0); $$.i = makenode(AGGREGATE_END_,$2.i,aggr); end_scope(); } set : SET_ { $$.i = makenode(SET_INIT_,0,0); } /* set_attrib for settable attributes without values */ set_attrib : NO_REFINE_ { $$.i = SET_NO_REFINE_ ; } set_attrib : HIT_PARTNER_ { $$.i = SET_HIT_PARTNER_ ; } set_attrib : FIXED_ { $$.i = SET_FIXED_ ; } set_attrib : BARE_ { $$.i = SET_BARE_ ; } set_attrib : NONCONTENT_ { $$.i = SET_NONCONTENT_ ; } set_attrib : NODISPLAY_ { $$.i = SET_NO_DISPLAY_ ; } set_attrib : AXIAL_POINT_ { $$.i = SET_AXIAL_POINT_ ; } set_attrib : TETRA_POINT_ { $$.i = SET_TETRA_PT_; } set_attrib : TRIPLE_POINT_ { $$.i = SET_TRIPLE_PT_; } vcommand : set element_gen set_attrib { int aggr; aggrtype = $3.i; aggr = makenode(AGGREGATE_,$2.i,0); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : set element_gen set_attrib WHERE_ rexpr { int aggr,where; aggrtype = $3.i; where = makenode(WHERE_,$2.i,$5.i); aggr = makenode(AGGREGATE_,where,0); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } /* setattrib for settable attributes with values */ setattrib : FRONTBODY_ { $$.i = SET_FRONTBODY_; tok=0; /* for UMINUS */ } setattrib : BACKBODY_ { $$.i = SET_BACKBODY_; tok=0; } setattrib : COLOR_ { $$.i = SET_COLOR_; tok=0; } setattrib : FRONTCOLOR_ { $$.i = SET_FRONTCOLOR_; tok=0; } setattrib : BACKCOLOR_ { $$.i = SET_BACKCOLOR_; tok=0; } setattrib : DENSITY_ { $$.i = SET_DENSITY_; tok=0; } setattrib : ORIGINAL_ { $$.i = SET_ORIGINAL_; tok=0; } setattrib : VOLCONST_ { $$.i = SET_VOLCONST_; tok=0; } setattrib : VOLUME_ { $$.i = SET_VOLUME_; tok=0; } setattrib : TARGET_ { $$.i = SET_TARGET_; tok=0; } setattrib : PRESSURE_ { $$.i = SET_PRESSURE_; tok=0; } setattrib : OPACITY_ { $$.i = SET_OPACITY_; tok=0; } setattrib : CONSTRAINT_ { $$.i = SET_CONSTRAINT_; tok=0; } setattrib : BOUNDARY_ { $$.i = SET_BOUNDARY_; tok=0; } setattrib : TAG_ { $$.i = SET_TAG_; tok=0; } setattrib : COORD_ { $$.i = SET_COORD_+$1.i; tok = 0; /* UMINUS bug */ } setattrib : PARAM_ { $$.i = SET_PARAM_+$1.i; tok=0; } setattrib : PHASE_ { $$.i = SET_PHASE_; tok=0; } setattrib : EXTRA_ATTRIBUTE_ { $$.i = SET_EXTRA_ATTR_ ; tok=0; $$.qnum = $1.qnum; $$.etype = $1.etype; strcpy(set_extra_name,EXTRAS($$.etype)[$$.qnum].name); } setattrib : ORIENTATION_ { $$.i = SET_ORIENTATION_ ; tok=0; } setattrib : WRAP_ { $$.i = SET_WRAP_ ; tok = 0;} setattribb : '.' setattrib { $$.i = $2.i;} setattribb : setattrib { $$.i = $1.i;} assignop : ASSIGN_ { $$.i = ASSIGN_; } assignop : ASSIGNOP_ { $$.i = $1.i; } assignop : EQ_ { kb_error(3415,"Expected assignment operator, got '='\n", Q_ERROR); } command : single '.' setattrib { $$.i = makenode(SINGLE_ELEMENT_,$1.i,0); } assignop rexpr { int mm; int type = list[$1.i].op1.eltype; begin_scope(); /* ended at end of aggregate */ elsym = symbol_add(default_name,type); elsym->localnum = list[$1.i].op2.localnum; strcpy(last_name,default_name); $1.symptr = elsym; attr_kind = $3.i; $4.symptr = 0; /* in case of WHERE */ assigntype = $5.i; mm = makenode(SET_ATTRIBUTE_A,$6.i,0); $$.i = makenode(SINGLE_ASSIGN_,$4.i,mm); end_scope(); } command : single '.' setattrib { $$.i = makenode(SINGLE_ELEMENT_,$1.i,0); } indexset assignop rexpr { int mm; int type = list[$1.i].op1.eltype; begin_scope(); /* ended at end of aggregate */ elsym = symbol_add(default_name,type); elsym->localnum = list[$1.i].op2.localnum; strcpy(last_name,default_name); $1.symptr = elsym; attr_kind = $3.i; subtree_swap(&$5.i,&$7.i); $4.symptr = 0; /* in case of WHERE */ assigntype = $6.i; mm = makenode(SET_ATTRIBUTE_A,$7.i,$5.i); $$.i = makenode(SINGLE_ASSIGN_,$4.i,mm); end_scope(); } vcommand : set element_gen setattribb rexpr { int aggr; int nn; aggrtype = SET_ATTRIBUTE_LOOP_; attr_kind = $3.i; elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ nn = makenode(SET_ATTRIBUTE_L,$4.i,0); aggr = makenode(AGGREGATE_,$2.i,nn); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : set element_gen setattribb indexset rexpr { int aggr; int nn; aggrtype = SET_ATTRIBUTE_LOOP_; attr_kind = $3.i; subtree_swap(&$4.i,&$5.i); /* get index eval in top of stack */ elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ nn = makenode(SET_ATTRIBUTE_L,$5.i,$4.i); aggr = makenode(AGGREGATE_,$2.i,nn); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : set element_gen ARRAY_ATTRIBUTE_ indexset rexpr { int aggr; int nn,mm,kk; aggrtype = SET_ATTRIBUTE_LOOP_; elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ kk = makenode(ATTRIB_LVALUE_,0,0); list[kk].op1.localnum = 0; list[kk].op2.name_id = set_name_eltype($3.qnum,$3.etype); subtree_swap(&$5.i,&kk); /* so datastart before rexpr */ subtree_swap(&$4.i,&kk); /* so datastart before indexset */ mm = makenode(ARRAY_LVALUE_INDEXED_,kk,$4.i); nn = makenode(ARRAY_ASSIGNOP_SINGLE_,mm,$5.i); list[nn].flags |= SET_ASSIGNOP; list[nn].op1.assigntype = ASSIGN_; list[nn].op2.name_id = set_name_eltype($3.qnum,$3.etype); aggr = makenode(AGGREGATE_,$2.i,nn); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : set element_gen ARRAY_ATTRIBUTE_ arraylvalue { int aggr; int nn,kk; aggrtype = SET_ATTRIBUTE_LOOP_; attr_kind = $3.i; elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ kk = makenode(ATTRIB_LVALUE_,0,0); list[kk].op1.localnum = 0; list[kk].op2.name_id = set_name_eltype($3.qnum,$3.etype); subtree_swap(&$4.i,&kk); /* so lvalue before rvalue */ nn = makenode(ARRAY_ASSIGNOP_ARRAY_,kk,$4.i); list[nn].flags |= SET_ASSIGNOP; list[nn].op1.assigntype = ASSIGN_; list[nn].op2.name_id = set_name_eltype($3.qnum,$3.etype); aggr = makenode(AGGREGATE_,$2.i,nn); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : set element_gen ARRAY_ATTRIBUTE_ rexpr { int aggr; int nn,kk; aggrtype = SET_ATTRIBUTE_LOOP_; attr_kind = $3.i; elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ kk = makenode(ATTRIB_LVALUE_,0,0); list[kk].op1.localnum = 0; list[kk].op2.name_id = set_name_eltype($3.qnum,$3.etype); subtree_swap(&$4.i,&kk); /* so rexpr before datastart */ nn = makenode(ARRAY_ASSIGNOP_SCALAR_,kk,$4.i); list[nn].op1.assigntype = ASSIGN_; list[nn].op2.name_id = set_name_eltype($3.qnum,$3.etype); list[nn].flags |= SET_ASSIGNOP; aggr = makenode(AGGREGATE_,$2.i,nn); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : set element_gen ARRAY_ATTRIBUTE_ rexpr WHERE_ rexpr { int aggr,where; int nn,kk; aggrtype = SET_ATTRIBUTE_LOOP_; attr_kind = $3.i; /* splice in the WHERE clause */ subtree_swap(&$4.i,&$6.i); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&$4.i,&where); /* get in proper linear order */ list[where].left = $2.i - where; list[where].right = $6.i - where; elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ kk = makenode(ATTRIB_LVALUE_,0,0); list[kk].op1.localnum = 0; list[kk].op2.name_id = set_name_eltype($3.qnum,$3.etype); subtree_swap(&$4.i,&kk); /* so rexpr before datastart */ nn = makenode(ARRAY_ASSIGNOP_SCALAR_,kk,$4.i); list[nn].flags |= SET_ASSIGNOP; list[nn].op2.name_id = set_name_eltype($3.qnum,$3.etype); list[nn].op1.assigntype = ASSIGN_; aggr = makenode(AGGREGATE_,$2.i,nn); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : set element_gen ARRAY_ATTRIBUTE_ indexset rexpr WHERE_ rexpr { int aggr,where; int nn,mm,kk; aggrtype = SET_ATTRIBUTE_LOOP_; /* splice in the WHERE clause */ subtree_swap(&$5.i,&$7.i); subtree_swap(&$4.i,&$7.i); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&$5.i,&where); /* get in proper linear order */ subtree_swap(&$4.i,&where); /* get in proper linear order */ list[where].left = $2.i - where; list[where].right = $7.i - where; elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ kk = makenode(ATTRIB_LVALUE_,0,0); list[kk].op1.localnum = 0; list[kk].op2.name_id = set_name_eltype($3.qnum,$3.etype); subtree_swap(&$5.i,&kk); /* so datastart before rexpr */ subtree_swap(&$4.i,&kk); /* so datastart before indexset */ mm = makenode(ARRAY_LVALUE_INDEXED_,kk,$4.i); nn = makenode(ARRAY_ASSIGNOP_SINGLE_,mm,$5.i); list[nn].flags |= SET_ASSIGNOP; list[nn].op1.assigntype = ASSIGN_; aggr = makenode(AGGREGATE_,$2.i,nn); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : set element_gen ARRAY_ATTRIBUTE_ arraylvalue WHERE_ rexpr { int aggr,where; int nn,kk; aggrtype = SET_ATTRIBUTE_LOOP_; attr_kind = $3.i; /* splice in the WHERE clause */ subtree_swap(&$4.i,&$6.i); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&$4.i,&where); /* get in proper linear order */ list[where].left = $2.i - where; list[where].right = $6.i - where; elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ kk = makenode(ATTRIB_LVALUE_,0,0); list[kk].op1.localnum = 0; list[kk].op2.name_id = set_name_eltype($3.qnum,$3.etype); subtree_swap(&$4.i,&kk); /* so lvalue before rvalue */ nn = makenode(ARRAY_ASSIGNOP_ARRAY_,kk,$4.i); list[nn].flags |= SET_ASSIGNOP; list[nn].op1.assigntype = ASSIGN_; aggr = makenode(AGGREGATE_,$2.i,nn); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : set element_gen setattribb rexpr WHERE_ rexpr { int aggr,where,nn; aggrtype = SET_ATTRIBUTE_LOOP_; attr_kind = $3.i; subtree_swap(&$4.i,&$6.i); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&$4.i,&where); /* get in proper linear order */ list[where].left = $2.i - where; list[where].right = $6.i - where; elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ nn = makenode(SET_ATTRIBUTE_L,$4.i,0); aggr = makenode(AGGREGATE_,where,nn); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : set element_gen setattribb indexset rexpr WHERE_ rexpr { int aggr,where,nn; aggrtype = SET_ATTRIBUTE_LOOP_; attr_kind = $3.i; subtree_swap(&$4.i,&$5.i); /* get index eval in top of stack */ subtree_swap(&$4.i,&$7.i); /* get index eval in top of stack */ subtree_swap(&$5.i,&$7.i); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&$4.i,&where); /* get in proper linear order */ subtree_swap(&$5.i,&where); /* get in proper linear order */ list[where].left = $2.i - where; list[where].right = $7.i - where; elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ nn = makenode(SET_ATTRIBUTE_L,$5.i,$4.i); aggr = makenode(AGGREGATE_,where,nn); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : set element_gen QUANTITY_ QUANTITY_NAME_ { int aggr,idnode; aggrtype = SET_NAMED_QUANTITY_; idnode = makenode(PUSH_NAMED_QUANTITY,$4.i,0); elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,$2.i,idnode); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : unset element_gen QUANTITY_ QUANTITY_NAME_ { int aggr,idnode; aggrtype = UNSET_NAMED_QUANTITY_; idnode = makenode(PUSH_NAMED_QUANTITY,$4.i,0); elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,$2.i,idnode); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : set element_gen QUANTITY_ QUANTITY_NAME_ WHERE_ rexpr { int aggr,where,idnode; aggrtype = SET_NAMED_QUANTITY_; idnode = makenode(PUSH_NAMED_QUANTITY,$4.i,0); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&idnode,&where); /* get in proper linear order */ list[where].left = $2.i - where; list[where].right = $6.i - where; elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,where,idnode); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : unset element_gen QUANTITY_ QUANTITY_NAME_ WHERE_ rexpr { int aggr,where,idnode; aggrtype = UNSET_NAMED_QUANTITY_; idnode = makenode(PUSH_NAMED_QUANTITY,$4.i,0); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&idnode,&where); /* get in proper linear order */ list[where].left = $2.i - where; list[where].right = $6.i - where; elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,where,idnode); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : set element_gen METHOD_INSTANCE_ METHOD_NAME_ { int aggr,idnode; aggrtype = SET_METHOD_INSTANCE_; idnode = makenode(PUSH_METHOD_INSTANCE_,$4.i,0); elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,$2.i,idnode); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : unset element_gen METHOD_INSTANCE_ METHOD_NAME_ { int aggr,idnode; aggrtype = UNSET_METHOD_INSTANCE_; idnode = makenode(PUSH_METHOD_INSTANCE_, $4.i,0); elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,$2.i,idnode); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : set element_gen METHOD_INSTANCE_ METHOD_NAME_ WHERE_ rexpr { int aggr,where,idnode; aggrtype = SET_METHOD_INSTANCE_; idnode = makenode(PUSH_METHOD_INSTANCE_,$4.i,0); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&idnode,&where); /* get in proper linear order */ list[where].left = $2.i - where; list[where].right = $6.i - where; elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,where,idnode); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : unset element_gen METHOD_INSTANCE_ METHOD_NAME_ WHERE_ rexpr { int aggr,where,idnode; aggrtype = UNSET_METHOD_INSTANCE_; idnode = makenode(PUSH_METHOD_INSTANCE_,$4.i,0); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&idnode,&where); /* get in proper linear order */ list[where].left = $2.i - where; list[where].right = $6.i - where; elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,where,idnode); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : set element_gen QUANTITY_NAME_ { int aggr,idnode; aggrtype = SET_NAMED_QUANTITY_; idnode = makenode(PUSH_NAMED_QUANTITY,$3.i,0); elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,$2.i,idnode); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : unset element_gen QUANTITY_NAME_ { int aggr,idnode; aggrtype = UNSET_NAMED_QUANTITY_; idnode = makenode(PUSH_NAMED_QUANTITY,$3.i,0); elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,$2.i,idnode); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : set element_gen QUANTITY_NAME_ WHERE_ rexpr { int aggr,where,idnode; aggrtype = SET_NAMED_QUANTITY_; idnode = makenode(PUSH_NAMED_QUANTITY,$3.i,0); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&idnode,&where); /* get in proper linear order */ list[where].left = $2.i - where; list[where].right = $5.i - where; elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,where,idnode); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : unset element_gen QUANTITY_NAME_ WHERE_ rexpr { int aggr,where,idnode; aggrtype = UNSET_NAMED_QUANTITY_; idnode = makenode(PUSH_NAMED_QUANTITY,$3.i,0); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&idnode,&where); /* get in proper linear order */ list[where].left = $2.i - where; list[where].right = $5.i - where; elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,where,idnode); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : set element_gen METHOD_NAME_ { int aggr,idnode; aggrtype = SET_METHOD_INSTANCE_; idnode = makenode(PUSH_METHOD_INSTANCE_,$3.i,0); elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,$2.i,idnode); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : unset element_gen METHOD_NAME_ { int aggr,idnode; aggrtype = UNSET_METHOD_INSTANCE_; idnode = makenode(PUSH_METHOD_INSTANCE_,$3.i,0); elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,$2.i,idnode); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : set element_gen METHOD_NAME_ WHERE_ rexpr { int aggr,where,idnode; aggrtype = SET_METHOD_INSTANCE_; idnode = makenode(PUSH_METHOD_INSTANCE_,$3.i,0); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&idnode,&where); /* get in proper linear order */ list[where].left = $2.i - where; list[where].right = $5.i - where; elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,where,idnode); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : unset element_gen METHOD_NAME_ WHERE_ rexpr { int aggr,where,idnode; aggrtype = UNSET_METHOD_INSTANCE_; idnode = makenode(PUSH_METHOD_INSTANCE_,$3.i,0); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&idnode,&where); /* get in proper linear order */ list[where].left = $2.i - where; list[where].right = $5.i - where; elsym = $2.symptr; $2.symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,where,idnode); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : set error { if ( tok == '-' ) kb_error(1897,"Syntax kludge: cannot have leading minus sign after ]. Use parentheses.\n", Q_ERROR); else kb_error(2532, "Syntax: SET element_gen [ name ] attribute rexpr [ WHERE rexpr ]\n",Q_ERROR); } /**************************************************************************/ unset : UNSET_ { $$.i = makenode(SET_INIT_,0,0); } unsetattrib: FIXED_ { $$.i = UNSET_FIXED_; } unsetattrib: HIT_PARTNER_ { $$.i = UNSET_HIT_PARTNER_; } unsetattrib: BARE_ { $$.i = UNSET_BARE_; } unsetattrib: NO_REFINE_ { $$.i = UNSET_NO_REFINE_; } unsetattrib: NONCONTENT_ { $$.i = UNSET_NONCONTENT_; } unsetattrib: NODISPLAY_ { $$.i = UNSET_NO_DISPLAY_; } unsetattrib: DENSITY_ { $$.i = UNSET_DENSITY_; } unsetattrib: VOLUME_ { $$.i = UNSET_TARGET_; } unsetattrib: TARGET_ { $$.i = UNSET_TARGET_; } unsetattrib: PRESSURE_ { $$.i = UNSET_PRESSURE_; } unsetattrib: TRIPLE_POINT_ { $$.i = UNSET_TRIPLE_PT_; } unsetattrib: TETRA_POINT_ { $$.i = UNSET_TETRA_PT_; } unsetattrib: AXIAL_POINT_ { $$.i = UNSET_AXIAL_POINT_; } unsetattrib: FRONTBODY_ { $$.i = UNSET_FRONTBODY_; } unsetattrib: BACKBODY_ { $$.i = UNSET_BACKBODY_; } unsetattrib: BODIES_ { $$.i = UNSET_FACET_BODY_; } vcommand : unset element_gen unsetattrib { int aggr; aggrtype = $3.i; aggr = makenode(AGGREGATE_,$2.i,0); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : unset element_gen unsetattrib WHERE_ rexpr { int aggr,where; aggrtype = $3.i; elsym = $2.symptr; where = makenode(WHERE_,$2.i,$5.i); aggr = makenode(AGGREGATE_,where,0); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } conname : CONSTRAINT_NAME_ { $$.i = $1.i; } vcommand : unset element_gen conname { int aggr; aggrtype = UNSET_CONSTRAINT_NAME; elsym = $2.symptr; aggr = makenode(AGGREGATE_,$2.i,0); list[aggr].stack_delta = 0; list[aggr].op3.connum = globals($3.i)->value.cnum; $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } rexpr : CONSTRAINT_NAME_ { real_val = globals($1.i)->value.cnum; $$.i = makenode(PUSHCONST,0,0); } vcommand : unset element_gen CONSTRAINT_ rexpr { int aggr; aggrtype = UNSET_CONSTRAINT_; elsym = $2.symptr; aggr = makenode(AGGREGATE_,$2.i,$4.i); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : unset element_gen CONSTRAINT_ rexpr WHERE_ rexpr { int aggr,where; aggrtype = UNSET_CONSTRAINT_; subtree_swap(&$4.i,&$6.i); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&$4.i,&where); /* get in proper linear order */ list[where].left = $2.i - where; list[where].right = $6.i - where; elsym = $2.symptr; aggr = makenode(AGGREGATE_,where,$4.i); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : unset element_gen conname WHERE_ rexpr { int aggr,where; aggrtype = UNSET_CONSTRAINT_NAME; where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; list[where].left = $2.i - where; list[where].right = $5.i - where; elsym = $2.symptr; aggr = makenode(AGGREGATE_,where,0); list[aggr].op3.connum = globals($3.i)->value.cnum; $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } bdryname : BOUNDARY_NAME_ { $$.i = $1.i; } vcommand : unset element_gen bdryname { int aggr; aggrtype = UNSET_BOUNDARY_NAME; elsym = $2.symptr; aggr = makenode(AGGREGATE_,$2.i,0); list[aggr].stack_delta = 0; list[aggr].op3.bdrynum = globals($3.i)->value.bnum; $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } rexpr : BOUNDARY_NAME_ { real_val = globals($1.i)->value.bnum; $$.i = makenode(PUSHCONST,0,0); } vcommand : unset element_gen BOUNDARY_ rexpr { int aggr; aggrtype = UNSET_BOUNDARY_; elsym = $2.symptr; aggr = makenode(AGGREGATE_,$2.i,$4.i); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : unset element_gen bdryname WHERE_ rexpr { int aggr,where; aggrtype = UNSET_BOUNDARY_NAME; where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; list[where].left = $2.i - where; list[where].right = $5.i - where; elsym = $2.symptr; aggr = makenode(AGGREGATE_,where,0); list[aggr].op3.bdrynum = globals($3.i)->value.bnum; $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : unset element_gen BOUNDARY_ rexpr WHERE_ rexpr { int aggr,where; aggrtype = UNSET_BOUNDARY_; subtree_swap(&$4.i,&$6.i); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&$4.i,&where); /* get in proper linear order */ list[where].left = $2.i - where; list[where].right = $6.i - where; elsym = $2.symptr; aggr = makenode(AGGREGATE_,where,$4.i); $$.i = makenode(AGGREGATE_END_,$1.i,aggr); end_scope(); } vcommand : UNSET_ error { kb_error(1898, "Syntax: UNSET element_gen attribute [ WHERE rexpr ]\n",Q_ERROR); } /**************************************************************************/ whole : EXPRESSION_START_ { YYABORT; /* no expression */ } vcommand : HELP_ WHILE_ { $$.i = makenode(HELP_KEYWORD,0,0); help_flag = 0; tok = 0; yyerrok; yyclearin ; } vcommand : HELP_ error { $$.i = makenode(HELP_KEYWORD,0,0); help_flag = 0; tok = 0; yyerrok; yyclearin ; } /**************************************************************************/ %% int yybegin() { int retval; PROF_START(yyparse); parse_errors = 0; perm_flag = 0; cond_expr_flag = 0; use_given_id = 0; parens = brace_depth = in_quote = 0; yylex_init(); reset_inputbuffer(); /* unput command start token */ tok = COMMAND_START_; unput_tok(); retval = yyparse(); PROF_FINISH(yyparse); return retval; } int yyerror(s) char *s; { char modmsg[1000]; if ( help_flag ) return 0; parens = brace_depth = in_quote = 0; strncpy(modmsg,s,998); if ( modmsg[strlen(modmsg)-1] != '\n' ) strcat(modmsg,"\n"); if ( datafile_flag ) { if ( listtop == 2 ) return 0; /* no expression */ kb_error(2407,modmsg,PARSE_ERROR); } else { /* fprintf(stderr,"tok = %d\n",tok); */ kb_error(2408,modmsg,SYNTAX_ERROR); } return 0; } evolver-2.30c.dfsg/src/quantity.c0000644000175300017530000037231511410765113017252 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /****************************************************************** * * File: quantity.c * * Purpose: General handling of general quantities. */ #include "include.h" struct gen_quant *Gen_quants; struct method_instance *Meth_inst; int compound_quant_list_head = -1; /* new system of general quantity allocation */ struct gen_quant **gen_quant_list; struct gen_quant *gen_quant_free; int gen_quant_list_max; int gen_quant_free_left; /* new system of general instance allocation */ struct method_instance **meth_inst_list; int meth_inst_list_max; struct method_instance *meth_inst_free; int meth_inst_free_left; /* element setup routines */ void (*q_setup[NUMELEMENTS]) ARGS((struct linsys *,QINFO,int)) = { q_vertex_setup,q_edge_setup,q_facet_setup,q_body_setup,q_facetedge_setup}; struct gen_quant_method basic_gen_methods[] = { {"null_length",EDGE,NEED_SIDE,NOSPEC,NULL, null_length_value, null_length_grad, null_length_hess,NULL}, {"null_area",FACET,NEED_SIDE,NOSPEC,NULL, null_area_value, null_area_grad, null_area_hess,NULL}, {"edge_tension",EDGE,NEED_SIDE,NOSPEC,q_edge_tension_init, q_edge_tension_value, q_edge_tension_gradient, q_edge_tension_hessian,NULL}, {"edge_length",EDGE,NEED_SIDE,NOSPEC,q_edge_tension_init, q_edge_tension_value, q_edge_tension_gradient, q_edge_tension_hessian,NULL}, {"density_edge_length",EDGE,NEED_SIDE,SPEC_USE_DENSITY,q_edge_tension_init, q_edge_tension_value, q_edge_tension_gradient, q_edge_tension_hessian,NULL}, {"facet_tension",FACET,NEED_SIDE,NOSPEC, q_facet_tension_init, q_facet_tension_value, q_facet_tension_gradient, q_facet_tension_hessian,NULL}, {"facet_area",FACET,NEED_SIDE,NOSPEC, q_facet_tension_init, q_facet_tension_value, q_facet_tension_gradient, q_facet_tension_hessian,NULL}, {"density_facet_area",FACET,NEED_SIDE,SPEC_USE_DENSITY, q_facet_tension_init, q_facet_tension_value, q_facet_tension_gradient, q_facet_tension_hessian,NULL}, {"facet_area_u",FACET,NEED_SIDE,NOSPEC, q_facet_tension_u_init, q_facet_tension_u_value, q_facet_tension_u_gradient, q_facet_tension_u_hessian,NULL}, {"density_facet_area_u",FACET,NEED_SIDE,SPEC_USE_DENSITY, q_facet_tension_u_init, q_facet_tension_u_value, q_facet_tension_u_gradient, q_facet_tension_u_hessian,NULL}, {"pos_area_hess",FACET,NEED_SIDE,NOSPEC, pos_area_hess_init, q_facet_tension_value, q_facet_tension_gradient, pos_area_hess,NULL}, {"sobolev_area",FACET,NEED_SIDE,NOSPEC, sobolev_area_init, q_facet_tension_value, q_facet_tension_gradient, sobolev_area_hess,NULL}, {"dirichlet_area",FACET,NEED_SIDE,NOSPEC, dirichlet_area_init, q_facet_tension_value, q_facet_tension_gradient, dirichlet_area_hess,NULL}, {"wulff_energy",FACET,0,NOSPEC, wulff_method_init, facet_wulff_value, facet_wulff_grad, null_q_hess,NULL}, {"area_square",FACET,NEED_SIDE,NOSPEC, NULL, area_square_value, area_square_gradient, null_q_hess,NULL}, {"vertex_scalar_integral", VERTEX,ORIENTABLE_METHOD,SPEC_SCALAR, NULL, vertex_scalar_integral,vertex_scalar_integral_grad, vertex_scalar_integral_hess,NULL}, {"edge_scalar_integral",EDGE,NEED_SIDE|NEED_GAUSS,SPEC_SCALAR, NULL, edge_scalar_integral,edge_scalar_integral_grad,edge_scalar_integral_hess,NULL}, {"edge_vector_integral",EDGE,NEED_SIDE|NEED_GAUSS|ORIENTABLE_METHOD, SPEC_VECTOR, NULL, edge_vector_integral,edge_vector_integral_grad,edge_vector_integral_hess,NULL}, {"edge_general_integral",EDGE,NEED_SIDE|NEED_GAUSS, SPEC_SCALAR|SPEC_EXTRADIM, edge_general_init, edge_general_value, edge_general_grad,edge_general_hess,NULL}, {"edge_area",EDGE,ORIENTABLE_METHOD,NOSPEC, NULL, q_edge_area,q_edge_area_grad,q_edge_area_hess,NULL}, {"edge_torus_area",EDGE,ORIENTABLE_METHOD,NOSPEC, NULL, q_edge_torus_area,q_edge_torus_area_grad,q_edge_torus_area_hess,NULL}, {"circular_arc_length",EDGE,NEED_SIDE,NOSPEC, circular_arc_length_init, circular_arc_length_value,circular_arc_length_grad,circular_arc_length_hess,NULL}, {"circular_arc_area",EDGE,ORIENTABLE_METHOD|NEED_SIDE,NOSPEC, circular_arc_area_init, circular_arc_area_value,circular_arc_area_grad,circular_arc_area_hess,NULL}, {"string_gravity",EDGE,NEED_SIDE|NEED_GAUSS|ORIENTABLE_METHOD, NOSPEC,string_gravity_init, string_gravity_energy,string_gravity_grads,string_gravity_hessian,NULL}, {"gap_energy",EDGE,NEED_SIDE,NOSPEC,NULL,gap_energy,gap_grads,null_q_hess,NULL}, {"klein_length",EDGE,0,NOSPEC,NULL,klein_length_method, klein_length_method_grad,null_q_hess,NULL}, {"klein_area",FACET,0,NOSPEC,NULL,klein_area_method, klein_area_method_grad,null_q_hess,NULL}, {"facet_scalar_integral",FACET,NEED_SIDE|NEED_GAUSS,SPEC_SCALAR, facet_scalar_integral_init, facet_scalar_integral, facet_scalar_integral_grad,facet_scalar_integral_hess,NULL}, {"facet_vector_integral",FACET,NEED_SIDE|NEED_NORMAL|NEED_GAUSS |ORIENTABLE_METHOD, SPEC_VECTOR, facet_vector_integral_init, facet_vector_integral, facet_vector_integral_grad, facet_vector_integral_hess,NULL}, {"facet_2form_integral",FACET,NEED_SIDE|NEED_GAUSS|ORIENTABLE_METHOD, SPEC_2FORM, facet_2form_integral_init, facet_2form_integral,facet_2form_integral_grad,facet_2form_integral_hess,NULL}, {"facet_2form_sq_integral",FACET,NEED_SIDE|NEED_GAUSS, SPEC_2FORM, facet_2form_sq_integral_init, facet_2form_sq_integral,facet_2form_sq_integral_grad,null_q_hess,NULL}, {"facet_general_integral",FACET,NEED_SIDE|NEED_NORMAL|NEED_GAUSS, SPEC_SCALAR|SPEC_EXTRADIM, facet_general_init, facet_general_value, facet_general_grad, facet_general_hess,NULL}, {"metric_facet_area",FACET,NEED_GAUSS|NEED_SIDE,NOSPEC, metric_area_init, metric_area_value, metric_area_grad, metric_area_hess,NULL}, {"metric_edge_length",EDGE,NEED_GAUSS,NOSPEC, metric_area_init, metric_area_value, metric_area_grad, metric_area_hess,NULL}, {"spherical_area",FACET,NEED_SIDE,NOSPEC, NULL, spherical_area_value,spherical_area_grad,NULL,NULL}, {"spherical_arc_length",EDGE,NEED_SIDE,NOSPEC, NULL, spherical_arc_length_value,spherical_arc_length_grad,spherical_arc_length_hess,NULL}, {"spherical_arc_area_n",EDGE,NEED_SIDE|ORIENTABLE_METHOD,NOSPEC, spherical_arc_area_init, spherical_arc_area_n_value, spherical_arc_area_n_grad,spherical_arc_area_n_hess,NULL}, {"spherical_arc_area_s",EDGE,NEED_SIDE|ORIENTABLE_METHOD,NOSPEC, spherical_arc_area_init, spherical_arc_area_s_value, spherical_arc_area_s_grad,spherical_arc_area_s_hess,NULL}, {"facet_volume",FACET,NEED_SIDE|TORUS_MODULO_MUNGE|ORIENTABLE_METHOD,NOSPEC, q_facet_volume_init, q_facet_volume,q_facet_volume_grad,q_facet_volume_hess,NULL}, {"facet_torus_volume",FACET,NEED_SIDE|TORUS_MODULO_MUNGE|ORIENTABLE_METHOD, NOSPEC, NULL, q_facet_torus_volume,q_facet_torus_volume_grad,q_facet_torus_volume_hess,NULL}, {"simplex_vector_integral",FACET,NEED_SIDE|NEED_GAUSS|ORIENTABLE_METHOD, SPEC_VECTOR, simplex_vector_integral_init, simplex_vector_integral, simplex_vector_integral_grad, simplex_vector_integral_hess,NULL}, {"simplex_k_vector_integral",FACET,NEED_SIDE|NEED_GAUSS|ORIENTABLE_METHOD, SPEC_KVECTOR, simplex_k_vector_integral_init, simplex_k_vector_integral, simplex_k_vector_integral_grad, simplex_k_vector_integral_hess,NULL}, {"edge_k_vector_integral",EDGE,NEED_SIDE|NEED_GAUSS|ORIENTABLE_METHOD, SPEC_KVECTOR, simplex_k_vector_integral_init, simplex_k_vector_integral, simplex_k_vector_integral_grad, simplex_k_vector_integral_hess,NULL}, {"stress_integral",FACET,NEED_SIDE|NEED_NORMAL,SPEC_SCALAR, stress_integral_init,stress_integral, stress_integral_grad, null_q_hess,NULL}, {"gravity_method",FACET,NEED_SIDE|ORIENTABLE_METHOD,NOSPEC, gravity_init,gravity_energy, gravity_grads,gravity_hessian,NULL}, {"full_gravity_method",FACET,NEED_SIDE|ORIENTABLE_METHOD,NOSPEC, full_gravity_init,gravity_energy, gravity_grads,gravity_hessian,NULL}, {"sqcurve_string",VERTEX,NEED_WINGS,NOSPEC,sqcurve_string_init, sqcurve_string_value, sqcurve_string_grad,sqcurve_string_hess,NULL}, {"sqcurve_string_marked",VERTEX,NEED_MARKED_WINGS,NOSPEC,sqcurve_string_marked_init, sqcurve_string_value, sqcurve_string_grad,sqcurve_string_hess,NULL}, {"sqcurve2_string",VERTEX,NEED_WINGS,NOSPEC,sqcurve2_string_init, sqcurve2_string_value, sqcurve2_string_grad,NULL,NULL}, {"sqcurve3_string",VERTEX,NEED_WINGS,NOSPEC,sqcurve3_string_init, sqcurve3_string_value, sqcurve3_string_grad,sqcurve3_string_hess,NULL}, {"sq_mean_curv_cyl",VERTEX,NEED_WINGS,NOSPEC,sq_mean_curv_cyl_init, sq_mean_curv_cyl_value, sq_mean_curv_cyl_grad,sq_mean_curv_cyl_hess,NULL}, {"sq_gaussian_curv_cyl",VERTEX,NEED_WINGS,NOSPEC,sq_gauss_curv_cyl_init, sq_gauss_curv_cyl_value, sq_gauss_curv_cyl_grad,sq_gauss_curv_cyl_hess,NULL}, {"mean_curvature_integral",EDGE,NEED_SIDE|NEED_WINGS,NOSPEC,mean_int_init, mean_int_value, mean_int_gradient,mean_int_hessian,NULL}, {"mean_curvature_integral_A",EDGE,NEED_SIDE|NEED_WINGS,NOSPEC,mean_int_a_init, mean_int_a_value, mean_int_a_gradient,mean_int_a_hessian,NULL}, {"gauss_curvature_integral",FACET,0,NOSPEC,gauss_integral_init, gauss_int_energy, gauss_int_gradient, null_q_hess ,NULL}, {"levine_energy",VERTEX,NEED_FULL_STAR,NOSPEC,levine_energy_init, levine_energy_value, levine_energy_grad, null_q_hess ,NULL}, /* {"sq_gauss_curvature",VERTEX,0,NOSPEC,sqgauss_method_init, sqgauss_method_value, sqgauss_method_grad, null_q_hess ,NULL}, */ {"star_gauss_curvature",VERTEX,NEED_PART_STAR,NOSPEC,star_gauss_method_init, star_gauss_method_value, star_gauss_method_grad, star_gauss_method_hess ,NULL}, {"sq_gauss_curvature",VERTEX,NEED_FULL_STAR,NOSPEC,star_sqgauss_method_init, star_sqgauss_method_value, star_sqgauss_method_grad, star_sqgauss_method_hess ,NULL}, {"sq_mean_curvature",VERTEX,0,NOSPEC,sqcurve_method_init, sqcurve_method_value, sqcurve_method_grad, null_q_hess, sqcurve_method_cleanup}, {"eff_area_sq_mean_curvature",VERTEX,0,NOSPEC,sqcurve_method_init, sqcurve_method_value, sqcurve_method_grad, null_q_hess, sqcurve_method_cleanup }, {"normal_sq_mean_curvature",VERTEX,0,NOSPEC,sqcurve_method_init, sqcurve_method_value, sqcurve_method_grad, null_q_hess, sqcurve_method_cleanup }, {"star_sq_mean_curvature",VERTEX,NEED_PART_STAR,NOSPEC,star_sqcurve_method_init, star_sqcurve_method_value, star_sqcurve_method_grad, star_sqcurve_method_hess ,NULL}, {"star_eff_area_sq_mean_curvature",VERTEX,NEED_PART_STAR,NOSPEC, star_sqcurve_method_init, star_sqcurve_method_value, star_sqcurve_method_grad, star_sqcurve_method_hess ,NULL}, {"star_normal_sq_mean_curvature",VERTEX,NEED_PART_STAR,NOSPEC, star_sqcurve_method_init, star_sqcurve_method_value, star_sqcurve_method_grad, star_sqcurve_method_hess ,NULL}, {"star_perp_sq_mean_curvature",VERTEX,NEED_PART_STAR,NOSPEC, star_sqcurve_method_init, star_sqcurve_method_value, star_sqcurve_method_grad, star_sqcurve_method_hess ,NULL}, {"circle_willmore",EDGE,NEED_WINGS|NEED_SIDE,NOSPEC, circle_willmore_init, circle_willmore_value, circle_willmore_grad, circle_willmore_hess ,NULL}, {"laplacian_mean_curvature",VERTEX,NEED_PART_STAR,NOSPEC, laplacian_mean_curvature_init, laplacian_mean_curvature_value, laplacian_mean_curvature_grad, NULL, NULL }, {"stokes2d",VERTEX,NEED_PART_STAR,NOSPEC, stokes2d_init, stokes2d_value, stokes2d_grad, stokes2d_hess ,NULL}, {"stokes2d_laplacian",VERTEX,NEED_PART_STAR,NOSPEC, stokes2d_init, stokes2d_laplacian, NULL, NULL ,NULL}, {"hooke_energy",EDGE,NEED_SIDE,NOSPEC,hooke_energy_init,hooke_energy, hooke_energy_gradient,hooke_energy_hessian,NULL}, {"hooke2_energy",EDGE,NEED_SIDE,NOSPEC,hooke2_energy_init,hooke2_energy, hooke2_energy_gradient,hooke2_energy_hessian,NULL}, {"hooke3_energy",EDGE,NEED_SIDE,NOSPEC,hooke3_energy_init,hooke3_energy, hooke3_energy_gradient,hooke3_energy_hessian,NULL}, {"local_hooke_energy",VERTEX,0,NOSPEC,local_hooke_init, local_hooke, local_hooke_gradient,null_q_hess,NULL}, {"dihedral_hooke",EDGE,NEED_WINGS,NOSPEC,NULL, dihedral_hooke_energy, dihedral_hooke_grad, dihedral_hooke_hess ,NULL}, {"linear_elastic",FACET,NEED_SIDE,NOSPEC,linear_elastic_init, linear_elastic_energy, linear_elastic_gradient,linear_elastic_hessian,NULL}, {"general_linear_elastic",FACET,NEED_SIDE,NOSPEC,general_linear_elastic_init, general_linear_elastic_energy, general_linear_elastic_gradient, general_linear_elastic_hessian,NULL}, {"linear_elastic_B",FACET,NEED_SIDE,NOSPEC,linear_elastic_B_init, linear_elastic_B_energy, linear_elastic_B_gradient,linear_elastic_B_hessian,NULL}, {"relaxed_elastic",FACET,NEED_SIDE,NOSPEC,relaxed_elastic_init, relaxed_elastic_energy, relaxed_elastic_gradient,relaxed_elastic_hessian,NULL}, {"relaxed_elastic1",FACET,NEED_SIDE,NOSPEC,relaxed_elastic_init, relaxed_elastic1_energy, relaxed_elastic1_gradient,relaxed_elastic1_hessian,NULL}, {"relaxed_elastic2",FACET,NEED_SIDE,NOSPEC,relaxed_elastic_init, relaxed_elastic2_energy, relaxed_elastic2_gradient,relaxed_elastic2_hessian,NULL}, {"relaxed_elastic_A",FACET,NEED_SIDE,NOSPEC,relaxed_elastic_A_init, relaxed_elastic_A_energy, relaxed_elastic_A_gradient,relaxed_elastic_A_hessian,NULL}, {"relaxed_elastic1_A",FACET,NEED_SIDE,NOSPEC,relaxed_elastic_A_init, relaxed_elastic1_A_energy, relaxed_elastic1_A_gradient,relaxed_elastic1_A_hessian,NULL}, {"relaxed_elastic2_A",FACET,NEED_SIDE,NOSPEC,relaxed_elastic_A_init, relaxed_elastic2_A_energy, relaxed_elastic2_A_gradient,relaxed_elastic2_A_hessian,NULL}, {"dirichlet_elastic",FACET,NEED_SIDE,NOSPEC,dirichlet_elastic_init, dirichlet_elastic_energy, dirichlet_elastic_gradient, dirichlet_elastic_hessian,NULL}, {"SVK_elastic",FACET,NEED_SIDE,NOSPEC,SVK_init, SVK_energy, SVK_gradient,SVK_hessian,NULL}, {"neo_hookean",FACET,NEED_SIDE,NOSPEC,Neo_Hookean_init, Neo_Hookean_energy, Neo_Hookean_gradient,Neo_Hookean_hessian,NULL}, {"knot_energy",VERTEX,0,NOSPEC,knot_power_init,knot_energy, knot_energy_gradient,knot_energy_hessian,NULL}, {"charge_gradient",VERTEX,0,NOSPEC,charge_gradient_init,charge_gradient, charge_gradient_gradient,null_q_hess,NULL}, {"uniform_knot_energy",VERTEX,0,NOSPEC,uniform_knot_energy_init, uniform_knot_energy, uniform_knot_energy_gradient,null_q_hess,NULL}, {"uniform_knot_energy_normalizer",VERTEX,0,NOSPEC,knot_power_init, uniform_normalization, null_q_grad, null_q_hess,NULL}, {"uniform_knot_normalizer1",VERTEX,0,NOSPEC,knot_power_init, uniform_normalization, null_q_grad, null_q_hess,NULL}, {"uniform_knot_normalizer2",VERTEX,0,NOSPEC,knot_power_init, uniform_binormalization, null_q_grad, null_q_hess,NULL}, {"edge_knot_energy_normalizer",EDGE,0,NOSPEC,knot_power_init, edge_normalization, null_q_grad, null_q_hess,NULL}, {"simon_knot_energy_normalizer",EDGE,0,NOSPEC,knot_power_init, simon_normalization, null_q_grad, null_q_hess,NULL}, {"edge_min_knot_energy",EDGE,0,NOSPEC,knot_power_init, edge_min_knot_energy, null_q_grad, null_q_hess,NULL}, {"edge_knot_energy",EDGE,0,NOSPEC,knot_power_init, edge_edge_knot_energy, edge_edge_knot_energy_gradient,null_q_hess,NULL}, {"edge_edge_knot_energy",EDGE,0,NOSPEC,knot_power_init, edge_edge_knot_energy, edge_edge_knot_energy_gradient,null_q_hess,NULL}, {"facet_knot_energy",VERTEX,0,NOSPEC,facet_knot_energy_init, facet_knot_energy, facet_knot_energy_gradient,null_q_hess,NULL}, {"facet_knot_energy_fix",VERTEX,0,NOSPEC,facet_knot_energy_fix_init, facet_knot_energy_fix, facet_knot_energy_fix_gradient,null_q_hess,NULL}, {"bi_surface",VERTEX,0,SPEC_SCALAR,bi_surface_init, bi_surface_energy, bi_surface_gradient,null_q_hess,NULL}, {"buck_knot_energy",EDGE,0,NOSPEC,knot_power_init, buck_knot_energy, buck_knot_energy_gradient,null_q_hess,NULL}, {"proj_knot_energy",EDGE,0,NOSPEC,knot_power_init, proj_knot_energy, proj_knot_energy_gradient,null_q_hess,NULL}, {"sin_knot_energy",EDGE,0,NOSPEC,NULL, sin_knot_energy, sin_knot_energy_gradient,null_q_hess,NULL}, {"circle_knot_energy",EDGE,0,NOSPEC,NULL, circle_knot_energy, circle_knot_energy_gradient,null_q_hess,NULL}, {"twist",EDGE,0,NOSPEC,NULL, twist, null_q_grad, null_q_hess,NULL}, {"writhe",EDGE,0,NOSPEC,NULL, writhe, writhe_gradient,null_q_hess,NULL}, {"average_crossings",EDGE,0,NOSPEC, NULL, average_crossing,null_q_grad,null_q_hess,NULL}, {"sphere_knot_energy",FACET,0,NOSPEC,sphere_knot_energy_init, sphere_knot_energy, sphere_knot_energy_gradient,null_q_hess,NULL}, {"knot_thickness_0",VERTEX,0,NOSPEC, knot_power_init,knot_thickness_0, knot_thickness_0_gradient, null_q_hess,NULL}, {"knot_thickness",VERTEX,0,NOSPEC, NULL,knot_thickness, null_q_grad, null_q_hess,NULL}, {"knot_thickness_p",VERTEX,0,NOSPEC, uniform_knot_energy_init, knot_thickness_p, knot_thickness_p_gradient, null_q_hess,NULL}, {"knot_thickness_p2",VERTEX,0,NOSPEC, uniform_knot_energy_init, knot_thickness_p2, knot_thickness_p2_gradient, null_q_hess,NULL}, {"knot_thickness2",VERTEX,0,NOSPEC, NULL, knot_thickness2, null_q_grad, null_q_hess,NULL}, {"knot_local_thickness",VERTEX,0,NOSPEC, NULL, knot_local_thickness, null_q_grad, null_q_hess,NULL}, {"curvature_function",VERTEX,0,NOSPEC,curvature_forces_init, curvature_forces_energy, curvature_forces,null_q_hess,NULL}, {"johndust",VERTEX,0,NOSPEC,NULL,johndust_energy, johndust_gradient,null_q_hess,NULL}, {"true_writhe",EDGE,0,NOSPEC,NULL, true_writhe, null_q_grad,null_q_hess,NULL}, {"true_average_crossings",EDGE,0,NOSPEC, NULL, true_average_crossing,null_q_grad,null_q_hess,NULL}, {"ackerman",VERTEX,0,NOSPEC,ackerman_init,ackerman_energy, ackerman_forces,null_q_hess,NULL}, {"carter_energy",FACET,NEED_SIDE,NOSPEC,carter_energy_init, carter_energy, carter_energy_gradient,null_q_hess,NULL}, {"curvature_binormal",VERTEX,0,NOSPEC,curvature_binormal_init, curvature_binormal_energy, curvature_binormal_force ,null_q_hess,NULL}, {"ddd_gamma_sq",EDGE,NEED_STRING_STAR,NOSPEC,ddd_gamma_sq_init, ddd_gamma_sq_energy, ddd_gamma_sq_gradient ,null_q_hess,NULL}, { " ",0,0,0,NULL,NULL,NULL,NULL,NULL} /* to signal end of array */ }; /*********************************************************************/ /************************************************************************* * * function: quantity_init() * * purpose: initialize quantity structures for a surface */ void quantity_init() { int k; /* set lists to empty; memory deallocation taken care of by global permanent memory freeing at the start of each new surface */ gen_quant_count = 0; gen_quant_list_max = 0; gen_quant_free_left = 0; gen_quant_free = NULL; meth_inst_count = 0; meth_inst_list_max = 0; meth_inst_free_left = 0; meth_inst_free = NULL; compound_quant_list_head = -1; meth_inst_count = LOW_INST; /* skip 0, so all indexes signable */ meth_inst_alloc = 20; dy_meth_inst = dy_calloc(meth_inst_alloc,sizeof(struct method_instance)); for ( k = 0 ; k < NUMELEMENTS ; k++ ) { quant_flags[k] = 0; global_meth_inst_count[k] = 0; } memset((char*)global_meth_inst,0,sizeof(global_meth_inst)); } /************************************************************************* * * function: find_quantity() * * purpose: find existing quantity by name in globals table. * * return: quantity number, or -1 if not found. */ int find_quantity(name) char *name; { int q; q = lookup_global_hash(name,0,QUANTITYNAME,HASH_LOOK); if ( (q & NAMETYPEMASK) == QUANTITYNAME ) return q & INDEXMASK; return -1; } /************************************************************************* * * function: find_method_instance() * * purpose: find existing instance by name * * return: instance number, or -1 if not found. */ int find_method_instance(name) char *name; { int n; n = lookup_global_hash(name,0,METHODNAME,HASH_LOOK); if ( (n & NAMETYPEMASK) == METHODNAME ) return n & INDEXMASK; return -1; } /************************************************************************* * * function: new_quantity() * * purpose: set up new named quantity. * returns number of new quantity. * If already exists, returns number, does not re-initialize. */ int new_quantity(name,mode) char *name; /* identifying name for this quantity */ int mode; /* Q_ENERGY, Q_FIXED, Q_CONSERVED, or Q_INFO */ { int q=0; /* new structure */ int g; /* hash return */ /* check to see if name used, and add if not */ g = lookup_global_hash(name,0,QUANTITYNAME,0); if ( g != 0 ) { int qq = g & INDEXMASK; if ( datafile_flag == IN_DATAFILE ) { if ( (GEN_QUANT(qq)->flags & Q_FORWARD_DEF) || addload_flag ) { q = qq; } else { sprintf(errmsg,"'%s' already declared.\n",name); kb_error(1555,errmsg,RECOVERABLE); } } } else /* need to allocate new structure */ { /* check to see if any free ones left */ if ( gen_quant_free_left <= 0 ) { gen_quant_free_left = 100; gen_quant_free = (struct gen_quant*)mycalloc(gen_quant_free_left, sizeof(struct gen_quant)); } if ( gen_quant_count >= gen_quant_list_max ) { if ( gen_quant_list_max == 0 ) { gen_quant_list_max = 1000; gen_quant_list = (struct gen_quant**)mycalloc(gen_quant_list_max, sizeof(struct gen_quant*)); } else { gen_quant_list = (struct gen_quant **)kb_realloc((char*)gen_quant_list, 2*gen_quant_list_max*sizeof(struct gen_quant*)); gen_quant_list_max *= 2; } } /* initialize */ q = gen_quant_count; gen_quant_list[q] = gen_quant_free; g = lookup_global_hash(name,q,QUANTITYNAME,HASH_ADD); GEN_QUANT(q)->num = q; GEN_QUANT(q)->modulus = 1.0; GEN_QUANT(q)->timestamp = -1; GEN_QUANT(q)->tolerance = -1.0; strncpy(GEN_QUANT(q)->name,name,sizeof(GEN_QUANT(q)->name)); gen_quant_count++; /* so seen by global_hash_expand */ gen_quant_free++; gen_quant_free_left--; } GEN_QUANT(q)->flags = mode; return q; } /************************************************************************* * * function: new_method_instance() * * purpose: set up new method instance. * If already exists, then returns instance number; * does not re-initialize. * return: index of new instance in METH_INST list * */ int new_method_instance(meth_name,inst_name) char *meth_name,*inst_name; { struct method_instance * m; /* new instance */ struct gen_quant_method *gm=NULL; int n=0,nb; int inst_num; /* number of new instance */ int g; /* hash return */ if ( meth_name ) { nb = sizeof(basic_gen_methods)/sizeof(struct gen_quant_method); for ( gm = basic_gen_methods,n = 0 ; n < nb ; n++,gm++ ) if ( stricmp(gm->name,meth_name) == 0 ) break; if ( n >= nb ) { sprintf(errmsg,"No method '%s' exists.\n",meth_name); kb_error(1559,errmsg,DATAFILE_ERROR); return -1; } } /* make sure there is room for new instance */ if ( meth_inst_free_left <= 0 ) { meth_inst_free_left = 100; meth_inst_free = (struct method_instance*)mycalloc(meth_inst_free_left, sizeof(struct method_instance)); } if ( meth_inst_count >= meth_inst_list_max ) { if ( meth_inst_list_max == 0 ) { meth_inst_list_max = 1000; meth_inst_list = (struct method_instance**)mycalloc(meth_inst_list_max, sizeof(struct method_instance*)); } else { meth_inst_list = (struct method_instance **)kb_realloc((char*)meth_inst_list, 2*meth_inst_list_max*sizeof(struct method_instance*)); meth_inst_list_max *= 2; } } inst_num = meth_inst_count; meth_inst_list[inst_num] = meth_inst_free; m = METH_INSTANCE(inst_num); memset(m,0,sizeof(struct method_instance)); m->self_id = METHBASE + inst_num; m->modulus = 1.0; m->gen_method = n; m->quant = -1; /* not attached to quantity yet */ m->timestamp = -1; if ( gm ) { m->type = gm->type; if ( gm->spec_flags & SPEC_USE_DENSITY ) m->flags |= USE_DENSITY; } if ( strlen(inst_name) >= MNAMESIZE ) { sprintf(errmsg,"Method name too long: %s\n",inst_name); kb_error(2856,errmsg,RECOVERABLE); } strncpy(m->name,inst_name,sizeof(m->name)); g = lookup_global_hash(inst_name,inst_num,METHODNAME,HASH_ADD); if ( g != 0 ) { int inum = g & INDEXMASK; if ( datafile_flag == IN_DATAFILE ) { if ( (METH_INSTANCE(inum)->flags & Q_FORWARD_DEF) || addload_flag ) { *METH_INSTANCE(inum) = *m; /* copy over initialization */ inst_num = inum; METH_INSTANCE(inum)->self_id = METHBASE + inst_num; } else { sprintf(errmsg,"'%s' already declared.\n",inst_name); memset((char*)m,0,sizeof(struct method_instance)); kb_error(1558,errmsg,RECOVERABLE); } } } else { meth_inst_count++; meth_inst_free++; meth_inst_free_left--; } return inst_num; } /********************************************************************* * * function: add_standard_quantity() * * purpose: for common quantities, creates method instance * and quantity. Global energy only. */ int add_standard_quantity(meth_name,modulus) char *meth_name; /* name of method (not instance) */ REAL modulus; { int mi; int q; char inst_name[40]; strncpy(inst_name,meth_name,25); strcat(inst_name,"_inst"); mi = new_method_instance(meth_name,inst_name); q = new_quantity(meth_name,Q_ENERGY); attach_method_num(q,mi); apply_method(NULLID,inst_name); METH_INSTANCE(mi)->modulus = 1.0; GEN_QUANT(q)->modulus = modulus; GEN_QUANT(q)->flags |= STANDARD_QUANTITY; return q; } /********************************************************************* * * function: attach_method() * * purpose: attach method instance to quantity * * return: instance number */ int attach_method(quantnum,meth_name) int quantnum; char *meth_name; { int inst_num; inst_num = find_method_instance(meth_name); if ( inst_num < 0 ) { sprintf(errmsg,"Undefined method instance '%s'. \n",meth_name); kb_error(1562,errmsg,DATAFILE_ERROR); return -1; } return attach_method_num(quantnum,inst_num); } int attach_method_num(quantnum,inst_num) int quantnum; int inst_num; { struct method_instance *m = METH_INSTANCE(inst_num); struct gen_quant_method *gm; struct gen_quant *quant = GEN_QUANT(quantnum); int i; if ( (inst_num < 0) || (inst_num >= meth_inst_count) ) kb_error(2446, "Internal error: Unknown method number in attach_method_num().\n", RECOVERABLE); if ( (m->quant >= 0) && (m->quant != quantnum) ) { sprintf(errmsg,"%s: Sorry, but for now, a method instance can only belong to one quantity.\n",m->name); kb_error(1564,errmsg,DATAFILE_ERROR); } m->quant = quant->num; gm = basic_gen_methods + m->gen_method; if ( (gm->gradient==NULL) && (quant->flags & (Q_ENERGY|Q_FIXED|Q_CONSERVED)) ) { sprintf(errmsg,"Sorry; method %s does not have a gradient available.\n",gm->name); kb_error(1565,errmsg,DATAFILE_ERROR); } for ( i = 0 ; i < quant->method_count ; i++ ) if ( quant->meth_inst[i] == inst_num ) goto attach_method_num_exit; if ( quant->meth_inst == NULL ) quant->meth_inst = quant->meth_inst_space; else if ( quant->method_count >= MAXMETH ) { if ( quant->meth_inst == quant->meth_inst_space ) { quant->meth_inst = (int*)mycalloc(quant->method_count+1,sizeof(int)); memcpy(quant->meth_inst,quant->meth_inst_space,MAXMETH*sizeof(int)); } else quant->meth_inst = (int*)kb_realloc((char*)(quant->meth_inst), (quant->method_count+1)*sizeof(int)); } quant->meth_inst[quant->method_count++] = inst_num; quant_flags[gm->type] |= quant->flags & (Q_ENERGY|Q_FIXED|Q_INFO|Q_CONSERVED); quant->flags |= (basic_gen_methods[m->gen_method].flags & TORUS_MODULO_MUNGE); attach_method_num_exit: return inst_num; } /********************************************************************* * * function: apply_method() * * purpose: make a method apply to an element * if element id inverted and method orientable, then * store method number as negative. * for null element, assumed to be global method of type */ void apply_method(id,method_name) element_id id; char *method_name; { int inst_num; /* search for method */ inst_num = find_method_instance(method_name); if ( inst_num < 0 ) { sprintf(errmsg, "Undefined method instance '%s' on %s %s. \n", method_name,typenames[id_type(id)],ELNAME(id)); kb_error(1566, errmsg,DATAFILE_ERROR); return; } /* really annoying in case of compound quantity if ( valid_id(id) && (m->quant == -1) ) { sprintf(errmsg,"Method instance '%s' not attached to a quantity.\n", method_name); kb_error(1567,errmsg,WARNING); } */ apply_method_num(id,inst_num); } /********************************************************************* * * function: apply_method_num() * * purpose: make a method apply to an element (by method number) * if element id inverted and method orientable, then * store method number as negative. * for null element, assumed to be global method of type * If method of opposite sign already existing, then * it is deleted, due to cancellation. */ void apply_method_num(id,inst_num) element_id id; int inst_num; /* method instance number */ { struct method_instance *m; struct element *e_ptr; int type; /* of element */ struct gen_quant_method *gm; int i; /* search for method */ if ( inst_num < -meth_inst_count ) { kb_error(2178,"Internal error: Undefined method instance. \n",DATAFILE_ERROR); return; } if ( inst_num > meth_inst_count ) { kb_error(2179,"Internal error: Undefined method instance. \n",DATAFILE_ERROR); return; } m = METH_INSTANCE(abs(inst_num)); if ( m->flags & GLOBAL_INST ) { /* trying to apply global method to single element */ if ( inverted(id) ) { kb_error(4569,"Cannot apply signed method/quantity to individual element.\n",DATAFILE_ERROR); } return; } gm = basic_gen_methods + m->gen_method; if ( valid_id(id) ) /* individual element */ { int *instlist,maxinst; int meth_offset; if ( inverted(id) && (gm->flags & ORIENTABLE_METHOD) ) inst_num = -inst_num; type = id_type(id); meth_offset = get_meth_offset(type); e_ptr = elptr(id); /* see if already there */ instlist = (int*)((char*)e_ptr + meth_offset); for ( i = 0 ; i < (int)e_ptr->method_count ; i++ ) { if ( instlist[i] == inst_num ) return; if ( instlist[i] == -inst_num ) { /* cancels out */ instlist[i] = instlist[--e_ptr->method_count]; return; } } /* add */ maxinst = EXTRAS(type)[web.meth_attr[type]].array_spec.datacount; if ( (int)e_ptr->method_count >= maxinst ) { int newc = maxinst+4; expand_attribute(type,web.meth_attr[type],&newc); e_ptr = elptr(id); /* since things moved */ instlist = (int*)((char*)e_ptr + meth_offset); } instlist[e_ptr->method_count] = inst_num; e_ptr->method_count++; } else /* global */ { m->flags |= GLOBAL_INST; type = gm->type; /* see if already there */ for ( i = 0 ; i < global_meth_inst_count[type] ; i++ ) if ( global_meth_inst[type][i] == inst_num ) break; if ( i >= global_meth_inst_count[type] ) { /* add it */ if ( global_meth_inst_count[type] >= MAXGLOBINST ) kb_error(1568,"Too many global method instances.\n",DATAFILE_ERROR); else global_meth_inst[type][global_meth_inst_count[type]++] = inst_num; } } } /********************************************************************* * * function: unapply_method() * * purpose: disable a method for an element. * */ void unapply_method(id,inst_num) element_id id; int inst_num; /* index in method instance list */ { struct element *e_ptr; int i; int type = id_type(id); int meth_offset = EXTRAS(type)[web.meth_attr[type]].offset; int *methlist = (int*)((char*)elptr(id) + meth_offset); if ( valid_id(id) ) /* individual element */ { e_ptr = elptr(id); /* see if already there */ for ( i = 0 ; i < (int)e_ptr->method_count ; i++ ) if ( abs(methlist[i]) == abs(inst_num) ) { /* delete */ methlist[i] = methlist[--e_ptr->method_count]; break; } } } /******************************************************************** * * function: apply_quantity() * * purpose: apply quantity methods to element, if appropriate */ void apply_quantity(id,quantnum) element_id id; int quantnum; { struct gen_quant *g = GEN_QUANT(quantnum); int i; int count = 0; if ( g->flags & Q_COMPOUND ) kb_error(2180,"Compound quantity should have individual method instances applied to elements.\n",RECOVERABLE); for ( i = 0 ; i < g->method_count ; i++ ) { struct method_instance *mi = METH_INSTANCE(g->meth_inst[i]); struct gen_quant_method *gm = basic_gen_methods + mi->gen_method; if ( gm->type == id_type(id) ) { apply_method_num(id,g->meth_inst[i]); count++; } } if ( count == 0 ) { sprintf(errmsg,"Quantity '%s' has no methods applying to this type element.\n", g->name); kb_error(1569,errmsg, WARNING); } } /******************************************************************** * * function: unapply_quantity() * * purpose: disable quantity methods to element, if appropriate */ void unapply_quantity(id,quantnum) element_id id; int quantnum; { struct gen_quant *g = GEN_QUANT(quantnum); int i; for ( i = 0 ; i < g->method_count ; i++ ) { struct method_instance *mi = METH_INSTANCE(g->meth_inst[i]); struct gen_quant_method *gm = basic_gen_methods + mi->gen_method; if ( gm->type == id_type(id) ) { unapply_method(id,g->meth_inst[i]); } } } /************************************************************************* * * function: q_info_init() * * purpose: allocates q_info arrays * */ void q_info_init(q_info,mode) struct qinfo *q_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ { int m,i; int maxgauss = (gauss1D_num > gauss2D_num) ? gauss1D_num : gauss2D_num; if ( web.dimension >= SOAPFILM ) { if ( gauss_lagrange[web.dimension][web.gauss2D_order].gnumpts > maxgauss ) maxgauss = gauss_lagrange[web.dimension][web.gauss2D_order].gnumpts; if ( gauss_lagrange[web.dimension-1][web.gauss1D_order].gnumpts > maxgauss ) maxgauss = gauss_lagrange[web.dimension-1][web.gauss1D_order].gnumpts; } else if ( gauss_lagrange[web.dimension][web.gauss1D_order].gnumpts > maxgauss ) maxgauss = gauss_lagrange[web.dimension][web.gauss1D_order].gnumpts; if ( web.skel[FACET].ctrlpts > MAXVCOUNT ) kb_error(1570,"Vertices in facet exceed MAXVCOUNT.\n",RECOVERABLE); memset((char*)q_info,0,sizeof(struct qinfo)); /* set up matrices in qinfo with lots of room */ q_info->xx = dmatrix(0,MAXVCOUNT,0,SDIM); if ( web.torus_flag ) { q_info->u = dmatrix(0,MAXVCOUNT,0,SDIM); if ( (mode == METHOD_GRADIENT) || (mode == METHOD_HESSIAN) ) q_info->ugrad = dmatrix(0,MAXVCOUNT,0,SDIM); if ( mode == METHOD_HESSIAN ) q_info->uhess = dmatrix4(MAXVCOUNT,MAXVCOUNT,SDIM,SDIM); } q_info->sides = dmatrix3(maxgauss,MAXVCOUNT,SDIM); if ( web.modeltype == LINEAR ) for ( m = 1 ; m < maxgauss ; m++ ) q_info->sides[m] = q_info->sides[0]; /* since all the same */ q_info->ss = dmatrix(0,2*SDIM,0,2*SDIM); if ( (mode == METHOD_GRADIENT) || (mode == METHOD_HESSIAN) ) q_info->grad = dmatrix(0,MAXVCOUNT,0,SDIM); q_info->gauss_pt = dmatrix(0,maxgauss,0,2*SDIM); /* extra for point number */ if ( web.torus_flag && (web.representation == SOAPFILM) ) { /* allocate some working space for facet_volume method */ int ectrl = web.lagrange_order + 1; q_info->uu[0] = (REAL **)temp_calloc(ectrl*3,sizeof(REAL*)); q_info->uu[1] = q_info->uu[0]+ectrl; q_info->uu[2] = q_info->uu[1]+ectrl; if ( (mode == METHOD_GRADIENT) || (mode == METHOD_HESSIAN) ) { q_info->uugrad[0] = (REAL **)temp_calloc(ectrl*3,sizeof(REAL*)); q_info->uugrad[1] = q_info->uugrad[0]+ectrl; q_info->uugrad[2] = q_info->uugrad[1]+ectrl; } if ( mode == METHOD_HESSIAN ) { q_info->uuhess[0] = (REAL ****)temp_calloc(ectrl*3,sizeof(REAL***)); q_info->uuhess[1] = q_info->uuhess[0]+ectrl; q_info->uuhess[2] = q_info->uuhess[1]+ectrl; q_info->uuhess[0][0] = (REAL***)temp_calloc(3*ectrl*ectrl,sizeof(REAL**)); for ( i = 0 ; i < 3 ; i++ ) for ( m = 0 ; m < ectrl ; m++ ) q_info->uuhess[i][m] = q_info->uuhess[0][0] + i*ectrl*ectrl + m*ectrl; } } } /*********************************************************************** * * function: q_info_free() * * purpose: deallocate all memory allocated to q_info structure. * */ void q_info_free(q_info) struct qinfo *q_info; { free_matrix(q_info->xx); free_matrix3(q_info->sides); free_matrix(q_info->ss); free_matrix(q_info->grad); free_matrix(q_info->gauss_pt); if ( q_info->uu[0] ) { temp_free((char*)q_info->uu[0]); temp_free((char*)q_info->uugrad[0]); } if ( q_info->uuhess[0] ) { temp_free((char*)q_info->uuhess[0][0]); temp_free((char*)q_info->uuhess[0]); } if ( q_info->u ) { free_matrix(q_info->u); free_matrix(q_info->ugrad); free_matrix4(q_info->uhess); } } /*************************************************************************** * * function: global_meth_needs() * * purpose: find set of q_info needs for active methods. To be called * by quantity calc functions after inspecting quantities. */ int global_meth_needs(type) int type; { int k; int needs = 0; for ( k = 0 ; k < global_meth_inst_count[type]; k++ ) { int mi = global_meth_inst[type][k]; if ( (METH_INSTANCE(mi)->flags & Q_DOTHIS) && (METH_INSTANCE(mi)->type == type) ) needs |= basic_gen_methods[METH_INSTANCE(mi)->gen_method].flags; } return needs; } #ifdef KSR __shared pthread_mutex_t ksrlock; int team_id = -1; int ksr_counter; int m_next() { int val; M_LOCK(&ksr_counter); val = ksr_counter++; M_UNLOCK(&ksr_counter); return val; } void m_fork(func,type,mode) void (*func)(); int type; int mode; { if ( team_id == -1 ) { team_id = pr_create_team(NUMPROCS); pthread_mutex_init(&ksrlock,pthread_mutexattr_default); } ksr_counter = 0; pr_pcall(team_id,func,copyargs(type,mode)); } #endif int *v_procnum; /* processors for vertex, index by ord */ #if defined(SHARED_MEMORY) /************************************************************************ * * function: make_el_list() * * purpose: make element list for processes to step through. * * algorithm: sort in top dimension coordinate */ long el_list_timestamp[NUMELEMENTS] = { -1,-1,-1,-1,-1 }; /* to see if need remake list */ int vertex_comp(a,b) vertex_id *a,*b; { REAL xa = get_coord(*a)[SDIM-1]; REAL xb = get_coord(*b)[SDIM-1]; if ( xa < xb ) return -1; if ( xa > xb ) return 1; return 0; } int edge_comp(a,b) edge_id *a,*b; { REAL xa = get_coord(get_edge_tailv(*a))[SDIM-1]; REAL xb = get_coord(get_edge_tailv(*b))[SDIM-1]; if ( xa < xb ) return -1; if ( xa > xb ) return 1; return 0; } int facet_comp(a,b) facet_id *a,*b; { REAL xa,xb; if ( web.representation == SIMPLEX ) { xa = get_coord(get_facet_vertices(*a)[0])[SDIM-1]; xb = get_coord(get_facet_vertices(*b)[0])[SDIM-1]; } else { xa = get_coord(get_fe_tailv(get_facet_fe(*a)))[SDIM-1]; xb = get_coord(get_fe_tailv(get_facet_fe(*b)))[SDIM-1]; } if ( xa < xb ) return -1; if ( xa > xb ) return 1; return 0; } void make_el_list(type) int type; /* element type */ { element_id id,*tlist; int pnum,i,bin,end,start; if ( el_list[type] ) myfree((char*)el_list[type]); tlist = el_list[type] = (element_id*)mycalloc(web.skel[type].count, sizeof(element_id)); FOR_ALL_ELEMENTS(type,id) *(tlist++) = id; switch(type) { case VERTEX: qsort((char*)(el_list[type]),web.skel[type].count, sizeof(element_id), FCAST vertex_comp); /* assign processor numbers */ if ( v_procnum ) myfree((char*)v_procnum); v_procnum = (int*)mycalloc(web.skel[VERTEX].max_ord+1,sizeof(int)); bin = (web.skel[type].count + nprocs - 1)/nprocs; for ( pnum = 0 ; pnum < nprocs ; pnum++ ) { start = pnum*bin; end = start + bin; if ( end > web.skel[type].count ) end = web.skel[type].count; for ( i = start ; i < end ; i++ ) v_procnum[loc_ordinal(el_list[VERTEX][i])] = pnum; } break; case EDGE: qsort((char*)(el_list[type]),web.skel[type].count, sizeof(element_id), FCAST edge_comp); break; case FACET: qsort((char*)(el_list[type]),web.skel[type].count, sizeof(element_id), FCAST facet_comp); break; } el_list_timestamp[type] = top_timestamp; } /*********************************************************************** * * function: multi_calc_quants() * * purpose: calculate all values of quantities in parallel. * This function called in parallel from calc_quants(). * m_next used to parcel out elements to processors. * */ void multi_calc_quants(type) int type; /* element type */ { int k; struct element *e_ptr; struct gen_quant_method *gm; int mi; REAL val; struct qinfo q_info; /* data passing structure */ int meth_offset = EXTRAS(type)[web.meth_attr[type]].offset; int me = GET_THREAD_ID; /* which process I am */ int global_needs; #ifdef THREADS __int32 multi_calc_quants_elapsed_time[2]; multi_calc_quants_elapsed_time[0] = 0; multi_calc_quants_elapsed_time[1] = 0; #endif #ifdef SIGUSR1 signal(SIGUSR1,catcher); /* to catch user interrupt */ #endif signal(SIGINT,catcher); /* to catch user interrupt */ m_breakflag[me] = 0; if ( setjmp(m_jumpbuf[me]) ) { q_info_free(&q_info); return; } PROF_START(multi_calc_quants) q_info_init(&q_info,METHOD_VALUE); global_needs = global_meth_needs(type); THREAD_FOR_ALL_NEW(type, /* following block is macro argument! */ { int setup_flag = 0; int needs; /* particular setup needs for current mode */ q_info.id = *idptr; e_ptr = elptr(q_info.id); /* get setup flags */ needs = global_needs; for ( k = 0 ; k < e_ptr->method_count ; k++ ) { int mm; mm = ((int*)((char*)e_ptr+meth_offset))[k]; mi = abs(mm); if ( (METH_INSTANCE(mi)->flags & Q_DOTHIS) && (METH_INSTANCE(mi)->type == type) ) needs |= basic_gen_methods[METH_INSTANCE(mi)->gen_method].flags; } for ( k = 0 ; k < global_meth_inst_count[type]; k++ ) { mi = global_meth_inst[type][k]; if ( (METH_INSTANCE(mi)->flags & Q_DOTHIS) && (METH_INSTANCE(mi)->type == type) ) { if ( !setup_flag ) { (*q_setup[type])(NULL,&q_info,needs); setup_flag = 1; } q_info.method = mi; gm = basic_gen_methods + METH_INSTANCE(mi)->gen_method; val = (*gm->value)(&q_info); if ( METH_INSTANCE(mi)->flags & ELEMENT_MODULUS_FLAG ) val *= *(REAL*)get_extra(q_info.id,METH_INSTANCE(mi)->elmodulus); METH_INSTANCE(mi)->procvalue[me] += val; METH_INSTANCE(mi)->procabstotal[me] += fabs(val); } } for ( k = 0 ; k < e_ptr->method_count ; k++ ) { int mm; mm = ((int*)((char*)e_ptr+meth_offset))[k]; q_info.method = mi = abs(mm); if ( (METH_INSTANCE(mi)->flags & Q_DOTHIS) && (METH_INSTANCE(mi)->type == type) ) { if ( !setup_flag ) { (*q_setup[type])(NULL,&q_info,needs); setup_flag = 1; } gm = basic_gen_methods + METH_INSTANCE(mi)->gen_method; val = (*gm->value)(&q_info); if ( METH_INSTANCE(mi)->flags & ELEMENT_MODULUS_FLAG ) val *= *(REAL*)get_extra(q_info.id,METH_INSTANCE(mi)->elmodulus); METH_INSTANCE(mi)->procvalue[me] += (mm < 0 ) ? -val : val; METH_INSTANCE(mi)->procabstotal[me] += fabs(val); } } } ) /* end THREAD_FOR_ALL_NEW macro */ q_info_free(&q_info); PROF_FINISH(multi_calc_quants) /* if ( verbose_flag ) { PROF_PRINT(multi_calc_quants) } */ } #endif /*********************************************************************** * * function: calc_quants() * * purpose: calculate all values of quantities. * Info passed to methods in global structure * */ REAL calc_quants(mode) int mode; /* energy, constraint, and/or info flag bits */ { int k; struct element *e_ptr; struct gen_quant *q = NULL; int type; /* element type */ struct method_instance * mi; struct gen_quant_method *gm; REAL energy = 0.0; struct qinfo q_info; /* data passing structure */ int todo = 0; /* whether any to do */ int global_needs; #ifdef PROFILING_ENABLED __int32 value_elapsed_time[2]; #endif PROF_START(calc_quants); if ( calc_quant_flag ) return 0.0; calc_quant_flag = 1; /* so no recursive evaluation */ /* method initialization */ for ( type = 0 ; type < NUMELEMENTS ; type++ ) quant_flags[type] = 0; for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); /* since some init may move things */ gm = basic_gen_methods + mi->gen_method; if ( mi->quant >= 0 ) q = GEN_QUANT(mi->quant); if ( (!(mi->flags&Q_COMPOUND) && (mi->quant >= 0) && ((q->modulus == 0.0) || !(q->flags & mode))) || (mi->modulus == 0.0) /* || (mi->timestamp == global_timestamp) */ ) { mi->flags &= ~Q_DOTHIS; if ( q->flags & mode ) mi->timestamp = global_timestamp; } else { int kk; mi->flags |= Q_DOTHIS; mi->newvalue = 0.0; for ( kk = 0 ; kk < MAXADDENDS ; kk++ ) mi->value_addends[kk] = 0.0; mi->abstotal = 0.0; mi->timestamp = global_timestamp; quant_flags[basic_gen_methods[mi->gen_method].type] |= mode; if ( gm->init ) (*gm->init)(METHOD_VALUE,mi); todo = 1; } } if ( !todo ) goto add_to_quantities; q_info_init(&q_info,METHOD_VALUE); for ( type = VERTEX ; type <= BODY ; type++ ) if ( quant_flags[type] & mode ) { int meth_offset = get_meth_offset(type); #if defined(SHARED_MEMORY) if ( (nprocs > 1) || threadflag ) { int i; if ( el_list_timestamp[VERTEX] < top_timestamp ) make_el_list(VERTEX); /* need for force */ if ( el_list_timestamp[type] < top_timestamp ) make_el_list(type); for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); if ( mi->flags & Q_DOTHIS ) for ( i = 0 ; i < nprocs ; i++ ) { mi->procvalue[i] = 0.0; mi->procabstotal[i] = 0.0; } } for ( i = 0 ; i < nprocs ; i++ ) proc_total_area[i] = 0.0; #ifdef SGI_MULTI if ( mpflag == M_INACTIVE ) m_rele_procs(); /* resume parked procs */ mpflag = M_ACTIVE; m_fork(multi_calc_quants,type,mode); m_park_procs(); mpflag = M_INACTIVE; #endif #ifdef THREADS m_type = type; thread_launch(TH_MULTI_CALC_QUANT,type); #endif /* sum separate process values */ for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); if ( mi->flags & Q_DOTHIS ) for ( i = 0 ; i < nprocs ; i++ ) { mi->newvalue += mi->procvalue[i]; mi->abstotal += mi->procabstotal[i]; } } for ( i = 0 ; i < nprocs ; i++ ) web.total_area += proc_total_area[i]; } else #endif { global_needs = global_meth_needs(type); FOR_ALL_ELEMENTS(type,q_info.id) { int setup_flag = 0; REAL value; int needs; e_ptr = elptr(q_info.id); /* get setup flags */ needs = global_needs; for ( k = 0 ; k < e_ptr->method_count ; k++ ) { int mm; mm = ((int*)((char*)e_ptr+meth_offset))[k]; mi = METH_INSTANCE(abs(mm)); if ( (mi->flags & Q_DOTHIS) && (mi->type == type) ) needs |= basic_gen_methods[mi->gen_method].flags; } for ( k = 0 ; k < global_meth_inst_count[type] ; k++ ) { int m = global_meth_inst[type][k]; mi = METH_INSTANCE(m); if ( (mi->flags & Q_DOTHIS) && (mi->type == type) ) { q_info.method = m; PROF_START(element_setup); if ( !setup_flag ) { (*q_setup[type])(NULL,&q_info,needs); setup_flag = 1; } PROF_FINISH(element_setup); mi = METH_INSTANCE(m); /* may have changed */ METHOD_PROFILING_START(mi,value); gm = basic_gen_methods + mi->gen_method; value = (*gm->value)(&q_info); if ( mi->flags & ELEMENT_MODULUS_FLAG ) value *= *(REAL*)get_extra(q_info.id,mi->elmodulus); binary_tree_add(mi->value_addends,value); mi->abstotal += fabs(value); METHOD_PROFILING_END(mi,value); } } for ( k = 0 ; k < (int)e_ptr->method_count ; k++ ) { int m,mm; mm = ((int*)((char*)e_ptr+meth_offset))[k]; m = abs(mm); q_info.method = m; mi = METH_INSTANCE(m); if ( (mi->flags & Q_DOTHIS) && (mi->type == type) ) { gm = basic_gen_methods + mi->gen_method; PROF_START(element_setup); if ( !setup_flag ) { (*q_setup[type])(NULL,&q_info,needs); setup_flag = 1; } PROF_FINISH(element_setup); mi = METH_INSTANCE(m); METHOD_PROFILING_START(mi,value); value = (*gm->value)(&q_info); if ( mi->flags & ELEMENT_MODULUS_FLAG ) value *= *(REAL*)get_extra(q_info.id,mi->elmodulus); if (mm < 0) value = -value; binary_tree_add(mi->value_addends,value); mi->abstotal += fabs(value); METHOD_PROFILING_END(mi,value); } } } } } q_info_free(&q_info); /* finish off binary tree addition */ for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { int kk; mi = METH_INSTANCE(k); if ( mi->flags & Q_DOTHIS ) for ( kk = 0 ; kk < MAXADDENDS ; kk++ ) mi->newvalue += mi->value_addends[kk]; } add_to_quantities: for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); if ( mi->flags & Q_DOTHIS ) mi->value = mi->modulus*mi->newvalue; } /* combine methods to quantities */ for ( k = 0 ; k < gen_quant_count ; k++ ) { q = GEN_QUANT(k); if ( q->flags & mode ) { #ifdef MPI_EVOLVER q->value = 0.0; #else q->value = q->volconst; #endif q->abstotal = 0.0; q->timestamp = global_timestamp; } } for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); if ( mi->quant < 0 ) continue; q = GEN_QUANT(mi->quant); if ( q->flags & mode ) { q->value += mi->value; q->abstotal += mi->abstotal; } } for ( k = compound_quant_list_head ; k >= 0 ; k = q->next_compound ) { q = GEN_QUANT(k); if ( (q->flags & mode) && ( q->flags & Q_COMPOUND ) ) { q->value = eval(&q->expr,NULL,NULLID,NULL); q->abstotal = 1.0; /* best I can think of for the moment */ } } /* set up body volumes */ if ( (mode & Q_FIXED) && web.torus_flag ) { /* munge fixed volumes modulo torus volume */ for ( k = 0 ; k < gen_quant_count ; k++ ) { q = GEN_QUANT(k); if ( (q->flags & Q_FIXED) && (q->flags & TORUS_MODULO_MUNGE) ) q->value -= web.torusv* (int)(0.5+(q->value - q->target)/(q->modulus*web.torusv)); } } if ( everything_quantities_flag ) { body_id b_id; FOR_ALL_BODIES(b_id) { q = GEN_QUANT(get_body_volquant(b_id)); if ( q->flags & mode ) set_body_volume(b_id,q->value,SETSTAMP); } } /* multiply by quantity modulus */ for ( k = 0 ; k < gen_quant_count ; k++ ) { q = GEN_QUANT(k); if ( q->flags & mode ) { q->value *= q->modulus; q->abstotal *= fabs(q->modulus); q->timestamp = global_timestamp; } } /* add to total energy, if needed */ if ( mode & Q_ENERGY ) { for ( k = 0 ; k < gen_quant_count ; k++ ) { q = GEN_QUANT(k); if ( q->flags & Q_ENERGY ) energy += q->value; } } /* take care of any cleanup */ for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); /* since some init may move things */ gm = basic_gen_methods + mi->gen_method; if ( gm->cleanup && mi->flags & Q_DOTHIS ) gm->cleanup(); } calc_quant_flag = 0; PROF_FINISH(calc_quants) return energy; } /* end calc_quants() */ /******************************************************************** * * function: quantity_attribute() * * purpose: calculate quantity for one element * Warning: does not do own initialization! * Uses any initialization left over from calc_quants(). */ REAL quantity_attribute(id,qnum) element_id id; int qnum; /* number of quantity */ { int k; struct element *e_ptr; struct gen_quant *q; int type = id_type(id); /* element type */ struct method_instance *mi; struct gen_quant_method *gm; REAL retval = 0.0,value; struct qinfo q_info; /* data passing structure */ int meth_offset = EXTRAS(type)[web.meth_attr[type]].offset; int *methlist = (int*)((char*)elptr(id) + meth_offset); int needs = 0; q_info_init(&q_info,METHOD_VALUE); /* terribly inefficient */ q_info.id = id; e_ptr = elptr(q_info.id); (*q_setup[type])(NULL,&q_info,needs); for ( k = 0 ; k < global_meth_inst_count[type] ; k++ ) { mi = METH_INSTANCE(global_meth_inst[type][k]); if ( mi->quant != qnum ) continue; if ( mi->type != type ) continue; q = GEN_QUANT(mi->quant); q_info.method = global_meth_inst[type][k]; gm = basic_gen_methods + mi->gen_method; if ( (gm->flags & ALL_NEEDS) & ~needs ) { needs |= gm->flags; (*q_setup[type])(NULL,&q_info,needs); } value = q->modulus*mi->modulus*(*gm->value)(&q_info); if ( mi->flags & ELEMENT_MODULUS_FLAG ) value *= *(REAL*)get_extra(q_info.id,mi->elmodulus); retval += value; } for ( k = 0 ; k < (int)e_ptr->method_count ; k++ ) { int mm = methlist[k]; int m = abs(mm); q_info.method = m; mi = METH_INSTANCE(m); if ( mi->quant != qnum ) continue; if ( mi->type != type ) continue; q = GEN_QUANT(mi->quant); gm = basic_gen_methods + mi->gen_method; if ( (gm->flags & ALL_NEEDS) & ~needs ) { needs |= gm->flags; (*q_setup[type])(NULL,&q_info,needs); } value = q->modulus*mi->modulus*(*gm->value)(&q_info); if ( mi->flags & ELEMENT_MODULUS_FLAG ) value *= *(REAL*)get_extra(q_info.id,mi->elmodulus); if ( mm < 0 ) retval -= value; else retval += value; } q_info_free(&q_info); return retval; } /******************************************************************** * * function: instance_attribute() * * purpose: calculate method instance for one element * Warning: does not do own initialization! * Uses any initialization left over from calc_quants(). */ REAL instance_attribute(id,qnum) element_id id; int qnum; /* number of instance */ { int k; struct element *e_ptr; int type = id_type(id); /* element type */ struct method_instance *mi=NULL; struct gen_quant_method *gm; REAL retval = 0.0; struct qinfo q_info; /* data passing structure */ int meth_offset = EXTRAS(type)[web.meth_attr[type]].offset; int *methlist = (int*)((char*)elptr(id) + meth_offset); int needs = 0; q_info_init(&q_info,METHOD_VALUE); /* terribly inefficient */ q_info.id = id; e_ptr = elptr(q_info.id); (*q_setup[type])(NULL,&q_info,needs); for ( k = 0 ; k < global_meth_inst_count[type] ; k++ ) { if ( qnum != global_meth_inst[type][k] ) continue; mi = METH_INSTANCE(global_meth_inst[type][k]); if ( mi->type != type ) continue; q_info.method = global_meth_inst[type][k]; gm = basic_gen_methods + mi->gen_method; if ( (gm->flags & ALL_NEEDS) & ~needs ) { needs |= gm->flags; (*q_setup[type])(NULL,&q_info,needs); } retval += mi->modulus*(*gm->value)(&q_info); break; } for ( k = 0 ; k < (int)e_ptr->method_count ; k++ ) { int mm = methlist[k]; int m = abs(mm); if ( qnum != m ) continue; mi = METH_INSTANCE(m); q_info.method = m; if ( mi->type != type ) continue; gm = basic_gen_methods + mi->gen_method; if ( (gm->flags & ALL_NEEDS) & ~needs ) { needs |= gm->flags; (*q_setup[type])(NULL,&q_info,needs); } if ( mm < 0 ) retval -= mi->modulus*(*gm->value)(&q_info); else retval += mi->modulus*(*gm->value)(&q_info); break; } if ( mi && (mi->flags & ELEMENT_MODULUS_FLAG) ) retval *= *(REAL*)get_extra(id,METH_INSTANCE(qnum)->elmodulus); q_info_free(&q_info); return retval; } #if defined(SHARED_MEMORY) /* SGI multiple processor stuff */ void m_fill_grad(struct hess_verlist *, REAL *,int,REAL *); void m_fill_mixed_entry(vertex_id,vertex_id,REAL**,int); /*********************************************************************** * * function: m_calc_quant_grads() * * purpose: calculate gradients of quantities * Called by calc_quant_grads() * */ void m_calc_quant_grads(type) int type; /* element type */ { int i,j; struct element *e_ptr; struct gen_quant *q; volgrad *vgptr; /* constraint gradients */ int flag; /* 0 if doing global quantities, 1 for local */ vertex_id v; struct gen_quant_method *gm; struct method_instance *mi; REAL val; REAL *f; int inum; struct qinfo q_info; /* data passing structure */ int meth_offset = get_meth_offset(type); int me = GET_THREAD_ID; int global_needs; #ifdef SIGUSR1 signal(SIGUSR1,catcher); /* to catch user interrupt */ #endif signal(SIGINT,catcher); /* to catch user interrupt */ m_breakflag[me] = 0; if ( setjmp(m_jumpbuf[me]) ) { q_info_free(&q_info); return; } q_info_init(&q_info,METHOD_GRADIENT); global_needs = global_meth_needs(type); THREAD_FOR_ALL_NEW(type, /* following block is macro argument! */ { int k; int setup_flag = 0; int needs; q_info.id = *idptr; e_ptr = elptr(q_info.id); /* get setup flags */ needs = global_needs; for ( k = 0 ; k < e_ptr->method_count ; k++ ) { int mm; mm = ((int*)((char*)e_ptr+meth_offset))[k]; mi = METH_INSTANCE(abs(mm)); if ( (mi->flags & Q_DOTHIS) && (mi->type == type) ) needs |= basic_gen_methods[mi->gen_method].flags; } inum = global_meth_inst_count[type]; for ( flag = 0 ; flag < 2 ; flag++,inum = e_ptr->method_count,k=0 ) for ( k = 0 ; k < inum ; k++ ) { int mm; int sign = 1; if ( flag ) { mm = ((int*)((char*)e_ptr+meth_offset))[k]; q_info.method = abs(mm); if ( mm < 0 ) sign = -1; } else q_info.method = global_meth_inst[type][k]; mi = METH_INSTANCE(q_info.method); if ( mi->quant >= 0 ) q = GEN_QUANT(mi->quant); if ( (mi->flags & Q_DOTHIS) && (mi->type == type) ) { REAL c = sign*mi->modulus*q->modulus; REAL *p; REAL **pp; gm = basic_gen_methods + mi->gen_method; if ( !setup_flag ) { (*q_setup[type])(NULL,&q_info,needs); setup_flag = 1; } for ( i = 0, pp = q_info.grad ; i < q_info.vcount ; i++,pp++ ) for ( j = 0, p = *pp ; j < SDIM ; j++,p++ ) *p = 0.0; val = (*gm->gradient)(&q_info); mi = METH_INSTANCE(q_info.method); if ( mi->flags & ELEMENT_MODULUS_FLAG ) { REAL emdls = *(REAL*)get_extra(q_info.id,mi->elmodulus); val *= emdls; for ( i = 0 ; i < q_info.vcount ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) q_info.grad[i][j] *= emdls; } mi->procvalue[me] += sign*val; mi->procabstotal[me] += fabs(val); if ( mi->flags & Q_COMPOUND ) { if ( q_info.vcount > MAXCOORD+2 ) kb_error(2181, "Too many vertices in method for compound quantity, due to lazy programmer.\n", RECOVERABLE); for ( i = 0 ; i < q_info.vcount ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) mi->grad[i][j] = q_info.grad[i][j]; } if ( mi->quant >= 0 && !(mi->flags & Q_COMPOUND) ) for ( i = 0 ; i < q_info.vcount ; i++ ) { REAL wforce[MAXCOORD]; /* unwrapped forces */ REAL *ff; v = q_info.v[i]; if ( q_info.wraps[i] ) { (*sym_form_pullback)(q_info.x[i],wforce,q_info.grad[i], q_info.wraps[i]); ff = wforce; } else ff = q_info.grad[i]; if ( q->flags & Q_ENERGY ) { int procnum = v_procnum[loc_ordinal(v)]; if ( procnum == me ) { /* no conflicts */ f = get_force(v); vector_add_smul(f,ff,-c, SDIM); } else /* have to save */ { int newp = ptop[me]++; struct procforce *pf; if ( newp >= pmax[me] ) { pbase[me] = (struct procforce *)kb_realloc( (char*)(pbase[me]),2*pmax[me]*sizeof(struct procforce)); pmax[me] = 2*pmax[me]; } pf = pbase[me] + newp; pf->v_id = v; pf->next = phead[procnum][me]; phead[procnum][me] = newp; for ( j = 0 ; j < SDIM ; j++ ) pf->f[j] = -c*ff[j]; } } else { M_LOCK(vgradbase); vgptr = get_bv_new_vgrad(q->fixnum,v); vgptr->bb_id = q->b_id; vgptr->qnum = mi->quant; vector_add_smul(vgptr->grad,ff, c, SDIM); M_UNLOCK(vgradbase); } } } } } ) /* end of macro */ q_info_free(&q_info); } /* m_calc_quant_grads */ /*********************************************************************** * * function: m_fix_grads() * * purpose: Processes add forces from other processes to own vertices. */ void m_fix_grads() { int me = GET_THREAD_ID; int pnum; int i,k; struct procforce *p; REAL *f; for ( pnum = 0 ; pnum < nprocs ; pnum++ ) { for ( k = phead[me][pnum] ; k >= 0 ; k = p->next ) { p = pbase[pnum] + k; f = get_force(p->v_id); for ( i = 0 ; i < SDIM ; i++ ) f[i] += p->f[i]; } } } #endif /*********************************************************************** * * function: calc_quant_grads() * * purpose: calculate gradients of quantities * Called by vol_project(), which does all other * structure initializing and messing around. * Also calculates quantity values themselves. * */ void calc_quant_grads(mode) int mode; /* energy or constraint, or Q_COMPOUND for aiding hessian */ { int i,k; struct element *e_ptr; struct gen_quant *q = NULL; volgrad *vgptr; /* constraint gradients */ int flag; /* 0 if doing global quantities, 1 for local */ vertex_id v; int type; /* element type */ struct method_instance *mi; struct gen_quant_method *gm; int inum; struct qinfo q_info; /* data passing structure */ int todo = 0; /* whether any to do */ int global_needs; #ifdef PROFILING_ENABLED __int32 grad_elapsed_time[2]; #endif PROF_START(calc_quant_grads); /* method initialization */ comp_quant_stamp = 0; for ( type = 0 ; type < NUMELEMENTS ; type++ ) quant_flags[type] = 0; for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); /* since some init may have moved things */ mi->stamp = 0; /* so only current methods used in eval_all() */ gm = basic_gen_methods + mi->gen_method; if ( mi->quant >= 0 ) q = GEN_QUANT(mi->quant); else q = NULL; if ( !q || (q->modulus==0.0) || !(q->flags & mode & ~Q_COMPOUND) || (mi->modulus==0.0) ) mi->flags &= ~Q_DOTHIS; else { mi->flags |= Q_DOTHIS; mi->newvalue = 0.0; mi->abstotal = 0.0; mi->timestamp = global_timestamp; quant_flags[basic_gen_methods[mi->gen_method].type] |= mode; if ( gm->init ) (*gm->init)(METHOD_GRADIENT,mi); todo = 1; } } if ( !todo ) { PROF_FINISH(calc_quant_grads); return; } q_info_init(&q_info,METHOD_GRADIENT); if ( compound_quant_list_head >= 0 ) { for ( k=LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); if ( mi->flags & Q_COMPOUND ) { mi->grad = dmatrix(0,MAXVCOUNT,0,SDIM); } } } for ( type = VERTEX ; type <= BODY ; type++ ) if ( quant_flags[type] & mode ) { int meth_offset = get_meth_offset(type); global_needs = global_meth_needs(type); #if defined(SHARED_MEMORY) if ((compound_quant_list_head == -1) && ((nprocs > 1) || threadflag) ) { if ( el_list_timestamp[VERTEX] < top_timestamp ) make_el_list(VERTEX); /* need for force */ if ( el_list_timestamp[type] < top_timestamp ) make_el_list(type); for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); if ( mi->flags & Q_DOTHIS ) for ( i = 0 ; i < nprocs ; i++ ) { mi->procvalue[i] = 0.0; mi->procabstotal[i] = 0.0; } } for ( i = 0 ; i < nprocs ; i++ ) proc_total_area[i] = 0.0; for ( i = 0 ; i < nprocs ; i++ ) { int j; ptop[i] = 0 ; if ( pbase[i] == NULL ) { pmax[i] = 1000; pbase[i] = (struct procforce *)mycalloc(pmax[i], sizeof(struct procforce)); } for ( j = 0 ; j < nprocs ; j++ ) phead[i][j] = -1; } #ifdef SGI_MULTI if ( mpflag == M_INACTIVE ) m_rele_procs(); /* resume parked procs */ mpflag = M_ACTIVE; m_fork(m_calc_quant_grads,type,mode); m_fork(m_fix_grads,0,0); m_park_procs(); mpflag = M_INACTIVE; #endif #ifdef THREADS m_type = type; thread_launch(TH_MULTI_QUANT_GRADS,type); thread_launch(TH_FIX_GRADS,0); #endif /* sum separate process values */ for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); if ( mi->flags & Q_DOTHIS ) for ( i = 0 ; i < nprocs ; i++ ) { mi->newvalue += mi->procvalue[i]; mi->abstotal += mi->procabstotal[i]; } } for ( i = 0 ; i < nprocs ; i++ ) web.total_area += proc_total_area[i]; } else #endif /* Don't put anything here; non-shared falls through!! */ FOR_ALL_ELEMENTS(type,q_info.id) { int j; int setup_flag = 0; int needs; e_ptr = elptr(q_info.id); /* get setup flags */ needs = global_needs; for ( k = 0 ; k < e_ptr->method_count ; k++ ) { int mm; mm = ((int*)((char*)e_ptr+meth_offset))[k]; mi = METH_INSTANCE(abs(mm)); if ( (mi->flags & Q_DOTHIS) && (mi->type == type) ) needs |= basic_gen_methods[mi->gen_method].flags; } inum = global_meth_inst_count[type]; ++comp_quant_stamp; for ( flag = 0 ; flag < 2 ; flag++,inum = e_ptr->method_count,k=0 ) { for ( k = 0 ; k < inum ; k++ ) { int mm; int sign = 1; if ( flag ) { mm = ((int*)((char*)e_ptr+meth_offset))[k]; q_info.method = abs(mm); if ( mm < 0 ) sign = -1; } else q_info.method = global_meth_inst[type][k]; mi = METH_INSTANCE(q_info.method); if ( mi->quant >= 0 ) q = GEN_QUANT(mi->quant); if ( (mi->flags & Q_DOTHIS) && (mi->type == type) ) { REAL c = 0; /* net coefficient */ REAL value; gm = basic_gen_methods + mi->gen_method; PROF_START(element_setup); if ( !setup_flag ) { (*q_setup[type])(NULL,&q_info,needs); setup_flag = 1; } PROF_FINISH(element_setup); mi = METH_INSTANCE(q_info.method); METHOD_PROFILING_START(mi,grad); for ( i = 0 ; i < q_info.vcount ; i++ ) /* methods don't know */ for ( j = 0 ; j < SDIM ; j++ ) /* how many */ q_info.grad[i][j] = 0.0; value = (*gm->gradient)(&q_info); if ( mi->flags & ELEMENT_MODULUS_FLAG ) { REAL emdls = *(REAL*)get_extra(q_info.id,mi->elmodulus); value *= emdls; for ( i = 0 ; i < q_info.vcount ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) q_info.grad[i][j] *= emdls; } mi->newvalue += sign*value; mi->abstotal += fabs(value); if ( mi->flags & Q_COMPOUND ) { for ( i = 0 ; i < q_info.vcount ; i++ ) { for ( j = 0 ; j < SDIM ; j++ ) mi->grad[i][j] = mi->modulus*q_info.grad[i][j]; } mi->stamp = comp_quant_stamp; } if ( mi->quant >= 0 ) c = sign*mi->modulus*q->modulus; for ( i = 0 ; i < q_info.vcount ; i++ ) { REAL wforce[MAXCOORD]; /* unwrapped forces */ REAL *ff; v = q_info.v[i]; if ( q_info.wraps[i] ) { (*sym_form_pullback)(q_info.x[i],wforce,q_info.grad[i], q_info.wraps[i]); ff = wforce; } else ff = q_info.grad[i]; if ( mi->quant >= 0 && !(mi->flags & Q_COMPOUND) ) { if ( q->flags & Q_ENERGY & mode ) { REAL *f = get_force(v); vector_add_smul(f,ff, -c, SDIM); } else if ( q->flags & (Q_FIXED|Q_CONSERVED) & mode ) { vgptr = get_bv_new_vgrad(q->fixnum,v); vgptr->bb_id = q->b_id; vgptr->qnum = mi->quant; vector_add_smul(vgptr->grad,ff, c, SDIM); } } if ( mode & mi->flags & Q_COMPOUND ) { vgptr = get_bv_new_vgrad(mi->self_id,v); vgptr->bb_id = mi->self_id; vgptr->qnum = mi->self_id; vector_add_smul(vgptr->grad,ff, mi->modulus, SDIM); } } METHOD_PROFILING_END(mi,grad); } } } /* end calculation of all method instances */ /* check out compound quantities */ for ( k = compound_quant_list_head ; k >= 0 ; k = q->next_compound ) { q = GEN_QUANT(k); if ( (q->flags & mode) && (q->flags & Q_COMPOUND) ) { for ( i = 0 ; i < q_info.vcount ; i++ ) { REAL dummy,partials[MAXCOORD]; comp_quant_vertex = i; /* so eval_all knows */ eval_all(&q->expr,NULL,SDIM,&dummy,partials,NULLID); v = q_info.v[i]; if ( q->flags & Q_ENERGY ) { REAL *f = get_force(v); vector_add_smul(f,partials, -q->modulus, SDIM); } else { vgptr = get_bv_new_vgrad(q->fixnum,v); vgptr->bb_id = q->b_id; vgptr->qnum = k; vector_add_smul(vgptr->grad,partials, q->modulus, SDIM); } } } } } } for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); if ( mi->flags & Q_DOTHIS ) { mi->value = mi->modulus*mi->newvalue; mi->abstotal *= fabs(mi->modulus); } } /* combine methods to quantities */ for ( k = 0 ; k < gen_quant_count ; k++ ) { q = GEN_QUANT(k); if ( q->flags & mode ) { q->value = q->volconst; q->abstotal = 0.0; q->timestamp = global_timestamp; } } for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); if ( mi->quant < 0 ) continue; q = GEN_QUANT(mi->quant); if ( q->flags & mode ) { q->value += q->modulus*mi->value; q->abstotal += fabs(q->modulus)*mi->abstotal; } } for ( k = 0 ; k < gen_quant_count ; k++ ) { q = GEN_QUANT(k); if ( q->flags & mode ) if ( q->flags & Q_COMPOUND ) { q->value = q->modulus*eval(&q->expr,NULL,NULLID,NULL); q->abstotal = 1.0; } } if ( (mode & Q_FIXED) && web.torus_flag ) { /* munge fixed volumes modulo torus volume */ for ( k = 0 ; k < gen_quant_count ; k++ ) { q = GEN_QUANT(k); if ( (q->flags & Q_FIXED) && (q->flags & TORUS_MODULO_MUNGE) ) q->value -= q->modulus*web.torusv* (int)(0.5+(q->value - q->target)/(q->modulus*web.torusv)); } } /* take care of any cleanup */ for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); /* since some init may move things */ gm = basic_gen_methods + mi->gen_method; if ( gm->cleanup && mi->flags & Q_DOTHIS ) gm->cleanup(); } if ( compound_quant_list_head >= 0 ) for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); if ( mi->flags & Q_COMPOUND ) { free_matrix(mi->grad); mi->grad = NULL; } } q_info_free(&q_info); PROF_FINISH(calc_quant_grads); } /* end calc_quant_grads() */ /*********************************************************************** * * function: calc_quant_hess() * * purpose: calculate hessians of quantities * */ void calc_quant_hess(S,rhs_mode,hess_mode,rhs) struct linsys *S; /* to gather hessian */ int rhs_mode; /* whether to do rhs */ int hess_mode; /* 1 for full hessian */ REAL *rhs; { int i,ii,k,j,jj,m,n; struct element *e_ptr; struct gen_quant *q = NULL; int flag; /* 0 if doing global quantities, 1 for local */ int type; /* element type */ struct method_instance *mi; struct gen_quant_method *gm; int inum; REAL g[MAXCOORD],*ggg; REAL **p1,**p2; struct hess_verlist *va,*vb; int mode = Q_ENERGY|Q_FIXED|Q_CONSERVED; REAL coeff; /* net modulus, including pressure if fixed quant */ REAL ccoeff=0.0; /* net modulus */ struct qinfo q_info; /* data passing structure */ int todo = 0; MAT2D(seconds,2*MAXCOORD,2*MAXCOORD); int global_needs; #ifdef PROFILING_ENABLED __int32 hess_elapsed_time[2]; #endif PROF_START(calc_quant_hess); if ( compound_quant_list_head >= 0 ) { /* get total gradients for compound quantities */ vgrad_end(); vgrad_init(1); calc_quant_grads(Q_COMPOUND|Q_FIXED|Q_ENERGY|Q_CONSERVED); if ( quantity_function_sparse_flag ) add_vgrads_to_update(S); compound_hess_flag = CH_GRADS; for ( k = 0 ; k < gen_quant_count ; k++ ) { q = GEN_QUANT(k); if ( !(q->flags & Q_COMPOUND ) ) continue; if ( q->flags & (Q_ENERGY|Q_FIXED|Q_CONSERVED) ) { REAL coeff = q->modulus; if ( q->flags & (Q_FIXED|Q_CONSERVED) ) coeff *= -q->pressure; if ( quantity_function_sparse_flag ) { /* second partials of quantity wrt methods */ REAL dummy,partials[2*MAXCOORD]; eval_second(&q->expr,NULL,q->method_count,&dummy,partials,seconds,NULLID); for ( i = 0 ; i < q->method_count ; i++ ) { int ii = METH_INSTANCE(q->meth_inst[i])->global_low_rank; for ( j = 0 ; j < q->method_count ; j++ ) { int jj = METH_INSTANCE(q->meth_inst[j])->global_low_rank; S->low_rank_form[ii][jj] += coeff*seconds[i][j]; } } } else /* old dense way */ FOR_ALL_VERTICES(comp_quant_vi) { REAL dummy,partials[2*MAXCOORD]; if ( get_vattr(comp_quant_vi) & FIXED ) continue; FOR_ALL_VERTICES(comp_quant_vj) { if ( comp_quant_vi > comp_quant_vj ) continue; if ( get_vattr(comp_quant_vj) & FIXED ) continue; eval_second(&q->expr,NULL,2*SDIM,&dummy,partials,seconds,NULLID); for ( m = 0 ; m < SDIM ; m++ ) for ( n = 0 ; n < SDIM ; n++ ) seconds[m+SDIM][n] *= coeff; fill_mixed_entry(S,comp_quant_vj,comp_quant_vi,seconds+SDIM); } } } } } compound_hess_flag = 0; comp_quant_stamp = 0; /* method initialization */ for ( type = 0 ; type < NUMELEMENTS ; type++ ) quant_flags[type] = 0; for ( k = LOW_INST ; k < meth_inst_count ;k++ ) { mi = METH_INSTANCE(k); mi->stamp = 0; /* so only current methods used in eval_all() */ gm = basic_gen_methods + mi->gen_method; if ( mi->quant >= 0 ) q = GEN_QUANT(mi->quant); if ( (!(mi->flags&Q_COMPOUND) && (mi->quant >= 0) && ((q->modulus == 0.0) || !(q->flags & mode))) || (mi->modulus == 0.0) || ((mi->quant >= 0) && (q->flags & Q_REDUNDANT)) ) mi->flags &= ~Q_DOTHIS; else { mi->flags |= Q_DOTHIS; if ( (gm->hessian == NULL) || (gm->hessian == null_q_hess) ) { sprintf(errmsg,"Method %s has no Hessian available.\n",gm->name); kb_error(1571,errmsg,RECOVERABLE); } mi->newvalue = 0.0; mi->abstotal = 0.0; mi->timestamp = global_timestamp; quant_flags[basic_gen_methods[mi->gen_method].type] |= mode; if ( gm->init ) (*gm->init)(METHOD_HESSIAN,mi); todo = 1; } } if ( !todo ) { PROF_FINISH(calc_quant_hess); return; } /* set up matrices in qinfo with lots of room */ q_info_init(&q_info,METHOD_HESSIAN); q_info.hess = dmatrix4(MAXVCOUNT,MAXVCOUNT,SDIM,SDIM); p1 = dmatrix(0,MAXCOORD-1,0,MAXCOORD-1); p2 = dmatrix(0,MAXCOORD-1,0,MAXCOORD-1); if ( compound_quant_list_head >= 0 ) { for ( k=LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); if ( mi->flags & Q_COMPOUND ) { mi->grad = dmatrix(0,MAXVCOUNT,0,SDIM); mi->hess = dmatrix4(MAXVCOUNT,MAXVCOUNT,SDIM,SDIM); } } } for ( type = VERTEX ; type <= BODY ; type++ ) if ( quant_flags[type] & mode ) { int meth_offset = get_meth_offset(type); #if defined(SHARED_MEMORY) if ( (compound_quant_list_head == -1) && ((nprocs > 1) || threadflag) ) { if ( el_list_timestamp[VERTEX] < top_timestamp ) make_el_list(VERTEX); /* need for force */ if ( el_list_timestamp[type] < top_timestamp ) make_el_list(type); for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); if ( mi->flags & Q_DOTHIS ) for ( i = 0 ; i < nprocs ; i++ ) { mi->procvalue[i] = 0.0; mi->procabstotal[i] = 0.0; } } m_hess_mode = hess_mode; m_rhs_mode = rhs_mode; #ifdef SGI_MULTI if ( mpflag == M_INACTIVE ) m_rele_procs(); /* resume parked procs */ mpflag = M_ACTIVE; m_quanrowstart = S->quanrowstart; m_bodyrowstart = S->bodyrowstart; m_fork(m_calc_quant_hess,type,mode,rhs); m_park_procs(); mpflag = M_INACTIVE; #endif #ifdef THREADS m_type = type; m_mode = mode; m_rhs = rhs; thread_launch(TH_MULTI_QUANT_HESS,type); #endif m_fix_hess(S); /* sum separate process values */ for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); if ( mi->flags & Q_DOTHIS ) for ( i = 0 ; i < nprocs ; i++ ) { mi->newvalue += mi->procvalue[i]; mi->abstotal += mi->procabstotal[i]; } } } else #endif { global_needs = global_meth_needs(type); FOR_ALL_ELEMENTS(type,q_info.id) { int setup_flag = 0; int needs; ++comp_quant_stamp; e_ptr = elptr(q_info.id); /* get setup flags */ needs = global_needs; for ( k = 0 ; k < e_ptr->method_count ; k++ ) { int mm; mm = ((int*)((char*)e_ptr+meth_offset))[k]; mi = METH_INSTANCE(abs(mm)); if ( (mi->flags & Q_DOTHIS) && (mi->type == type) ) needs |= basic_gen_methods[mi->gen_method].flags; } inum = global_meth_inst_count[type]; for ( flag = 0 ; flag < 2 ; flag++,inum = e_ptr->method_count,k=0 ) for ( k = 0 ; k < inum ; k++ ) { int mm; int sign = 1; if ( flag ) { mm = ((int*)((char*)e_ptr+meth_offset))[k]; q_info.method = abs(mm); if ( mm < 0 ) sign = -1; } else q_info.method = global_meth_inst[type][k]; mi = METH_INSTANCE(q_info.method); if ( (mi->flags & Q_DOTHIS) && (mi->type == type) ) { REAL value; if ( mi->quant >= 0 ) q = GEN_QUANT(mi->quant); PROF_START(element_setup); if ( !setup_flag ) { (*q_setup[type])(S,&q_info,needs); setup_flag = 1; } PROF_FINISH(element_setup); coeff = sign*q->modulus*mi->modulus; if ( q->flags & (Q_FIXED|Q_CONSERVED) ) { ccoeff = coeff; coeff *= -q->pressure; } mi = METH_INSTANCE(q_info.method); METHOD_PROFILING_START(mi,hess); gm = basic_gen_methods + mi->gen_method; zerohess(&q_info); if ( hess_mode ) value = (*gm->hessian)(&q_info); else value = (*gm->gradient)(&q_info); if ( mi->flags & ELEMENT_MODULUS_FLAG ) { REAL emdls = *(REAL*)get_extra(q_info.id,mi->elmodulus); value *= emdls; for ( i = 0 ; i < q_info.vcount ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) q_info.grad[i][j] *= emdls; for ( i = 0 ; i < q_info.vcount ; i++ ) for ( ii = 0 ; ii < q_info.vcount ; ii++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) q_info.hess[i][ii][j][jj] *= emdls; } mi->newvalue += sign*value; mi->abstotal += fabs(value); /* unwrap */ if ( sym_flags & NEED_FORM_UNWRAPPING ) { /* gradient */ REAL grad[MAXCOORD]; for ( i = 0 ; i < q_info.vcount ; i++ ) { if ( q_info.wraps[i] ) { (*sym_form_pullback)(q_info.x[i],grad,q_info.grad[i], q_info.wraps[i]); for ( j = 0 ; j < SDIM ; j++ ) q_info.grad[i][j] = grad[j]; } } if ( hess_mode ) { for ( i = 0 ; i < q_info.vcount ; i++ ) for ( ii = 0 ; ii < q_info.vcount ; ii++ ) { if ( q_info.wraps[i] ) for ( jj = 0 ; jj < SDIM ; jj++ ) { REAL tmp[MAXCOORD]; for ( j = 0 ; j < SDIM ; j++ ) tmp[j]=q_info.hess[i][ii][j][jj]; (*sym_form_pullback)(q_info.x[i],grad,tmp,q_info.wraps[i]); for ( j = 0 ; j < SDIM ; j++ ) q_info.hess[i][ii][j][jj] = grad[j]; } if ( q_info.wraps[ii] ) for ( j = 0 ; j < SDIM ; j++ ) { (*sym_form_pullback)(q_info.x[ii],grad, q_info.hess[i][ii][j],q_info.wraps[ii]); for ( jj = 0 ; jj < SDIM ; jj++ ) q_info.hess[i][ii][j][jj] = grad[jj]; } } } } if ( mi->flags & Q_COMPOUND ) { mi->vlist = q_info.v; /* for eval_sec */ for ( i = 0 ; i < q_info.vcount ; i++ ) for ( m = 0 ; m < SDIM ; m++ ) mi->grad[i][m] = mi->modulus*q_info.grad[i][m]; for ( i = 0 ; i < q_info.vcount ; i++ ) for ( j = 0 ; j < q_info.vcount ; j++ ) for ( m = 0 ; m < SDIM ; m++ ) for ( n = 0 ; n < SDIM ; n++ ) mi->hess[i][j][m][n] = mi->modulus*q_info.hess[i][j][m][n]; mi->stamp = comp_quant_stamp; } else { for ( i = 0 ; i < q_info.vcount ; i++ ) { REAL grad[MAXCOORD]; va = get_vertex_vhead(q_info.v[i]); for ( j = 0 ; j < SDIM ; j++ ) grad[j] = coeff*q_info.grad[i][j]; fill_grad(S,va,grad,rhs); } /* second derivatives */ if ( hess_mode && (mode & (Q_FIXED|Q_ENERGY|Q_CONSERVED)) ) for ( i = 0 ; i < q_info.vcount ; i++ ) { va = get_vertex_vhead(q_info.v[i]); if ( va->freedom == 0 ) continue; for ( j = i ; j < q_info.vcount ; j++ ) { vb = get_vertex_vhead(q_info.v[j]); if ( vb->freedom == 0 ) continue; for ( n = 0 ; n < SDIM ; n++ ) for ( m = 0 ; m < SDIM ; m++ ) q_info.hess[i][j][m][n] *= coeff; fill_mixed_entry(S,q_info.v[i],q_info.v[j],q_info.hess[i][j]); if ( (i != j) && (q_info.v[i] == q_info.v[j]) ) /* also transpose */ { MAT2D(transpose,MAXCOORD,MAXCOORD); for ( n = 0 ; n < SDIM ; n++ ) for ( m = 0 ; m < SDIM ; m++ ) transpose[m][n] = q_info.hess[i][j][n][m]; fill_mixed_entry(S,q_info.v[i],q_info.v[j],transpose); } } /* end inner vertex loop */ /* fixed quantity gradients for left side */ if ( q->flags & (Q_FIXED|Q_CONSERVED) ) { /* find entry */ int currentrow; if ( va->proj ) { vec_mat_mul(q_info.grad[i],va->proj,g,SDIM, va->freedom); ggg = g; } else ggg = q_info.grad[i]; currentrow = S->quanrowstart + mi->quant; for ( m = 0 ; m < va->freedom ; m++ ) { sp_hash_search(S,va->rownum+m,currentrow,ccoeff*ggg[m]); } } } /* end second derivatives */ } /* end non-compound */ METHOD_PROFILING_END(mi,hess); } /* end if */ } /* end method instance loop */ /* Here we take care of compound quantities */ for ( k = compound_quant_list_head ; k >= 0 ; k = q->next_compound ) { q = GEN_QUANT(k); if ( (q->flags & mode) && (q->flags & Q_COMPOUND) ) { for ( i = 0 ; i < q_info.vcount ; i++ ) { REAL dummy,partials[MAXCOORD]; REAL grad[MAXCOORD]; comp_quant_vertex = i; /* so eval_all knows */ comp_quant_type = type; eval_all(&q->expr,NULL,SDIM,&dummy,partials,NULLID); va = get_vertex_vhead(q_info.v[i]); for ( j = 0 ; j < SDIM ; j++ ) grad[j] = q->modulus*partials[j]; fill_grad(S,va,grad,rhs); /* fixed quantity gradients for left side */ if ( (q->flags & (Q_FIXED|Q_CONSERVED)) && hess_mode ) { /* find entry */ int currentrow; if ( va->proj ) { vec_mat_mul(grad,va->proj,g,SDIM, va->freedom); ggg = g; } else ggg = grad; currentrow = S->quanrowstart + k; for ( m = 0 ; m < va->freedom ; m++ ) { sp_hash_search(S,va->rownum+m,currentrow,q->modulus*ggg[m]); } } } /* second derivatives */ compound_hess_flag = CH_HESS; if ( hess_mode ) for ( i = 0 ; i < q_info.vcount ; i++ ) { REAL dummy[MAXCOORD]; REAL partials[MAXCOORD]; va = get_vertex_vhead(q_info.v[i]); if ( va->freedom == 0 ) continue; for ( j = i ; j < q_info.vcount ; j++ ) { vb = get_vertex_vhead(q_info.v[j]); if ( vb->freedom == 0 ) continue; comp_quant_vertexi = i; /* so eval_all knows */ comp_quant_vertexj = j; /* so eval_all knows */ eval_second(&q->expr,NULL,SDIM,dummy,partials,seconds,NULLID); for ( n = 0 ; n < SDIM ; n++ ) for ( m = 0 ; m < SDIM ; m++ ) q_info.hess[i][j][m][n] = q->modulus*seconds[m][n]; fill_mixed_entry(S,q_info.v[i],q_info.v[j],q_info.hess[i][j]); if ( (i != j) && (q_info.v[i] == q_info.v[j]) ) /* also transpose */ { MAT2D(transpose,MAXCOORD,MAXCOORD); for ( n = 0 ; n < SDIM ; n++ ) for ( m = 0 ; m < SDIM ; m++ ) transpose[m][n] = q_info.hess[i][j][n][m]; fill_mixed_entry(S,q_info.v[i],q_info.v[j],transpose); } } /* end inner vertex loop */ } /* end second derivatives */ compound_hess_flag = 0; } } /* end compound quantities */ } /* end all element loop */ } } /* end single thread */ /* free stuff */ free_matrix4(q_info.hess); free_matrix(p1); free_matrix(p2); q_info_free(&q_info); if ( compound_quant_list_head >= 0 ) for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); if ( mi->flags & Q_COMPOUND ) { free_matrix(mi->grad); mi->grad = NULL; free_matrix4(mi->hess); mi->hess = NULL; } } for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); if ( mi->flags & Q_DOTHIS ) { mi->value = mi->modulus*mi->newvalue; mi->abstotal *= fabs(mi->modulus); } } /* combine methods to quantities */ for ( k = 0 ; k < gen_quant_count ; k++ ) { q = GEN_QUANT(k); if ( q->flags & mode ) { q->value = q->volconst; q->abstotal = 0.0; q->timestamp = global_timestamp; } } for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); if ( mi->flags & Q_COMPOUND ) continue; gm = basic_gen_methods + mi->gen_method; if ( mi->quant >= 0 ) q = GEN_QUANT(mi->quant); if ( (mi->quant >= 0) && (q->flags & mode) ) { q->value += q->modulus*mi->value; q->abstotal += fabs(q->modulus)*mi->abstotal; } } for ( k = compound_quant_list_head ; k >= 0 ; k = q->next_compound ) { q = GEN_QUANT(k); if ( (q->flags & mode) && ( q->flags & Q_COMPOUND ) ) { q->value = q->modulus*eval(&q->expr,NULL,NULLID,NULL); q->abstotal = 1.0; } } if ( (mode & Q_FIXED) && web.torus_flag ) { /* munge fixed volumes modulo torus volume */ for ( k = 0 ; k < gen_quant_count ; k++ ) { q = GEN_QUANT(k); if ( (q->flags & Q_FIXED) && (q->flags & TORUS_MODULO_MUNGE) ) q->value -= q->modulus*web.torusv* (int)(0.5+(q->value - q->target)/(q->modulus*web.torusv)); } } /* take care of any cleanup */ for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); /* since some init may move things */ gm = basic_gen_methods + mi->gen_method; if ( gm->cleanup && mi->flags & Q_DOTHIS ) gm->cleanup(); } vgrad_end(); PROF_FINISH(calc_quant_hess); } #if defined(SHARED_MEMORY) /* SGI multiple processor stuff */ /********************************************************************** Hash list routines for Hessian matrix entries. Key is (row,col). **********************************************************************/ #define PRIME 99991 #define hash(row,col) (abs((row)*97+(col)*PRIME)) static int m_max_fill[MAXPROCS]; /* max number of entries until enlarge */ static int m_hashcount[MAXPROCS]; /* current number of entries */ static int m_hash_per_row[MAXPROCS]; /* estimate size of table */ static int m_hash_extraprobes; /* for measuring efficiency */ static int m_bodyrowstart,m_quanrowstart,m_total_rows; struct hess_entry *m_hashtable[MAXPROCS]; /* the table */ int m_table_size[MAXPROCS]; /* hashtable size */ void m_hess_hash_search(int,int,REAL,int); void m_hess_hash_init(void); void m_hess_hash_expand(void); /******************************************************************** * * function: m_hess_hash_init() * * purpose: Initialize hash table. */ void m_hess_hash_init() { int i; int me = GET_THREAD_ID; if ( m_hash_per_row[me] < 1 ) m_hash_per_row[me] = 1; m_table_size[me] = m_hash_per_row[me]*SDIM*web.skel[VERTEX].max_ord/nprocs; m_max_fill[me] = 4*m_table_size[me]/5; if ( !hessian_quiet_flag ) { sprintf(msg,"m_Hess init alloc: %d\n",m_table_size[me]); outstring(msg); } m_hashcount[me] = 0; if ( m_hashtable[me] ) temp_free((char*)m_hashtable[me]); m_hashtable[me] = (struct hess_entry *)mycalloc(m_table_size[me],sizeof(struct hess_entry)); for ( i = 0 ; i < m_table_size[me] ; i++ ) m_hashtable[me][i].row = HASHEMPTY; m_hash_extraprobes = 0; } /******************************************************************** * * function: m_hess_hash_expand() * * purpose: Expands hash table */ void m_hess_hash_expand() { struct hess_entry *newtable,*oldtable; int i; int me = GET_THREAD_ID; struct hess_entry *e; int newsize; int oldsize = m_table_size[me]; if ( !m_hashtable[me] ) m_hess_hash_init(); newsize = m_table_size[me]*2; oldtable = m_hashtable[me]; newtable = (struct hess_entry *)mycalloc(newsize,sizeof(struct hess_entry)); for ( i = 0 ; i < newsize ; i++ ) newtable[i].row = HASHEMPTY; m_table_size[me] = newsize; m_max_fill[me] = 4*m_table_size[me]/5; m_hashtable[me] = newtable; /* reinsert */ m_hashcount[me] = 0; for ( i = 0, e = oldtable ; i < oldsize ; i++,e++ ) if ( e->row != HASHEMPTY ) m_hess_hash_search(e->col,e->row,e->value,me); myfree((char*)oldtable); } /******************************************************************** * * function: m_hess_hash_search() * * purpose: Finds existing entry or allocates entry. * Installs key values, and adds hessian value. */ void m_hess_hash_search(col,row,value,tid) int row,col; /* meant to do upper triangle */ REAL value; /* value to add */ int tid; /* thread id */ { struct hess_entry *e; int spot; struct hess_entry *hashtab; int tab_size = m_table_size[tid]; if ( row > col ) return; if ( m_hashcount[tid] >= m_max_fill[tid] ) m_hess_hash_expand(); hashtab = m_hashtable[tid]; /* search hash table */ spot = hash(row,col) % tab_size; e = hashtab + spot; while ( e->row != HASHEMPTY ) { if ( (e->row == row) && (e->col == col) ) { e->value += value; return; } spot++; if ( spot >= tab_size ) spot -= tab_size; e = hashtab + spot; m_hash_extraprobes++; } /* if here, then have empty slot and need to insert */ e->col = col; e->row = row; m_hashcount[tid]++; e->value = value; } /*********************************************************************** * * function: m_calc_quant_hess() * * purpose: calculate hessians of quantities * Called by calc_quant_hess() per processor * */ void m_calc_quant_hess(type,mode,rhs) int type; /* element type */ int mode; /* ENERGY, FIXED, or INFO_ONLY */ REAL *rhs; { int i,n,j,m,nn; struct element *e_ptr; struct gen_quant *q; int flag; /* 0 if doing global quantities, 1 for local */ struct gen_quant_method *gm; struct method_instance *mi; int inum; struct qinfo q_info; /* data passing structure */ int meth_offset = get_meth_offset(type); int me = GET_THREAD_ID; int start,end,bin; REAL **p1,**p2; REAL coeff,ccoeff=0.0; struct hess_verlist *va,*vb; REAL g[MAXCOORD],*ggg; int global_needs; #ifdef SIGUSR1 signal(SIGUSR1,catcher); /* to catch user interrupt */ #endif signal(SIGINT,catcher); /* to catch user interrupt */ m_breakflag[me] = 0; if ( setjmp(m_jumpbuf[me]) ) { q_info_free(&q_info); return; } /* set up matrices in qinfo with lots of room */ q_info_init(&q_info,METHOD_HESSIAN); q_info.hess = dmatrix4(MAXVCOUNT,MAXVCOUNT,SDIM,SDIM); p1 = dmatrix(0,MAXCOORD-1,0,MAXCOORD-1); p2 = dmatrix(0,MAXCOORD-1,0,MAXCOORD-1); /* initialize local list */ m_hess_hash_init(); global_needs = global_meth_needs(type); bin = (web.skel[type].count + nprocs - 1)/nprocs; start = me*bin; end = start + bin; if ( end > web.skel[type].count ) end = web.skel[type].count; for ( nn = start ; nn < end ; nn++ ) { int k; int setup_flag = 0; int needs; q_info.id = el_list[type][nn]; e_ptr = elptr(q_info.id); /* get setup flags */ needs = global_needs; for ( k = 0 ; k < e_ptr->method_count ; k++ ) { int mm; mm = ((int*)((char*)e_ptr+meth_offset))[k]; mi = METH_INSTANCE(abs(mm)); if ( (mi->flags & Q_DOTHIS) && (mi->type == type) ) needs |= basic_gen_methods[mi->gen_method].flags; } inum = global_meth_inst_count[type]; for ( flag = 0 ; flag < 2 ; flag++,inum = e_ptr->method_count,k=0 ) for ( k = 0 ; k < inum ; k++ ) { int mm,ii,jj; int sign = 1; REAL value; if ( flag ) { mm = ((int*)((char*)e_ptr+meth_offset))[k]; q_info.method = abs(mm); if ( mm < 0 ) sign = -1; } else q_info.method = global_meth_inst[type][k]; mi = METH_INSTANCE(q_info.method); if ( (mi->flags & Q_DOTHIS) && (mi->type == type) ) { q = GEN_QUANT(mi->quant); coeff = sign*q->modulus*mi->modulus; if ( !setup_flag ) { (*q_setup[type])(NULL,&q_info,needs); setup_flag = 1; } if ( q->flags & (Q_FIXED|Q_CONSERVED) ) { ccoeff = coeff; coeff *= -q->pressure; } mi = METH_INSTANCE(q_info.method); gm = basic_gen_methods + mi->gen_method; zerohess(&q_info); if ( m_hess_mode ) value = (*gm->hessian)(&q_info); else value = (*gm->gradient)(&q_info); if ( mi->flags & ELEMENT_MODULUS_FLAG ) { REAL emdls = *(REAL*)get_extra(q_info.id,mi->elmodulus); value *= emdls; for ( i = 0 ; i < q_info.vcount ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) q_info.grad[i][j] *= emdls; for ( i = 0 ; i < q_info.vcount ; i++ ) for ( ii = 0 ; ii < q_info.vcount ; ii++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) q_info.hess[i][ii][j][jj] *= emdls; } mi->newvalue += sign*value; mi->abstotal += fabs(value); /* unwrap */ if ( sym_flags & NEED_FORM_UNWRAPPING ) { /* gradient */ int ii,jj; REAL grad[MAXCOORD]; for ( i = 0 ; i < q_info.vcount ; i++ ) { if ( q_info.wraps[i] ) { (*sym_form_pullback)(q_info.x[i],grad,q_info.grad[i], q_info.wraps[i]); for ( j = 0 ; j < SDIM ; j++ ) q_info.grad[i][j] = grad[j]; } } if ( m_hess_mode ) for ( i = 0 ; i < q_info.vcount ; i++ ) for ( ii = 0 ; ii < q_info.vcount ; ii++ ) { if ( q_info.wraps[i] ) for ( jj = 0 ; jj < SDIM ; jj++ ) { REAL tmp[MAXCOORD]; for ( j = 0 ; j < SDIM ; j++ ) tmp[j]=q_info.hess[i][ii][j][jj]; (*sym_form_pullback)(q_info.x[i],grad,tmp,q_info.wraps[i]); for ( j = 0 ; j < SDIM ; j++ ) q_info.hess[i][ii][j][jj] = grad[j]; } if ( q_info.wraps[ii] ) for ( j = 0 ; j < SDIM ; j++ ) { (*sym_form_pullback)(q_info.x[ii],grad,q_info.hess[i][ii][j], q_info.wraps[ii]); for ( jj = 0 ; jj < SDIM ; jj++ ) q_info.hess[i][ii][j][jj] = grad[jj]; } } } for ( i = 0 ; i < q_info.vcount ; i++ ) { REAL grad[MAXCOORD]; va = get_vertex_vhead(q_info.v[i]); for ( j = 0 ; j < SDIM ; j++ ) grad[j] = coeff*q_info.grad[i][j]; m_fill_grad(va,grad,me,rhs); } /* second derivatives */ if ( m_hess_mode ) for ( i = 0 ; i < q_info.vcount ; i++ ) { va = get_vertex_vhead(q_info.v[i]); if ( va->freedom == 0 ) continue; for ( j = i ; j < q_info.vcount ; j++ ) { vb = get_vertex_vhead(q_info.v[j]); if ( vb->freedom == 0 ) continue; for ( n = 0 ; n < SDIM ; n++ ) for ( m = 0 ; m < SDIM ; m++ ) q_info.hess[i][j][m][n] *= coeff; m_fill_mixed_entry(q_info.v[i],q_info.v[j], q_info.hess[i][j],me); if ( (i != j) && (q_info.v[i] == q_info.v[j]) ) m_fill_mixed_entry(q_info.v[i],q_info.v[j], q_info.hess[i][j],me); } /* end inner vertex loop */ /* fixed quantity gradients for left side */ if ( q->flags & (Q_FIXED|Q_CONSERVED) ) { /* find entry */ int currentrow; if ( va->proj ) { vec_mat_mul(q_info.grad[i],va->proj,g,SDIM, va->freedom); ggg = g; } else ggg = q_info.grad[i]; currentrow = m_quanrowstart + mi->quant; for ( m = 0 ; m < va->freedom ; m++ ) { m_hess_hash_search(currentrow,va->rownum+m, ccoeff*ggg[m],me); } } } /* end second derivatives */ } /* end if */ } /* end method instance loop */ } /* end all element loop */ /* free stuff */ free_matrix4(q_info.hess); free_matrix(p1); free_matrix(p2); q_info_free(&q_info); } /************************************************************************* * * function: m_fill_grad() * * purpose: Process gradient of function at constraint * Just stores in local list for future processing. */ void m_fill_grad(v,grad,tid,rhs) struct hess_verlist *v; REAL *grad; REAL *rhs; int tid; /* thread id */ { REAL g[MAXCOORD]; int k,a,b; if ( rhs_flag ) { M_LOCK(rhs); if ( v->proj ) { vec_mat_mul(grad,v->proj,g,SDIM,v->freedom); for ( k = 0 ; k < v->freedom ; k++ ) rhs[v->rownum+k] -= g[k]; } else for ( k = 0 ; k < v->freedom ; k++ ) rhs[v->rownum+k] -= grad[k]; M_UNLOCK(rhs); } if ( hess_flag && v->conhess ) { for ( a = 0 ; a < v->freedom ; a++ ) for ( b = 0 ; b <= a ; b++ ) { m_hess_hash_search(v->rownum+a,v->rownum+b, SDIM_dot(grad,v->conhess[a][b]),tid); } } } /************************************************************************* * * function: m_fill_mixed_entry() * * purpose: fill proper hessian matrix spot for mixed vertex second derivs * For multi-proc, just stores in local list. */ void m_fill_mixed_entry(v_id1,v_id2,mixed,tid) vertex_id v_id1,v_id2; REAL **mixed; /* full dim values */ int tid; /* thread id */ { int k,j; REAL **oo; MAT2D(temp_mat,MAXCOORD,MAXCOORD); MAT2D(temp_mat2,MAXCOORD,MAXCOORD); struct hess_verlist *v1,*v2; v1 = get_vertex_vhead(v_id1); v2 = get_vertex_vhead(v_id2); if ( v1->proj ) { tr_mat_mul(v1->proj,mixed,temp_mat,SDIM,v1->freedom,SDIM); oo = temp_mat; } else oo = mixed; if ( v2->proj ) { mat_mult(oo,v2->proj,temp_mat2,v1->freedom,SDIM,v2->freedom); oo = temp_mat2; } if ( v1->rownum < v2->rownum ) for ( j = 0 ; j < v1->freedom ; j++ ) for ( k = 0 ; k < v2->freedom ; k++ ) m_hess_hash_search(v2->rownum+k,v1->rownum+j,oo[j][k],tid); else for ( j = 0 ; j < v1->freedom ; j++ ) for ( k = 0 ; k < v2->freedom ; k++ ) m_hess_hash_search(v1->rownum+j,v2->rownum+k,oo[j][k],tid); } /******************************************************************* * * function: m_fix_hess() * * purpose: stores local lists into regular hessian list. * THis version still serial!! */ void m_fix_hess(S) struct linsys *S; { int p,i; for ( p = 0 ; p < nprocs ; p++ ) { struct hess_entry *e; int end = m_table_size[p]; for ( i = 0, e = m_hashtable[p] ; i < end ; i++,e++ ) if ( e->row != HASHEMPTY ) sp_hash_search(S,e->row,e->col,e->value); myfree((char*)(m_hashtable[p])); m_hashtable[p] = NULL; m_hash_per_row[p] = 1 + (5*m_hashcount[p])/(4*SDIM*web.skel[VERTEX].max_ord); /* for next time */ if ( !hessian_quiet_flag ) { sprintf(msg,"m_hashcount[%d]: %d m_table_size[%d]: %d\n", p,m_hashcount[p],p,m_table_size[p]); outstring(msg); } } if ( !hessian_quiet_flag ) { sprintf(msg,"m_hash_extraprobes: %d\n",m_hash_extraprobes); outstring(msg); } } #endif /******************************************************************* * * function: q_vertex_setup() * * purpose: calculate vertex attributes needed for quantities. */ void q_vertex_setup(S,v_info,needs) struct linsys *S; struct qinfo *v_info; int needs; /* particular setup needs for current mode */ { /* struct vertex *v_ptr = (struct vertex *)elptr(v_info->id); */ int i,j; vertex_id q_id; /* W A R N I N G */ /* Be sure to place all data consistently for all methods !!! */ v_info->S = S; q_id = v_info->id; v_info->vcount = 1; v_info->v[0] = v_info->id; v_info->x[0] = get_coord(v_info->v[0]); if ( needs & NEED_WINGS ) /* pair of adjacent vertices */ { facetedge_id right_fe; edge_id e_id[3]; right_fe = get_vertex_fe(v_info->id); if ( !valid_id(right_fe) ) goto vset_exit; v_info->vcount++; v_info->v[1] = get_fe_headv(right_fe); e_id[1] = get_fe_edge(right_fe); e_id[2] = get_next_tail_edge(e_id[1]); /* get incoming first, outgoing second, if possible */ if ( positive_id(e_id[1]) && !positive_id(e_id[2]) ) { edge_id etmp = e_id[1]; e_id[1] = e_id[2]; e_id[2] = etmp; } if ( !equal_id(e_id[1],e_id[2]) ) { v_info->vcount++; v_info->v[2] = get_edge_headv(e_id[2]); } for ( i = 1 ; i < v_info->vcount ; i++ ) { v_info->x[i] = get_coord(v_info->v[i]); if ( web.symmetry_flag ) { (*sym_wrap)(v_info->x[i],v_info->xx[i],get_edge_wrap(e_id[i])); v_info->x[i] = v_info->xx[i]; } for ( j = 0 ; j < SDIM ; j++ ) v_info->sides[0][i-1][j] = v_info->x[i][j] - v_info->x[0][j]; } } if ( needs & NEED_MARKED_WINGS ) { /* pair of marked edge vertices for sqcurve_string_marked */ edge_id thise,starte; edge_id e_id[3]; starte = get_vertex_edge(v_info->id); i = 1; thise = starte; do { if ( !valid_id(thise) ) goto vset_exit; if ( (marked_edge_attr>0) && *EINT(thise,marked_edge_attr) ) { e_id[i] = thise; v_info->vcount++; v_info->v[i] = get_edge_headv(e_id[i]); i++; if ( i >= 3 ) break; } thise = get_next_tail_edge(thise); } while ( !equal_id(thise,starte) ); for ( i = 1 ; i < v_info->vcount ; i++ ) { v_info->x[i] = get_coord(v_info->v[i]); if ( web.symmetry_flag ) { (*sym_wrap)(v_info->x[i],v_info->xx[i],get_edge_wrap(e_id[i])); v_info->x[i] = v_info->xx[i]; } for ( j = 0 ; j < SDIM ; j++ ) v_info->sides[0][i-1][j] = v_info->x[i][j] - v_info->x[0][j]; } } if ( needs & (NEED_FULL_STAR|NEED_PART_STAR) ) /* ring of facets */ { facetedge_id fe,startfe,next_fe; edge_id e_id; int k=0; if ( get_vattr(q_id) & BARE_NAKED ) goto nostar; fe = get_vertex_fe(q_id); if ( !valid_id(fe) ) goto nostar; if ( inverted(get_fe_facet(fe)) ) fe = inverse_id(get_prev_edge(fe)); startfe = fe; if ( get_vattr(q_id) & AXIAL_POINT ) { /* want to walk around outside ring */ facetedge_id fa,next_fe; WRAPTYPE wrap = get_fe_wrap(fe); WRAPTYPE wrap0 = wrap; REAL *y; v_info->axial_order = 0; next_fe = fa = get_next_edge(fe); do { v_info->v[k+1] = get_fe_tailv(next_fe); v_info->wraps[k+1] = wrap; y = get_coord(v_info->v[k+1]); (*sym_wrap)(y,v_info->sides[0][k],wrap); for ( j = 0 ; j < SDIM ; j++ ) v_info->sides[0][k][j] -= v_info->x[0][j]; v_info->vcount++; k++; if ( k >= MAXVCOUNT-1 ) { sprintf(errmsg, "quantity_setup: More than %d vertices around vertex %s.\n", MAXVCOUNT-1, ELNAME(v_info->id)); kb_error(1337,errmsg,RECOVERABLE); } wrap = (*sym_compose)(wrap,get_fe_wrap(next_fe)); next_fe = get_next_edge(next_fe); next_fe = inverse_id(get_next_facet(next_fe)); next_fe = get_next_edge(next_fe); if ( next_fe == fa ) v_info->axial_order++; } while ( (next_fe != fa) || (*sym_compose)((*sym_inverse)(wrap),wrap0)); v_info->flags &= ~INCOMPLETE_STAR; } else { /* normal vertex star */ /* first, see if we have partial star, and if so, start at one end */ fe = startfe; v_info->flags &= ~INCOMPLETE_STAR; do { next_fe = get_next_facet(fe); if ( equal_id(next_fe,fe) ) { /* have end of star */ startfe = fe; v_info->flags |= INCOMPLETE_STAR; if ( needs & NEED_FULL_STAR ) { sprintf(errmsg,"Vertex %s does not have a complete star (as needed by some method instance).\n",ELNAME(v_info->id)); kb_error(1573,errmsg,RECOVERABLE); } break; } fe = inverse_id(get_prev_edge(next_fe)); } while ( !equal_id(fe,startfe) ); /* now do the actual work */ do { e_id = get_fe_edge(fe); v_info->v[k+1] = get_edge_headv(e_id); if(web.symmetry_flag) v_info->wraps[k+1] = get_edge_wrap(e_id); get_edge_side(e_id,v_info->sides[0][k]); v_info->vcount++; k++; if ( k >= MAXVCOUNT-1 ) { sprintf(errmsg, "quantity_setup: More than %d vertices around vertex %s.\n", MAXVCOUNT-1, ELNAME(v_info->id)); kb_error(1572,errmsg,RECOVERABLE); } if ( !equal_id(fe,startfe) && equal_id(fe,get_next_facet(fe)) ) break; /* found end of partial star */ fe = get_prev_edge(fe); next_fe = get_next_facet(fe); fe = inverse_id(next_fe); } while ( !equal_id(fe,startfe) ); } nostar: ; } vset_exit: ; } /******************************************************************* * * function: q_edge_setup() * * purpose: calculate edge attributes needed for quantities. */ void q_edge_setup(S,e_info,needs) struct linsys *S; struct qinfo *e_info; int needs; /* particular setup needs for current mode */ { int i,j; if ( web.modeltype == QUADRATIC ) { q_edge_setup_q(e_info,needs); return; } if ( web.modeltype == LAGRANGE ) { q_edge_setup_lagrange(e_info,needs); return; } /* W A R N I N G */ /* Be sure to place all data consistently for all methods !!! */ e_info->S = S; e_info->vcount = (web.representation == SIMPLEX) ? web.dimension : 2; e_info->v[0] = get_edge_tailv(e_info->id); e_info->v[1] = get_edge_headv(e_info->id); e_info->x[0] = get_coord(e_info->v[0]); e_info->x[1] = get_coord(e_info->v[1]); if ( web.symmetry_flag ) { WRAPTYPE wrap = get_edge_wrap(e_info->id); (*sym_wrap)(e_info->x[1],e_info->xx[1],wrap); e_info->x[1] = e_info->xx[1]; e_info->wraps[1] = wrap; } if ( needs & NEED_SIDE ) for ( i = 0 ; i < SDIM ; i++ ) e_info->sides[0][0][i] = e_info->x[1][i] - e_info->x[0][i]; if ( needs & NEED_WINGS ) { facetedge_id fe,left_fe,right_fe; e_info->vcount = 4; /* need wing vertices, also */ fe = get_edge_fe(e_info->id); left_fe = inverse_id(get_prev_edge(fe)); right_fe = inverse_id(get_prev_edge(get_next_facet(fe))); e_info->v[2] = get_fe_headv(left_fe); e_info->v[3] = get_fe_headv(right_fe); if ( web.symmetry_flag ) { e_info->wraps[2] = get_edge_wrap(get_fe_edge(left_fe)); e_info->wraps[3] = get_edge_wrap(get_fe_edge(right_fe)); } for ( i = 2 ; i <= 3 ; i++ ) { e_info->x[i] = get_coord(e_info->v[i]); if ( web.symmetry_flag ) { (*sym_wrap)(e_info->x[i],e_info->xx[i],e_info->wraps[i]); e_info->x[i] = e_info->xx[i]; } for ( j = 0 ; j < SDIM ; j++ ) e_info->sides[0][i-1][j] = e_info->x[i][j] - e_info->x[0][j]; } } if ( needs & NEED_STRING_STAR ) { edge_id e_id1,e_id2 = e_info->id,e_id3; /* careful of weird packaging */ e_info->vcount = 4; /* need vertices of adjacent edges also */ e_id1 = inverse_id(get_next_tail_edge(e_id2)); e_id3 = inverse_id(get_next_head_edge(e_id2)); /* have to leave first two vertices in place for other methods */ e_info->v[2] = get_edge_tailv(e_id1); e_info->v[3] = get_edge_headv(e_id3); for ( j = 0 ; j < 4 ; j++ ) e_info->x[j] = get_coord(e_info->v[j]); for ( j = 0 ; j < SDIM ; j++ ) { e_info->sides[0][0][j] = e_info->x[1][j] - e_info->x[0][j]; e_info->sides[0][1][j] = e_info->x[0][j] - e_info->x[2][j]; e_info->sides[0][2][j] = e_info->x[3][j] - e_info->x[1][j]; } } if ( needs & NEED_GAUSS ) { int m; for ( m = 0 ; m < gauss1D_num ; m++ ) for ( i = 0 ; i < SDIM ; i++ ) e_info->gauss_pt[m][i] = gauss1poly[0][m]*e_info->x[0][i] + gauss1poly[1][m]*e_info->x[1][i]; } } /******************************************************************* * * function: q_edge_setup_q() * * purpose: calculate edge attributes needed for quantities. * For quadratic model. */ void q_edge_setup_q(e_info,needs) struct qinfo *e_info; int needs; /* particular setup needs for current mode */ { int i,j; /* W A R N I N G */ /* Be sure to place all data consistently for all methods !!! */ e_info->vcount = (web.representation == SIMPLEX) ? web.dimension : 3; e_info->v[0] = get_edge_tailv(e_info->id); e_info->v[1] = get_edge_midv(e_info->id); e_info->v[2] = get_edge_headv(e_info->id); e_info->x[0] = get_coord(e_info->v[0]); e_info->x[1] = get_coord(e_info->v[1]); e_info->x[2] = get_coord(e_info->v[2]); if ( web.symmetry_flag ) { WRAPTYPE wrap = get_edge_wrap(e_info->id); (*sym_wrap)(e_info->x[2],e_info->xx[2],wrap); e_info->x[2] = e_info->xx[2]; e_info->wraps[2] = wrap; } if ( needs & NEED_SIDE ) { /* tangent vectors at gauss points */ int m; REAL t; for ( m = 0 ; m < gauss1D_num ; m++ ) for ( i = 0 ; i < SDIM ; i++ ) { for ( j = 0, t = 0.0 ; j < edge_ctrl ; j++ ) t += gauss1polyd[j][m]*e_info->x[j][i]; e_info->sides[m][0][i] = t; } } if ( needs & NEED_WINGS ) { kb_error(1575,"Can't do quadratic model with edge WINGS (needed for some method instance).\n",RECOVERABLE); } if ( needs & NEED_GAUSS ) { int m; REAL t; for ( m = 0 ; m < gauss1D_num ; m++ ) for ( i = 0 ; i < SDIM ; i++ ) { for ( j = 0, t = 0.0 ; j < edge_ctrl ; j++ ) t += gauss1poly[j][m]*e_info->x[j][i]; e_info->gauss_pt[m][i] = t; } } } /******************************************************************* * * function: q_edge_setup_lagrange() * * purpose: calculate edge attributes needed for quantities. * For Lagrange model. */ void q_edge_setup_lagrange(e_info,needs) struct qinfo *e_info; int needs; /* particular setup needs for current mode */ { int i; int ctrl = web.skel[EDGE].ctrlpts; int dim = (web.representation==STRING) ? 1 : web.dimension - 1 ; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss1D_order]; vertex_id *v; /* W A R N I N G */ /* Be sure to place all data consistently for all methods !!! */ e_info->vcount = ctrl; v = get_edge_vertices(e_info->id); if ( inverted(e_info->id) ) for ( i = 0 ; i < ctrl ; i++ ) { e_info->v[i] = v[ctrl-i-1]; e_info->x[i] = get_coord(v[ctrl-i-1]); } else for ( i = 0 ; i < ctrl ; i++ ) { e_info->v[i] = v[i]; e_info->x[i] = get_coord(v[i]); } if ( web.symmetry_flag ) { WRAPTYPE wrap = get_edge_wrap(e_info->id); (*sym_wrap)(e_info->x[ctrl-1],e_info->xx[ctrl-1],wrap); e_info->x[ctrl-1] = e_info->xx[ctrl-1]; e_info->wraps[ctrl-1] = wrap; } if ( needs & NEED_SIDE ) { /* tangent vectors at gauss points */ int m; for ( m = 0 ; m < gl->gnumpts ; m++ ) mat_mult(gl->gpolypart[m],e_info->x,e_info->sides[m],dim,ctrl,SDIM); } if ( needs & NEED_WINGS ) { kb_error(1576, "Can't do Lagrange model with WINGS (needed for some method instance).\n", RECOVERABLE); } if ( needs & NEED_GAUSS ) mat_mult(gl->gpoly,e_info->x,e_info->gauss_pt,gl->gnumpts,ctrl,SDIM); } /******************************************************************* * * function: q_facet_setup() * * purpose: calculate facet attributes needed for quantities. */ void q_facet_setup(S,f_info,needs) struct linsys *S; struct qinfo *f_info; int needs; /* particular setup needs for current mode */ { facetedge_id fe_id; int i,j; f_info->S = S; if ( web.modeltype == QUADRATIC ) { q_facet_setup_q (f_info,needs); return; } if ( web.modeltype == LAGRANGE ) { q_facet_setup_lagrange(f_info,needs); return;} /* W A R N I N G */ /* Be sure to place all data consistently for all methods !!! */ f_info->vcount = web.dimension+1; if ( web.representation == SIMPLEX ) { vertex_id *vv = get_facet_vertices(f_info->id); for ( i = 0 ; i <= web.dimension ; i++ ) { f_info->v[i] = vv[i]; f_info->x[i] = f_info->xx[i]; } } else { fe_id = get_facet_fe(f_info->id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { f_info->v[i] = get_fe_tailv(fe_id); f_info->x[i] = f_info->xx[i]; fe_id = get_next_edge(fe_id); } } get_facet_verts(f_info->id,f_info->x,f_info->wraps); /* in tail order */ /* fan of sides from v0 */ if ( needs & NEED_SIDE ) { for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) f_info->sides[0][i][j] = f_info->x[i+1][j] - f_info->x[0][j]; } if ( needs & NEED_NORMAL ) { cross_prod(f_info->sides[0][0],f_info->sides[0][1],f_info->normal); } if ( needs & NEED_GAUSS ) mat_mult(gpoly,f_info->x,f_info->gauss_pt,gauss2D_num,ctrl_num,SDIM); } /******************************************************************* * * function: q_facet_setup_q() * * purpose: calculate facet attributes needed for quantities. * Quadratic model. */ void q_facet_setup_q(f_info,needs) struct qinfo *f_info; int needs; /* particular setup needs for current mode */ { facetedge_id fe_id; int i,m; /* W A R N I N G */ /* Be sure to place all data consistently for all methods !!! */ f_info->vcount = 6; fe_id = get_facet_fe(f_info->id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { f_info->v[2*i] = get_fe_tailv(fe_id); f_info->x[2*i] = f_info->xx[2*i]; f_info->v[2*i+1] = get_fe_midv(fe_id); f_info->x[2*i+1] = f_info->xx[2*i+1]; fe_id = get_next_edge(fe_id); } get_facet_verts(f_info->id,f_info->x,f_info->wraps); /* in tail order */ /* tangents at gauss points */ for ( m = 0 ; m < gauss2D_num ; m++ ) mat_mult(gpolypartial[m],f_info->x,f_info->sides[m], web.dimension,FACET_CTRL,SDIM); /* if ( needs & NEED_NORMAL ) */ if ( needs & NEED_GAUSS ) mat_mult(gpoly,f_info->x,f_info->gauss_pt,gauss2D_num,FACET_CTRL,SDIM); } /******************************************************************* * * function: q_facet_setup_lagrange() * * purpose: calculate facetedge attributes needed for quantities. * For Lagrange model. */ void q_facet_setup_lagrange(f_info,needs) struct qinfo *f_info; int needs; /* particular setup needs for current mode */ { int i,j; int ctrl = web.skel[FACET].ctrlpts; int dim = web.dimension; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; vertex_id *v; /* W A R N I N G */ /* Be sure to place all data consistently for all methods !!! */ f_info->vcount = ctrl; v = get_facet_vertices(f_info->id); if ( inverted(f_info->id) ) /* invert orientation of vertices */ for ( j = 0 ; j <= web.lagrange_order ; j++ ) for ( i = 0 ; i+j <= web.lagrange_order ; i++ ) { f_info->v[j*(web.lagrange_order+1)-j*(j-1)/2+i] = v[i*(web.lagrange_order+1)-i*(i-1)/2+j]; f_info->x[j*(web.lagrange_order+1)-j*(j-1)/2+i] = f_info->xx[i*(web.lagrange_order+1)-i*(i-1)/2+j]; } else for ( i = 0 ; i < ctrl ; i++ ) { f_info->v[i] = v[i]; f_info->x[i] = f_info->xx[i]; } get_facet_verts(f_info->id,f_info->x,f_info->wraps); if ( needs & NEED_SIDE ) { /* tangent vectors at gauss points */ int m; for ( m = 0 ; m < gl->gnumpts ; m++ ) mat_mult(gl->gpolypart[m],f_info->x,f_info->sides[m],dim,ctrl,SDIM); } /* always need gauss */ mat_mult(gl->gpoly,f_info->x,f_info->gauss_pt,gl->gnumpts,ctrl,SDIM); } /******************************************************************* * * function: q_body_setup() * * purpose: calculate body attributes needed for quantities. */ void q_body_setup(S,b_info,needs) struct linsys *S; struct qinfo *b_info; int needs; /* particular setup needs for current mode */ { } /******************************************************************* * * function: q_facetedge_setup() * * purpose: calculate facetedge attributes needed for quantities. */ void q_facetedge_setup(S,fe_info,needs) struct linsys *S; struct qinfo *fe_info; int needs; /* particular setup needs for current mode */ { } /*****************************************************************8 * * functions: null_q_value(), null_q_grad(), null_q_hess() * * purpose: traps for undefined quantity methods. */ REAL null_q_value(q_info) struct qinfo *q_info; { sprintf(errmsg,"Method value function not implemented for %s.\n", basic_gen_methods[METH_INSTANCE(q_info->method)->gen_method].name); kb_error(1577,errmsg,RECOVERABLE); return 0.0; } REAL null_q_grad(q_info) struct qinfo *q_info; { sprintf(errmsg,"Method gradient function not implemented for %s.\n", basic_gen_methods[METH_INSTANCE(q_info->method)->gen_method].name); kb_error(1578,errmsg,RECOVERABLE); return 0.0; } REAL null_q_hess(q_info) struct qinfo *q_info; { sprintf(errmsg,"Quantity hessian function not implemented for %s.\n", basic_gen_methods[METH_INSTANCE(q_info->method)->gen_method].name); kb_error(1579,errmsg,RECOVERABLE); return 0.0; } /* handy for zeroing out gradient and hessian */ void zerohess(q_info) struct qinfo *q_info; { int m,i,j,k; for ( m = 0 ; m < q_info->vcount ; m++ ) for ( j = 0 ; j < SDIM ; j++ ) q_info->grad[m][j] = 0.0; for ( m = 0 ; m < q_info->vcount ; m++ ) for ( i = 0 ; i < q_info->vcount ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( k = 0 ; k < SDIM ; k++ ) q_info->hess[m][i][j][k] = 0.0; } /********************************************************************* Phase space evolution - film with inertia. Coordinates represent position and velocity. Requested by Jeremy Ackerman **********************************************************************/ /*********************************************************************** * * function: ackerman_init() * * purpose: initial checks for inertial motion */ void ackerman_init(mode,mi) int mode; struct method_instance *mi; { int sdim2=2*SDIM; if ( 2*SDIM > MAXCOORD ) kb_error(1580,"ackerman method: Dimension too high for phase space motion.\n",RECOVERABLE); expand_attribute(VERTEX,V_COORD_ATTR,&sdim2); expand_attribute(VERTEX,V_OLDCOORD_ATTR,&sdim2); expand_attribute(VERTEX,V_FORCE_ATTR,&sdim2); expand_attribute(VERTEX,V_VELOCITY_ATTR,&sdim2); ackerman_flag = 1; } /*********************************************************************** * * function: ackerman_energy() * * purpose: dummy energy */ REAL ackerman_energy(v_info) struct qinfo *v_info; { return 0.0; } /*********************************************************************** * * function: ackerman_forces() * * purpose: forces in phase space */ REAL ackerman_forces(v_info) struct qinfo *v_info; { REAL *f = get_force(v_info->id); REAL *x = get_coord(v_info->id); /* REAL star = get_vertex_star(v_info->id); */ int i; /* modify first order forces to second order */ for ( i = 0 ; i < SDIM ; i++ ) { f[i+SDIM] = f[i]; f[i] = 0.0; v_info->grad[0][i] = -x[i+SDIM]; } return 0.0; } /************************************************************************ * * function: add_vgrads_to_update() * * purpose: add method gradients to low rank update part of hessian. */ void add_vgrads_to_update(S) struct linsys *S; { vertex_id v_id; FOR_ALL_VERTICES(v_id) { struct hess_verlist *v1 = get_vertex_vhead(v_id); struct volgrad *vgptr = get_vertex_vgrad(v_id); for ( ; vgptr ; vgptr = vgptr->chain ) { struct method_instance *mi; int j; if ( !(vgptr->qnum & METHBASE ) ) continue; mi = METH_INSTANCE(vgptr->qnum - METHBASE); if ( v1->proj ) { REAL tempvec[MAXCOORD]; vec_mat_mul(vgptr->grad,v1->proj,tempvec,SDIM,v1->freedom); for ( j = 0 ; j < v1->freedom ; j++ ) S->low_rank_vectors[mi->global_low_rank][v1->rownum+j] += tempvec[j]; } else { for ( j = 0 ; j < SDIM ; j++ ) S->low_rank_vectors[mi->global_low_rank][v1->rownum+j] += vgptr->grad[j]; } } } } evolver-2.30c.dfsg/src/utility.c0000644000175300017530000035633211410765113017100 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************* * * file: utility.c * * Purpose: Various utility routines. */ #include "include.h" /************************************************************** * * Function: catcher() * * Purpose: User interrupt catcher. Sets breakflag so * anybody in a long loop can test to see if * user wants to break out. */ #ifdef WIN32 #define write(a,b,c) erroutstring(b) #endif void catcher(sig) int sig; { #ifdef SIGALRM if ( sig == SIGALRM ) signal(SIGALRM,catcher); #endif if ( sig == SIGINT ) { signal(SIGINT,catcher); #ifdef SGI_MULTI if ( mpflag == M_ACTIVE ) { if ( m_breakflag[GET_THREAD_ID] ) longjmp(m_jumpbuf[GET_THREAD_ID],1); else m_breakflag[GET_THREAD_ID] = BREAKFULL; if ( GET_THREAD_ID == 0 ) write(2,"\nWill break after iteration.\n",29); breakflag = BREAKFULL; iterate_flag = 0; } else if ( GET_THREAD_ID > 0 ) {} else #endif if ( iterate_flag == 1 ) { write(2,"\nWill break after iteration.\n",29); breakflag = BREAKFULL; iterate_flag = 0; } else if ( iterate_flag == 2 ) /* graphing or something */ { write(2,"\nWill abort operation.\n",24); breakflag = BREAKFULL; iterate_flag = 0; } else { write(2,"\nAborting operation.\n",22); if ( TRY_GRAPH_MUTEX(IMMEDIATE_TIMEOUT) ) ABORT_GRAPH_MUTEX else erroutstring("In GRAPH_MUTEX. May be deadlocked with graphics thread.\n"); #ifdef WIN32 /* separate thread for signal handler, so can't longjmp out of error */ erroutstring("Can't nicely abort command in WIN32. Hang in there, or\n"); erroutstring("use Task Manager to kill Evolver if you think it's hung.\n"); #elif defined(MAC_OS_X) if ( iterate_flag > 2 ) erroutstring("Haven't figured out how to handle this yet.\n"); #else kb_error(1357,"",RECOVERABLE_QUIET); #endif } while ( commandfd && (commandfd != stdin) ) pop_commandfd(); quiet_flag = 0; } #ifdef SIGTERM if ( sig == SIGTERM ) /* for outside kill and dump */ { sprintf(errmsg,"Caught SIGTERM. proc %d ",getpid()); erroutstring(errmsg); do_dump(NULL); /* dump to default */ my_exit(1); } #endif #ifdef SIGHUP if ( sig == SIGHUP ) /* for outside kill and dump */ { do_dump(NULL); /* dump to default */ my_exit(1); } #endif #ifdef SIGPIPE else if ( sig == SIGPIPE ) { signal(SIGPIPE,catcher); write(2,"Broken pipe signal.\n",20); broken_pipe_flag = 1; } #endif } /*********************************************************************** ************************************************************************ Memory allocation routines ************************************************************************/ /* Header structure for each block */ struct memhead { struct memhead *prev, *next; /* doubly linked list */ size_t size; int type; /* see below */ #ifdef MEMSTRINGS char file[28]; int line; #endif }; /* Memory block list heads */ struct memhead *perm_block_head; struct memhead *temp_block_head; struct memhead *graph_block_head; struct memhead *eternal_block_head; /*********************************************************************** * * function: kb_checksum() * * purpose: to calculate a simple checksum on a block of memory. */ int kb_checksum(mem,size) char *mem; int size; { int sum = 0; int *a,*b; b = (int *)mem; a = b + size/sizeof(int); do { sum += *a; } while ( --a != b); return sum; } /******************************************************************** ********************************************************************* Memory allocation routines. These put my own headers on memory blocks, and keep linked lists for easy garbage collection. To start with, there are surface-permanent and command-temporary lists, but there could be more, for example of info that needs to be distributed on a network implementation, instead of the present dymem. ********************************************************************/ void mem_sanity_check ARGS((void)); /******************************************************************** * * function: mem_sanity_check() * * purpose: Checks all lists. */ void mem_sanity_check() { struct memhead *head; int blocktype; THREADBLOCK(blocktype); #if defined(_WIN32) && defined(_HEAPOK) #ifndef _HEAPINFO #define _HEAPINFO _heapinfo #endif if ( _heapchk() != _HEAPOK ) kb_error(2580,"Internal error: Corrupt heap! Memory is trashed.\n",RECOVERABLE); #endif for ( head = eternal_block_head; head ; head = head->next ) { if ( head->type != ETERNAL_BLOCK ) kb_error(2581,"Bad eternal memory block chain!\n",RECOVERABLE); } for ( head = perm_block_head ; head ; head = head->next ) { if ( head->type != PERM_BLOCK ) kb_error(2460,"Bad permanent memory block chain!\n",RECOVERABLE); } if ( blocktype == TEMP_BLOCK ) for ( head = temp_block_head ; head ; head = head->next ) { if ( head->type != TEMP_BLOCK ) kb_error(3208,"Bad temporary memory block chain!\n",RECOVERABLE); } if ( blocktype == GRAPH_BLOCK ) for ( head = graph_block_head ; head ; head = head->next ) { if ( head->type != GRAPH_BLOCK ) kb_error(2461,"Bad graphics memory block chain!\n",RECOVERABLE); } } /******************************************************************** * * function: mem_list_summary() * * purpose: Prints summary stats of memory lists. */ void mem_list_summary() { struct memhead *head; size_t total=0,subtotal; size_t count=0,subcount; for ( head = eternal_block_head, subcount=subtotal=0 ; head ; head = head->next ) { if ( head->type != ETERNAL_BLOCK ) kb_error(2462,"Bad eternal memory block chain!\n",RECOVERABLE); else { subtotal += head->size; subcount++; } } if ( verbose_flag ) { sprintf(errmsg,"Session: %7d blocks, %10d bytes \n",subcount,subtotal); outstring(errmsg); } total += subtotal; count += subcount; for ( head = perm_block_head, subcount=subtotal=0 ; head ; head = head->next ) { if ( head->type != PERM_BLOCK ) kb_error(2463,"Bad permanent memory block chain!\n",RECOVERABLE); else { subtotal += head->size; subcount++; } } if ( verbose_flag ) { sprintf(errmsg,"Permanent: %5d blocks, %10d bytes \n",subcount,subtotal); outstring(errmsg); } total += subtotal; count += subcount; for ( head = temp_block_head, subcount=subtotal=0 ; head ; head = head->next ) { if ( head->type != TEMP_BLOCK ) kb_error(2464,"Bad temporary memory block chain!\n",RECOVERABLE); else { subtotal += head->size; subcount++; } } for ( head = graph_block_head ; head ; head = head->next ) { if ( head->type != GRAPH_BLOCK ) kb_error(3210,"Bad graphics memory block chain!\n",RECOVERABLE); else { subtotal += head->size; subcount++; } } if ( verbose_flag ) { sprintf(errmsg,"Temporary: %5d blocks, %10d bytes\n",subcount,subtotal); outstring(errmsg); } total += subtotal; count += subcount; if ( verbose_flag ) sprintf(errmsg,"Total data memory: %d blocks, %d bytes.\n",count,total); else sprintf(errmsg,"Total data memory: %d bytes.\n",total); outstring(errmsg); #ifdef LINUX if ( verbose_flag ) { FILE *fd; int i,vmem=0,mempages=0; char procname[100]; sprintf(procname,"/proc/%d/stat",getpid()); fd = fopen(procname,"r"); if ( fd == NULL ) perror(procname); for ( i = 0 ; i < 22 ; i++ ) fscanf(fd,"%s",msg); fscanf(fd,"%d",&vmem); sprintf(errmsg,"Virtual memory: %d\n",vmem); outstring(errmsg); fscanf(fd,"%d",&mempages); sprintf(errmsg,"Resident memory: %d\n",mempages*getpagesize()); outstring(errmsg); } #endif } /******************************************************************** * * function: mem_list_dump() * * purpose: Prints details of memory lists. */ void mem_list_dump() { struct memhead *head; size_t subcount; #ifdef MEMSTRINGS outstring("\nEternal memory blocks:\n"); for ( head = eternal_block_head, subcount=0 ; head ; head = head->next ) { subcount++; sprintf(msg,"%5d. %10d bytes at %p; %28s line %4d\n", subcount,head->size,head,head->file,head->line); outstring(msg); } outstring("\nPermanent memory blocks:\n"); for ( head = perm_block_head, subcount=0 ; head ; head = head->next ) { subcount++; sprintf(msg,"%5d. %10d bytes at %p; %28s line %4d\n", subcount,head->size,head,head->file,head->line); outstring(msg); } outstring("\nTemporary memory blocks:\n"); for ( head = temp_block_head, subcount=0 ; head ; head = head->next ) { subcount++; sprintf(msg,"%5d. %10d bytes at %p; %28s line %4d\n", subcount,head->size,head,head->file,head->line); outstring(msg); } outstring("\nGraph memory blocks:\n"); for ( head = graph_block_head, subcount=0 ; head ; head = head->next ) { subcount++; sprintf(msg,"%5d. %10d bytes at %p; %28s line %4d\n", subcount,head->size,head,head->file,head->line); outstring(msg); } #else outstring("\nEternal memory blocks:\n"); for ( head = eternal_block_head, subcount=0 ; head ; head = head->next ) { subcount++; sprintf(msg,"%5d. %10d bytes at %p\n", subcount,head->size,head); outstring(msg); } outstring("\nPermanent memory blocks:\n"); for ( head = perm_block_head, subcount=0 ; head ; head = head->next ) { subcount++; sprintf(msg,"%5d. %10d bytes at %p\n", subcount,head->size,head); outstring(msg); } outstring("\nTemporary memory blocks:\n"); for ( head = temp_block_head, subcount=0 ; head ; head = head->next ) { subcount++; sprintf(msg,"%5d. %10d bytes at %p\n", subcount,head->size,head); outstring(msg); } outstring("\nGraph memory blocks:\n"); for ( head = graph_block_head, subcount=0 ; head ; head = head->next ) { subcount++; sprintf(msg,"%5d. %10d bytes at %p\n", subcount,head->size,head); outstring(msg); } #endif } /******************************************************************** Generic list calloc,realloc,free routines. *********************************************************************/ /******************************************************************** * * function: list_calloc() * * purpose: allocate memory block on desired list. */ #ifdef MEMSTRINGS char *list_calloc(num,size,type,file,line) size_t num; /* number of chunks */ size_t size; /* size of chunks */ int type; /* memory list type */ char *file; size_t line; #else char *list_calloc(num,size,type) size_t num; /* number of chunks */ size_t size; /* size of chunks */ int type; /* memory list type */ #endif { struct memhead **listhead=NULL; struct memhead *ptr; switch(type) { case PERM_BLOCK: listhead = &perm_block_head; break; case TEMP_BLOCK: listhead = &temp_block_head; break; case GRAPH_BLOCK: listhead = &graph_block_head; break; case ETERNAL_BLOCK: listhead = &eternal_block_head; break; default: kb_error(2452,"Internal error: illegal memory block type.\n", RECOVERABLE); } ptr = (struct memhead *)calloc(sizeof(struct memhead)+num*size,1); if ( ptr == NULL ) { mem_sanity_check(); sprintf(errmsg,"Internal error: Cannot allocate memory size %d*%d = %d.\n", num,size,num*size); kb_error(2550,errmsg, RECOVERABLE); } /* fill in header */ ENTER_MEM_MUTEX ptr->next = *listhead; if ( *listhead ) (*listhead)->prev = ptr; *listhead = ptr; LEAVE_MEM_MUTEX ptr->size = num*size; ptr->type = type; #ifdef MEMSTRINGS strncpy(ptr->file,file,sizeof(ptr->file)-1); ptr->line = line; #endif if ( memdebug ) mem_sanity_check(); return (char*)(ptr+1); } /************************************************************************ * * function: list_realloc() * * purpose: reallocate memory block on a list. * */ #ifdef MEMSTRINGS char *list_realloc(ptr,size,type,file,line) char *ptr; /* old block */ size_t size; /* new size */ int type; /* of list */ char *file; size_t line; #else char *list_realloc(ptr,size,type) char *ptr; /* old block */ size_t size; /* new size */ int type; /* of list */ #endif { struct memhead **listhead=NULL; struct memhead *newhead; struct memhead *oldhead; char *newptr; size_t oldsize; switch(type) { case PERM_BLOCK: listhead = &perm_block_head; break; case TEMP_BLOCK: listhead = &temp_block_head; break; case GRAPH_BLOCK: listhead = &graph_block_head; break; case ETERNAL_BLOCK: listhead = &eternal_block_head; break; default: kb_error(2453,"Internal error: illegal memory block type.\n", RECOVERABLE); } #ifdef MEMSTRINGS if ( ptr == NULL ) return list_calloc(size,1,type,file,line); #else if ( ptr == NULL ) return list_calloc(size,1,type); #endif oldhead = (struct memhead *)ptr - 1; if ( oldhead->type != type ) { sprintf(errmsg,"Internal error: Trying to realloc memory block to different list.\n"); #ifdef MEMSTRINGS sprintf(errmsg+strlen(errmsg),"File %s, line %d; original alloc %s:%d\n", file,line,oldhead->file,oldhead->line); #endif kb_error(2448,errmsg,RECOVERABLE); } oldsize = oldhead->size; ENTER_MEM_MUTEX /* since manipulating shared variables */ /* delete from my list */ if ( oldhead->next ) oldhead->next->prev = oldhead->prev; if ( oldhead->prev ) oldhead->prev->next = oldhead->next; else *listhead = oldhead->next; newhead = (struct memhead *)realloc((char*)oldhead,sizeof(struct memhead)+size); if ( newhead == NULL ) { sprintf(errmsg, "Internal error: Cannot reallocate memory from old size %d to new %d.\n", oldsize,size); kb_error(1360,errmsg,RECOVERABLE); } /* fill in header */ newhead->next = *listhead; newhead->prev = NULL; if ( *listhead ) (*listhead)->prev = newhead; *listhead = newhead; LEAVE_MEM_MUTEX newhead->size = size; newhead->type = type; #ifdef MEMSTRINGS strncpy(newhead->file,file,sizeof(newhead->file)-1); newhead->line = line; #endif newptr = (char*)(newhead+1); if (oldsize < size ) memset(newptr+oldsize,0,size-oldsize); if ( memdebug ) mem_sanity_check(); return newptr; } /* end of list_realloc() */ /************************************************************************** * * function: list_free() * * purpose: free one block on a memory list. */ void list_free(ptr,type) char *ptr; int type; /* of memory list */ { struct memhead *head; struct memhead **listhead=NULL; if ( !ptr ) { if ( memdebug ) erroutstring("\n"); return; } if ( memdebug ) mem_sanity_check(); switch(type) { case PERM_BLOCK: listhead = &perm_block_head; break; case TEMP_BLOCK: listhead = &temp_block_head; break; case GRAPH_BLOCK: listhead = &graph_block_head; break; case ETERNAL_BLOCK: listhead = &eternal_block_head; break; default: kb_error(2454,"Internal error: illegal memory block type.\n", RECOVERABLE); } head = (struct memhead *)ptr - 1; if ( memdebug ) { #ifdef MEMSTRINGS sprintf(msg," allocated from %s:%d\n",head->file,head->line); #else sprintf(msg,"\n"); #endif erroutstring(msg); } if ( head->type != type ) { switch ( head->type ) { case ETERNAL_BLOCK: case TEMP_BLOCK: case PERM_BLOCK: case GRAPH_BLOCK: #ifdef MEMSTRINGS sprintf(errmsg,"Internal error: Trying to free memory block from wrong type list.\nAllocated from %s:%d\n", head->file,head->line); #else sprintf(errmsg,"Internal error: Trying to free memory block from wrong type list.\n"); #endif kb_error(2465,errmsg,RECOVERABLE); default: #ifdef MEMSTRINGS sprintf(errmsg,"Internal error: Trying to free corrupt memory block.\nAllocated from %s:%d\n", head->file,head->line); kb_error(2466,errmsg,RECOVERABLE); #else kb_error(3209, "Internal error: trying to free corrupt memory block.\n", RECOVERABLE); #endif } } /* delete from my list */ ENTER_MEM_MUTEX if ( head->next ) head->next->prev = head->prev; if ( head->prev ) head->prev->next = head->next; else *listhead = head->next; LEAVE_MEM_MUTEX free((char*)head); if ( memdebug ) mem_sanity_check(); } /************************************************************************** * * function: list_free_all() * * purpose: free all blocks on a list. */ void list_free_all(type) int type; /* of list */ { struct memhead **listhead=NULL; struct memhead *head,*nexthead; switch(type) { case PERM_BLOCK: listhead = &perm_block_head; break; case TEMP_BLOCK: listhead = &temp_block_head; break; case GRAPH_BLOCK: listhead = &graph_block_head; break; case ETERNAL_BLOCK: listhead = &eternal_block_head; break; default: kb_error(2455,"Internal error: illegal memory block type.\n", RECOVERABLE); } ENTER_MEM_MUTEX for ( head = *listhead; head ; head = nexthead ) { if ( head->type != type ) kb_error(3905,"Internal error: corrupt memory list.\n",RECOVERABLE); if ( memdebug ) { #ifdef MEMSTRINGS sprintf(msg,"Freeing %p, %d bytes, allocated at %s:%d\n",head+1, head->size,head->file,head->line); #else sprintf(msg,"Freeing %p, %d bytes\n",head+1,head->size); #endif erroutstring(msg); } nexthead = head->next; free((char*)head); } *listhead = NULL; LEAVE_MEM_MUTEX } /* end list_free_all */ /******************************************************************** Permanent memory allocation. These blocks will be automatically freed at the start of a new surface, eliminating a lot of clean-up drudgery. ********************************************************************/ /********************************************************************* * * function: kb_calloc * * purpose: memory allocation with error detection * Also memory debugging. * Called by macro mycalloc(num,size). */ #ifdef MEMSTRINGS char *kb_calloc(num,size,file,line) size_t num,size; char *file; size_t line; #else char *kb_calloc(num,size) size_t num,size; #endif { char *ptr; if ( memdebug ) { #ifdef MEMSTRINGS sprintf(errmsg,"mycalloc %30.30s %5d: %10d ",file,line,num*size); erroutstring(errmsg); #else sprintf(errmsg,"mycalloc %d*%d = %d bytes ",num,size,num*size); erroutstring(errmsg); #endif } #ifdef MEMSTRINGS ptr = list_calloc(num,size,PERM_BLOCK,file,line); #else ptr = list_calloc(num,size,PERM_BLOCK); #endif if ( memdebug ) { sprintf(errmsg,"at %p\n",ptr); erroutstring(errmsg); mem_sanity_check(); } return ptr; } /* end kb_calloc */ /********************************************************************* * * function: KB_realloc * * purpose: memory re-allocation with error detection * Called through macro kb_realloc for optional memory debug. */ #ifdef MEMSTRINGS char *KB_realloc(ptr,size,file,line) char *ptr; size_t size; char *file; size_t line; #else char *KB_realloc(ptr,size) char *ptr; size_t size; #endif { char *newptr; if ( memdebug ) { #ifdef MEMSTRINGS sprintf(errmsg,"%30.30s %4d: realloc old %p size %d to size %d \n", file,line,ptr, (ptr ? ((struct memhead *)ptr)[-1].size:0) ,size); #else sprintf(errmsg,"realloc old %p size %d to size %d \n",ptr, (ptr ? ((struct memhead *)ptr)[-1].size:0 ) ,size); #endif erroutstring(errmsg); } #ifdef MEMSTRINGS if ( ptr == NULL ) newptr = kb_calloc(size,1,file,line); else newptr = list_realloc(ptr,size,PERM_BLOCK,file,line); #else if ( ptr == NULL ) newptr = mycalloc(size,1); else newptr = list_realloc(ptr,size,PERM_BLOCK); #endif if ( memdebug ) { sprintf(errmsg,"at %p\n",newptr); erroutstring(errmsg); mem_sanity_check(); } return newptr; } /* end kb_realloc() */ /******************************************************************** * * function: myfree() * * purpose: Freeing memory. Common call for permanent memory freeing. * */ void myfree(ptr) char *ptr; { if ( memdebug ) { sprintf(errmsg,"free %p ",ptr); erroutstring(errmsg); } list_free(ptr,PERM_BLOCK); } /******************************************************************** Temporary memory allocation. These blocks will be automatically freed at the end of each command, although subroutines should practice good memory hygeine and free all their own memory. Mostly intended in case of error recovery. ********************************************************************/ /******************************************************************** * * function: kb_temp_calloc() * * purpose: memory allocation that can be undone by longjmp at error. * */ #ifdef MEMSTRINGS char *kb_temp_calloc(num,size,file,line) size_t num,size; char *file; size_t line; #else char *kb_temp_calloc(num,size) size_t num,size; #endif { char *ptr; int blocktype = TEMP_BLOCK; if ( memdebug ) { #ifdef MEMSTRINGS sprintf(errmsg,"%30.30s %4d: ",file,line); erroutstring(errmsg); #endif sprintf(errmsg,"temp_calloc %9d bytes ",num*size); erroutstring(errmsg); } THREADBLOCK(blocktype); #ifdef MEMSTRINGS ptr = list_calloc(num,size,blocktype,file,line); #else ptr = list_calloc(num,size,blocktype); #endif if ( memdebug ) { sprintf(errmsg,"at %p \n",ptr); erroutstring(errmsg); mem_sanity_check(); } return ptr; } /******************************************************************** * * function: temp_realloc() * * purpose: Changing size of temporary memory allocation. * */ #ifdef MEMSTRINGS char * kb_temp_realloc(ptr,size,file,line) char *ptr; size_t size; char *file; size_t line; #else char * kb_temp_realloc(ptr,size) char *ptr; size_t size; #endif { char *newptr; int blocktype = TEMP_BLOCK; if ( memdebug ) { #ifdef MEMSTRINGS sprintf(errmsg,"%30.30s %4d: temp_realloc\n old size %9d at %p to size %9d", file,line, (ptr ? ((struct memhead *)ptr)[-1].size:0),ptr ,size); #else sprintf(errmsg,"temp_realloc old %p size %9d to size %9d",ptr, (ptr ? ((struct memhead *)ptr)[-1].size:0 ) ,size); #endif erroutstring(errmsg); } THREADBLOCK(blocktype); #ifdef MEMSTRINGS newptr = list_realloc(ptr,size,blocktype,file,line); #else newptr = list_realloc(ptr,size,blocktype); #endif if ( memdebug ) { sprintf(errmsg," at %p \n",newptr); erroutstring(errmsg); mem_sanity_check(); } return newptr; } /******************************************************************** * * function: temp_free() * * purpose: Usual freeing of temporary memory allocation. * */ void temp_free(ptr) char *ptr; { int blocktype = TEMP_BLOCK; if ( memdebug ) { sprintf(errmsg,"temp_free %p ",ptr); erroutstring(errmsg); } THREADBLOCK(blocktype); list_free(ptr,blocktype); } /* end temp_free */ /******************************************************************** * * function: temp_free_all() * * purpose: Freeing of all temporary memory in case of error. * */ void temp_free_all() { int blocktype = 0; #ifdef _DEBUGXX int oldmemdebug = memdebug; memdebug=1; #endif if ( memdebug ) erroutstring("Entering temp_free_all\n"); THREADBLOCK(blocktype); list_free_all(blocktype); #ifdef MPI_EVOLVER if ( this_task == 0 && blocktype != GRAPH_BLOCK ) mpi_temp_free_all(); #endif #ifdef _DEBUGXX memdebug = oldmemdebug; #endif /* pointers to be annulled in main thread */ if ( blocktype == TEMP_BLOCK ) { saved.coord = NULL; vhead = NULL; pressures = NULL; conrhs = NULL; vgradbase = NULL; rightside = NULL; vpressures = NULL; e_curve = NULL; v_curve = NULL; } } /******************************************************************** Memory allocation routines for the block of memory holding dynamic structures of surface description. Enables exporting surface info to multiple processors. Returns are offsets from start of dynamic region. *********************************************************************/ #define ALIGNSIZE 8 struct dy_head { int size; /* bytes in block, including structures */ int state; /* see defines; also used as magic number */ DY_OFFSET prev; /* linear block list */ DY_OFFSET next; DY_OFFSET prevfree; /* disordered free block list */ DY_OFFSET nextfree; }; #define DY_FREE 0xCCAA #define DY_USED 0xDDBB #define DY_STARTSIZE 0x1000 int dy_minfrag = 2*sizeof(struct dy_head); int dy_overhead = sizeof(struct dy_head); DY_OFFSET dy_firstblock; DY_OFFSET dy_lastblock; DY_OFFSET dy_calloc(num,size) int num; int size; { struct dy_head *ptr,*newptr,*lastptr; DY_OFFSET off; int needed; int surplus; int asize; int i; if ( dymemsize == 0 ) /* initial allocation */ { if ( sizeof(struct dy_head) % ALIGNSIZE ) { sprintf(errmsg, "Internal error: sizeof(struct dy_head) %d not multiple of ALIGNSIZE %d\n", sizeof(struct dy_head),ALIGNSIZE); kb_error(1364,errmsg,UNRECOVERABLE); } dymem = mycalloc(DY_STARTSIZE,1); dymemsize = DY_STARTSIZE; strcpy(dymem,"DY_MEM"); /* so looks pretty in debugger */ dy_firstblock = sizeof(struct dy_head); ptr = (struct dy_head *)dymem+1; /* so offset 0 not used */ ptr->size = DY_STARTSIZE-sizeof(struct dy_head); ptr->state = DY_FREE; ptr->next = 0; ptr->prev = 0; dy_lastblock = sizeof(struct dy_head); dy_freestart = sizeof(struct dy_head); ptr->prevfree = 0; /* first in list */ ptr->nextfree = 0; /* last in list */ } /* size of needed block */ asize = ((num*size + ALIGNSIZE - 1)/ALIGNSIZE)*ALIGNSIZE; if ( asize <= dy_minfrag ) asize = dy_minfrag; needed = sizeof(struct dy_head) + asize; if ( memdebug ) { sprintf(errmsg,"dy_alloc %d*%d = %d bytes.\n",num,size,asize); erroutstring(errmsg); } retry_alloc: /* look for first fit in free block list */ if ( dy_freestart ) for ( off = dy_freestart ; off > 0 ; off = ptr->nextfree ) { ptr = (struct dy_head *)(dymem + off); /* check size */ if ( needed > ptr->size ) { if ( ptr->nextfree == 0 ) break; else continue; } surplus = ptr->size - needed; if ( surplus > dy_minfrag ) { /* divide block */ newptr = (struct dy_head *)(dymem + off + needed); newptr->size = ptr->size - needed; newptr->state = DY_FREE; newptr->next = ptr->next; newptr->prev = off; if ( newptr->next ) ((struct dy_head *)(dymem + newptr->next))->prev = off + needed; else dy_lastblock = off + needed; newptr->nextfree = ptr->nextfree; newptr->prevfree = ptr->prevfree; ptr->size = needed; ptr->next = off + needed; if (ptr->prevfree ) ((struct dy_head *)(dymem + ptr->prevfree))->nextfree = off + needed; else dy_freestart = off + needed; if ( ptr->nextfree ) ((struct dy_head *)(dymem + ptr->nextfree))->prevfree = off + needed; } else { /* remove from freelist */ if ( ptr->prevfree ) ((struct dy_head *)(dymem + ptr->prevfree))->nextfree = ptr->nextfree; else dy_freestart = ptr->nextfree; if ( ptr->nextfree ) ((struct dy_head *)(dymem + ptr->nextfree))->prevfree = ptr->prevfree; ptr->nextfree = 0; ptr->prevfree = 0; } ptr->state = DY_USED; #ifdef _DEBUG dy_check(); #endif /* some handy debugging equivalences */ Globals = globals(0); for ( i = 0 ; i < NUMELEMENTS ; i++ ) Extras[i] = EXTRAS(i); return off + sizeof(struct dy_head); } /* if here, then no block big enough */ if ( memdebug ) erroutstring ("expanding dymem.\n"); ENTER_GRAPH_MUTEX dymem = kb_realloc(dymem,2*dymemsize); LEAVE_GRAPH_MUTEX lastptr = (struct dy_head *)(dymem + dy_lastblock); if ( lastptr->state == DY_FREE ) { /* combine with last block */ lastptr->size += dymemsize; } else { /* put in free block list at head */ ptr = (struct dy_head *)(dymem + dymemsize); ptr->size = dymemsize; ptr->state = DY_FREE; ptr->prev = dy_lastblock; ptr->next = 0; lastptr = (struct dy_head *)(dymem + dy_lastblock); dy_lastblock = dymemsize; lastptr->next = dymemsize; ptr->prevfree = 0; ptr->nextfree = dy_freestart; if ( dy_freestart ) ((struct dy_head *)(dymem+dy_freestart))->prevfree = dymemsize; dy_freestart = dymemsize; } dymemsize = 2*dymemsize; goto retry_alloc; } DY_OFFSET dy_realloc(old,newnum,newsize) DY_OFFSET old; int newnum; int newsize; { DY_OFFSET newspot; struct dy_head *ptr; int i; if ( memdebug ) { erroutstring("dy_realloc "); } ENTER_GRAPH_MUTEX newspot = dy_calloc(newnum,newsize); if ( old ) { ptr = (struct dy_head *)(dymem + old) - 1; if ( ptr->size < newnum*newsize ) memcpy(dymem+newspot,dymem+old,ptr->size); else memcpy(dymem+newspot,dymem+old,newnum*newsize); dy_free(old); } LEAVE_GRAPH_MUTEX /* some handy debugging equivalences */ Globals = globals(0); for ( i = 0 ; i < NUMELEMENTS ; i++ ) Extras[i] = EXTRAS(i); #ifdef _DEBUG dy_check(); #endif return newspot; } void dy_free(spot) DY_OFFSET spot; { DY_OFFSET off = spot - sizeof(struct dy_head); struct dy_head *ptr = (struct dy_head *)(dymem + off); struct dy_head *prevhead,*nexthead; if ( ptr->state != DY_USED ) kb_error(1366,"Internal error: Trying to dy_free unallocated block.\n", RECOVERABLE); memset(dymem+spot,0,ptr->size - dy_overhead); ptr->nextfree = dy_freestart; ptr->prevfree = 0; if ( dy_freestart ) ((struct dy_head *)(dymem + dy_freestart))->prevfree = off; dy_freestart = off; ptr->state = DY_FREE; /* see if can combine with nbrs */ /* check preceding block */ if ( ptr->prev ) { prevhead = (struct dy_head *)(dymem + ptr->prev); if ( prevhead->state == DY_FREE ) { /* combine */ prevhead->size += ptr->size; prevhead->next = ptr->next; if ( ptr->next ) { nexthead = (struct dy_head *)(dymem + ptr->next); nexthead->prev = ptr->prev; } if ( off == dy_lastblock ) dy_lastblock = ptr->prev; /* remove from freelist */ if ( ptr->nextfree ) ((struct dy_head*)(dymem+ptr->nextfree))->prevfree = ptr->prevfree; if ( ptr->prevfree ) ((struct dy_head*)(dymem+ptr->prevfree))->nextfree = ptr->nextfree; else dy_freestart = ptr->nextfree; memset((char*)ptr,0,sizeof(struct dy_head)); ptr = prevhead; /* ready for next stage of combining */ } } /* check following block */ if ( ptr->next ) { nexthead = (struct dy_head *)(dymem + ptr->next); if ( nexthead->state == DY_FREE ) { /* combine */ if ( ptr->next == dy_lastblock ) dy_lastblock = nexthead->prev; prevhead = ptr; ptr = nexthead; prevhead->size += ptr->size; prevhead->next = ptr->next; if ( ptr->next ) { nexthead = (struct dy_head *)(dymem + ptr->next); nexthead->prev = ptr->prev; } /* remove from freelist */ if ( ptr->nextfree ) ((struct dy_head*)(dymem+ptr->nextfree))->prevfree = ptr->prevfree; if ( ptr->prevfree ) ((struct dy_head*)(dymem+ptr->prevfree))->nextfree = ptr->nextfree; else dy_freestart = ptr->nextfree; memset((char*)ptr,0,sizeof(struct dy_head)); } } #ifdef _DEBUG dy_check(); #endif } void dy_check() { struct dy_head *head,*nexthead; int freeblocks = 0; int usedblocks = 0; int freebytes = 0; int usedbytes = 0; DY_OFFSET off; for ( off = dy_firstblock ; off != 0 ; off = head->next ) { head = (struct dy_head *)(dymem + off); switch ( head->state ) { case DY_FREE: freeblocks++; freebytes += head->size; break; case DY_USED: usedblocks++; usedbytes += head->size; break; default: kb_error(2468,"Internal error: Corrupt dy_mem; bad state.\n", RECOVERABLE); return; } if ( head->next ) { nexthead = (struct dy_head *)(dymem + head->next); if ( off != nexthead->prev ) kb_error(2469,"Internal error: Corrupt dy_mem; bad prev.\n",RECOVERABLE); } } if ( memdebug ) { sprintf(errmsg,"Dymem arena: %d\n",dymemsize); erroutstring(errmsg); sprintf(errmsg,"Dymem free blocks: %d free bytes: %d\n",freeblocks,freebytes); erroutstring(errmsg); sprintf(errmsg,"Dymem used blocks: %d used bytes: %d\n",usedblocks,usedbytes); erroutstring(errmsg); } /* check freelist integrity */ for ( off = dy_freestart ; (off != 0) && freeblocks ; off = head->nextfree ) { head = (struct dy_head *)(dymem + off); if ( head->state != DY_FREE ) kb_error(2555,"Internal error: Corrupt dy_mem nextfree.\n",RECOVERABLE); if ( head->nextfree ) { nexthead = (struct dy_head *)(dymem + head->nextfree); if ( off != nexthead->prevfree ) kb_error(2470,"Internal error: Corrupt dy_mem prevfree.\n",RECOVERABLE); } freeblocks--; } if ( freeblocks || (off != 0) ) kb_error(2471,"Internal error: Corrupt dy_mem free list.\n",RECOVERABLE); } /* end dy_check() */ /********* end of dynamic memory stuff **************************/ /************************************************************** * * Function: calc_edge() * * Purpose: Calculate the length of an edge from its endpoint * coordinates. Puts result into edge structure. * */ void calc_edge(e_id) edge_id e_id; { REAL len=0.0; REAL s[MAXCOORD]; REAL midx[MAXCOORD]; int i,j,k; vertex_id tv = get_edge_tailv(e_id); REAL *xt=get_coord(tv); REAL euclidean; if ( everything_quantities_flag ) { instance_attribute(e_id,length_method_number); /* legitimate methods set edge length */ return; } get_edge_side(e_id,s); euclidean = SDIM_dot(s,s); if ( klein_metric_flag ) { for ( i = 0 ; i < SDIM ; i++ ) midx[i] = xt[i] + s[i];/*unwrap head*/ len = klein_length(xt,midx); } else if ( web.metric_flag ) { /* energy due to linear tension, metric evaluated at midpoint */ for ( k = 0, len = 0.0 ; k < gauss1D_num ; k++ ) { REAL sum; for ( i = 0 ; i < SDIM ; i++ ) midx[i] = gauss1Dpt[k]*s[i] + xt[i]; if ( web.conformal_flag ) { REAL gg = eval(&web.metric[0][0],midx,NULLID,NULL); len += gauss1Dwt[k]*sqrt(gg*euclidean); } else { for ( sum = 0.0, i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) sum += s[i]*s[j]*eval(&web.metric[i][j],midx,NULLID,NULL); len += gauss1Dwt[k]*sqrt(sum); } } } else { switch (web.modeltype) { case LINEAR: len = sqrt(euclidean); break; case QUADRATIC: { MAT2D(x,3,MAXCOORD); REAL tang[MAXCOORD]; int m; len = 0; get_edge_verts(e_id,x,NULL); for ( m = 0 ; m < gauss1D_num ; m++ ) { for ( j = 0 ; j < SDIM ; j ++ ) { tang[j] = 0.0; for ( k = 0 ; k < edge_ctrl ; k++ ) tang[j] += gauss1polyd[k][m]*x[k][j]; } len += gauss1Dwt[m]*sqrt(SDIM_dot(tang,tang)); } } break; case LAGRANGE: { /* tangent vectors at gauss points */ int m; MAT2D(x,20,MAXCOORD); REAL tang[MAXCOORD]; int ctrl = web.skel[EDGE].ctrlpts; struct gauss_lag *gl = &gauss_lagrange[1][web.gauss1D_order]; len = 0.0; get_edge_verts(e_id,x,NULL); for ( m = 0 ; m < gl->gnumpts ; m++ ) { vec_mat_mul(gl->gpolypart[m][0],x,tang,ctrl,SDIM); len += gl->gausswt[m]*sqrt(SDIM_dot(tang,tang)); } } break; } /* end switch */ } set_edge_length(e_id,len); } /************************************************************ * * get_edge_side() * * Purpose: calculate the vector of an edge * * Input: edge_id e_id - specifies edge * REAL *x; - 3 components of vector * * Output: x[] has side components */ void get_edge_side(e_id,x) edge_id e_id; REAL *x; { REAL *y,*z; int i; REAL w[MAXCOORD]; y = get_coord(get_edge_tailv(e_id)); z = get_coord(get_edge_headv(e_id)); if ( web.symmetry_flag ) { (*sym_wrap)(z,w,get_edge_wrap(e_id)); z = w; } for ( i = 0 ; i < SDIM ; i++ ) x[i] = z[i] - y[i]; } /************************************************************ * * get_edge_tail_tangent() * * Purpose: Calculate the tangent vector of an edge at the tail. * Magnitude of tangent equal to length, pretty much. * * Input: edge_id e_id - specifies edge * REAL *x; - 3 components of vector * * Output: x[] has side components */ void get_edge_tail_tangent(e_id,x) edge_id e_id; REAL *x; { int i,n; REAL w[MAXCOORD],ww[MAXCOORD]; if ( web.lagrange_order == 1 ) { REAL *y,*z; y = get_coord(get_edge_tailv(e_id)); z = get_coord(get_edge_headv(e_id)); if ( web.symmetry_flag ) { (*sym_wrap)(z,w,get_edge_wrap(e_id)); z = w; } for ( i = 0 ; i < SDIM ; i++ ) x[i] = z[i] - y[i]; return; } if ( web.modeltype == QUADRATIC ) { vertex_id v[3]; REAL *xx[3]; v[0] = get_edge_tailv(e_id); v[1] = get_edge_midv(e_id); v[2] = get_edge_headv(e_id); xx[0] = get_coord(v[0]); xx[1] = get_coord(v[1]); xx[2] = get_coord(v[2]); if ( web.symmetry_flag ) { WRAPTYPE wrap = get_edge_wrap(e_id); (*sym_wrap)(xx[2],w,wrap); xx[2] = w; if ( inverted(e_id) ) { (*sym_wrap)(xx[1],ww,wrap); xx[1] = ww; } } if ( circular_arc_flag ) { REAL dx1 = xx[1][0] - xx[0][0]; REAL dx2 = xx[2][0] - xx[1][0]; REAL dy1 = xx[1][1] - xx[0][1]; REAL dy2 = xx[2][1] - xx[1][1]; REAL denom1 = sqrt(dx1*dx1+dy1*dy1); REAL denom2 = sqrt(dx2*dx2+dy2*dy2); REAL chord,sinth,costh,angle,length; if (denom1*denom2 == 0.0) {x[0] = x[1] = 0.0; return ;} chord = sqrt((dx1+dx2)*(dx1+dx2)+(dy1+dy2)*(dy1+dy2)); sinth = (dx1*dy2 - dy1*dx2)/denom1/denom2; costh = (dx1*dx2 + dy1*dy2)/denom1/denom2; angle = atan2(sinth,costh); if ( sinth == 0.0 ) { for ( i = 0 ; i < SDIM ; i++ ) x[i] = xx[2][i] - xx[0][i]; return; } length = chord*angle/sinth; x[0] = length*(costh*(dx1+dx2) + sinth*(dy1+dy2))/chord; x[1] = length*(costh*(dy1+dy2) - sinth*(dx1+dx2))/chord; return; } else { /* regular quadratic */ for ( i = 0 ; i < SDIM ; i++ ) x[i] = -3*xx[0][i] + 4*xx[1][i] - xx[2][i]; } return; } if ( web.modeltype == LAGRANGE ) { struct gauss_lag *gl = &gauss_lagrange[1][web.gauss1D_order]; int ctrl = web.skel[EDGE].ctrlpts; int spot = inverted(e_id) ? ctrl-1 : 0; vertex_id *v; REAL *xx[MAXLAGRANGE+1]; v = get_edge_vertices(e_id); for ( i = 0 ; i < ctrl ; i++ ) xx[i] = get_coord(v[i]); if ( web.symmetry_flag ) { WRAPTYPE wrap = get_edge_wrap(e_id); (*sym_wrap)(xx[ctrl-1],w,wrap); xx[ctrl-1] = w; } for ( i = 0 ; i < SDIM ; i++ ) { x[i] = 0; for ( n = 0 ; n < ctrl ; n++ ) x[i] += gl->lpolypart[spot][0][n]*xx[n][i]; x[i] *= ctrl; /* scaling to length of edge */ if ( inverted(e_id) ) x[i] = -x[i]; } } } /************************************************************* * * function: get_edge_adjust() * * purpose: adjust a side vector for torus wrap. * used only in torvol.c. * */ void get_edge_adjust(e_id,w) edge_id e_id; REAL *w; { REAL x[MAXCOORD]; int i ; for ( i = 0 ; i < SDIM ; i++ ) x[i] = 0.0; (*sym_wrap)(x,w,get_edge_wrap(e_id)); } /*************************************************************** * * Function: get_vertex_evalence * * Purpose: return number of edges around vertex * */ int get_vertex_evalence(v_id) vertex_id v_id; { int n = 0; edge_id e_id; edge_id next_e; int attr = get_vattr(v_id); if ( attr & (Q_MIDPOINT|Q_MIDEDGE) ) return 1; e_id = get_vertex_edge(v_id); if ( !valid_element(e_id) ) return 0; next_e = e_id; do { n++; next_e = get_next_tail_edge(next_e); if ( n > 2*web.skel[EDGE].count ) { sprintf(errmsg, "Internal error: vertex_evalence() in infinite loop on vertex %s.\n", ELNAME(v_id)); kb_error(2482,errmsg,RECOVERABLE); } } while ( !equal_id(next_e,e_id) ); return n; } /*************************************************************** * * Function: get_edge_valence * * Purpose: return number of facets around edge * */ int get_edge_valence(e_id) edge_id e_id; { int n = 0,m=0; facetedge_id fe = get_edge_fe(e_id); facetedge_id next_fe = fe; if ( !valid_id(fe) ) return 0; do { if ( valid_id(get_fe_facet(fe)) ) n++; next_fe = get_next_facet(next_fe); m++; /* infinite loop preventer */ if ( m > web.skel[FACETEDGE].count ) { sprintf(errmsg, "Internal error: edge_valence() in infinite loop on edge %s.\n", ELNAME(e_id)); kb_error(2480,errmsg,RECOVERABLE); } } while ( next_fe != fe ); return n; } /*************************************************************** * * Function: get_facet_valence * * Purpose: return number of edges around facet * */ int get_facet_valence(f_id) facet_id f_id; { int n = 0; facetedge_id fe = get_facet_fe(f_id); facetedge_id next_fe = fe; if ( !valid_id(fe) ) return 0; do { n++; next_fe = get_next_edge(next_fe); if ( n > 2*web.skel[EDGE].count ) { sprintf(errmsg, "Internal error: facet_valence() in infinite loop on facet %s.\n", ELNAME(f_id)); kb_error(2481,errmsg,RECOVERABLE); } } while ( valid_id(next_fe) && (next_fe != fe) ); return n; } /*************************************************************** * * Function: get_body_valence * * Purpose: return number of facets around a body * */ int get_body_valence(b_id) body_id b_id; { int n = 0; facet_id f_id = get_body_facet(b_id); facet_id next_f = f_id; if ( !valid_id(f_id) ) return 0; if ( web.representation == STRING ) return get_facet_valence(f_id); /* if ( bfacet_timestamp < top_timestamp ) { make_bfacet_lists(); bfacet_timestamp = top_timestamp; } */ do { n++; next_f = get_next_body_facet(next_f); } while ( (next_f != f_id) && (n < 3*web.skel[FACET].count) ); return n; } /********************************************************************* * * function: set_facet_vertices() * * purpose: Fill in vertex lists of facets. */ void set_facet_vertices ARGS((void)); void set_facet_vertices() { /* have to make expandable v list for quadratic mode */ } /**************************************************************** * * function: begin_normal_motion() * * purpose: calculates vertex normals as volume gradients, normalized to * unit length. * */ void begin_normal_motion() { vertex_id v_id; facetedge_id fe; MAT2D(normals,MAXCOORD,MAXCOORD); int i; pt_type *vn; int normcount; if ( vertex_normals != NULL ) myfree((char*)vertex_normals); vertex_normals = (pt_type *)mycalloc( web.skel[VERTEX].max_ord+1,sizeof(pt_type)); FOR_ALL_VERTICES(v_id) { REAL mag; fe = get_vertex_fe(v_id); if ( !valid_id(fe) ) continue; normcount = new_calc_vertex_normal(v_id,normals); project_vertex_normals(v_id,normals,normcount); vn = vertex_normals + loc_ordinal(v_id); mag = sqrt(dot(normals[0],normals[0],SDIM)); if ( mag != 0.0 ) for ( i = 0 ; i < SDIM ; i++ ) (*vn)[i] = normals[0][i]/mag; } normal_motion_flag = 1; } /***************************************************************** * * function: end_normal_motion() * * purpose: free memory used in normal motion mode * */ void end_normal_motion() { if ( vertex_normals != NULL ) myfree((char*)vertex_normals); vertex_normals = NULL; normal_motion_flag = 0; } /*********************************************************** * * Function: vertex_total_vol_grad() * * Purpose: Calculate the gradient of combined body volume at a vertex. * Meant for triple lines and such in surface diffusion. * * Input: vertex id * norm - pointer to place for normal vector * * * Output: Average normal, normalized to unit length. * * Return value: Length of original normal, i.e. volume gradient. */ REAL vertex_total_vol_grad(v_id,norm) vertex_id v_id; REAL *norm; { int i,j; REAL y[MAXCOORD],z[MAXCOORD]; REAL fnorm[MAXCOORD]; facetedge_id fe_id,first_fe; REAL size; /* check for string */ if ( web.representation == STRING ) { edge_id e_id,start_e; norm[0] = norm[1] = 0.0; start_e = e_id = get_vertex_edge(v_id); do { facetedge_id fe,fe2; facet_id f_id; get_edge_side(e_id,y); fe = get_edge_fe(e_id); if ( valid_id(fe) ) { fe2 = get_next_facet(fe); if ( equal_id(fe,fe2) ) { /* valence 1, so count it according to facet orientation */ f_id = get_fe_facet(fe); if ( inverted(f_id) ) { norm[0] -= y[1]; norm[1] += y[0]; } else { norm[0] += y[1]; norm[1] -= y[0]; } } } e_id = get_next_tail_edge(e_id); } while ( !equal_id(e_id,start_e) ); goto normalize; } /* Get here only for soapfilm model. */ first_fe = fe_id = get_vertex_first_facet(v_id); for ( i = 0 ; i < SDIM ; i++ ) norm[i] = 0.0; do { facet_id f_id = get_fe_facet(fe_id); body_id b_id = get_facet_body(f_id); body_id bb_id = get_facet_body(invert(f_id)); if ( valid_id(b_id) != valid_id(bb_id) ) { /* one-sided, so count it */ get_fe_side(fe_id,z); get_fe_side(get_prev_edge(fe_id),y); cross_prod(y,z,fnorm); if ( valid_id(b_id) ) for ( i = 0 ; i < SDIM ; i++ ) norm[i] += fnorm[i]; else for ( i = 0 ; i < SDIM ; i++ ) norm[i] -= fnorm[i]; } fe_id = get_next_vertex_facet(v_id,fe_id); } while ( !equal_id(fe_id,first_fe) ); normalize: if ( get_vattr(v_id) & CONSTRAINT ) { conmap_t * conmap = get_v_constraint_map(v_id); int oncount = 0,hitcount; struct constraint *con[MAXCONPER]; int conlist[MAXCONPER]; REAL perp[MAXCOORD]; for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { if ( conmap[j] & CON_HIT_BIT ) { conlist[oncount] = conmap[j]; con[oncount++] = get_constraint(conmap[j]); } } hitcount = constr_proj(TANGPROJ,oncount,con,get_coord(v_id), norm,perp,conlist,DETECT,v_id); if ( hitcount != oncount ) { clear_v_constraint_status(v_id); for ( j = 0 ; j < hitcount ; j++ ) set_v_constraint_status(v_id,conlist[j]); } for ( j = 0 ; j < SDIM ; j++ ) norm[j] -= perp[j]; } size = sqrt(SDIM_dot(norm,norm)); if ( size <= 0.0 ) return 0.0; for ( i = 0 ; i < SDIM ; i++ ) norm[i] /= size; return size/web.simplex_factorial; } /*********************************************************** * * Function: calc_vertex_normal * * Purpose: Calculate average normal of facets around a vertex. * Only does the facets * in the same face as given facet; will not cross * multiple edges. * * Input: vertex id * facet-edge id - with tail at vertex, for defining face * norm - pointer to place for normal vector * * * Output: Average normal, normalized to unit length. * * Return value: Length of original normal, i.e. volume gradient. */ REAL calc_vertex_normal(v_id,fe,norm) vertex_id v_id; facetedge_id fe; REAL *norm; { int i,j; REAL y[MAXCOORD],z[MAXCOORD]; REAL fnorm[MAXCOORD]; facetedge_id sidea; struct boundary *bdry; /* to check staying on same boundary */ REAL size; /* check for string */ if ( web.representation == STRING ) { edge_id e_id,ee_id; e_id = get_vertex_edge(v_id); ee_id = get_next_tail_edge(e_id); get_edge_side(e_id,z); get_edge_side(ee_id,y); if ( equal_id(e_id,ee_id) ) { norm[0] = y[1]; norm[1] = -y[0]; } else { norm[0] = y[1] - z[1]; norm[1] = z[0] - y[0]; } if (inverted(e_id)) { norm[0] = -norm[0]; norm[1] = -norm[1];} goto normalize; } if ( !valid_id(fe) ) { fe = get_vertex_fe(v_id); if ( !valid_id(fe) ) { for ( i = 0 ; i < SDIM ; i++ ) norm[i] = 0.0; return 0.0; } } for ( i = 0 ; i < SDIM ; i++ ) norm[i] = 0.0; bdry = get_facet_boundary(get_fe_facet(fe)); /* original boundary */ /* go around one way to edge of face */ sidea = fe; do { get_fe_side(sidea,z); get_fe_side(get_prev_edge(sidea),y); cross_prod(y,z,fnorm); for ( i = 0 ; i < SDIM ; i++ ) norm[i] += fnorm[i]; /* go to next facet */ sidea = get_prev_edge(sidea); if ( equal_id(sidea,get_next_facet(sidea)) ) break; /* edge */ sidea = fe_inverse(get_next_facet(sidea)); if ( bdry != get_facet_boundary(get_fe_facet(sidea)) ) break; if ( equal_id(sidea,fe) ) goto onormalize; /* went all the way around */ } while ( equal_id(get_next_facet(sidea),get_prev_facet(sidea)) ); /* go around the other way */ sidea = fe; while ( equal_id(get_next_facet(sidea),get_prev_facet(sidea)) ) { if ( equal_id(sidea,get_next_facet(sidea)) ) break; /* edge */ sidea = fe_inverse(get_next_facet(sidea)); sidea = get_next_edge(sidea); if ( bdry != get_facet_boundary(get_fe_facet(sidea)) ) break; get_fe_side(sidea,z); get_fe_side(get_prev_edge(sidea),y); cross_prod(y,z,fnorm); for ( i = 0 ; i < SDIM ; i++ ) norm[i] += fnorm[i]; } /* orient */ onormalize: if ( inverted(get_fe_facet(fe)) ) for ( i = 0 ; i < SDIM ; i++ ) norm[i] = -norm[i]; normalize: if ( get_vattr(v_id) & CONSTRAINT ) { conmap_t * conmap = get_v_constraint_map(v_id); int oncount = 0,hitcount; struct constraint *con[MAXCONPER]; int conlist[MAXCONPER]; REAL perp[MAXCOORD]; for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { if ( conmap[j] & CON_HIT_BIT ) { conlist[oncount] = conmap[j]; con[oncount++] = get_constraint(conmap[j]); } } hitcount = constr_proj(TANGPROJ,oncount,con,get_coord(v_id), norm,perp,conlist,DETECT,v_id); if ( hitcount != oncount ) { clear_v_constraint_status(v_id); for ( j = 0 ; j < hitcount ; j++ ) set_v_constraint_status(v_id,conlist[j]); } for ( j = 0 ; j < SDIM ; j++ ) norm[j] -= perp[j]; } size = sqrt(SDIM_dot(norm,norm)); if ( size <= 0.0 ) return 0.0; for ( i = 0 ; i < SDIM ; i++ ) norm[i] /= size; return size/web.simplex_factorial; } /*********************************************************** * * Function: calc_vertex_smooth_normal * * Purpose: Calculate average normal of facets around a vertex * for grapics normal interpolation. Only does the facets * in the same face as given facet; will not cross * multiple edges, or boundary or constraint edges * when different from facet. Only uses first 3 dimensions. * Does not project normal to constraints. * * Input: vertex id * facet-edge id - with tail at vertex, for defining face * norm - pointer to place for normal vector * * * Output: Average normal, normalized to unit length. * * Return value: none. */ void calc_vertex_smooth_normal(v_id,fe,norm) vertex_id v_id; facetedge_id fe; REAL *norm; { int i; REAL y[MAXCOORD],z[MAXCOORD]; REAL fnorm[MAXCOORD]; facetedge_id sidea; facet_id f_id = get_fe_facet(fe); struct boundary *bdry; /* to check staying on same boundary */ REAL size; /* check for string */ if ( web.representation != SOAPFILM ) { kb_error(2208,"Internal error: Using calc_vertex_smooth_normal() on non-soapfilm surface.\n", RECOVERABLE); } if ( !valid_id(fe) ) kb_error(2209,"Internal error: Invalid fe in calc_vertex_smooth_normal().\n",RECOVERABLE); bdry = get_facet_boundary(f_id); /* original boundary */ get_facet_normal(f_id,norm); /* starter normal */ /* go around one way to edge of face */ sidea = fe; for(;;) { edge_id e_id; /* go to next facet */ sidea = get_prev_edge(sidea); if ( equal_id(sidea,get_next_facet(sidea)) ) break; /* valence 1 */ if ( !equal_id(get_next_facet(sidea),get_prev_facet(sidea)) ) break; e_id = get_fe_edge(sidea); if ( !equal_constr(f_id,e_id) ) break; if ( bdry != get_edge_boundary(e_id) ) break; sidea = fe_inverse(get_next_facet(sidea)); if ( bdry != get_facet_boundary(get_fe_facet(sidea)) ) break; if ( equal_id(sidea,fe) ) goto snormalize; /* went all the way around */ get_fe_side(sidea,z); get_fe_side(get_prev_edge(sidea),y); cross_prod(y,z,fnorm); for ( i = 0 ; i < SDIM ; i++ ) norm[i] += fnorm[i]/2; } /* go around the other way */ sidea = fe; while ( equal_id(get_next_facet(sidea),get_prev_facet(sidea)) ) { edge_id e_id; if ( equal_id(sidea,get_next_facet(sidea)) ) break; /* edge */ e_id = get_fe_edge(sidea); if ( !equal_constr(f_id,e_id) ) break; if ( bdry != get_edge_boundary(e_id) ) break; sidea = fe_inverse(get_next_facet(sidea)); sidea = get_next_edge(sidea); if ( bdry != get_facet_boundary(get_fe_facet(sidea)) ) break; get_fe_side(sidea,z); get_fe_side(get_prev_edge(sidea),y); cross_prod(y,z,fnorm); for ( i = 0 ; i < SDIM ; i++ ) norm[i] += fnorm[i]/2; } snormalize: size = sqrt(SDIM_dot(norm,norm)); if ( size <= 0.0 ) return; for ( i = 0 ; i < SDIM ; i++ ) norm[i] /= size; } /*************************************************************************** * * function: lagrange_edge_normal() * * purpose: compute geometric normal space at a lagrange point of an edge. */ int lagrange_edge_normal ARGS((vertex_id,edge_id,REAL**)); int lagrange_edge_normal(v_id,e_id,norm) vertex_id v_id; edge_id e_id; REAL **norm; { MAT2D(vx,MAXVCOUNT,MAXCOORD); struct gauss_lag *gl = &gauss_lagrange[1][web.gauss1D_order]; vertex_id *v = get_edge_vertices(e_id); int k,m,i; int ctrl = web.skel[EDGE].ctrlpts; REAL tang[MAXCOORD],*t=tang; get_edge_verts(e_id,vx,NULL); for ( k = 0 ; k < ctrl ; k++ ) if ( v[k] == v_id ) break; #ifdef VOLUMEGRAD /* volume gradient */ norm[0] = norm[1] = 0.0; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL y,dx; for ( y = 0.0, dx = 0.0, j = 0 ; j < ctrl ; j++ ) { y += gl->gpoly[m][j]*vx[j][1]; dx += gl->gpolypart[m][0][j]*vx[j][0]; } norm[0] -= gl->gausswt[m]*y*gl->gpolypart[m][0][k]; norm[1] -= gl->gausswt[m]*dx*gl->gpoly[m][k]; } #else /* geometric normal */ for ( i = 0 ; i < SDIM ; i++ ) tang[i] = 0.0; for ( m = 0 ; m < ctrl ; m++ ) { REAL prime; prime = gl->lpolypart[k][0][m]; for ( i = 0 ; i < SDIM ; i++ ) tang[i] += prime*vx[m][i]; } return kernel_basis_rows(&t,norm,1,SDIM); #endif } /*************************************************************************** * * function: lagrange_facet_normal() * * purpose: compute geometric normal at a lagrange point of a facet. */ int lagrange_facet_normal ARGS((vertex_id,facet_id,REAL**)); int lagrange_facet_normal(v_id,f_id, norm) vertex_id v_id; facet_id f_id; REAL **norm; { vertex_id *v = get_facet_vertices(f_id); struct gauss_lag *gl = &gauss_lagrange[web.dimension][web.gauss2D_order]; MAT2D(sides,MAXCOORD,MAXCOORD); MAT2D(vx,MAXVCOUNT,MAXCOORD); int ctrl = web.skel[FACET].ctrlpts; int i,j,k,kk; get_facet_verts(f_id,vx,NULL); for ( k = 0 ; k < ctrl ; k++ ) if ( v[k] == v_id ) break; if ( k >= ctrl ) { sprintf(errmsg, "Internal error: Can't find vertex %s in facet %s.\n",ELNAME(v_id), ELNAME1(f_id)); kb_error(2210,errmsg,RECOVERABLE); } for ( j = 0 ; j < SDIM ; j++ ) sides[0][j] = 0.0; for ( i = 1 ; i <= web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( kk = 0, sides[i][j] = 0.0 ; kk < ctrl ; kk++ ) sides[i][j] += gl->lpolypart[k][i-1][kk]*vx[kk][j]; if ( inverted(f_id) ) for ( j = 0 ; j < SDIM ; j++ ) sides[1][j] = -sides[1][j]; return kernel_basis_rows(sides+1,norm,web.dimension,SDIM); } /*************************************************************************** * * function: quadratic_edge_normal() * * purpose: compute geometric normal space at a point of a quadratic edge. */ REAL quad_tang_coeffs[3][3] = { { -1.5,2.0,-0.5}, {-0.5,0.0,0.5},{ 0.5,-2.0,1.5}}; int quadratic_edge_normal ARGS((vertex_id,edge_id,REAL**)); int quadratic_edge_normal(v_id,e_id,norm) vertex_id v_id; edge_id e_id; REAL **norm; { MAT2D(vx,MAXVCOUNT,MAXCOORD); vertex_id *v = get_edge_vertices(e_id); int k,m,i; int ctrl = web.skel[EDGE].ctrlpts; /* should be 3 */ REAL tang[MAXCOORD],*t=tang; get_edge_verts(e_id,vx,NULL); /* has midv in vx[1] */ for ( k = 0 ; k < ctrl ; k++ ) if ( v[k] == v_id ) break; if ( k != 0 ) k = 3 - k; /* switch vertices 1 and 2 compared to lagrange */ /* geometric normal */ for ( i = 0 ; i < SDIM ; i++ ) tang[i] = 0.0; for ( m = 0 ; m < ctrl ; m++ ) { REAL prime; prime = quad_tang_coeffs[k][m]; for ( i = 0 ; i < SDIM ; i++ ) tang[i] += prime*vx[m][i]; } return kernel_basis_rows(&t,norm,1,SDIM); } /*************************************************************************** * * function: quadratic_facet_normal() * * purpose: compute geometric normal at a lagrange point of a facet. */ int qlconvert[6] = { 0, 1, 2, 4, 5, 3}; /* index conversion */ int lqconvert[6] = { 0, 1, 2, 5, 3, 4}; /* index conversion */ int quadratic_facet_normal ARGS((vertex_id,facet_id,REAL**)); int quadratic_facet_normal(v_id,f_id, norm) vertex_id v_id; facet_id f_id; REAL **norm; { vertex_id v[6]; struct gauss_lag *gl = &gauss_lagrange[web.dimension][web.gauss2D_order]; MAT2D(sides,MAXCOORD,MAXCOORD); MAT2D(vx,MAXVCOUNT,MAXCOORD); int ctrl = web.skel[FACET].ctrlpts; int i,j,k,kk; facetedge_id fe_id; fe_id = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { v[qlconvert[2*i]] = get_fe_tailv(fe_id); v[qlconvert[2*i+1]] = get_fe_midv(fe_id); fe_id = get_next_edge(fe_id); } get_facet_verts(f_id,vx,NULL); for ( k = 0 ; k < ctrl ; k++ ) if ( v[k] == v_id ) break; if ( k >= ctrl ) { sprintf(errmsg,"Internal error: Can't find vertex %s in facet %s.\n", ELNAME(v_id), ELNAME1(f_id)); kb_error(2211,errmsg,RECOVERABLE); } for ( j = 0 ; j < SDIM ; j++ ) sides[0][j] = 0.0; for ( i = 1 ; i <= web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( kk = 0, sides[i][j] = 0.0 ; kk < ctrl ; kk++ ) sides[i][j] += gl->lpolypart[k][i-1][kk]*vx[lqconvert[kk]][j]; return kernel_basis_rows(sides+1,norm,web.dimension,SDIM); } /*********************************************************** * * Function: new_calc_vertex_normal * * Purpose: Calculate subspace normal to surface for purposes * of Hessian normal or normal motion. Normal space * is essentially that spanned by the volume normals * of all adjacent bodies. Idea is to rule out * purely tangential motions. * * Input: vertex id * norm - pointer to place for normal basis * * * Output: Basis for normal space if not full dimension * Return value: Dimension of normal */ int new_calc_vertex_normal(v_id,norm) vertex_id v_id; REAL **norm; /* returned basis */ { int i,j,kk; REAL y[MAXCOORD],z[MAXCOORD]; MAT2D(fnorm,MAXCOORD,MAXCOORD); MAT2D(a,2,MAXCOORD); facetedge_id sidea,sideb,ffe; struct boundary *bdry; /* to check staying on same boundary */ REAL size; int retval = 0; facetedge_id fe; int valence,bares=0,singles=0,doubles=0,triples=0,bareflag=0; int vattr; if (web.representation == SIMPLEX ) return simplex_vertex_normal(v_id,norm); vattr = get_vattr(v_id); if ( hessian_special_normal_flag ) { REAL *x = get_coord(v_id); for ( i = 0 ; i < SDIM ; i++ ) norm[0][i] = eval(hessian_special_normal_expr+i,x,v_id,NULL); size = sqrt(SDIM_dot(norm[0],norm[0])); if ( size == 0.0 ) { sprintf(errmsg, "hessian_special_normal_vector is zero at vertex %s.\n", ELNAME(v_id)); kb_error(2212,errmsg,RECOVERABLE); } for ( i = 0 ; i < SDIM ; i++ ) norm[0][i] = norm[0][i]/size; return 1; /* one-dimensional normal only */ } if ( hessian_normal_one_flag ) { MAT2D(mat,MAXCOORD,MAXCOORD); facet_id f_id; edge_id e_id, starte; for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) mat[i][j] = 0.0; if ( web.representation == SOAPFILM ) { facetedge_id fe_id,startfe; fe_id = startfe = get_vertex_first_facet(v_id); do { f_id = get_fe_facet(fe_id); get_facet_normal(f_id,fnorm[0]); size = SDIM_dot(fnorm[0],fnorm[0]); for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) mat[i][j] += fnorm[0][i]*fnorm[0][j]/size; fe_id = get_next_vertex_facet(v_id,fe_id); } while ( !equal_element(fe_id,startfe) ); } else if ( web.representation == STRING ) { e_id = starte = get_vertex_edge(v_id); do { get_edge_side(e_id,fnorm[0]); mat[0][0] += fnorm[0][1]*fnorm[0][1]; mat[0][1] -= fnorm[0][1]*fnorm[0][0]; mat[1][0] -= fnorm[0][0]*fnorm[0][1]; mat[1][1] += fnorm[0][0]*fnorm[0][0]; e_id = get_next_tail_edge(e_id); } while ( !equal_element(e_id,starte) ); } /* find eigenvector */ for ( kk = 0 ; kk < 10 ; kk++ ) { matvec_mul(mat,fnorm[0],y,SDIM,SDIM); matvec_mul(mat,y,fnorm[0],SDIM,SDIM); } size = sqrt(SDIM_dot(fnorm[0],fnorm[0])); for ( i = 0 ; i < SDIM ; i++ ) norm[0][i] = fnorm[0][i]/size; return 1; } /* end hessian_normal_one_flag */ if ( vattr & Q_MIDFACET ) { /* Lagrange model */ facet_id f_id = get_vertex_facet(v_id); retval = lagrange_facet_normal(v_id,f_id,norm); goto ncn_exit; } /* Want to check for bare edges in SOAPFILM model */ /* so can handle same way as string edges */ if ( web.representation == SOAPFILM ) { edge_id e_id,start_e; e_id = get_vertex_edge(v_id); if ( !valid_id(e_id) ) goto full_dim; /* bare point */ start_e = e_id; do { valence = get_edge_valence(e_id); if ( valence == 0 ) bares++; if ( valence == 1 ) singles++; if ( valence == 2 ) doubles++; if ( valence >= 3 ) triples++; if ( vattr & (Q_MIDPOINT|Q_MIDEDGE) ) break; e_id = get_next_tail_edge(e_id); } while ( !equal_id(e_id,start_e) ); if ( triples >= SDIM ) goto full_dim; if ( singles >= SDIM ) goto full_dim; if ( bares >= SDIM ) goto full_dim; if ( bares && (singles+doubles+triples) ) goto full_dim; if ( triples >= 1 ) { if ( vattr & (Q_MIDPOINT|Q_MIDEDGE) ) bareflag = 1; /* effectively */ else goto tripletest; } if ( (singles==0) && (doubles==0) ) bareflag = 1; } if ( (vattr & Q_MIDEDGE) && (bareflag || (web.representation == STRING)) ) { edge_id e_id = get_vertex_edge(v_id); retval = lagrange_edge_normal(v_id,e_id,norm); goto ncn_exit; } if ( (vattr & Q_MIDPOINT) && (bareflag || (web.representation == STRING)) ) { edge_id e_id = get_vertex_edge(v_id); retval = quadratic_edge_normal(v_id,e_id,norm); goto ncn_exit; } /* check for string */ if ( web.representation == STRING ) { /* Must be endpoint of string edge */ edge_id e_id,ee_id,eee_id; e_id = get_vertex_edge(v_id); if ( !valid_id(e_id) ) goto full_dim; ee_id = get_next_tail_edge(e_id); eee_id = get_next_tail_edge(ee_id); if ( !equal_id(e_id,eee_id) ) goto full_dim; /* triple vertex at least */ if ( (web.modeltype == LAGRANGE) && (web.lagrange_order >= 2) ) retval = lagrange_edge_normal(v_id,e_id,norm); else if ( web.modeltype == QUADRATIC ) { MAT2D(vx,3,MAXCOORD); REAL *t = y; get_edge_verts(e_id,vx,NULL); for ( i = 0 ; i < SDIM ; i++ ) y[i] = -1.5*vx[0][i] + 2*vx[1][i] - 0.5*vx[2][i]; retval = kernel_basis_rows(&t,norm,1,SDIM); } else { REAL *t = y; get_edge_side(e_id,y); if ( !equal_id(e_id,ee_id ) ) { get_edge_side(ee_id,z); for ( i = 0 ; i < SDIM ; i++ ) y[i] -= z[i]; } retval = kernel_basis_rows(&t,norm,1,SDIM); } if ( inverted(e_id) ) for ( i = 0 ; i < SDIM ; i++ ) norm[0][i] = -norm[0][i]; goto ncn_exit; } /* now have 2D facet */ fe = get_vertex_fe(v_id); if ( !valid_id(fe) ) goto full_dim; bdry = get_facet_boundary(get_fe_facet(fe)); /* original boundary */ sidea = fe; if ( vattr & Q_MIDPOINT ) { /* quadratic model */ /* already tested for bare or triple edges, so one or two facets */ /* If two, combine normals */ MAT2D(norm2,MAXCOORD,MAXCOORD); facet_id f_id = get_fe_facet(sidea); if ( inverted(f_id) ) sidea = inverse_id(sidea); if ( !equal_id(get_next_facet(sidea),get_prev_facet(sidea)) ) goto tripletest; retval = quadratic_facet_normal(v_id,get_fe_facet(sidea),norm); sideb = get_next_facet(sidea); if ( equal_id(sidea,sideb) ) goto ncn_exit; sideb = inverse_id(sideb); retval = quadratic_facet_normal(v_id,get_fe_facet(sideb),norm2); for ( i = 0 ; i < retval ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) norm[i][j] += norm2[i][j]; goto ncn_exit; } if ( vattr & Q_MIDEDGE ) { /* Lagrange model */ MAT2D(norm2,MAXCOORD,MAXCOORD); facet_id f_id = get_fe_facet(sidea); if ( inverted(f_id) ) sidea = inverse_id(sidea); if ( !equal_id(get_next_facet(sidea),get_prev_facet(sidea)) ) goto tripletest; retval = lagrange_facet_normal(v_id,get_fe_facet(sidea),norm); sideb = get_next_facet(sidea); if ( equal_id(sidea,sideb) ) goto ncn_exit; sideb = inverse_id(sideb); retval = lagrange_facet_normal(v_id,get_fe_facet(sideb),norm2); for ( i = 0 ; i < retval ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) norm[i][j] += norm2[i][j]; goto ncn_exit; } /* usual case of facet in N dim */ /* if get here, know no triple lines */ { for ( j = 0 ; j < SDIM ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) norm[j][i] = 0.0; /* go around one way to edge of face */ do { if ( vattr & AXIAL_POINT ) { get_fe_side(sidea,z); get_fe_side(get_next_edge(sidea),y); cross_prod(y,z,fnorm[1]); retval = 1; } else { switch ( web.modeltype ) { case LINEAR: get_fe_side(sidea,a[0]); get_fe_side(inverse_id(get_prev_edge(sidea)),a[1]); if ( SDIM == 3 ) { cross_prod(a[0],a[1],fnorm[0]); retval = 1; } else retval = kernel_basis_rows(a,fnorm,2,SDIM); break; case QUADRATIC: retval = quadratic_facet_normal(v_id,get_fe_facet(sidea),fnorm); break; case LAGRANGE: retval = lagrange_facet_normal(v_id,get_fe_facet(sidea),fnorm); break; } } for ( j = 0 ; j < retval ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) norm[j][i] += fnorm[j][i]; /* go to next facet */ sidea = get_prev_edge(sidea); if ( equal_id(sidea,get_next_facet(sidea)) ) break; /* edge */ sidea = fe_inverse(get_next_facet(sidea)); if ( bdry != get_facet_boundary(get_fe_facet(sidea)) ) break; if ( equal_id(sidea,fe) ) { if ( inverted(get_fe_facet(sidea)) ) for ( i = 0 ; i < SDIM ; i++ ) norm[0][i] = -norm[0][i]; goto ncn_exit; /* went all the way around */ } } while ( equal_id(get_next_facet(sidea),get_prev_facet(sidea)) ); } /* go around the other way */ sidea = fe; while ( equal_id(get_next_facet(sidea),get_prev_facet(sidea)) ) { if ( equal_id(sidea,get_next_facet(sidea)) ) break; /* edge */ sidea = fe_inverse(get_next_facet(sidea)); sidea = get_next_edge(sidea); if ( bdry != get_facet_boundary(get_fe_facet(sidea)) ) break; get_fe_side(sidea,z); if ( get_vattr(v_id) & AXIAL_POINT ) { REAL *u = fnorm[0]; switch ( web.modeltype ) { case LINEAR: get_fe_side(get_next_edge(sidea),y); cross_prod(y,z,fnorm[0]); break; case QUADRATIC: quadratic_facet_normal(v_id,get_fe_facet(sidea),&u); break; case LAGRANGE: lagrange_facet_normal(v_id,get_fe_facet(sidea),&u); break; } } else { switch ( web.modeltype ) { case LINEAR: get_fe_side(sidea,a[0]); get_fe_side(inverse_id(get_prev_edge(sidea)),a[1]); if ( SDIM == 3 ) { cross_prod(a[0],a[1],fnorm[0]); retval = 1; } else retval = kernel_basis_rows(a,fnorm,2,SDIM); break; case QUADRATIC: retval = quadratic_facet_normal(v_id,get_fe_facet(sidea),fnorm); break; case LAGRANGE: retval = lagrange_facet_normal(v_id,get_fe_facet(sidea),fnorm); break; } } for ( j = 0 ; j < retval ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) norm[j][i] += fnorm[j][i]; } if ( !equal_id(get_next_facet(sidea),get_prev_facet(sidea)) ) goto tripletest; if( inverted(get_fe_facet(sidea)) ) for ( i = 0 ; i < SDIM ; i++ ) norm[0][i] = -norm[0][i]; goto ncn_exit; tripletest: /* if get here, know there are one or two triple edges into vertex */ { /* triple line */ REAL *t = fnorm[0]; int triple_count = 0; facetedge_id start_e; edge_id e_id; for ( i = 0 ; i < SDIM ; i++ ) fnorm[0][i] = 0.; start_e = e_id = get_vertex_edge(v_id); do { int valence = get_edge_valence(e_id); if ( valence >= 3 ) { if ( web.modeltype==QUADRATIC ) { retval = quadratic_edge_normal(v_id,e_id,norm); goto ncn_exit; /* lazy; just use first triple */ } if ( web.modeltype==LAGRANGE ) { retval = lagrange_edge_normal(v_id,e_id,norm); goto ncn_exit; /* lazy; just use first triple */ } /* accumulate tangents */ ffe = get_edge_fe(e_id); if ( !equal_id(get_next_facet(ffe),get_prev_facet(ffe)) ) { /* triple again */ triple_count++; get_fe_side(ffe,z); switch ( triple_count ) { case 1: for ( i = 0 ; i < SDIM ; i++ ) fnorm[0][i] += z[i]; break; case 2: for ( i = 0 ; i < SDIM ; i++ ) fnorm[0][i] -= z[i]; break; default: goto full_dim; /* more than 2 */ } } } e_id = get_next_tail_edge(e_id); } while ( !equal_element(start_e,e_id) ); retval = kernel_basis_rows(&t,norm,1,SDIM); } goto ncn_exit; ncn_exit: return retval; full_dim: for ( i = 0 ; i < SDIM ; i++ ) { for ( j = 0 ; j < SDIM ; j++ ) norm[i][j] = 0.0; norm[i][i] = 1.0; } return SDIM; } /************************************************************************* * * Function: project_vertex_normals() * * Purpose: Take incoming normal basis, project to constraints, * and orthonormalize. Meant to be called after * new_calc_vertex_normal(). * * Return: Number of independent basis vectors. * Basis vectors altered in place. */ int project_vertex_normals(v_id,normals,normcount) vertex_id v_id; REAL **normals; int normcount; { conmap_t *conmap; int oncount; MAT2D(grads,MAXCOORD,MAXCOORD); MAT2D(pp,MAXCOORD,MAXCOORD); MAT2D(qq,MAXCOORD,MAXCOORD); REAL dummy; int i,j,n; conmap = get_v_constraint_map(v_id); oncount = 0; if ( conmap[0] ) { /* calculate projection matrix to fewer degrees of freedom */ for ( j = 1; j <= (int)conmap[0] ; j++ ) { struct constraint *constr; if ( !(conmap[j] & CON_HIT_BIT) ) continue; constr = get_constraint(conmap[j]); eval_all(constr->formula,get_coord(v_id),SDIM, &dummy, grads[oncount],v_id); oncount++; } if ( oncount > 0 ) { /* project basis to constraints */ oncount = gram_schmidt(grads,oncount,SDIM); mat_mul_tr(grads,normals,qq,oncount,SDIM,normcount); tr_mat_mul(grads,qq,pp,oncount,SDIM,normcount); for ( j = 0 ; j < normcount ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) normals[j][i] -= pp[i][j]; } /* orthonormalize */ for ( i = 0 ; i < normcount ; i++ ) { /* subtract previous components */ REAL sum; for ( j = 0 ; j < i ; j++ ) { sum = dot(normals[i],normals[j],SDIM); for ( n = 0 ; n < SDIM ; n++ ) normals[i][n] -= sum*normals[j][n]; } /* normalize */ sum = dot(normals[i],normals[i],SDIM); sum = sqrt(sum); if ( sum > hessian_epsilon ) for ( n = 0 ; n < SDIM ; n++ ) normals[i][n] /= sum; else /* have to skip this direction */ { for ( n = 0 ; n < SDIM ; n++ ) normals[i][n] = normals[normcount-1][n]; normcount--; i--; } } } return normcount; } /*************************************************************** * * Function: get_facet_normal() * * Purpose: find facet normal (length = area). * 3D only * */ void get_facet_normal(f_id,normal) facet_id f_id; REAL *normal; { facetedge_id fe; REAL side1[MAXCOORD],side2[MAXCOORD]; int i; if ( web.representation == SIMPLEX ) kb_error(1368,"Internal error: Trying to take normal in simplex model.\n", RECOVERABLE); if ( web.modeltype == LINEAR ) /* better for strange symmetries */ { MAT2D(x,FACET_VERTS,MAXCOORD); get_facet_verts(f_id,x,NULL); /* in tail order */ for ( i = 0 ; i < SDIM ; i++ ) { side1[i] = x[1][i] - x[0][i]; side2[i] = x[2][i] - x[1][i]; } } else { fe = get_facet_fe(f_id); get_edge_side(get_fe_edge(fe),side1); fe = get_next_edge(fe); get_edge_side(get_fe_edge(fe),side2); } cross_prod(side1,side2,normal); for ( i = 0 ; i < SDIM ; i++ ) normal[i] /= 2; /* triangle factor */ } #ifdef NEWFACETNORMAL attempt to do normal at arbitrary point in facet. Incomplete /*************************************************************** * * Function: new_get_facet_normal() * * Purpose: find facet normal (length = area). * Any dimension, linear, quadratic, or Lagrange model. * Needs barycentric coords for quadratic or Lagrange. * */ void new_get_facet_normal(f_id,normal,b) facet_id f_id; REAL *normal; REAL *b; /* barycentric coords */ { facetedge_id fe; MAT2D(x,MAXCOORD+1,MAXCOORD); int i; int m; MAT2D(sides,MAXCOORD,MAXCOORD); normal[i] /= 2; /* triangle factor */ if ( web.modeltype == LINEAR ) { get_facet_verts(f_id,x,NULL); /* in tail order */ /* fan of sides from v0 */ for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) sides[i][j] = x[i+1][j] - x[0][j]; kernel_basis_rows(sides,&normal,web.dimension,SDIM); } else if ( web.modeltype == QUADRATIC ) { get_facet_verts(f_id,x,NULL); /* in tail order */ /* tangents at point */ mat_mult(gpolypartial[m],x,sides,web.dimension,FACET_CTRL,SDIM); kernel_basis_rows(sides,&normal,web.dimension,SDIM); } else if ( web.modeltype == LAGRANGE ) { int ctrl = web.skel[FACET].ctrlpts; int dim = web.dimension; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; get_facet_verts(f_id,x,NULL); /* tangent vectors at gauss points */ mat_mult(gl->gpolypart[m],x,sides,dim,ctrl,SDIM); kernel_basis_rows(sides,&normal,web.dimension,SDIM); } } #endif /*************************************************************** * * Function: get_edge_verts() * * Purpose: to get all vertices of an edge, nicely * displaced relative to first vertex of edge in case of symmetry gp * NOTE: Quadratic model: midv in verts[1] * */ void get_edge_verts(e_id,verts,wraps) edge_id e_id; REAL **verts; /* third spot NULL if don't want midpts in quadratic */ WRAPTYPE *wraps; /* wraps of vertices; NULL if not wanted */ { int j,k; WRAPTYPE wrap = 0; REAL *x; if ( web.representation == SIMPLEX ) kb_error(1369,"Internal error: Can't do get_edge_verts for simplex model.\n",RECOVERABLE); if ( web.modeltype == LAGRANGE ) { vertex_id *v = get_edge_vertices(e_id); int n = web.skel[EDGE].ctrlpts; if ( web.symmetry_flag ) { wrap = get_edge_wrap(e_id); } for ( j = 0 ; j < n ; j++ ) { x = get_coord(v[j]); if ( wrap && (j > 0) && (inverted(e_id) || (j==n-1))) (*sym_wrap)(x,verts[j],wrap); else for ( k = 0 ; k < SDIM ; k++ ) verts[j][k] = x[k]; } return; } if ( web.symmetry_flag ) { x = get_coord(get_edge_tailv(e_id)); for ( j = 0 ; j < SDIM ; j++ ) verts[0][j] = x[j]; wrap = get_edge_wrap(e_id); if ( (web.modeltype == QUADRATIC) && verts[2] ) { if ( wraps ){wraps[0] = 0; wraps[2] = wrap; } x = get_coord(get_edge_headv(e_id)); (*sym_wrap)(x,verts[2],wrap); x = get_coord(get_edge_midv(e_id)); if ( inverted(e_id) ) { if ( wraps ) wraps[1] = wrap; (*sym_wrap)(x,verts[1],wrap); } else { for ( j = 0 ; j < SDIM ; j++ ) verts[1][j] = x[j]; if ( wraps ) wraps[1] = 0; } } else { if ( wraps ){wraps[0] = 0; wraps[1] = wrap; } x = get_coord(get_edge_headv(e_id)); (*sym_wrap)(x,verts[1],wrap); } } else { x = get_coord(get_edge_tailv(e_id)); for ( j = 0 ; j < SDIM ; j++ ) verts[0][j] = x[j]; x = get_coord(get_edge_headv(e_id)); if ( (web.modeltype == QUADRATIC) && verts[2] ) { for ( j = 0 ; j < SDIM ; j++ ) verts[2][j] = x[j]; x = get_coord(get_edge_midv(e_id)); for ( j = 0 ; j < SDIM ; j++ ) verts[1][j] = x[j]; } else for ( j = 0 ; j < SDIM ; j++ ) verts[1][j] = x[j]; } } /*************************************************************** * * Function: get_facet_verts() * * Purpose: to get all three vertices of a facet, nicely * displaced relative to first vertex of facet in case of symmetry gp * */ void get_facet_verts(f_id,verts,wraps) facet_id f_id; REAL **verts; /* fourth spot NULL if don't want midpts in quadratic */ WRAPTYPE *wraps; /* wraps of vertices; NULL if not wanted */ { facetedge_id fe; int i,j; WRAPTYPE wrap,fewrap; REAL *x; if ( web.modeltype == LAGRANGE ) { vertex_id *v = get_facet_vertices(f_id); int ctrlpts = web.skel[FACET].ctrlpts; int n = web.lagrange_order; f_id = positive_id(f_id); /* since vertices in particular order */ for ( i = 0 ; i < ctrlpts ; i++ ) { x = get_coord(v[i]); for ( j = 0 ; j < SDIM ; j++ ) verts[i][j] = x[j]; } if ( web.symmetry_flag ) { if ( wraps ) for ( j = 0 ; j < ctrlpts ; j++ ) wraps[j] = 0; /* gotta go through the 3 edges */ /* first edge */ fe = get_facet_fe(f_id); wrap = get_fe_wrap(fe); if ( wrap && inverted(get_fe_edge(fe)) ) for ( j = 1 ; j < n ; j++ ) { (*sym_wrap)(get_coord(v[j]),verts[j],wrap); if ( wraps ) wraps[j] = wrap; } if ( wrap ) { (*sym_wrap)(get_coord(v[n]),verts[n],wrap); if ( wraps ) wraps[n] = wrap ; } /* second edge */ fe = get_next_edge(fe); if ( wrap && !inverted(get_fe_edge(fe)) ) for ( j = 1 ; j < n ; j++ ) { int m = n*(j+1) - j*(j-1)/2; (*sym_wrap)(get_coord(v[m]),verts[m],wrap); if ( wraps ) wraps[m] = wrap; } fewrap = get_fe_wrap(fe); wrap = (*sym_compose)(wrap,fewrap); if ( wrap && inverted(get_fe_edge(fe)) ) for ( j = 1 ; j < n ; j++ ) { int m = n*(j+1) - j*(j-1)/2; (*sym_wrap)(get_coord(v[m]),verts[m],wrap); if ( wraps ) wraps[m] = wrap; } if ( wrap ) { int m = n*(n+3)/2; (*sym_wrap)(get_coord(v[m]),verts[m],wrap); if ( wraps ) wraps[m] = wrap; } /* third edge */ fe = get_next_edge(fe); if ( wrap && !inverted(get_fe_edge(fe)) ) for ( j = 1 ; j < n ; j++ ) { int m = n*j + j*(3-j)/2; (*sym_wrap)(get_coord(v[m]),verts[m],wrap); if ( wraps ) wraps[m] = wrap; } fewrap = get_fe_wrap(fe); wrap = (*sym_compose)(wrap,fewrap); if ( wrap && inverted(get_fe_edge(fe)) ) for ( j = 1 ; j < n ; j++ ) { int m = n*j + j*(3-j)/2; (*sym_wrap)(get_coord(v[m]),verts[m],wrap); if ( wraps ) wraps[m] = wrap; } } return; } if ( (web.modeltype == QUADRATIC) && verts[3] ) { get_facet_verts_q(f_id,verts,wraps); return; } if ( web.representation == SIMPLEX ) { vertex_id *v = get_facet_vertices(f_id); for ( i = 0 ; i <= web.dimension ; i++ ) { x = get_coord(v[i]); for ( j = 0 ; j < SDIM ; j++ ) verts[i][j] = x[j]; } return; } fe = get_facet_fe(f_id); if ( web.symmetry_flag ) { wrap = 0; for ( i = 0 ; i < FACET_VERTS ; i++ ) { x = get_coord(get_fe_tailv(fe)); if ( wraps ) wraps[i] = wrap; (*sym_wrap)(x,verts[i],wrap); fewrap = get_fe_wrap(fe); wrap = (*sym_compose)(wrap,fewrap); fe = get_next_edge(fe); } } else for ( i = 0 ; i < FACET_VERTS ; i++ ) { vertex_id v_id = get_fe_tailv(fe); x = get_coord(v_id); for ( j = 0 ; j < SDIM ; j++ ) verts[i][j] = x[j]; fe = get_next_edge(fe); } } /*************************************************************** * * Function: get_facet_verts_special() * * Purpose: to get all three vertices of a facet, nicely * displaced to fundamental cell in case of symmetry group. * Extra work to get them to come out close to origin. * */ void get_facet_verts_special(f_id,verts,wraps) facet_id f_id; REAL **verts; /* fourth spot NULL if don't want midpts in quadratic */ WRAPTYPE *wraps; /* wraps of vertices; NULL if not wanted */ { facetedge_id fe; int i,j,ii; WRAPTYPE wrap,fewrap; REAL *x; if ( web.modeltype == LAGRANGE ) { vertex_id *v = get_facet_vertices(f_id); int k = web.skel[FACET].ctrlpts; for ( i = 0 ; i < k ; i++ ) { x = get_coord(v[i]); for ( j = 0 ; j < SDIM ; j++ ) verts[i][j] = x[j]; } return; } if ( (web.modeltype == QUADRATIC) && verts[3] ) { get_facet_verts_q(f_id,verts,wraps); return; } if ( web.representation == SIMPLEX ) { vertex_id *v = get_facet_vertices(f_id); for ( i = 0 ; i <= web.dimension ; i++ ) { x = get_coord(v[i]); for ( j = 0 ; j < SDIM ; j++ ) verts[i][j] = x[j]; } return; } fe = get_facet_fe(f_id); if ( web.symmetry_flag ) { /* start with point closest to origin */ if ( !(sym_flags & HAS_FIXED_PTS) ) { REAL dd,mindd; int mini=0; facetedge_id minfe=fe; mindd = 1e40; for ( i = 0 ; i < FACET_VERTS ; i++, fe = get_next_edge(fe) ) { x = get_coord(get_fe_tailv(fe)); dd = SDIM_dot(x,x); if ( dd < 0.9*mindd ) { mindd = dd; mini = i; minfe = fe;} } i = mini; fe = minfe; } else { i = 0; } wrap = 0; for ( ii = 0 ; ii < FACET_VERTS ; ii++ ) { int jj = (i+ii)%FACET_VERTS; x = get_coord(get_fe_tailv(fe)); if ( wraps ) wraps[jj] = wrap; (*sym_wrap)(x,verts[jj],wrap); fewrap = get_fe_wrap(fe); wrap = (*sym_compose)(wrap,fewrap); fe = get_next_edge(fe); } } else for ( i = 0 ; i < FACET_VERTS ; i++ ) { x = get_coord(get_fe_tailv(fe)); for ( j = 0 ; j < SDIM ; j++ ) verts[i][j] = x[j]; fe = get_next_edge(fe); } } /*************************************************************** * * Function: get_facet_verts_q() * * Purpose: to get all vertices of a facet, nicely * displaced to fundamental cell in case of symmetry group. * Quadratic model. Midpoints interleaved with corners. * */ void get_facet_verts_q(f_id,verts,wraps) facet_id f_id; REAL **verts; WRAPTYPE *wraps; /* NULL if not wanted */ { facetedge_id fe; int i,j,ii; WRAPTYPE wrap,fewrap; REAL *x; if ( web.representation == SIMPLEX ) { kb_error(1370,"No quadratic model with simplices.\n",RECOVERABLE); } fe = get_facet_fe(f_id); if ( web.symmetry_flag ) { i = 0; wrap = 0; for ( ii = 0 ; ii < FACET_VERTS ; ii++ ) { int jj = (i+ii)%FACET_VERTS; x = get_coord(get_fe_tailv(fe)); if ( wraps ) wraps[2*jj] = wrap; (*sym_wrap)(x,verts[2*jj],wrap); x = get_coord(get_fe_midv(fe)); if ( !inverted(get_fe_edge(fe)) ) { if ( wraps ) wraps[2*jj+1] = wrap; (*sym_wrap)(x,verts[2*jj+1],wrap); } fewrap = get_fe_wrap(fe); wrap = (*sym_compose)(wrap,fewrap); if ( inverted(get_fe_edge(fe)) ) { if ( wraps ) wraps[2*jj+1] = wrap; (*sym_wrap)(x,verts[2*jj+1],wrap); } fe = get_next_edge(fe); } } else for ( i = 0 ; i < FACET_VERTS ; i++ ) { x = get_coord(get_fe_tailv(fe)); for ( j = 0 ; j < SDIM ; j++ ) verts[2*i][j] = x[j]; x = get_coord(get_fe_midv(fe)); for ( j = 0 ; j < SDIM ; j++ ) verts[2*i+1][j] = x[j]; fe = get_next_edge(fe); } } /***************************************************************** * * Function: path_open() * * Purpose: Open file for reading on EVOLVERPATH */ #ifdef __WIN32__ FILE *path_open(char *name,int mode) /* else TC++ has conniptions */ #else FILE *path_open(name,mode) char *name; int mode; /* NOTDATAFILENAME or SETDATAFILENAME */ #endif { char path[PATHSIZE]; size_t len; FILE *fd = NULL; char *env; #ifdef MPI_EVOLVER /* use filename as format string to generate various file names */ char taskpath[PATHSIZE]; sprintf(taskpath,name,this_task); name = taskpath; #endif env = getenv("EVOLVERPATH"); #if defined(WIN32) && !defined(__BORLANDC__) /* Using wildcards! */ /* try given name */ strncpy(path,name,sizeof(path)); for(;;) { /* try paths in EVOLVERPATH */ intptr_t ret; struct _finddata_t finddata; ret = _findfirst(path,&finddata); if ( ret != -1 ) { /* finddata.name only has filename, not path stuff */ char *slash = strrchr(path,'/'); if ( !slash ) slash = strrchr(path,'\\'); if ( slash ) slash++; else slash = path; strncpy(slash,finddata.name,sizeof(path)-(slash-path)); fd = fopen(path,"r"); _findclose(ret); break; } if ( env == NULL ) break; len = strcspn(env,ENVPATHCHAR); if ( len == 0 ) break; strncpy(path,env,len); path[len] = PATHCHAR; strncpy(path+len+1,name,sizeof(path)-len-2); if ( env[len] == 0 ) env = NULL; /* end of EVOLVERPATH */ else env += len+1; } /* try .fe extension */ if ( fd == NULL) { env = getenv("EVOLVERPATH"); strncpy(path,name,sizeof(path)); strcat(path,".fe"); for ( ;; ) { intptr_t ret; struct _finddata_t finddata; ret = _findfirst(path,&finddata); if ( ret != -1 ) { char *slash = strrchr(path,'/'); if ( !slash ) slash = strrchr(path,'\\'); if ( slash ) slash++; else slash = path; strncpy(slash,finddata.name,sizeof(path)-(slash-path)); fd = fopen(path,"r"); _findclose(ret); /* scrape the ".fe" off the end of the path since user didn't give it */ path[strlen(path)-3] = 0; break; } /* try paths in EVOLVERPATH */ if ( env == NULL ) break; len = strcspn(env,ENVPATHCHAR); if ( len == 0 ) break; strncpy(path,env,len); path[len] = PATHCHAR; strncpy(path+len+1,name,sizeof(path)-len-2); strcat(path,".fe"); if ( env[len] == 0 ) env = NULL; /* end of EVOLVERPATH */ else env += len+1; } } #elif defined(_GLOB_H) /* Using wildcards! */ /* try given name */ strncpy(path,name,sizeof(path)); for(;;) { /* try paths in EVOLVERPATH */ int ret; glob_t globdata; memset(&globdata,0,sizeof(globdata)); ret = glob(path,0,NULL,&globdata); if ( ret == 0 ) /* success */ { strncpy(path,globdata.gl_pathv[0],sizeof(path)); fd = fopen(path,"r"); globfree(&globdata); break; } if ( env == NULL ) break; len = strcspn(env,ENVPATHCHAR); if ( len == 0 ) break; strncpy(path,env,len); path[len] = PATHCHAR; strncpy(path+len+1,name,sizeof(path)-len-2); if ( env[len] == 0 ) env = NULL; /* end of EVOLVERPATH */ else env += len+1; } /* try .fe extension */ if ( fd == NULL) { env = getenv("EVOLVERPATH"); strncpy(path,name,sizeof(path)); strcat(path,".fe"); for ( ;; ) { glob_t globdata; int ret; ret = glob(path,0,NULL,&globdata); if ( ret == 0 ) /* success */ { strncpy(path,globdata.gl_pathv[0],sizeof(path)); fd = fopen(path,"r"); globfree(&globdata); /* scrape the ".fe" off the end of the path since user didn't give it */ path[strlen(path)-3] = 0; break; } /* try paths in EVOLVERPATH */ if ( env == NULL ) break; len = strcspn(env,ENVPATHCHAR); if ( len == 0 ) break; strncpy(path,env,len); path[len] = PATHCHAR; strncpy(path+len+1,name,sizeof(path)-len-2); strcat(path,".fe"); if ( env[len] == 0 ) env = NULL; /* end of EVOLVERPATH */ else env += len+1; } } #else /* try given name */ strncpy(path,name,sizeof(path)); while ( (fd = fopen(path,"r")) == NULL) { /* try paths in EVOLVERPATH */ if ( env == NULL ) break; len = strcspn(env,ENVPATHCHAR); if ( len == 0 ) break; strncpy(path,env,len); path[len] = PATHCHAR; strncpy(path+len+1,name,sizeof(path)-len-2); if ( env[len] == 0 ) env = NULL; /* end of EVOLVERPATH */ else env += len+1; } /* try .fe extension */ if ( fd == NULL) { env = getenv("EVOLVERPATH"); strncpy(path,name,sizeof(path)); strcat(path,".fe"); while ( (fd = fopen(path,"r")) == NULL) { /* try paths in EVOLVERPATH */ if ( env == NULL ) break; len = strcspn(env,ENVPATHCHAR); if ( len == 0 ) break; strncpy(path,env,len); path[len] = PATHCHAR; strncpy(path+len+1,name,sizeof(path)-len-2); strcat(path,".fe"); if ( env[len] == 0 ) env = NULL; /* end of EVOLVERPATH */ else env += len+1; } } #endif if ( fd && (mode==SETDATAFILENAME) ) { /* has to be set so repeat open works */ /* copy from end, getting same number of directories */ char *found; /* from findfile() */ char *user; /* user input search string */ found = path + strlen(path) - 1; user = name + strlen(name) - 1; while ( user >= name ) { while ( (*user != PATHCHAR) && (*user != '\\') && (user >= name) ) user--; while ( (*found != PATHCHAR) && (*found != '\\') && (found >= path) ) found--; if ( user >= name ) user--; else break; if ( found >= path ) found--; else break; } strncpy(datafilename,found+1,PATHSIZE); } return fd; } /************************************************************************** * * function: calc_view_transform_gens() * * purpose: evaluate expressions in view_transform_gens * * return: 1 if anything changed; 0 else. */ int calc_view_transform_gens() { int n,i,j,nmax; REAL value; int change = 0; nmax = web.torus_flag ? transform_gen_count - SDIM : transform_gen_count; for ( n = 0 ; n < nmax ; n++ ) for ( i = 0 ; i <= SDIM ; i++ ) for ( j = 0 ; j <= SDIM ; j++ ) { value = eval(&view_transform_gens_expr[n][i][j],NULL,NULLID,NULL); if ( value != view_transform_gens[n][i][j] ) { view_transform_gens[n][i][j] = value; change = 1; } } return change; } /******************************************************************* * * function: calc_periods() * * purpose: calculate torus periods from expressions */ void calc_periods(mode) int mode; /* NO_ADJUST_VOLUMES or ADJUST_VOLUMES */ { int i,j; REAL value; REAL old_torusv = web.torusv; MAT2D(invper,MAXCOORD,MAXCOORD); /* so don't have temporary bad values in web.inverse_periods since graphgen() may be using it */ int k; if ( torus_period_expr[0][0].start ) { for ( i = 0 ; i < SDIM ; i++ ) { for ( j = 0 ; j < SDIM ; j++ ) { value = eval(&torus_period_expr[i][j],NULL,NULLID,NULL); invper[j][i] = web.torus_period[i][j] = value; } } web.torusv = det_adjoint(invper,SDIM); if ( web.torusv == 0.0 ) kb_error(1377,"Degenerate torus unit cell.\n",RECOVERABLE); for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) web.inverse_periods[i][j] = invper[i][j]/web.torusv; } if ( web.torus_display_period ) { REAL det; for ( i = 0 ; i < SDIM ; i++ ) { for ( j = 0 ; j < SDIM ; j++ ) { value = eval(&torus_display_period_expr[i][j],NULL,NULLID,NULL); invper[j][i] = web.torus_display_period[i][j] = value; } } det = det_adjoint(invper,SDIM); if ( det == 0.0 ) kb_error(3377,"Degenerate display periods.\n",RECOVERABLE); for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) web.inverse_display_periods[i][j] = invper[i][j]/det; } /* if change in periods, adjust volumes and volconsts */ if ( (mode == ADJUST_VOLUMES) && (old_torusv != 0.0) && !web.pressure_flag) { body_id b_id; value = web.torusv/old_torusv; FOR_ALL_BODIES(b_id) { set_body_volume(b_id,value*(get_body_volume(b_id)),SETSTAMP); save_body_volume(b_id); if ( get_battr(b_id) & FIXEDVOL ) set_body_fixvol(b_id,value*(get_body_fixvol(b_id))); set_body_volconst(b_id,value*(get_body_volconst(b_id))); if ( everything_quantities_flag ) { struct gen_quant *q = GEN_QUANT(get_body_volquant(b_id)); q->value *= value; q->target *= value; q->volconst *= value; } } } /* adjust view transform generators for automatic torus symmetries */ if ( web.torus_flag ) { if ( view_transform_gens == NULL ) { view_transform_gens = dmatrix3(SDIM,SDIM+1,SDIM+1); transform_gen_count = SDIM; } if ( transform_gen_swap == NULL ) transform_gen_swap = (int*)mycalloc(SDIM,sizeof(int)); for ( j=transform_gen_count-SDIM, k=0 ; j n ) x[m-1] = 0.5; } /**************************************************************** * * function: binom_coeff() * * purpose: calculate binomial coefficient (small numbers only) * */ int binom_coeff(n,k) int n,k; { int i, c; if ( n < k ) return 0; if ( (n-k) < k ) k = n - k; for ( i = 0, c = 1 ; i < k ; i++ ) { c *= n - i; c /= i+1; } return c; } /********************************************************************** * * function: set_e_phase_density() * * purpose: calculate edge density from phases of neighboring facets. */ void set_e_phase_density(e_id) edge_id e_id; { facetedge_id fe = get_edge_fe(e_id); int i = get_f_phase(get_fe_facet(fe)); int j = get_f_phase(get_fe_facet(get_next_facet(fe))); set_edge_density(e_id,phase_data[i][j]); } /********************************************************************** * * function: set_f_phase_density() * * purpose: calculate facet density from phases of neighboring bodies. */ void set_f_phase_density(f_id) facet_id f_id; { int i = get_b_phase(get_facet_body(f_id)); int j = get_b_phase(get_facet_body(inverse_id(f_id))); set_attr(f_id,DENSITY); set_facet_density(f_id,phase_data[i][j]); } /********************************************************************** * * function: make_vedge_lists() * * purpose: construct from scratch edge lists aound vertices. * These are singly linked circular lists with links residing * in edge structures. */ void make_vedge_lists() { vertex_id v_id; edge_id e_id; if ( web.representation == SIMPLEX ) return; /* clear out all old data */ MFOR_ALL_EDGES(e_id) { set_next_tail_edge(e_id,NULLID); set_next_tail_edge(inverse_id(e_id),NULLID); } MFOR_ALL_VERTICES(v_id) { if ( !(get_vattr(v_id) & (Q_MIDEDGE|Q_MIDFACET|Q_MIDPOINT)) ) set_vertex_edge(v_id,NULLID); } /* now splice in all edges */ MFOR_ALL_EDGES(e_id) { insert_vertex_edge(get_edge_tailv(e_id),e_id); insert_vertex_edge(get_edge_headv(e_id),inverse_id(e_id)); } vedge_timestamp = top_timestamp; } /********************************************************************** * * function: make_vfacet_lists() * * purpose: construct from scratch facet lists aound vertices. * These are singly linked circular lists with links residing * in facet structures. * * Should only be used currently with SIMPLEX model, since others * use edge links around vertex. */ void make_vfacet_lists() { vertex_id v_id; facet_id f_id, ff_id,fff_id; int i; int facet_verts; /* set all facet vertices */ facet_verts = web.skel[FACET].ctrlpts; expand_attribute(FACET,F_NEXT_VFACET_ATTR,&facet_verts); if ( web.representation == SOAPFILM ) /* set up facet vertex lists */ { if ( web.modeltype != LAGRANGE ) FOR_ALL_FACETS(f_id) { facetedge_id fe = get_facet_fe(f_id); vertex_id *fv = get_facet_vertices(f_id); for ( i = 0 ; i < FACET_VERTS ; i++ ) { v_id = get_fe_tailv(fe); fv[i] = v_id; fe = get_next_edge(fe); if ( web.modeltype == QUADRATIC ) fv[i+FACET_VERTS] = get_edge_midv(get_fe_edge(fe)); } } } else if ( web.representation == STRING ) { edge_id e_id; FOR_ALL_EDGES(e_id) { facetedge_id fe = get_edge_fe(e_id); if ( !valid_id(fe) ) continue; f_id = get_fe_facet(fe); if ( !valid_id(f_id) ) continue; set_vertex_facet(get_edge_tailv(e_id),f_id); set_vertex_facet(get_edge_headv(e_id),f_id); } } else if ( web.representation == SIMPLEX ) FOR_ALL_FACETS(f_id) /* simplex vertex to facet links */ { vertex_id *fv = get_facet_vertices(f_id); for ( i = 0 ; i < facet_verts ; i++ ) { set_vertex_facet(fv[i],f_id); } } /* start by making one-element loops on facets */ FOR_ALL_VERTICES(v_id) { f_id = get_vertex_facet(v_id); if ( valid_id(f_id) ) { set_next_vertex_facet(v_id,f_id,f_id); set_vertex_facet(v_id,f_id); } } if ( web.representation == STRING ) { /* not using explicit linked list; since get_next_vertex_facet() uses edge list around vertex and facet list around edge. */ } else FOR_ALL_FACETS(f_id) { vertex_id *fv = get_facet_vertices(f_id); for ( i = 0 ; i < facet_verts ; i++ ) { v_id = fv[i]; ff_id = get_vertex_facet(v_id); if ( !equal_id(f_id,ff_id) ) { /* splice in after first one */ fff_id = get_next_vertex_facet(v_id,ff_id); if ( !equal_element(f_id,fff_id) ) { set_next_vertex_facet(v_id,ff_id,f_id); set_next_vertex_facet(v_id,f_id,fff_id); } } } } vfacet_timestamp = top_timestamp; } /********************************************************************** * * function: make_bfacet_lists() * * purpose: construct from scratch facet lists aound bodies. * These are doubly linked circular lists with links residing * in facet structures. */ void make_bfacet_lists() { body_id b_id; facet_id f_id, ff_id,fff_id,fi_id; int four = 4; if ( bfacet_timestamp >= top_timestamp ) return; if ( web.skel[BODY].count == 0 ) return; expand_attribute(FACET,F_NEXT_BFACET_ATTR,&four); /* start by making one-element loops on facets pointed to by body facet_edge link */ FOR_ALL_BODIES(b_id) { /* make sure legal pointer */ f_id = get_body_facet(b_id); if ( !valid_id(f_id) || #ifdef MPI_EVOLVER !mpi_remote_present(f_id) || #endif !equal_id(b_id,get_facet_body(f_id)) ) { set_body_facet(b_id,NULLID); FOR_ALL_FACETS(f_id) { if ( equal_id(b_id,get_facet_body(f_id)) ) {set_body_facet(b_id,f_id); break; } if ( equal_id(b_id,get_facet_body(inverse_id(f_id))) ) {set_body_facet(b_id,inverse_id(f_id)); break; } } } f_id = get_body_facet(b_id); if ( valid_id(f_id) ) { set_next_body_facet(f_id,f_id); set_prev_body_facet(f_id,f_id); } } /* now splice in all other facets */ FOR_ALL_FACETS(f_id) { b_id = get_facet_body(f_id); if ( valid_id(b_id) ) { ff_id = get_body_facet(b_id); if ( !equal_id(f_id,ff_id) ) { /* splice in after first one */ fff_id = get_next_body_facet(ff_id); set_next_body_facet(ff_id,f_id); set_prev_body_facet(f_id,ff_id); set_next_body_facet(f_id,fff_id); set_prev_body_facet(fff_id,f_id); } } fi_id = inverse_id(f_id); b_id = get_facet_body(fi_id); if ( valid_id(b_id) ) { ff_id = get_body_facet(b_id); if ( !equal_id(fi_id,ff_id) ) { /* splice in after first one */ fff_id = get_next_body_facet(ff_id); set_next_body_facet(ff_id,fi_id); set_prev_body_facet(fi_id,ff_id); set_next_body_facet(fi_id,fff_id); set_prev_body_facet(fff_id,fi_id); } } } bfacet_timestamp = top_timestamp; } /******************************************************************* * * function: insert_vertex_edge(v_id,e_id) * * purpose: fix up edge links around vertex when edge added * */ void insert_vertex_edge(v_id,e_id) vertex_id v_id; edge_id e_id; { edge_id ee_id,eee_id; int n; #ifdef MPI_EVOLVER if ( !elptr(v_id) || !elptr(e_id) ) return; #endif if ( !equal_id(v_id,get_edge_tailv(e_id)) ) { kb_error(1372,"Internal error: Trying to insert edge to wrong vertex.\n", WARNING); return; } ee_id = get_vertex_edge(v_id); if ( !valid_id(ee_id) ) { set_next_tail_edge(e_id,e_id); set_vertex_edge(v_id,e_id); return; } /* go around to be sure not already there */ eee_id = ee_id; n = 0; do { eee_id = get_next_tail_edge(eee_id); if ( equal_id(eee_id,e_id) ) return; if ( ++n > 2*web.skel[EDGE].count+5 ) /* sanity check */ { sprintf(errmsg,"Internal error: Vertex %s edge loop not closed.\n", ELNAME(v_id)); kb_error(1373,errmsg,WARNING); return; } } while ( !equal_id(eee_id,ee_id) ); /* insert into existing list */ ee_id = get_next_tail_edge(eee_id); set_next_tail_edge(eee_id,e_id); set_next_tail_edge(e_id,ee_id); } /* end insert_vertex_edge */ /******************************************************************* * * function: remove_vertex_edge(v_id,e_id) * * purpose: fix up edge links around vertex when edge removed * */ void remove_vertex_edge(v_id,e_id) vertex_id v_id; edge_id e_id; /* tail is v_id */ { edge_id ee_id,eee_id,eeee_id; vertex_id vv_id = get_edge_tailv(e_id); int n = 0; #ifdef MPI_EVOLVER if ( !mpi_remote_present(v_id) ) return; #endif if ( !equal_id(v_id,vv_id) ) { sprintf(errmsg, "Internal error: Trying to detach edge %s from vertex %s instead of %s.\n", ELNAME(e_id),ELNAME1(v_id),ELNAME2(vv_id)); kb_error(1374,errmsg, WARNING); return; } /* make sure edge actually in vertex edge loop */ ee_id = get_vertex_edge(v_id); if ( !valid_id(ee_id)) return; eee_id = ee_id; do { #ifdef MPI_EVOLVER if ( !mpi_remote_present(eee_id) ) return; /* fail, but don't crash */ #endif if ( equal_id(eee_id,e_id) ) break; eee_id = get_next_tail_edge(eee_id); if ( ++n > 2*web.skel[EDGE].count ) /* sanity check */ { kb_error(1375,"Internal error: Vertex edge loop not closed.\n",WARNING); break; } } while ( !equal_id(eee_id,ee_id) ); if ( !equal_id(eee_id,e_id) ) return; ee_id = get_next_tail_edge(e_id); if ( equal_id(e_id,ee_id) ) { set_vertex_edge(v_id,NULLID); return; } #ifdef MPI_EVOLVER if ( !mpi_remote_present(ee_id) ) return; #endif eee_id = ee_id; eeee_id = get_next_tail_edge(ee_id); #ifdef MPI_EVOLVER if ( !mpi_remote_present(eeee_id) ) return; #endif n = 0; while ( !equal_id(e_id,eeee_id) ) { eee_id = eeee_id; eeee_id = get_next_tail_edge(eee_id); #ifdef MPI_EVOLVER if ( !mpi_remote_present(eeee_id) ) return; #endif if ( ++n > 2*web.skel[EDGE].count ) /* sanity check */ { sprintf(errmsg, "Internal error: Vertex %s edge loop not closed.\n", ELNAME(v_id)); kb_error(1376,errmsg, WARNING); break; } } set_next_tail_edge(eee_id,ee_id); set_next_tail_edge(e_id,e_id); set_vertex_edge(v_id,ee_id); /* clean up in case of constraint content quantities */ if ( everything_quantities_flag && (web.representation == STRING) ) { int k,n; conmap_t *map = get_v_constraint_map(v_id); #define MAXTODO 10 conmap_t to_do[MAXTODO]; /* make list since unset and set can change order in map */ for ( k = 1, n = 0; k <= (int)*map ; k++ ) { int connum = map[k]&CONMASK; struct constraint *con = get_constraint(connum); if ( (con->attr & CON_CONTENT) && (n < MAXTODO) ) to_do[n++] = connum; } for ( k = 0 ; k < n ; k++ ) { unset_v_constraint_map(v_id,to_do[k]); set_v_constraint_map(v_id,to_do[k]); } } } /* end remove_vertex_edge() */ /******************************************************************* * * function: get_vertex_first_facet() * * purpose: find some facet adjoining vertex, first facet in * implicit list found by following vertex edge list * around and then facets around edge. * * Returns facetedge_id if possible, otherwise facet_id */ element_id get_vertex_first_facet(v_id) vertex_id v_id; { /* special cases */ if ( web.representation == SIMPLEX ) { if ( vfacet_timestamp < top_timestamp ) make_vfacet_lists(); return get_vertex_facet(v_id); } if ( get_vattr(v_id) & Q_MIDFACET ) return get_vertex_facet(v_id); return get_next_vertex_facet(v_id,NULLID); } /* end get_vertex_first_facet() */ /************************************************************************ * * Function: get_next_vertex_facet() * * Purpose: get next facet in implicit chain around vertex. * * Returns facetedge_id if possible, otherwise facet_id */ element_id get_next_vertex_facet(v_id,prev_id) vertex_id v_id; element_id prev_id; /* either facet_id or facetedge_id; if null, gets first one */ { edge_id start_e,ee_id; facetedge_id fe_id,start_fe, first_fe = NULLID; facet_id next_f; int found; /* whether old f_id found yet */ if ( web.representation == SIMPLEX ) { return get_simplex_next_vertex_facet(v_id,prev_id); } if ( id_type(prev_id) == FACET ) return NULLID; /* interior Lagrange point */ /* now trace loop of edges and facetedges */ start_e = get_vertex_edge(v_id); if ( !valid_id(start_e) ) return NULLID; ee_id = start_e; found = !valid_id(prev_id); do { fe_id = start_fe = get_edge_fe(ee_id); if ( valid_id(fe_id) ) do { facetedge_id ret_fe = fe_id; /* return value */ next_f = get_fe_facet(fe_id); if ( web.representation == STRING ) { if ( !valid_id(next_f) ) return NULLID; if ( !valid_id(get_prev_edge(fe_id)) && inverted(next_f) ) { /* in case facet doesn't have positive facet */ next_f = positive_id(next_f); ret_fe = inverse_id(fe_id); } } if ( !inverted(next_f) ) { if ( !valid_id(first_fe) ) first_fe = ret_fe; if ( found ) return ret_fe; if ( equal_element(fe_id,prev_id) ) found = 1; } fe_id = get_next_facet(fe_id); } while ( !equal_id(start_fe,fe_id) ); ee_id = get_next_tail_edge(ee_id); } while ( !equal_id(ee_id,start_e) ); if ( found ) return first_fe; /* looped all the way around */ return NULLID; } /*********************************************************************** vertex facet list stuff for simplex model */ /******************************************************************* * * function: insert_vertex_facet(v_id,f_id) * * purpose: fix up facet links around vertex when facet added * */ void insert_vertex_facet(v_id,f_id) vertex_id v_id; facet_id f_id; { facet_id ff_id,fff_id; int n; ff_id = get_vertex_facet(v_id); if ( !valid_id(ff_id) ) { set_next_vertex_facet(v_id,f_id,f_id); set_vertex_facet(v_id,f_id); set_vertex_facet(v_id,f_id); return; } /* go around to be sure not already there */ fff_id = ff_id; n = 0; do { fff_id = get_next_vertex_facet(v_id,fff_id); if ( equal_element(fff_id,f_id) ) return; if ( ++n > 2*web.skel[FACET].count+5 ) /* sanity check */ { kb_error(2441,"Internal error: Vertex facet loop not closed.\n",WARNING); return; } } while ( !equal_id(fff_id,ff_id) ); /* insert into existing list */ ff_id = get_next_vertex_facet(v_id,fff_id); set_next_vertex_facet(v_id,fff_id,f_id); set_next_vertex_facet(v_id,f_id,ff_id); } /******************************************************************* * * function: remove_vertex_facet(v_id,f_id) * * purpose: fix up facet links around vertex when facet removed * */ void remove_vertex_facet(v_id,f_id) vertex_id v_id; facet_id f_id; { facet_id ff_id,fff_id,ffff_id; int n = 0; /* find it */ ff_id = get_vertex_facet(v_id); if ( !valid_id(ff_id)) return; fff_id = ff_id; do { if ( equal_element(fff_id,f_id) ) break; fff_id = get_next_vertex_facet(v_id,fff_id); if ( ++n > 2*web.skel[FACET].count ) /* sanity check */ { kb_error(2442,"Internal error: Vertex facet loop not closed.\n",WARNING); break; } } while ( !equal_element(fff_id,ff_id) ); if ( !equal_id(fff_id,f_id) ) return; ff_id = get_next_vertex_facet(v_id,f_id); if ( equal_element(f_id,ff_id) ) { set_vertex_facet(v_id,NULLID); return; } fff_id = ff_id; ffff_id = get_next_vertex_facet(v_id,ff_id); n = 0; while ( !equal_element(f_id,ffff_id) ) { fff_id = ffff_id; ffff_id = get_next_vertex_facet(v_id,fff_id); if ( ++n > 2*web.skel[FACET].count ) /* sanity check */ { kb_error(2443,"Internal error: Vertex facet loop not closed.\n",WARNING); break; } } set_next_vertex_facet(v_id,fff_id,ff_id); set_next_vertex_facet(v_id,f_id,f_id); set_vertex_facet(v_id,ff_id); if ( web.representation==SIMPLEX ) set_vertex_facet(v_id,ff_id); } facet_id get_simplex_next_vertex_facet(v_id,f_id) vertex_id v_id; facet_id f_id; { int i; vertex_id *v = get_facet_vertices(f_id); facet_id *f = F_ELID(f_id,F_NEXT_VFACET_ATTR); int vmax = web.skel[FACET].ctrlpts; for ( i = 0 ; i < vmax ; i++ ) if (equal_id(v_id,v[i])) return f[i] ; sprintf(errmsg,"Internal error: get_next_vertex_facet failure v %s f %s\n", ELNAME(v_id), ELNAME1(f_id)); kb_error(1306,errmsg,RECOVERABLE); return NULLID; } void set_next_vertex_facet(v_id,f_id,ff_id) vertex_id v_id; facet_id f_id,ff_id; { vertex_id *v = get_facet_vertices(f_id); facet_id *f = F_ELID(f_id,F_NEXT_VFACET_ATTR); int vmax = web.skel[FACET].ctrlpts; int i; for ( i = 0 ; i < vmax ; i++ ) { if (equal_id(v_id,v[i])) { f[i] = ff_id ; break; } } } /* end vertex facet list stuff for simplex model ***********************************************************************/ /************************************************************************ * * function: count_fixed_vol() * * purpose: return number of fixed volumes and quantities * */ int count_fixed_vol() { int retval = 0; body_id bi_id; /* identifier for body i */ int n; struct gen_quant *gq; if ( !everything_quantities_flag && !web.pressure_flag ) FOR_ALL_BODIES(bi_id) if ( get_battr(bi_id) & (FIXEDVOL) ) retval++; for ( n = 0 ; n < gen_quant_count ; n++ ) { gq = GEN_QUANT(n); if ( gq->flags & (Q_FIXED|Q_CONSERVED) ) retval++; } return retval; } /***************************************************************** * * Random number generator using the Mersenne Twister. * * A C-program for MT19937B: Integer Version * genrand() generate one pseudorandom integer which is * uniformly distributed among the 32bit unsigned integers * sgenrand(seed) set initial values to the working area of 624 words. * sgenrand(seed) must be called once before calling genrand() * (seed is any integer except 0). * Modified to produce batch of drands by K. Brakke * Reference: Makoto Matsumoto and Takuji Nishimura, Mersenne Twister: A 623-Dimensionally Equidistributed Uniform Pseudo-Random Number Generator, ACM Transactions on Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp. 3-30. In ACM on-line Library, http://www.acm.org/pubs/citations/journals/tomacs/1998-8-1/p3-matsumoto/ */ /* LICENCE CONDITIONS: Matsumoto and Nishimura consent to GNU General Public Licence for this code. */ /* Period parameters */ #define N 624 #define NR 312 #define M 397 #define MATRIX_A 0x9908b0df /* constant vector a */ #define UPPER_MASK 0x80000000 /* most significant w-r bits */ #define LOWER_MASK 0x7fffffff /* least significant r bits */ /* for tempering */ #define TEMPERING_MASK_B 0x9d2c5680 #define TEMPERING_MASK_C 0xefc60000 #define TEMPERING_SHIFT_U(y) (y >> 11) #define TEMPERING_SHIFT_S(y) (y << 7) #define TEMPERING_SHIFT_T(y) (y << 15) #define TEMPERING_SHIFT_L(y) (y >> 18) static unsigned long ptgfsr[N]; /* set initial seeds: N = 624 words */ static REAL drands[NR]; /* batch of random doubles */ static int initflag; static REAL rfactor = 0.0; static int k = N; static int kr = NR; void sgenrand(unsigned long); void sgenrand(seed) unsigned long seed; /* seed should not be 0 */ { int k; /* setting initial seeds to ptgfsr[N] using */ /* the generator Line 25 of Table 1 in */ /* [KNUTH 1981, The Art of Computer Programming */ /* Vol. 2 (2nd Ed.), pp102] */ ptgfsr[0]= seed & 0xffffffff; for (k=1; k> 1) ^ mag01[y & 0x1]; } for (;kk> 1) ^ mag01[y & 0x1]; } y = (ptgfsr[N-1]&UPPER_MASK)|(ptgfsr[0]&LOWER_MASK); ptgfsr[N-1] = ptgfsr[M-1] ^ (y >> 1) ^ mag01[y & 0x1]; for ( k = 0 ; k < N ; k++ ) { y = ptgfsr[k]; y ^= TEMPERING_SHIFT_U(y); y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B; y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C; y &= 0xffffffff; /* you may delete this line if word size = 32 */ y ^= TEMPERING_SHIFT_L(y); ptgfsr[k] = y; } for ( kr = 0 ; kr < NR ; kr++ ) drands[kr] = (ptgfsr[2*kr]*rfactor + ptgfsr[2*kr+1])*rfactor; k = 0; kr = 0; } return drands[kr++]; } #ifdef MSC /* for METIS and other third parties */ double drand48() { return (double)kb_drand(); } void srand48(int seed) { kb_initr(seed); } #endif /*********************************************************************** * * function: wrap_vertex() * * purpose: Adjust vertex position by given wrap. * Volconst adjustments taken care of in volume calculation. * */ void wrap_vertex(v_id,wrap) vertex_id v_id; int wrap; { int i; REAL newx[MAXCOORD]; REAL *x = get_coord(v_id); edge_id e_id,start_e; if ( (web.modeltype != LINEAR) && (web.modeltype != QUADRATIC) ) kb_error(4323, "Can only do vertex wrap in LINEAR and QUADRATIC models so far.\n", RECOVERABLE); if ( !web.symmetry_flag ) return; if ( get_vattr(v_id) & (Q_MIDPOINT|Q_MIDEDGE|Q_MIDFACET) ) return; ENTER_GRAPH_MUTEX; sym_wrap(x,newx,wrap); for ( i = 0 ; i < SDIM ; i++ ) x[i] = newx[i]; start_e = e_id = get_vertex_edge(v_id); do { set_edge_wrap(e_id,sym_compose(wrap,get_edge_wrap(e_id))); if ( (web.modeltype == QUADRATIC) && !inverted(e_id) ) { x = get_coord(get_edge_midv(e_id)); sym_wrap(x,newx,wrap); for ( i = 0 ; i < SDIM ; i++ ) x[i] = newx[i]; } e_id = get_next_tail_edge(e_id); } while ( !equal_id(start_e,e_id) ); LEAVE_GRAPH_MUTEX; } evolver-2.30c.dfsg/src/geomgraph.c0000644000175300017530000004771611410765113017351 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************** * * File: geomgraph.c * * Contents: Routines for interface with geomview via pipe * Meant to coexist with mvgraph.c for minneview, * which has several things this file uses. * * Version using OFF format */ #include "include.h" static FILE *pfd = NULL; #ifdef OOGL #ifdef WIN32 #define popen _popen #define pipe(fds) _pipe(fds,256,O_BINARY) #endif static char gv_version[100]; /* geomview version string */ static char pipename[100]; /* for named pipe to geomview */ static char geom_name[100]; /* for geomview name of object */ char *base_name; /* for forming pipe name */ static int gdim; /* 3 or 4 dimensions */ /* for facets */ static float *vvlist; /* vertex list */ static struct flist_t { int num; /* number of vertices */ int v[3]; /* vertex list */ int colors; /* number of colors following */ float c[4]; /* color rgba */ } *fflist; static int *vindex; /* for translating vertex id to index */ static int vercount,vmaxcount; static int ecount; static int fcount,fmaxcount; /* for edges */ struct elist_t { float x[2][4]; /* 2 endpoints */ float c[4]; /* colors */ vertex_id v_id[2]; /* endpoints, for picking */ } *eelist; int edgecount; /* actual edges */ int edgemax; /* allocated */ /*************************************************************** * * function: Begin_geomview() * * purpose: Spawns geomview. * */ void Begin_geomview(outname) char *outname; /* if want particular name for pipe, NULL else */ { if ( geomview_flag ) kb_error(1657,"Cannot have multiple geomview displays.\n",RECOVERABLE); gv_version[0] = '0'; gv_version[1] = 0; base_name = strrchr(datafilename,PATHCHAR); if ( base_name == NULL ) base_name = datafilename; else base_name++; /* skip '/' */ /* using named pipe */ if ( outname ) strcpy(pipename,outname); else { sprintf(pipename,"/tmp/%s.XXXXXX",base_name); #ifdef LINUX /* gcc complains about mktemp, so we use mkstemp */ mkstemp(pipename); /* actually creates file */ unlink(pipename); /* so can recreate it later as pipe */ #else mktemp(pipename); #endif } /* spawn geomview */ if ( geompipe_flag == GEOM_TO_GEOMVIEW) { #ifdef WIN32 kb_error(2414,"There is no Windows version of geomview.\n",RECOVERABLE); #else int gv_pid; int to_gv_pipe[2]; pipe(gv_pipe); /* from geomview stdout */ pipe(to_gv_pipe); /* to geomview stdin */ signal(SIGINT,SIG_IGN); /* to avoid geomview 1.6 CTRL-C bug */ gv_pid = fork(); if ( gv_pid == 0 ) { /* child */ close(0); dup(to_gv_pipe[0]); close(to_gv_pipe[0]); close(to_gv_pipe[1]); close(1); dup(gv_pipe[1]); close(gv_pipe[0]); close(gv_pipe[1]); signal(SIGINT,SIG_IGN); execlp("geomview","geomview","-c","(interest (pick world))","-",NULL); perror("geomview"); /* only error gets here */ kb_error(1660,"geomview invocation failed.\n",RECOVERABLE); } /* evolver */ signal(SIGINT,catcher); close(gv_pipe[1]); close(to_gv_pipe[0]); pfd = fdopen(to_gv_pipe[1],"w"); /* hooked to stdin of geomview*/ /* get geomview version */ fprintf(pfd,"(echo (geomview-version) \"\n\")\n"); fflush(pfd); read(gv_pipe[0],gv_version,sizeof(gv_version)); if ((strcmp(gv_version,"\"1.6") > 0)&&(strcmp(gv_version,"\"1.6.1p7") < 0)) { geomview_bug_flag = 1; } #endif } else if ( geompipe_flag == GEOM_PIPE_COMMAND ) { pfd = popen(outname,"w"); if ( pfd == NULL ) { sprintf(errmsg,"%s: spawn failed.\n",outname); kb_error(2075,errmsg,RECOVERABLE); } } else /* just a pipe, without geomview */ { #ifdef WIN32 outstring("Warning: Windows named pipes do not appear as regular files,\n"); outstring("so an ordinary file will be created instead and not deleted when pipe closed.\n"); prompt("Enter file name: ",pipename,sizeof(pipename)); #else sprintf(msg,"test -p %s || (test -x /etc/mknod && /etc/mknod %s p) || (test -s /usr/sbin/mknod && /usr/sbin/mknod %s p) || (test -s /usr/bin/mkfifo && /usr/bin/mkfifo %s) || /bin/mknod %s p", pipename,pipename,pipename,pipename,pipename); if ( system(msg) != 0 ) { sprintf(errmsg,"Pipe creation failed for %s\n",pipename); kb_error(1661,errmsg,RECOVERABLE) ; } sleep(1); sprintf(msg,"Pipe name: %s\n",pipename); outstring(msg); #endif pfd = fopen(pipename,"w"); if ( pfd == NULL ) { perror(pipename); kb_error(1662,"Check that needed directories exists and try again.\n", RECOVERABLE); } } geomview_flag = 1; /* tell everybody we're initialized */ OOGL_flag = 1; /* tell everybody we're initialized */ UpdateOOGL(); /* make initial display */ } /********************************************************************* * * function: end_geomview_object() * * purpose; delete current surface from geomview display * */ void end_geomview_object() { if ( geomview_flag && geom_name[0] ) fprintf(pfd,"(delete %s)\n", geom_name); } /******************************************************************* * * function: End_geomview() * * purpose: Close down geomview process. * */ void End_geomview() { fprintf(pfd,"(exit)\n"); fclose(pfd); #ifndef WIN32 if ( geompipe_flag == GEOM_NAMED_PIPE) { sprintf(msg,"rm %s",pipename); system(msg); } #endif geomview_flag = 0; geompipe_flag = GEOM_TO_GEOMVIEW; geom_name[0] = 0; OOGL_flag = 0; pfd = NULL; } /************************************************************************ * * function: geomview_start() * * purpose: initialize for new surface in geomview. * */ void geomview_start() { vertex_id v_id; int i,k; float *vv; gdim = 3; if ( view_4D_flag ) gdim = 4; /* initialize lists for facets */ vmaxcount = vercount = web.skel[VERTEX].count; ecount = web.skel[EDGE].count; if ( ecount <= 0 ) ecount = 1; /* GV bug */ fmaxcount = web.skel[FACET].count+1; fcount = 0; vvlist = (float *)temp_calloc(vmaxcount*gdim,sizeof(float)); fflist = (struct flist_t *)temp_calloc(fmaxcount, sizeof(struct flist_t)); vindex = (int *)temp_calloc(web.skel[VERTEX].max_ord+2,sizeof(int)); /* pick lists, elements in order fed to geomview */ if ( vpicklist ) myfree((char*)vpicklist); vpicklist = (vertex_id*)mycalloc(vmaxcount,sizeof(vertex_id)); if ( fpicklist ) myfree((char*)fpicklist); fpicklist = (facet_id*)mycalloc(fmaxcount,sizeof(facet_id)); vv = vvlist; k = 0; FOR_ALL_VERTICES(v_id) { REAL *x = get_coord(v_id); for ( i = 0 ; i < gdim ; i++,vv++ ) if ( i < web.sdim ) *vv = (float)x[i]; vindex[loc_ordinal(v_id)] = k; /* so can reference by list order */ vpicklist[k] = v_id; k++; } /* initialize list for edges */ if ( web.representation == STRING ) edgemax = web.skel[EDGE].count; else edgemax = 100; edgecount = 0; eelist = (struct elist_t *)temp_calloc(edgemax,sizeof(struct elist_t)); } /*********************************************************************** * * function: geomview_edge() * * purpose: add an edge to geomview display list. * */ void geomview_edge(gdata,e_id) struct graphdata *gdata; edge_id e_id; { struct elist_t *ee; int color; int j,k; color = gdata[0].ecolor; if ( color == CLEAR ) return; if ( (color < 0) || (color >= IRIS_COLOR_MAX) ) color = DEFAULT_EDGE_COLOR; if ( edgecount >= edgemax-1 ) { eelist = (struct elist_t*)temp_realloc((char*)eelist, 2*edgemax*sizeof(struct elist_t)); edgemax *= 2; } ee = eelist + edgecount; if ( edge_rgb_color_attr > 0 ) { ee->c[0] = (float)(((color>>24)&0xFF)/255.); ee->c[1] = (float)(((color>>16)&0xFF)/255.); ee->c[2] = (float)(((color>>8)&0xFF)/255.); ee->c[3] = (float)(facet_alpha_flag ? (color&0xFF)/255. : facet_alpha); } else { for ( j = 0 ; j < 3 ; j++ ) ee->c[j] = (float)(rgb_colors[color][j]); ee->c[3] = (float)(facet_alpha); } /* record for later VECT format */ for ( k = 0 ; k < 2 ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) ee->x[k][j] = (float)gdata[k].x[j]; /* pick data */ if ( valid_id(e_id) ) { ee->v_id[0] = get_edge_tailv(e_id); ee->v_id[1] = get_edge_headv(e_id); } else { ee->v_id[0] = gdata[0].v_id; ee->v_id[1] = gdata[1].v_id; } edgecount++; } /************************************************************************ * * Function: geomview_facet() * * Purpose: Accepts facets from graphgen() and plots them. */ void geomview_facet(gdata,f_id) struct graphdata *gdata; facet_id f_id; { struct flist_t *ff; int color; int i,j,k; int dim = (web.sdim < gdim) ? web.sdim : gdim; color = gdata[0].color; if ( color == UNSHOWN ) goto do_edges; if ( (color >= IRIS_COLOR_MAX) ) color = DEFAULT_EDGE_COLOR; if ( fcount >= fmaxcount-2 ) { fflist = (struct flist_t*)temp_realloc((char*)fflist, 2*fmaxcount*sizeof(struct flist_t)); fpicklist = (facet_id*)kb_realloc((char*)fpicklist, 2*fmaxcount*sizeof(facet_id)); fmaxcount *= 2; } if ( color != CLEAR ) { ff = fflist + fcount; ff->num = FACET_VERTS; /* 3 vertices on polygon */ ff->colors = 4; if ( facet_rgb_color_attr > 0 ) { ff->c[0] = (float)(((color>>24)&0xFF)/255.); ff->c[1] = (float)(((color>>16)&0xFF)/255.); ff->c[2] = (float)(((color>>8)&0xFF)/255.); ff->c[3] = (float)(facet_alpha_flag ? (color&0xFF)/255. : facet_alpha); } else { for ( j = 0 ; j < 3 ; j++ ) ff->c[j] = (float)rgb_colors[color][j]; ff->c[3] = (float)facet_alpha; } fpicklist[fcount] = f_id; if ( gdata[0].flags & LIST_FACET ) for ( i = 0 ; i < FACET_VERTS ; i++ ) { ff->v[i] = vindex[loc_ordinal(gdata[i].v_id)]; } else if ( gdata[0].flags & SIMPLE_FACET ) /* can use regular vertices */ { facetedge_id fe = get_facet_fe(f_id); if ( valid_id(fe) ) for ( i = 0 ; i < FACET_VERTS ; i++ ) { ff->v[i] = vindex[loc_ordinal(get_fe_tailv(fe))]; fe = get_next_edge(fe); } } else /* have to add vertices to list also */ { while ( vercount + FACET_VERTS > vmaxcount ) { vvlist=(float*)temp_realloc((char*)vvlist, gdim*2*vmaxcount*sizeof(float)); vpicklist=(vertex_id*)kb_realloc((char*)vpicklist, 2*vmaxcount*sizeof(vertex_id)); vmaxcount *= 2; } for ( k = 0 ; k < FACET_VERTS ; k++,vercount++ ) { for ( j = 0 ; j < dim ; j++ ) vvlist[vercount*gdim+j] = (float)gdata[k].x[j]; vpicklist[vercount] = gdata[k].v_id; ff->v[k] = vercount; } } fcount++; } if ( gdata[0].color != gdata[0].backcolor ) if ( gdata[0].backcolor != CLEAR ) { /* add perturbed back face */ ff = fflist + fcount; ff->num = 3; /* 3 vertices on polygon */ ff->colors = 4; color = gdata[0].backcolor; if ( facet_rgb_color_attr > 0 ) { ff->c[0] = (float)(((color>>24)&0xFF)/255.); ff->c[1] = (float)(((color>>16)&0xFF)/255.); ff->c[2] = (float)(((color>>8)&0xFF)/255.); ff->c[3] = (float)(facet_alpha_flag ? (color&0xFF)/255. : facet_alpha); } else for ( j = 0 ; j < 3 ; j++ ) ff->c[j] = (float)rgb_colors[color][j]; ff->c[3] = (float)facet_alpha; fpicklist[fcount] = f_id; while ( vercount + 3 > vmaxcount ) { vvlist=(float*)temp_realloc((char*)vvlist, gdim*2*vmaxcount*sizeof(float)); vpicklist=(vertex_id*)kb_realloc((char*)vpicklist, 2*vmaxcount*sizeof(vertex_id)); vmaxcount *= 2; } for ( k = 0 ; k < 3 ; k++,vercount++ ) { for ( j = 0 ; j < dim ; j++ ) vvlist[vercount*gdim+j] = (float)(gdata[2-k].x[j] - thickness*gdata[0].norm[j]); vpicklist[vercount] = vpicklist[ff->v[k]]; ff->v[k] = vercount; } fcount++; } /* do designated edges */ do_edges: gdata[FACET_VERTS] = gdata[0]; /* last is first */ for ( k = 0 ; k < FACET_VERTS ; k++ ) if ( (gdata[k].etype & EBITS) != INVISIBLE_EDGE ) geomview_edge(gdata+k,gdata[k].id); } /******************************************************************* * * Function: geomview_end() * * Purpose: Finish off geomview stuffing. * */ void geomview_end() { int i,j,k; float *vv = vvlist; struct flist_t *ff = fflist; int ret; /* return code from fprintf or fwrite */ base_name = strrchr(datafilename,PATHCHAR); if ( base_name == NULL ) base_name = datafilename; else base_name++; /* skip '/' */ if ( !base_name || !base_name[0] ) goto g_exit; /* no surface */ setting_backcull = 0; /* so read_pick() knows */ /* if ( gdim < 4 ) */ { /* command format wrapper */ strncpy(geom_name,base_name,sizeof(geom_name)); fprintf(pfd,"(geometry %s ",geom_name); if ( transforms_flag && (transform_count>1) && !transform_colors_flag ) fprintf(pfd," { INST \nunit { = \n"); fprintf(pfd,"LIST {\n"); if ( strcmp(gv_version,"\"1.6") > 0 ) { if ( backcull_flag ) fprintf(pfd,"{appearance { backcull}}\n"); else fprintf(pfd,"{appearance {-backcull}}\n"); setting_backcull = 1; } } /* facets */ if ( fcount > 0 ) { if ( gv_binary_flag ) { if ( gdim == 3 ) ret =fprintf(pfd,"{OFF BINARY\n"); else ret =fprintf(pfd,"{4OFF BINARY\n"); if ( ret < 0 ) return; /* broken pipe */ fwrite(&vercount,1,sizeof(int),pfd); fwrite(&fcount,1,sizeof(int),pfd); fwrite(&ecount,1,sizeof(int),pfd); fwrite(vvlist,vercount*gdim,sizeof(float),pfd); fwrite(fflist,fcount,sizeof(struct flist_t),pfd); } else { if ( gdim == 3 ) ret =fprintf(pfd,"{OFF\n"); /* COFF used for vertex colors only */ else ret =fprintf(pfd,"{4OFF\n"); /* COFF used for vertex colors only */ if ( ret < 0 ) return; /* broken pipe */ fprintf(pfd,"%d %d %d\n",vercount,fcount,ecount); for ( k = 0, vv = vvlist ; (k < vercount) ; k++,vv+=gdim ) { if ( gdim == 3 ) fprintf(pfd,"%f %f %f\n",(DOUBLE)vv[0],(DOUBLE)vv[1],(DOUBLE)vv[2]); else fprintf(pfd,"%f %f %f %f\n",(DOUBLE)vv[0],(DOUBLE)vv[1],(DOUBLE)vv[2], (DOUBLE)vv[3]); } for ( k = 0, ff = fflist ; (k < fcount) ; k++,ff++ ) fprintf(pfd,"3 %d %d %d %f %f %f\n",ff->v[0],ff->v[1], ff->v[2], (DOUBLE)ff->c[0],(DOUBLE)ff->c[1],(DOUBLE)ff->c[2]); } fprintf(pfd,"}\n"); } /* end facets */ /* edges */ if ( edgecount > 0 ) { struct elist_t *ee; int new_vmax; /* append edge vertices to pick list */ if ( fcount > 0 ) { new_vmax = vercount + 2*edgecount; } else { new_vmax = 2*edgecount; vercount = 0; } gv_vect_start = vercount; vpicklist=(vertex_id*)kb_realloc((char*)vpicklist, new_vmax*sizeof(vertex_id)); for ( i = 0, ee = eelist ; i < edgecount ; i++,ee++,vercount += 2 ) { vpicklist[vercount] = ee->v_id[0]; vpicklist[vercount+1] = ee->v_id[1]; } if ( gv_binary_flag & 0 ) /* Geomview vect binary fouled up somehow */ { int tmp; if ( gdim == 4 ) fprintf(pfd,"{4VECT BINARY\n"); else fprintf(pfd,"{VECT BINARY\n"); fwrite(&edgecount,1,sizeof(int),pfd); /* NPOLYLINES */ tmp = 2*edgecount; fwrite(&tmp,1,sizeof(int),pfd); /* NVERTICES */ fwrite(&edgecount,1,sizeof(int),pfd); /* NCOLORS */ tmp = 2; for ( i = 0 ; (i < edgecount) ; i++ ) fwrite(&tmp,1,sizeof(int),pfd); /* vertices per line */ tmp = 1; for ( i = 0 ; (i < edgecount) ; i++ ) fwrite(&tmp,1,sizeof(int),pfd); /* colors per line */ for ( i = 0,ee=eelist ; (i < edgecount) ; i++,ee++ ) /* vertex coords */ { fwrite(ee->x[0],gdim,sizeof(float),pfd); /* vertices */ fwrite(ee->x[1],gdim,sizeof(float),pfd); } for ( i = 0,ee=eelist ; (i < edgecount) ; i++,ee++ ) /* line colors */ fwrite(ee->c,4,sizeof(float),pfd); /* colors */ } else { /* first line: nlines,nvertices,ncolors */ if ( gdim == 4 ) fprintf(pfd,"{4VECT \n%d %d %d\n",edgecount,2*edgecount,edgecount); else fprintf(pfd,"{VECT \n%d %d %d\n",edgecount,2*edgecount,edgecount); for ( i = 0 ; (i < edgecount) ; i++ ) fprintf(pfd,"2\n"); /* vert per edge */ for ( i = 0 ; (i < edgecount) ; i++ ) fprintf(pfd,"1\n"); /* colors per edge */ for ( i = 0,ee=eelist ; (i < edgecount) ; i++,ee++ ) /* vertex coords */ { if ( gdim == 4 ) fprintf(pfd,"%f %f %f %f %f %f %f %f\n",(DOUBLE)ee->x[0][0], (DOUBLE)ee->x[0][1], (DOUBLE)ee->x[0][2],(DOUBLE)ee->x[0][3], (DOUBLE)ee->x[1][0],(DOUBLE)ee->x[1][1],(DOUBLE)ee->x[1][2], (DOUBLE)ee->x[1][3]); else fprintf(pfd,"%f %f %f %f %f %f\n", (DOUBLE)ee->x[0][0],(DOUBLE)ee->x[0][1],(DOUBLE)ee->x[0][2], (DOUBLE)ee->x[1][0],(DOUBLE)ee->x[1][1],(DOUBLE)ee->x[1][2]); } for ( i = 0,ee=eelist ; (i < edgecount) ; i++,ee++ ) /* line colors */ fprintf(pfd,"%f %f %f %f\n",(DOUBLE)ee->c[0],(DOUBLE)ee->c[1], (DOUBLE)ee->c[2],(DOUBLE)ee->c[3]); } fprintf(pfd,"}\n"); } /* end edges */ /* end wrapping */ fprintf(pfd,"}\n"); /* end LIST */ /* transforms, if any */ if ( transforms_flag && (transform_count>1) && !transform_colors_flag ) { fprintf(pfd,"\n}\ntlist { =\n"); fprintf(pfd,"\n"); for ( k = 0 ; k < transform_count ; k++ ) { for ( i = 0 ; i < 4 ; i++ ) { for ( j = 0 ; j < 4 ; j++ ) { if ( view_4D_flag ) /* pass on 4x4 corner */ fprintf(pfd,"%f ",(DOUBLE)view_transforms[k][j][i]); else if ( (i < SDIM) && (j < SDIM) ) fprintf(pfd,"%f ",(DOUBLE)view_transforms[k][j][i]); else if ( (i < SDIM) && (j == 3) ) fprintf(pfd,"%f ",(DOUBLE)view_transforms[k][SDIM][i]); else if ( (j < SDIM) && (i == 3) ) fprintf(pfd,"%f ",(DOUBLE)view_transforms[k][j][SDIM]); else if ( (j == 3) && (i == 3) ) fprintf(pfd,"%f ",(DOUBLE)view_transforms[k][SDIM][SDIM]); else fprintf(pfd,"%f ",(DOUBLE)identmat[j][i]); } fprintf(pfd,"\n"); } fprintf(pfd,"\n"); } fprintf(pfd,"}\n}\n"); } fprintf(pfd,"\n)"); /* matches (geometry */ fprintf(pfd,"\n(event-pick on)\n"); fprintf(pfd,"(pickable %s)\n",geom_name); fprintf(pfd,"(interest (pick World))\n"); /* enable picking */ fflush(pfd); g_exit: temp_free((char*)vvlist); temp_free((char*)eelist); temp_free((char*)fflist); temp_free((char*)vindex); } #else /********************************* * stubs for non-geomview systems * *********************************/ void Begin_geomview(name) char *name; { kb_error(2076,"This Evolver not compiled for Geomview. Compile with -DOOGL.\n",RECOVERABLE); } void End_geomview() { } void end_geomview_object() { } void geomview_facet(gdata,f_id) struct graphdata *gdata; facet_id f_id; { } #endif /************************************************************************** * * function: geomview_command() * * purpose: forward a command to geoview, if active. */ void geomview_command(cmd) char *cmd; { if ( geomview_flag ) { fprintf(pfd,cmd); fflush(pfd); } else kb_error(1663,"Geomview not active.\n",WARNING); } evolver-2.30c.dfsg/src/bk.c0000644000175300017530000015746511410765113015777 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************************/ /* File: bk.c */ /* */ /* Bunch-Kaufman factoring */ /* and general linear system mongering */ /************************************************************************/ #include "include.h" /**************************************************************************/ #define RSWAP(a,b) {REAL rtmp = a; a = b ; b = rtmp; } #define RPTRSWAP(a,b) {REAL *rtmp = a; a = b ; b = rtmp; } #define ISWAP(a,b) {int rtmp = a; a = b ; b = rtmp; } /* local prototypes to keep certain compilers happy */ struct xsorting { int inx; /* original row number */ REAL x; /* x value of vertex */ int ord; /* ordinal of vertex */ int deg; /* degree of freedom */ }; void sp_aug_solve ARGS(( struct linsys *,REAL *,REAL *,REAL *,REAL *,REAL *)); int xcomp ARGS((struct xsorting *, struct xsorting*)); void BK_hess_project ARGS(( struct linsys *,REAL *,REAL *x)); /*************************************************************************** * * function: sp_aug_solve() * * purpose: common steps in Hessian solving. * */ void sp_aug_solve(S,X,B,T1,T2,T3) struct linsys *S; REAL *X; /* incoming */ REAL *B; /* incoming rhs, possibly null */ REAL *T1,*T2,*T3; /* intermediates and final, preallocated */ { if ( sparse_constraints_flag ) { int i,j; memset(T1,0,S->CN*sizeof(REAL)); for ( i = 0 ; i < S->CN ; i++ ) for ( j = S->CIA[i] ; j < S->CIA[i+1] ; j++ ) T1[i] += S->CA[j]*X[S->CJA[j]]; if ( B ) for ( i = 0 ; i < S->CN ; i++ ) T1[i] += B[i]; /* corrections */ #ifdef BLAS if ( blas_flag ) LD_block_solve(S->CHinvCinv,T1,T2,S->CN); else #endif matvec_mul(S->CHinvCinv,T1,T2,S->CN,S->CN); memset(T3,0,S->N*sizeof(REAL)); for ( i = 0 ; i < S->CN ; i++ ) for ( j = S->CIA[i] ; j < S->CIA[i+1] ; j++ ) T3[S->CJA[j]] += S->CA[j]*T2[i]; sp_solution(S,T3,T3); } else { int i; matvec_mul(S->C,X,T1,S->CN,S->N); if ( B ) for ( i = 0 ; i < S->CN ; i++ ) T1[i] += B[i]; /* corrections */ #ifdef BLAS if ( blas_flag ) LD_block_solve(S->CHinvCinv,T1,T2,S->CN); else #endif matvec_mul(S->CHinvCinv,T1,T2,S->CN,S->CN); /* Lagrange multipliers */ vec_mat_mul(T2,S->HinvC,T3,S->CN,S->N); } } /******************************************************************* * * function: bk_mul() * * purpose: multiply vector by original sparse matrix */ void bk_mul(S,w,v) struct linsys *S; REAL *w; /* in */ REAL *v; /* out */ { int row,col,i; PROF_START(hessian_mul); memset((char*)v,0,S->N*sizeof(REAL)); for ( row = 0 ; row < S->N ; row++ ) { for ( i = S->IA[row]-A_OFF ; i < S->IA[row+1]-A_OFF ; i++ ) { col = S->JA[i] - A_OFF; v[row] += S->A[i]*w[col]; if ( col != row ) v[col] += S->A[i]*w[row]; } } if ( (S->CN > 0) && S->CHinvCinv ) { /* project back */ REAL *tempv = (REAL*)temp_calloc(2*S->N,sizeof(REAL)); REAL *tempvv = tempv + S->N; sp_aug_solve(S,v,NULL,tempv,tempvv,tempv); for ( i = 0 ; i < S->N ; i++ ) v[i] -= tempv[i]; temp_free((char*)tempv); } PROF_FINISH(hessian_mul); } /* end bk_mul() */ /******************************************************************* * * function: free_system() * * purpose: free all memory allocated to linear system structure. */ void free_system(S) struct linsys *S; { if ( S->IA ) temp_free((char*)S->IA); if ( S->JA && !(S->flags & S_JA_INCLUDED) ) temp_free((char*)S->JA); if ( S->A ) temp_free((char*)S->A); if ( S->pIA ) temp_free((char*)S->pIA); if ( S->pJA ) temp_free((char*)S->pJA); if ( S->pA ) temp_free((char*)S->pA); if ( S->P ) temp_free((char*)S->P); if ( S->IP ) temp_free((char*)S->IP); if ( S->ISP ) temp_free((char*)S->ISP); if ( S->psize ) temp_free((char*)S->psize); if ( S->LIA ) temp_free((char*)S->LIA); if ( S->LJA ) temp_free((char*)S->LJA); if ( S->LIJA ) temp_free((char*)S->LIJA); if ( S->LA ) temp_free((char*)S->LA); if ( S->CIA ) temp_free((char*)S->CIA); if ( S->CJA ) temp_free((char*)S->CJA); if ( S->CA ) temp_free((char*)S->CA); if ( S->coninx ) temp_free((char*)S->coninx); if ( S->coninxinv ) temp_free((char*)S->coninxinv); if ( S->rowmag ) temp_free((char*)S->rowmag); if ( S->apinv ) temp_free((char*)S->apinv); if ( S->HinvC ) free_matrix(S->HinvC); if ( S->CHinvCinv ) { if ( blas_flag ) { temp_free((char*)S->CHinvCinv[0]); temp_free((char*)S->CHinvCinv); } else free_matrix(S->CHinvCinv); } if ( S->C ) { temp_free((char *)S->C[0]); temp_free((char*)S->C);} if ( S->stree ) { int k; for ( k = 0 ; k <= S->streemax ; k++ ) { if ( S->stree[k].u.info.mat) { temp_free((char*)S->stree[k].u.info.mat); temp_free((char*)S->stree[k].u.info.vlist); } } } if ( S->low_rank_vectors ) free_matrix(S->low_rank_vectors); if ( S->low_rank_form ) free_matrix(S->low_rank_form); if ( S->low_rank_inverse_form ) free_matrix(S->low_rank_inverse_form); memset((char*)S,0,sizeof(struct linsys)); } /* end free_system() */ /************************************************************************ * * function: find_ordering() * * purpose: find order of vertex degree of freedom variables. * Currently in x coordinate order. Not very useful. * in: array Hessian data * n total degrees * out: P permutation vector, P[0] is first variable to do. */ /* comparison function for sorting */ int xcomp(a,b) struct xsorting *a,*b; { if ( a->x < b->x ) return -1; if ( a->x > b->x ) return 1; if ( a->ord < b->ord ) return -1; if ( a->ord > b->ord ) return 1; if ( a->deg < b->deg ) return -1; if ( a->deg > b->deg ) return 1; return 0; } /* end xcomp() */ void find_ordering(verlist,n,P) struct hess_verlist *verlist; /* pointers to rows */ int n; /* size of system */ int *P; /* preallocated for return of permutation */ { int i,j; struct xsorting *xlist,*Bptr; vertex_id v_id; /* sort vertices in x order */ xlist = (struct xsorting *)temp_calloc(n,sizeof(struct xsorting)); for ( i = 0, Bptr = xlist ; i < n ; i++,Bptr++ ) Bptr->inx = i; FOR_ALL_VERTICES(v_id) { struct hess_verlist *v; /* current vertex */ v = verlist + loc_ordinal(v_id); Bptr = xlist + v->rownum; for ( j = 0 ; j < v->freedom ; j++, Bptr++ ) { Bptr->ord = loc_ordinal(v->v_id); Bptr->deg = j; Bptr->x = get_coord(v->v_id)[0]; } } qsort((char*)xlist,n,sizeof(struct xsorting),FCAST xcomp); for ( i = 0 ; i < n ; i++ ) { P[i] = xlist[i].inx + A_OFF; } temp_free((char*)xlist); } /* end find_ordering() */ /********************************************************************** * * function: bk_AIJ_setup() * * purpose: convert raw Hessian data to standard sparse format * */ void bk_AIJ_setup(N,S) int N; /* size of system */ struct linsys *S; /* pointer to empty structure */ { #ifdef OLDHASH int i,j,n; int total = 0; struct hess_entry *e; int spot; int *ptr,*cptr,*dptr; int isize,dsize; PROF_START(hessian_AIJ_setup); S->flags &= ~ S_ODRV_REORDERED; S->flags |= S_JA_INCLUDED; /* S->JA not separately allocated */. if ( augmented_hessian_mode ) N = total_rows; S->N = N; for ( i = 0 ; i < N ; i++ ) total += array[i].count; /* incoming list of hess_entry structs is compacted in place */ isize = sizeof(int); dsize = sizeof(REAL); /* delete row numbers */ e = hashtable; ptr = (int*)e; if ( 2*isize == dsize ) for ( n = 0; n < total; n++,e++,ptr+=3 ) { ptr[0] = ((int*)e)[0]; ptr[1] = ((int*)e)[1]; ptr[2] = e->col; } else if ( isize == dsize ) for ( n = 0; n < total; n++,e++,ptr+=2 ) { ptr[0] = ((int*)e)[0]; ptr[1] = e->col; } else if ( 4*isize == dsize ) for ( n = 0; n < total; n++,e++,ptr+=5 ) { ptr[0] = ((int*)e)[0]; ptr[1] = ((int*)e)[1]; ptr[2] = ((int*)e)[2]; ptr[3] = ((int*)e)[3]; ptr[4] = e->col; } else /* i.e. 10-byte long double */ { char *p = (char*)ptr; for ( n = 0; n < total; n++,e++,p+=dsize+isize ) { *(REAL *)p = e->value; *(int *)(p+dsize) = e->col; } ptr = (int *)p; } /* now extract cols to room at the end */ S->JA = cptr = ptr; S->A = (REAL*)(hashtable); hashtable = NULL; ptr = (int*)(S->A); dptr = ptr; if ( 2*isize == dsize ) for ( n = 0 ; n < total; n++,dptr+=3,ptr+=2,cptr++) { ptr[0] = dptr[0]; ptr[1] = dptr[1]; *cptr = dptr[2]+A_OFF; } else if ( isize == dsize ) for ( n = 0 ; n < total; n++,dptr+=2,ptr+=1,cptr++) { ptr[0] = dptr[0]; *cptr = dptr[1]+A_OFF; } else if ( 4*isize == dsize ) for ( n = 0 ; n < total; n++,dptr+=5,ptr+=4,cptr++) { ptr[0] = dptr[0]; ptr[1] = dptr[1]; ptr[2] = dptr[2]; ptr[3] = dptr[3]; *cptr = dptr[4]+A_OFF; } else { char *p = (char *)ptr; REAL *d = (REAL *)ptr; for ( n = 0 ; n < total; n++,p+=isize+dsize,d++,cptr++ ) { *d = *(REAL*)p; *cptr = *(int*)(p+dsize)+A_OFF; } } /* allocate other bits */ S->IA = (int *)temp_calloc(N+1,sizeof(int)); if ( S->P == NULL ) S->P = (int *)temp_calloc(N,sizeof(int)); if ( S->IP == NULL ) S->IP = (int *)temp_calloc(N,sizeof(int)); S->flags &= ~ S_ODRV_REORDERED; for ( i = 0, spot = 0 ; i < N ; i++ ) { S->IA[i] = spot + A_OFF; spot += harray[i].count; } S->IA[N] = spot + A_OFF; /* test for NaN's */ for ( i = 0 ; i < S->IA[S->N]-A_OFF ; i++ ) if ( !is_finite(S->A[i]) ) { kb_error(1822,"NaNs in Hessian. Replacing with big value.\n",WARNING); S->A[i] = 1e30; } PROF_FINISH(hessian_AIJ_setup); #endif } /* end bk_AIJ_setup() */ /********************************************************************** * * function: bk_constraint_setup() * * purpose: get matrices needed for handling constraints * */ void bk_constraint_setup(concount,S) int concount; /* number of constraints */ struct linsys *S; { int i,j; PROF_START(hessian_constraint_setup); if ( S->CN == 0 ) { S->N = S->A_rows; goto debug_print; } if ( S->A_rows < S->CN ) { sprintf(errmsg,"Degrees of freedom, %d, fewer than constraints, %d.\n", S->A_rows,S->CN); kb_error(3382,errmsg,RECOVERABLE); } if ( augmented_hessian_mode ) { /* Keeping constraints in sparse augmented hessian. */ /* So not much to do, except eliminate unused constraints, */ /* building constraint conversion index in the process, */ /* and make sure zero elements exist on diagonal for constraints. */ if ( S->A_rows + S->CN > S->maxN ) kb_error(2549,"Internal error: S->N got bigger than expected.\n", RECOVERABLE); S->N = S->A_rows + S->CN; /* convert entries in S->JA */ for ( i = 0 ; i < S->IA[S->A_rows]-A_OFF ; i++ ) if ( S->JA[i]-A_OFF >= S->A_rows ) S->JA[i] = S->A_rows + S->coninx[S->JA[i]-A_OFF-S->A_rows] + A_OFF; /* create diagonal for constraints */ for ( i = 1 ; i <= S->CN ; i++ ) { S->IA[i+S->A_rows] = S->IA[i+S->A_rows-1] + 1; S->JA[S->IA[i+S->A_rows-1]-A_OFF] = i + S->A_rows - 1 + A_OFF; S->A[S->IA[i+S->A_rows-1]-A_OFF] = 0.0; } if ( S->IA[S->N] > S->maxA ) kb_error(2598,"Internal error: S->A got bigger than expected.\n", RECOVERABLE); } else if ( sparse_constraints_flag ) { /* Have to strip constraint gradients from A and put in CA */ int k,kk; /* First, count entries for each constraint */ S->CIA = (int *)temp_calloc(concount+1,sizeof(int)); for ( i = S->A_rows ; i > 0 ; i-- ) { for ( j = S->IA[i]-A_OFF - 1 ; j > S->IA[i-1]-A_OFF ; j-- ) if ( S->JA[j]-A_OFF >= S->A_rows ) S->CIA[S->JA[j]-A_OFF - S->A_rows]++; else break; } /* Running totals to S->CIA */ for ( i = 0, j = 0 ; i < concount ; i++ ) { int tmp = S->CIA[i]; S->CIA[i] = j; j += tmp; } S->CJA = (int *)temp_calloc(j,sizeof(int)); S->CA = (REAL*)temp_calloc(j,sizeof(REAL)); for ( i = concount ; i > 0 ; i-- ) S->CIA[i] = S->CIA[i-1]; /* place constraint gradients and compact A,JA */ for ( i = 1, kk = 0, j = 0 ; i <= S->A_rows ; i++ ) { for ( ; j < S->IA[i]-A_OFF ; j++ ) if ( S->JA[j]-A_OFF >= S->A_rows ) { k = S->CIA[S->JA[j]-A_OFF-S->A_rows+1]++; S->CJA[k] = i-1; S->CA[k] = S->A[j]; } else { S->JA[kk] = S->JA[j]; S->A[kk] = S->A[j]; kk++; } S->IA[i] = kk+A_OFF; } S->N = S->A_rows; /* check the constraints we thought were going to be used actually are */ for ( i = 0 ; i < S->concount ; i++ ) { /* a check on if any fixed constraints are null */ if ( (S->CIA[i] != S->CIA[i+1]) && (S->coninx[i] == -1) ) { struct hess_verlist *vh = vhead + web.skel[VERTEX].max_ord+1+ optparamcount + i; if ( valid_id(vh->v_id) ) sprintf(errmsg, "Internal error: hessian setup skipped fixed body %s.\n", ELNAME(vh->v_id)); else sprintf(errmsg, "Internal error: hessian setup skipped fixed quantity %s.\n", GEN_QUANT(vh->v_id)->name); kb_error(2552,errmsg,RECOVERABLE); } if ( (S->CIA[i] == S->CIA[i+1]) && (S->coninx[i] != -1) ) { struct hess_verlist *vh = vhead + web.skel[VERTEX].max_ord+1+ optparamcount + i; if ( valid_id(vh->v_id) ) sprintf(errmsg, "Fixed body %s is not connected to any mobile vertices.\n", ELNAME(vh->v_id)); else sprintf(errmsg, "Fixed quantity %s is not connected to any mobile vertices.\n", GEN_QUANT(vh->v_id)->name); kb_error(3362,errmsg,RECOVERABLE); } } /* Strip out empty constraints */ for ( i = 0 ; i < S->CN ; i++ ) S->CIA[i+1] = S->CIA[S->coninxinv[i]+1]; } else /* dense constraints */ { int startj,newj; S->C = (REAL**)temp_calloc(S->CN,sizeof(REAL*)); S->C[0] = (REAL*)temp_calloc(S->CN*S->N,sizeof(REAL)); for ( i = 1 ; i < S->CN ; i++ ) S->C[i] = S->C[0] + i*S->N; for ( i = 0, startj = 0, newj = 0 ; i < S->N ; i++ ) { /* fill in C (really C transpose) */ /* and compact JA, A */ for ( j = startj ; j < S->IA[i+1]-A_OFF ; j++ ) if ( S->JA[j]-A_OFF >= S->A_rows ) { int row = S->coninx[S->JA[j]-A_OFF - S->A_rows]; S->C[row][i] = S->A[j]; } else /* keep in A but move it down */ { S->A[newj] = S->A[j]; S->JA[newj] = S->JA[j]; newj++; } startj = S->IA[i+1] - A_OFF; /* for next loop */ S->IA[i+1] = newj + A_OFF; } } debug_print: /* some debug printing */ if ( hess_debug ) { vertex_id v_id; printf("N = %d\n",S->N); printf("IA: "); for ( i = 0 ; i <= S->N ; i++ ) printf(" %d",S->IA[i]); printf("\nJA: "); for ( i = 0 ; i < S->IA[S->N]-A_OFF ; i++ ) printf(" %d",S->JA[i]); printf("\n"); /* column labels */ MFOR_ALL_VERTICES(v_id) { struct hess_verlist *vh = get_vertex_vhead(v_id); for ( j = 0 ; j < vh->freedom ; j++ ) { sprintf(msg,"v%s.%d",ELNAME(v_id),j+1); printf("%10s",msg); } } for ( i = 0 ; i < optparamcount ; i++ ) printf("%10s",globals(optparam[i].pnum)->name); if ( augmented_hessian_mode ) { if ( !everything_quantities_flag ) { for ( i = 0 ; i <= web.skel[BODY].max_ord ; i++ ) if ( S->coninx[i] >= 0 ) { sprintf(msg,"b%d",i+1); printf("%10s",msg); } for ( i = 0 ; i < gen_quant_count ; i++ ) if ( S->coninx[i + web.skel[BODY].max_ord+1] >= 0 ) { sprintf(msg,"q%d",i+1); printf("%10s",msg); } } else /* just the quantities */ for ( i = 0 ; i < gen_quant_count ; i++ ) if ( S->coninx[i] >= 0 ) { sprintf(msg,"q%d",i+1); printf("%10s",msg); } } printf("\n"); /* now the body of the sparse part of the hessian, just upper triangle */ for ( i = 0 ; i < S->N ; i++ ) { int k,m; for ( m = 0 ; m < i ; m++ ) printf(" "); for ( m = i, k = S->IA[i]-A_OFF ; m < S->N ; m++ ) if ( (m == S->JA[k]-A_OFF) && (k < S->IA[i+1]-A_OFF) ) { printf(" %9.6f",(DOUBLE)S->A[k]); k++; } else printf(" %9.6f",0.0); printf("\n"); } /* Add on low-rank update */ if ( S->low_rank ) { printf("With low-rank update: \n"); for ( i = 0 ; i < S->N ; i++ ) { int k,m,ii,jj; for ( m = 0 ; m < i ; m++ ) printf(" "); for ( m = i, k = S->IA[i]-A_OFF ; m < S->N ; m++ ) { REAL val; if ( (m == S->JA[k]-A_OFF) && (k < S->IA[i+1]-A_OFF) ) { val = S->A[k]; k++; } else val = 0.0; /* add low-rank stuff */ if ( m < S->low_rank_vecsize ) for ( ii = 0 ; ii < S->low_rank ; ii++ ) for ( jj = 0 ; jj < S->low_rank ; jj++ ) val += S->low_rank_vectors[ii][i]*S->low_rank_form[ii][jj]* S->low_rank_vectors[jj][m]; printf(" %9.6f",(DOUBLE)val); } printf("\n"); } } if ( !augmented_hessian_mode ) { printf("Constraints:\n"); if ( sparse_constraints_flag ) { for ( i = 0 ; i < S->CN ; i++ ) { int k = 0; for ( j = S->CIA[i] ; j < S->CIA[i+1] ; j++ ) { for ( ; k < S->CJA[j]-1 ; k++ ) printf("%9.6f ",0.0); printf("%9.6f ",S->CA[j]); k = S->CJA[j]; } } } else { for ( i = 0 ; i < S->CN ; i++ ) { printf("C%d:",i+1); for ( j = 0 ; j < S->N ; j++ ) printf(" %9.6f",(double)(S->C[i][j])); printf("\n"); } } printf("\n"); } } /* end hess_debug */ PROF_FINISH(hessian_constraint_setup); } /* end bk_constraint_setup */ /********************************************************************* * * function: BK_hess_project_setup() * * purpose: Set up projection to constraints using hessian metric. * Forms S->HinvC and CHinvCinv, unless augmented_hessian * is in effect. */ void BK_hess_project_setup(S) struct linsys *S; { int i; int con_index=0; PROF_START(hessian_project_setup); if ( S->CN == 0 ) goto set_counts; if ( augmented_hessian_mode ) goto adjust_index; if ( !sparse_constraints_flag ) { /* HinvC */ if ( S->HinvC ) free_matrix(S->HinvC); S->HinvC = dmatrix(0,S->CN-1,0,S->N-1); for ( i = 0 ; i < S->CN ; i++ ) sp_solution(S,S->C[i],S->HinvC[i]); } if ( S->CHinvCinv ) free_matrix(S->CHinvCinv); #ifdef BLAS if ( blas_flag ) { int fillsize = ((S->CN+BLAS_BLOCKSIZE)*(S->CN+BLAS_BLOCKSIZE+1))/2; S->CHinvCinv = (REAL**)temp_calloc(S->CN,sizeof(REAL*)); S->CHinvCinv[0] = (REAL*)temp_calloc(fillsize,sizeof(REAL)); for ( i = 0 ; i < S->CN ; i += BLAS_BLOCKSIZE ) { int j; for ( j = i ; (j < i+BLAS_BLOCKSIZE) && (j < S->CN-1) ; j++ ) { S->CHinvCinv[j+1] = S->CHinvCinv[j] + i + BLAS_BLOCKSIZE; } } } else #endif S->CHinvCinv = dmatrix(0,S->CN-1,0,S->CN-1); if ( sparse_constraints_flag ) { if ( sp_CHinvC_func == NULL ) kb_error(2445,"sparse_constraints need ysmp off (mindeg mode).\n", RECOVERABLE); (*sp_CHinvC_func)(S); } else { /* CHinvCinv */ #ifdef BLAS if ( blas_flag ) { for ( i = 0 ; i < S->CN ; i += BLAS_BLOCKSIZE ) { int rowcount = (S->CN-i < BLAS_BLOCKSIZE) ? S->CN-i : BLAS_BLOCKSIZE; int colcount = (S->CN < i+BLAS_BLOCKSIZE) ? S->CN : i+BLAS_BLOCKSIZE; int transa = 'T'; int transb = 'N'; REAL alpha = 1.0; REAL beta = 0.0; int stridea = i+BLAS_BLOCKSIZE; int strideb = S->C[1]-S->C[0]; /* WHoops! rows individually alloc! */ int stridec = S->CHinvCinv[1] - S->CHinvCinv[0]; mat_mul_tr(S->HinvC+i,S->C,S->CHinvCinv+i,rowcount,S->N,colcount); /* DGEMM(&transa,&transb,&rowcount,&colcount,&S->N,&alpha,S->HinvC[i], &stridea,S->C[0],&strideb,&beta,S->CHinvCinv[i],&stridec); */ } } else #endif mat_mul_tr(S->HinvC,S->C,S->CHinvCinv,S->CN,S->N,S->CN); } #ifdef BLAS if ( blas_flag ) con_index = LD_block_factor(S->CHinvCinv,S->CN); else #endif /* Kludge here since mat_inv() doesn't calculate index correctly */ /* con_index = matrix_index(S->CHinvCinv,S->CN); mat_inv(S->CHinvCinv,S->CN); */ con_index = mat_inv_sym(S->CHinvCinv,S->CN); if ( con_index < 0 ) kb_error(1823,"Constraints not independent, or Hessian too singular.\n", RECOVERABLE); adjust_index: /* adjust index for constraints */ if ( augmented_hessian_mode ) { S->neg -= (S->CN - S->degencon); S->pos -= (S->CN - S->degencon); S->zero -= S->degencon; } else { S->neg -= con_index; S->pos -= S->CN - con_index; } set_counts: eigen_neg = S->neg; eigen_pos = S->pos; PROF_FINISH(hessian_project_setup); } /* end BK_hess_project_setup */ /************************************************************************* * * function: BK_hess_project() * * purpose: project vector onto constraints of system using Hessian metric * */ void BK_hess_project(S,B,x) struct linsys *S; /* factored and constrained system */ REAL *B; /* incoming vector */ REAL *x; /* solution, may be incoming */ { REAL *T1,*T2,*T3; int i; if ( S->CN == 0 ) return; T1 = (REAL *)temp_calloc(2*S->CN+S->N,sizeof(REAL)); T2 = T1 + S->CN; T3 = T2 + S->CN; sp_aug_solve(S,B,NULL,T1,T2,T3); for ( i = 0 ; i < S->N ; i++ ) x[i] = B[i] - T3[i]; temp_free((char*)T1); } /* end BK_hess_project */ /*********************************************************************** * * function: lowest_eigenpair() * * purpose: find lowest eigenvalue and corresponding eigenvalue * * return: lowest eigenvalue; */ REAL lowest_eigenpair(S,v) struct linsys *S; REAL *v; /* eigenvector, preallocated */ { REAL lo; int old_quiet; #ifdef XXX REAL old_ev,new_ev; int i; int krydim = 20; int nlook = 2; REAL evalues[5]; REAL *vtmp = (REAL*)temp_calloc(S->N,sizeof(REAL)); #endif /* find lower bound on lowest eigenvalue */ lo = -0.01; S->lambda = lo; sp_factor(S); (*sp_hess_project_setup_func)(S); while ( S->neg > 0) { S->lambda *= 10; sp_factor(S); (*sp_hess_project_setup_func)(S); } old_quiet = quiet_flag; quiet_flag = 1; do_ritz(S,S->lambda,1,&v); quiet_flag = old_quiet; return last_eigenvalue; #ifdef XXX //lanczos(S,krydim,evalues,nlook); /* inverse iteration to find eigenvector */ S->lambda = evalues[0] - .0001; sp_factor(S); (*sp_hess_project_setup_func)(S); for ( i = 0 ; i < S->N ; i++ ) v[i] = drand48(); old_ev = new_ev = 11231.0; /* weird number, nonzero */ do { sp_hessian_solve(S,v,vtmp,NO_SET_PRESSURE); old_ev = new_ev; new_ev = sqrt(dot(vtmp,vtmp,S->N)); for ( i = 0 ; i < S->N ; i++ ) v[i] = vtmp[i]/new_ev; } while ( fabs(1/old_ev-1/new_ev) > 1000*machine_eps ); temp_free((char*)vtmp); if ( S->zero != 0 ) last_eigenvalue = S->lambda; else last_eigenvalue = S->lambda + 1/new_ev; return S->lambda + 1/new_ev; #endif } /* end lowest_eigenpair */ /*********************************************************************** * * function: cg_lowest_eigenpair() * * purpose: find lowest eigenvalue and corresponding eigenvalue * Uses conjugate gradient on sphere to minimize XHX * Hessian_metric used. * * return: lowest eigenvalue; */ REAL old_cg_lowest_eigenpair(S,x) struct linsys *S; REAL *x; /* eigenvector, preallocated */ { REAL norm_inv; int i; REAL *h=NULL; /* search direction */ REAL *ah=NULL; REAL *f=NULL,*If=NULL; /* gradient of XAX, form and vector */ REAL *mx=NULL; /* MX */ REAL *mh=NULL; /* MH */ REAL cgamma; /* cg coefficient */ REAL xax,old_xax; int count; int maxcount; char response[100]; REAL **CCinv=NULL; REAL *Cf=NULL,*Gf=NULL; h = (REAL *)temp_calloc(S->N,sizeof(REAL)); ah = (REAL *)temp_calloc(S->N,sizeof(REAL)); f = (REAL *)temp_calloc(S->N,sizeof(REAL)); if ( web.area_norm_flag || hessian_linear_metric_flag ) { mx = (REAL *)temp_calloc(S->N,sizeof(REAL)); mh = (REAL *)temp_calloc(S->N,sizeof(REAL)); } if ( S->CN ) { CCinv = dmatrix(0,S->CN,0,S->CN); mat_mul_tr(S->C,S->C,CCinv,S->CN,S->N,S->CN); mat_inv(CCinv,S->CN); Cf = (REAL*)temp_calloc(S->CN,sizeof(REAL)); Gf = (REAL*)temp_calloc(S->CN,sizeof(REAL)); } /* initial random guess */ for ( i = 0 ; i < S->N ; i++ ) x[i] = drand48() - .5; /* project to constraints */ if ( S->CN ) { matvec_mul(S->C,x,Cf,S->CN,S->N); matvec_mul(CCinv,Cf,Gf,S->CN,S->CN); for ( i = 0 ; i < S->CN ; i++ ) vector_add_smul(x,S->C[i],-Gf[i],S->N); } if ( web.area_norm_flag || hessian_linear_metric_flag ) norm_inv = 1/sqrt(sparse_metric_dot(x,x,&Met)); else norm_inv = 1/sqrt(dot(x,x,S->N)); for ( i = 0 ; i < S->N ; i++ ) x[i] *= norm_inv; xax = 1e30; /* silly value for start of convergence test */ for(;;) { prompt("Enter max iterations: ",response,sizeof(response)); maxcount = atoi(response); if ( maxcount == 0 ) break; if ( maxcount < 0 ) { count = 0 ; maxcount = -maxcount; } else count = 0; do { REAL a,b,c,evalue,denom,q2,q1comp,hnorm,hnorm_inv; old_xax = xax; count++; /* get cg search direction */ (*sp_mul_func)(S,x,f); /* AX */ xax = dot(x,f,S->N); if ( web.area_norm_flag || hessian_linear_metric_flag ) (*sp_mul_func)(&Met,x,mx); else mx = x; for ( i = 0 ; i < S->N ; i++ ) f[i] -= xax*mx[i]; /* convert form f to vector If with cg metric I */ If = f; /* project to constraints */ if ( S->CN ) { matvec_mul(S->C,If,Cf,S->CN,S->N); matvec_mul(CCinv,Cf,Gf,S->CN,S->CN); for ( i = 0 ; i < S->CN ; i++ ) vector_add_smul(If,S->C[i],-Gf[i],S->N); } /* project If tangent to Steifel manifold */ if ( web.area_norm_flag || hessian_linear_metric_flag ) c = sparse_metric_dot(x,If,&Met); else c = dot(x,If,S->N); for ( i = 0 ; i < S->N ; i++ ) If[i] -= c*x[i]; /* compute Ah */ /* proper Hessian is A - XAX M */ (*sp_mul_func)(S,h,ah); if ( web.area_norm_flag || hessian_linear_metric_flag ) (*sp_mul_func)(&Met,h,mh); else mh = h; for ( i = 0 ; i < S->N ; i++ ) ah[i] -= xax*mh[i]; /* compute gamma */ if ( count <= 1 ) cgamma = 0.0; else { cgamma = -dot(If,ah,S->N)/dot(h,ah,S->N); } /* compute search direction */ for ( i = 0 ; i < S->N ; i++ ) h[i] = If[i] + cgamma*h[i]; if ( web.area_norm_flag || hessian_linear_metric_flag ) hnorm = sqrt(sparse_metric_dot(h,h,&Met)); else hnorm = sqrt(dot(h,h,S->N)); hnorm_inv = 1/hnorm; /* recompute Ah */ (*sp_mul_func)(S,h,ah); #ifdef XXX projecting off XAX M seems to hurt if ( web.area_norm_flag || hessian_linear_metric_flag ) (*sp_mul_func)(&Met,h,mh); else mh = h; for ( i = 0 ; i < S->N ; i++ ) ah[i] -= xax*mh[i]; #endif /* find minimum along geodesic */ a = xax; b = dot(x,ah,S->N)/hnorm; c = dot(h,ah,S->N)/hnorm/hnorm; evalue = 0.5*(a+c-sqrt((a-c)*(a-c)+4*b*b)); /* smallest ev */ denom = sqrt(b*b + (evalue - a)*(evalue - a)); if ( b < 0.0 ) denom = -denom; q2 = (evalue - a)/denom; q1comp = (evalue-a)*(evalue-a)/denom/(denom+b); /* rotate h and x */ for ( i = 0 ; i < S->N ; i++ ) { REAL xtmp = x[i]*hnorm; REAL htmp = h[i]*hnorm_inv; x[i] += -q1comp*x[i] + q2*htmp; h[i] += -q2*xtmp -q1comp*h[i]; } if ( (count < maxcount) && (maxcount > 5) && (count % (maxcount/5) == 0) ) { #ifdef LONGDOUBLE sprintf(msg,"%3d. %3.*Lg\n",count,DPREC,xax); #else sprintf(msg,"%3d. %3.17g\n",count,xax); #endif outstring(msg); } } while ( (fabs(xax - old_xax) > 10*machine_eps*fabs(xax)) && (countCN ) { temp_free((char*)Gf); temp_free((char*)Cf); free_matrix(CCinv); } last_eigenvalue = xax; return xax; } /* end old_cg_lowest_eigenpair */ /*********************************************************************** * * function: cg_lowest_eigenpair() * * purpose: find lowest eigenvalue and corresponding eigenvalue * Uses conjugate gradient on sphere to minimize XHX * Hessian_metric used. * * return: lowest eigenvalue; */ REAL cg_lowest_eigenpair(S,x) struct linsys *S; REAL *x; /* eigenvector, preallocated */ { REAL norm_inv; int i,j; REAL *h; /* search direction */ REAL *ah; /* (A-XAX M)H */ REAL *ax; /* AX */ REAL *mx; /* MX */ REAL *mh; /* MH */ REAL xax,old_xax; int count; int maxcount; char response[100]; REAL *Cf,*Gf; REAL **P; /* matrix of coeff for Lagrange mult */ REAL **Pinv; /* inverse of P */ h = (REAL *)temp_calloc(S->N,sizeof(REAL)); ah = (REAL *)temp_calloc(S->N,sizeof(REAL)); ax = (REAL *)temp_calloc(S->N,sizeof(REAL)); if ( web.area_norm_flag || hessian_linear_metric_flag ) { mx = (REAL *)temp_calloc(S->N,sizeof(REAL)); mh = (REAL *)temp_calloc(S->N,sizeof(REAL)); } else { mx = x; mh = h; } P = dmatrix(0,S->CN+1,0,S->CN+1); Pinv = dmatrix(0,S->CN+1,0,S->CN+1); Cf = (REAL*)temp_calloc(S->CN+2,sizeof(REAL)); Gf = (REAL*)temp_calloc(S->CN+2,sizeof(REAL)); if ( S->CN ) { /* CGC goes in upper left corner of P */ mat_mul_tr(S->C,S->C,P,S->CN,S->N,S->CN); } /* initial random guess */ for ( i = 0 ; i < S->N ; i++ ) x[i] = drand48() - .5; /* project to constraints */ if ( S->CN ) { matvec_mul(S->C,x,Cf,S->CN,S->N); matcopy(Pinv,P,S->CN,S->CN); mat_inv(Pinv,S->CN); matvec_mul(Pinv,Cf,Gf,S->CN,S->CN); for ( i = 0 ; i < S->CN ; i++ ) vector_add_smul(x,S->C[i],-Gf[i],S->N); } if ( web.area_norm_flag || hessian_linear_metric_flag ) norm_inv = 1/sqrt(sparse_metric_dot(x,x,&Met)); else norm_inv = 1/sqrt(dot(x,x,S->N)); for ( i = 0 ; i < S->N ; i++ ) x[i] *= norm_inv; xax = 1e30; /* silly value for start of convergence test */ for(;;) { prompt("Enter max iterations: ",response,sizeof(response)); maxcount = atoi(response); if ( maxcount == 0 ) break; if ( maxcount < 0 ) { count = 0 ; maxcount = -maxcount; } else count = 0; do { REAL a,b,c,evalue,denom,q2,q1comp,hnorm,hnorm_inv; int pdim; old_xax = xax; count++; /* get cg search direction */ /* calculate needed vectors */ /* MX */ if ( web.area_norm_flag || hessian_linear_metric_flag ) (*sp_mul_func)(&Met,x,mx); /* MH */ if ( web.area_norm_flag || hessian_linear_metric_flag ) (*sp_mul_func)(&Met,h,mh); /* (A - XAX M)X */ (*sp_mul_func)(S,x,ax); xax = dot(x,ax,S->N); for ( i = 0 ; i < S->N ; i++ ) ax[i] -= xax*mx[i]; /* (A - XAX M)H */ (*sp_mul_func)(S,h,ah); for ( i = 0 ; i < S->N ; i++ ) ah[i] -= xax*mh[i]; /* fill in P, which already has CGC in upper left */ if ( S->CN > 0 ) { matvec_mul(S->C,mx,P[S->CN],S->CN,S->N); matvec_mul(S->C,ah,P[S->CN+1],S->CN,S->N); } P[S->CN][S->CN] = dot(mx,mx,S->N); P[S->CN+1][S->CN] = dot(ah,mx,S->N); #ifdef SYMMETRICWAY /* this way takes twice as long */ P[S->CN+1][S->CN+1] = dot(ah,ah,S->N); for ( i = 0 ; i <= S->CN ; i++ ) for ( j = S->CN ; j < S->CN+2 ; j++ ) P[i][j] = P[j][i]; #else P[S->CN+1][S->CN+1] = dot(ah,h,S->N); P[S->CN][S->CN+1] = dot(mx,h,S->N); for ( j = 0 ; j < S->CN ; j++ ) P[j][S->CN+1] = dot(h,S->C[j],S->N); #endif /* fill in right side */ if ( S->CN > 0 ) matvec_mul(S->C,ax,Cf,S->CN,S->N); Cf[S->CN] = dot(mx,ax,S->N); Cf[S->CN+1] = dot(ah,ax,S->N); /* solve */ pdim = S->CN + ( count<=1 ? 1 : 2 ); matcopy(Pinv,P,pdim,pdim); mat_inv(Pinv,pdim); matvec_mul(Pinv,Cf,Gf,pdim,pdim); /* compute new search direction h */ for ( i = 0 ; i < S->N ; i++ ) { #ifdef SYMMETRICWAY if ( count > 1 ) h[i] = -Gf[S->CN+1]*ah[i]; else h[i] = 0.0; #else if ( count > 1 ) h[i] = -Gf[S->CN+1]*h[i]; else h[i] = 0.0; #endif h[i] += ax[i] - Gf[S->CN]*mx[i]; for ( j = 0 ; j < S->CN ; j++ ) h[i] -= Gf[j]*S->C[j][i]; } /* recompute Ah */ (*sp_mul_func)(S,h,ah); if ( web.area_norm_flag || hessian_linear_metric_flag ) hnorm = sqrt(sparse_metric_dot(h,h,&Met)); else hnorm = sqrt(dot(h,h,S->N)); hnorm_inv = 1/hnorm; /* find minimum along geodesic */ a = xax; b = dot(x,ah,S->N)/hnorm; c = dot(h,ah,S->N)/hnorm/hnorm; evalue = 0.5*(a+c-sqrt((a-c)*(a-c)+4*b*b)); /* smallest ev */ denom = sqrt(b*b + (evalue - a)*(evalue - a)); if ( b < 0.0 ) denom = -denom; q2 = (evalue - a)/denom; q1comp = (evalue-a)*(evalue-a)/denom/(denom+b); /* rotate h and x */ for ( i = 0 ; i < S->N ; i++ ) { REAL xtmp = x[i]*hnorm; REAL htmp = h[i]*hnorm_inv; x[i] += -q1comp*x[i] + q2*htmp; h[i] += -q2*xtmp -q1comp*h[i]; } if ( (count < maxcount) && (maxcount > 5) && (count % (maxcount/5) == 0) ) { #ifdef LONGDOUBLE sprintf(msg,"%3d. %3.*Lg\n",count,DPREC,xax); #else sprintf(msg,"%3d. %3.17g\n",count,xax); #endif outstring(msg); } } while ( (fabs(xax - old_xax) > 10*machine_eps*fabs(xax)) && (countN); ah = dmatrix(0,n-1,0,S->N); f = dmatrix(0,n-1,0,S->N); Q = dmatrix(0,n-1,0,S->N); hx = dmatrix(0,2*n-1,0,S->N); xh = (REAL**)temp_calloc(2*n,sizeof(REAL*)); work = (REAL*)temp_calloc(2*n,sizeof(REAL)); for ( i = 0 ; i < n ; i++ ) { xh[i] = x[i]; xh[i+n] = Q[i]; } NN = dmatrix(0,n-1,0,n-1); L = dmatrix(0,n-1,0,n-1); Linv = dmatrix(0,n-1,0,n-1); B = dmatrix(0,2*n-1,0,2*n-1); xax = B; hax = B+n; xah = (REAL**)temp_calloc(n,sizeof(REAL*)); hah = (REAL**)temp_calloc(n,sizeof(REAL*)); for ( j = 0 ; j < n ; j++ ) { xah[j] = B[j]+n; hah[j] = B[j+n]+n; } evalues = (REAL *)temp_calloc(2*n,sizeof(REAL)); evectors = dmatrix(0,2*n-1,0,2*n-1); if ( web.area_norm_flag || hessian_linear_metric_flag ) { mx = dmatrix(0,n-1,0,S->N); mh = dmatrix(0,n-1,0,S->N); } if ( S->CN ) { CCinv = dmatrix(0,S->CN,0,S->CN); mat_mul_tr(S->C,S->C,CCinv,S->CN,S->N,S->CN); mat_inv(CCinv,S->CN); Cf = dmatrix(0,S->CN-1,0,n-1); Gf = dmatrix(0,S->CN-1,0,n-1); } /* initial random guess */ for ( j = 0 ; j < n ; j++ ) for ( i = 0 ; i < S->N ; i++ ) x[j][i] = drand48() - .5; /* project to constraints */ if ( S->CN ) { mat_mul_tr(S->C,x,Cf,S->CN,S->N,n); mat_mult(CCinv,Cf,Gf,S->CN,S->CN,n); for ( j = 0 ; j < n ; j++ ) for ( i = 0 ; i < S->CN ; i++ ) vector_add_smul(x[j],S->C[i],-Gf[i][j],S->N); } /* project to Steifel manifold XMX = I */ LQ_decomp(x,n,S->N,x,L,M); trxax = 1e30; /* silly value for start of convergence test */ for(;;) { prompt("Enter max iterations: ",response,sizeof(response)); maxcount = atoi(response); if ( maxcount == 0 ) break; if ( maxcount < 0 ) { count = 0 ; maxcount = -maxcount; } else count = 0; do { REAL denom; old_trxax = trxax; count++; /* get cg search direction */ for ( j = 0 ; j < n ; j++ ) (*sp_mul_func)(S,x[j],f[j]); /* AX */ mat_mul_tr(x,f,xax,n,S->N,n); if ( web.area_norm_flag || hessian_linear_metric_flag ) for ( j = 0 ; j < n ; j++ ) (*sp_mul_func)(&Met,x[j],mx[j]); else mx = x; mat_mult(xax,mx,ah,n,n,S->N); /* ah just used as temp */ for ( j = 0 ; j < n ; j++ ) for ( i = 0 ; i < S->N ; i++ ) f[j][i] -= ah[j][i]; /* convert form f to vector If with cg metric I */ If = f; /* project to constraints */ if ( S->CN ) { mat_mul_tr(S->C,If,Cf,S->CN,S->N,n); mat_mult(CCinv,Cf,Gf,S->CN,S->CN,n); for ( j = 0 ; j < n ; j++ ) for ( i = 0 ; i < S->CN ; i++ ) vector_add_smul(If[j],S->C[i],-Gf[i][j],S->N); } /* project If tangent to Steifel manifold */ mat_mul_tr(mx,If,NN,n,S->N,n); tr_mat_mul(NN,x,ah,n,n,S->N); for ( j = 0 ; j < n ; j++ ) for ( i = 0 ; i < S->N ; i++ ) If[j][i] -= ah[j][i]; if ( count <= 1 ) cgamma = 0.0; else { /* proper Hessian is A - XAX M */ REAL numer; for ( j = 0 ; j < n ; j++ ) (*sp_mul_func)(S,h[j],ah[j]); if ( web.area_norm_flag || hessian_linear_metric_flag ) for ( j = 0 ; j < n ; j++ ) (*sp_mul_func)(&Met,h[j],mh[j]); else mh = h; for ( j = 0 ; j < n ; j++ ) for ( i = 0 ; i < S->N ; i++ ) for ( k = 0 ; k < n ; k++ ) ah[j][i] -= xax[j][k]*mh[k][i]; for ( j = 0, numer = denom = 0.0 ; j < n ; j++ ) { numer += dot(If[j],ah[j],S->N); denom += dot(h[j],ah[j],S->N); } cgamma = -numer/denom; } for ( j = 0 ; j < n ; j++ ) for ( i = 0 ; i < S->N ; i++ ) h[j][i] = If[j][i] + cgamma*h[j][i]; /* normalize h to be orthonormal wrt M, hMh = I */ LQ_decomp(h,n,S->N,Q,L,M); matcopy(Linv,L,n,n); mat_inv(Linv,n); /* compute Ah */ for ( j = 0 ; j < n ; j++ ) (*sp_mul_func)(S,h[j],ah[j]); /* load up matrix */ /* B = | xax xah | */ /* | hax hah | */ mat_mul_tr(h,ah,hah,n,S->N,n); mat_mult(Linv,hah,NN,n,n,n); mat_mul_tr(NN,Linv,hah,n,n,n); mat_mul_tr(Q,f,hax,n,S->N,n); for ( i = 0 ; i < n ; i++ ) for ( j = 0 ; j < n ; j++ ) xah[i][j] = hax[j][i]; jacobi_eigenpairs(B,2*n,evalues,evectors,work); /* returns eigenvectors in columns of evectors, in descending order */ /* use lowest n eigenvectors */ for ( j = n, trxax = 0.0; j < 2*n ; j++ ) trxax += evalues[j]; /* rotate h and x */ tr_mat_mul(evectors,xh,hx,2*n,2*n,S->N); matcopy(x,hx+n,n,S->N); mat_mult(L,hx,h,n,n,S->N); if ( (count < maxcount) && (maxcount > 5) && (count % (maxcount/5) == 0) ) { sprintf(msg,"%3d. ",count); for ( j = 0 ; (j < 4)&&(j 10*machine_eps*fabs(trxax)) && (countzero != 0 ) last_eigenvalue = S->lambda; else last_eigenvalue = evalues[0]; free_matrix(h); free_matrix(ah); free_matrix(hx); free_matrix(f); free_matrix(B); free_matrix(L); free_matrix(Q); free_matrix(Linv); temp_free((char*)hah); temp_free((char*)xah); temp_free((char*)evalues); temp_free((char*)work); free_matrix(evectors); if ( web.area_norm_flag || hessian_linear_metric_flag ) { free_matrix(mx); free_matrix(mh); } if ( S->CN ) { free_matrix(Gf); free_matrix(Cf); free_matrix(CCinv); } } /* end cg_ritz() */ /************************************************************************ * * function: sp_hessian_solve() * * purpose: solve hessian with constraints * */ void sp_hessian_solve(S,rhs,X,set_pressure_flag) struct linsys *S; REAL *rhs; REAL *X; /* solution */ int set_pressure_flag; /* whether to set body pressures */ { /* solve stuff */ int i,j,ii,jj; REAL *T1,*T2,*T3; body_id b_id; struct gen_quant*q; if ( (S->CN > 0) && augmented_hessian_mode ) { /* Kludge since volume deltas got in with wrong sign */ for ( i = S->N - S->CN ; i < S->N ; i++ ) rhs[i] = - rhs[i]; } sp_solution(S,rhs,X); /* check decrease in residual */ if ( itdebug || hess_debug ) { REAL *xrhs = (REAL *)temp_calloc(S->maxN,sizeof(REAL)); REAL rhsmag = 0.0; REAL resmag = 0.0; sp_hessian_mult(S,X,xrhs); for ( i = 0 ; i < S->N ; i++ ) { rhsmag += rhs[i]*rhs[i]; resmag += (rhs[i] - xrhs[i])*(rhs[i] - xrhs[i]); } if ( resmag > rhsmag/10 ) { sprintf(errmsg,"Hessian solve only reduced residual from %g to %g.\n", sqrt(rhsmag),sqrt(resmag)); kb_error(2561,errmsg,WARNING); } temp_free((char*)xrhs); } if ( S->CN > 0 ) /* have constraints */ { if ( augmented_hessian_mode ) { /* remap Lagrange multipliers*/ for ( i = 0 ; i < S->concount ; i++ ) if ( S->coninx[i] >= 0 ) pressures[i] = -X[S->N - S->CN + S->coninx[i]]; } else { REAL *con_rhs = (REAL*)temp_calloc(S->CN,sizeof(REAL)); for ( i = 0 ; i < S->concount ; i++ ) { j = S->coninx[i]; if ( j >= 0 ) con_rhs[j] = rhs[S->N+i]; } T1 = (REAL*)temp_calloc(S->CN,sizeof(REAL)); T2 = (REAL*)temp_calloc(S->CN,sizeof(REAL)); T3 = (REAL*)temp_calloc(S->N,sizeof(REAL)); sp_aug_solve(S,X,con_rhs,T1,T2,T3); for ( i = 0 ; i < S->N ; i++ ) X[i] -= T3[i]; for ( i = 0 ; i < S->CN ; i++ ) con_rhs[i] = -T2[i]; /* remap Lagrange multipliers*/ for ( i = 0 ; i < S->concount ; i++ ) if ( S->coninx[i] >= 0 ) pressures[i] = con_rhs[S->coninx[i]]; temp_free((char*)T1); temp_free((char*)T2); temp_free((char*)T3); temp_free((char*)con_rhs); } if ( set_pressure_flag == SET_PRESSURE ) { if ( ! everything_quantities_flag ) FOR_ALL_BODIES(b_id) { REAL p = get_body_pressure(b_id)+pressures[loc_ordinal(b_id)]; set_body_pressure(b_id,p); } for ( i = 0 ; i < gen_quant_count ; i++ ) { q = GEN_QUANT(i); if ( q->flags & (Q_FIXED|Q_CONSERVED) ) { q->pressure += pressures[everything_quantities_flag ? i : web.skel[BODY].max_ord+1+i]; if ( valid_id(q->b_id) ) set_body_pressure(q->b_id,q->pressure); } } } } if ( hess_debug ) { #ifdef MPI_EVOLVER for ( ii = 0 ; ii < S->N ; ii++ ) printf(" %20.15f X[%d] \n",(DOUBLE)X[ii],ii); #else vertex_id v_id; MFOR_ALL_VERTICES(v_id) { struct hess_verlist *vh = get_vertex_vhead(v_id); for ( j = 0 ; j < vh->freedom ; j++ ) { ii = vh->rownum+j; printf(" %20.15f X[%d] v%d.%d\n",(DOUBLE)X[ii],ii,ordinal(v_id)+1,j+1); } } #endif for ( i = 0 ; i < optparamcount ; i++ ) { ii = optparam[i].pnum; jj = optparam[i].rownum; printf(" %f X[%d] %s\n",(DOUBLE)X[jj],jj,globals(ii)->name); } } } /* end sp_hessian_solve */ /************************************************************************ * * function: sp_hessian_solve_multi() * * purpose: solve hessian with constraints for multiple rhs * */ void sp_hessian_solve_multi(S,rhs,X,rk) struct linsys *S; REAL **rhs; REAL **X; /* solution */ int rk; /* number of right sides */ { /* solve stuff */ int k; for ( k = 0 ; k < rk ; k++ ) sp_hessian_solve(S,rhs[k],X[k],0); #ifdef oldstuff (*sp_solve_multi_func)(S,rhs,X,rk); if ( S->CN > 0 ) /* have constraints */ { REAL **con_rhs = dmatrix(0,rk-1,0,S->CN); for ( i = 0 ; i < S->concount ; i++ ) { int j = S->coninx[i]; if ( j >= 0 ) for ( k = 0 ; k < rk ; k++ ) con_rhs[k][j] = rhs[k][S->N+i]; } T1 = (REAL*)temp_calloc(S->CN,sizeof(REAL)); T2 = (REAL*)temp_calloc(S->CN,sizeof(REAL)); T3 = (REAL*)temp_calloc(S->N,sizeof(REAL)); for ( k = 0 ; k < rk ; k++ ) { matvec_mul(S->C,X[k],T1,S->CN,S->N); for ( i = 0 ; i < S->CN ; i++ ) T1[i] += con_rhs[k][i]; /* corrections */ #ifdef BLAS if ( blas_flag ) LD_block_solve(S->CHinvCinv,T1,T2,S->CN); else #endif matvec_mul(S->CHinvCinv,T1,T2,S->CN,S->CN); /* Lagrange multipliers */ vec_mat_mul(T2,S->HinvC,T3,S->CN,S->N); for ( i = 0 ; i < S->N ; i++ ) X[k][i] -= T3[i]; for ( i = 0 ; i < S->CN ; i++ ) con_rhs[k][i] = -T2[i]; } /* conrhs not used?? */ temp_free((char*)T1); temp_free((char*)T2); temp_free((char*)T3); free_matrix(con_rhs); } #endif } /* end sp_hessian_solve_multi */ /******************************************************************************* * * function: bk_eigenprobe() * * purpose: find number of eigenvalues less than, equal, or greater than * desired value. Asks user for probe values. */ void bk_eigenprobe(S) struct linsys *S; { for(;;) { char response[100]; prompt("Enter probe value: ",response,sizeof(response)); if ( (response[0] == 0) || (response[0] == 'q') ) break; S->lambda = atof(response); sp_factor(S); (*sp_hess_project_setup_func)(S); sprintf(msg,"Eigencounts: %d <, %d ==, %d > \n",S->neg,S->zero,S->pos); outstring(msg); } return; } /* end bk_eigenprobe */ /******************************************************************************* * * function: bk_inverse_it() * * purpose: Find eigenvector near probe value. */ void bk_inverse_it(S,V) struct linsys *S; REAL *V; /* for eigenvector return */ { int i,ii,k,its; REAL t,oldt; char response[100]; REAL *W = NULL, *WW = NULL; /* for use when metric is needed */ prompt("Enter probe value: ",response,sizeof(response)); if ( (response[0] == 0) || (response[0] == 'q') ) return; S->lambda = atof(response); sp_factor(S); (*sp_hess_project_setup_func)(S); sprintf(msg,"Eigencounts: %d <, %d ==, %d > \n",S->neg,S->zero,S->pos); outstring(msg); if ( web.area_norm_flag || hessian_linear_metric_flag ) W = (REAL*)temp_calloc(S->N,sizeof(REAL)); /* inverse iteration */ /* random starting vector */ for ( i = 0 ; i < S->N ; i++ ) V[i] = drand48(); t = 1/sqrt(dot(V,V,S->N)); for ( i = ii = 0 ; i < S->N ; i++,ii++ ) V[i] *= t; prompt("Enter maximum iterations: ",response,sizeof(response)); its = atoi(response); if ( its == 0 ) goto afterits; oldt = 1e30; /* for convergence test */ for ( k = 0, ii=0 ; k < its ; k++,ii++ ) { REAL oldv0=1.0; /* for sign */ int oldvi=0; /* index of nonzero component */ REAL eps = 1/sqrt((REAL)(S->N))/2; for ( i = 0, oldv0 = 0.0 ; i < S->N ; i++ ) if ( fabs(V[i]) > eps ) { oldvi = i; oldv0 = V[i]; break; } if ( web.area_norm_flag || hessian_linear_metric_flag ) { (*sp_mul_func)(&Met,V,W); WW = W; } else WW = V; sp_hessian_solve(S,WW,V,NO_SET_PRESSURE); if ( web.area_norm_flag || hessian_linear_metric_flag ) t = 1/sqrt(sparse_metric_dot(V,V,&Met)); else t = 1/sqrt(dot(V,V,S->N)); if ( V[oldvi]*oldv0 < 0. ) t = -t; /* get sign right */ #ifdef LONGDOUBLE if ( k % 10 == 0 ) printf("%d ev = %*.*Lf\n",k,DWIDTH,DPREC,S->lambda+t); #else if ( k % 10 == 0 ) printf("%d ev = %20.15f\n",k,S->lambda+t); #endif for ( i = 0 ; i < S->N ; i++ ) V[i] *= t; if ( fabs(t-oldt) <= 100*machine_eps*fabs(t) ) { /* does twice as many iterations as needed to get t converged */ /* so eigenvector gets fully converged also */ ii -= 2; if ( ii <= 0 )break; } oldt = t; } #ifdef LONGDOUBLE printf("%d ev = %*.*Lf\n",k,DWIDTH,DPREC,S->lambda+t); #else printf("%d ev = %20.15f\n",k,S->lambda+t); #endif afterits: if ( web.area_norm_flag || hessian_linear_metric_flag ) temp_free((char*)W); return; } /* end bk_inverse_it() */ /************************************************************************* * * function: sp_Hessian_solver() * * purpose; Set up and solve sparse Hessian with given right hand side. */ void sp_Hessian_solver(S,rhs,Xptr) struct linsys *S; /* system */ REAL *rhs; /* right side */ REAL **Xptr; /* returned vector */ { REAL *X; if ( *Xptr == NULL ) *Xptr = (REAL*)temp_calloc(S->N,sizeof(REAL)); X = *Xptr; (*sp_AIJ_setup_func)(S->A_rows,S); (*sp_constraint_setup_func) (web.skel[BODY].max_ord+1 + gen_quant_count,S); if ( sp_ordering_func ) (*sp_ordering_func)(S); sp_factor(S); (*sp_hess_project_setup_func)(S); if ( hess_debug ) { int i; puts("rhs:"); for ( i = 0 ; i < S->N ; i++ ) printf("%g \n",(DOUBLE)rhs[i]); } sp_hessian_solve(S,rhs,X,SET_PRESSURE); if ( S->neg > 0 ) { sprintf(msg,"Hessian not positive definite. Index: %d\n",S->neg); kb_error(1825,msg,WARNING); } if ( hess_debug ) { int i; REAL *out = (REAL*)temp_calloc(S->N,sizeof(REAL)); puts("X:"); for ( i = 0 ; i < S->N ; i++ ) printf("%g \n",(DOUBLE)X[i]); bk_mul(S,X,out); puts("check:"); for ( i = 0 ; i < S->N ; i++ ) printf("%g \n",(DOUBLE)out[i]); temp_free((char*)out); } } /* end sp_hessian_solver() */ /************************************************************************** * * function: sparse_metric_dot() * * purpose: Take dot product of vectors when metric is sparse linear system. * Metric sparse storage has diagonal element first in each row. * */ REAL sparse_metric_dot(u,v,M) REAL *u,*v; /* the vectors to be dotted */ struct linsys *M; /* the metric */ { int i,j,end; REAL sum; sum = 0.0; for ( i = 0 ; i < M->N ; i++ ) { j = M->IA[i]-A_OFF; end = M->IA[i+1]-A_OFF; sum += M->A[j]*u[i]*v[i]; for ( j++ ; j < end ; j++ ) { int ii = M->JA[j]-A_OFF; sum += M->A[j]*(u[i]*v[ii] + u[ii]*v[i]); } } return sum; } /* end sparse_metric_dot() */ /************************************************************************* * * function: sp_CHinvC() * * purpose: Find inner product of matrix using hessian inverse as metric. * C stored sparsely in S. * * Want to use sparsity in case of large CN. So solve H X = C * for one constraint at a time, and sparse-multiply the result * by the rest of the constraint rows. Avoids forming full * CN x N matrix. */ void sp_CHinvC(S) struct linsys *S; /* factored system */ { int n; /* row index */ int i,j,k; REAL *BB,*Y; if ( S->P == NULL ) kb_error(2530,"Internal error: Must call sp_factor before sp_CHinvC.\n", RECOVERABLE); BB = (REAL*)temp_calloc(S->N,sizeof(REAL)); /* expanded rhs */ Y = (REAL*)temp_calloc(S->N,sizeof(REAL)); /* solution */ for ( i = 0 ; i < S->CN ; i++ ) { /* solve H Y = C */ memset(BB,0,S->N*sizeof(REAL)); for ( n = S->CIA[i] ; n < S->CIA[i+1] ; n++ ) BB[S->CJA[n]] = S->CA[n]; sp_solution(S,BB,Y); /* multiply by rows of C */ for ( j = 0 ; j <= i ; j++ ) { REAL sum; for ( k = S->CIA[j] , sum = 0.0 ; k < S->CIA[j+1] ; k++ ) sum += S->CA[k]*Y[S->CJA[k]]; S->CHinvCinv[i][j] = sum; if ( ! blas_flag ) S->CHinvCinv[j][i] = sum; /* symmetric */ } } temp_free((char*)BB); temp_free((char*)Y); } /************************************************************************* * * function: sp_solution() * * purpose: solve system possibly in sparse + low rank update */ void sp_solution(S,rhs,X) struct linsys *S; /* factored sparse matrix */ REAL *rhs; /* right side */ REAL *X; /* for solution */ { REAL *Y; /* working full-size vector */ REAL *Z; /* low rank working vector */ REAL *W; /* low rank working vector */ int i; if ( S->low_rank == 0 ) { /* no low-rank update */ (*sp_solve_func)(S,rhs,X); return; } /* rest is solution of system with low rank update */ Y = (REAL*)temp_calloc(S->N,sizeof(REAL)); Z = (REAL*)temp_calloc(S->low_rank, sizeof(REAL)); W = (REAL*)temp_calloc(S->low_rank, sizeof(REAL)); (*sp_solve_func)(S,rhs,Y); matvec_mul(S->low_rank_vectors,Y,Z,S->low_rank,S->low_rank_vecsize); matvec_mul(S->low_rank_form,Z,W,S->low_rank,S->low_rank); matvec_mul(S->low_rank_inverse_form,W,Z,S->low_rank,S->low_rank); vec_mat_mul(Z,S->low_rank_vectors,Y,S->low_rank,S->low_rank_vecsize); for ( i = 0 ; i < S->low_rank_vecsize ; i++ ) Y[i] = rhs[i] - Y[i]; for ( ; i < S->N ; i++ ) Y[i] = rhs[i]; (*sp_solve_func)(S,Y,X); temp_free((char*)Y); temp_free((char*)Z); temp_free((char*)W); } /************************************************************************** * * function: sp_factor() * * purpose: factor sparse matrix, and set up low-rank corrections if present. * */ void sp_factor(S) struct linsys *S; { (*sp_factor_func)(S); /* factor just the matrix */ if ( S->low_rank ) { REAL **W = dmatrix(0,S->low_rank-1,0,S->N-1); REAL **Z = dmatrix(0,S->low_rank-1,0,S->low_rank-1); int i; (*sp_solve_multi_func)(S,S->low_rank_vectors,W,S->low_rank); mat_mul_tr(S->low_rank_vectors,W,Z,S->low_rank,S->low_rank_vecsize, S->low_rank); mat_mult(S->low_rank_form,Z,S->low_rank_inverse_form,S->low_rank, S->low_rank,S->low_rank); for ( i = 0 ; i < S->low_rank ; i++ ) S->low_rank_inverse_form[i][i] += 1; mat_inv(S->low_rank_inverse_form,S->low_rank); free_matrix(W); free_matrix(Z); } } /* end sp_factor() */ evolver-2.30c.dfsg/src/check.c0000644000175300017530000007630211410765113016446 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /*********************************************************************** * * File: check.c * * Contents: Various integrity and consistency checks. * */ #include "include.h" /* maximum errors of any type before aborting */ #define MAXERR 20 int wrap_check ARGS((void)); int check_body_facets ARGS((void)); /********************************************************************* * * Function: run_checks() * * Purpose: Overall control of checking. */ int run_checks() { int numerr = 0; if ( memdebug ) memory_report(); numerr += list_check(); if ( web.representation != SIMPLEX ) { numerr += facetedge_check(REGCHECK); if ( web.skel[BODY].count > 0 ) numerr += facet_body_check(); numerr += collapse_check(); numerr += vertex_facet_check(); } numerr += method_check(); if ( web.symmetry_flag ) numerr += wrap_check(); #ifdef MPI_EVOLVER numerr += mpi_task_checks(); #endif check_count = numerr; return numerr; } /* end run_checks() */ /********************************************************************** * * Function: method_check() * * purpose: see if any method applied twice to an element. * * return: number of duplications. */ int method_check() { int type; element_id id; int count = 0; int k,n; for ( type = VERTEX; type < FACETEDGE ; type++ ) { int meth_offset = get_meth_offset(type); FOR_ALL_ELEMENTS(type,id) { struct element *e_ptr = elptr(id); int *instlist = (int*)((char*)e_ptr + meth_offset); for ( k = 0; k < (int)e_ptr->method_count ; k++ ) for ( n = k+1; n < (int)e_ptr->method_count ; n++ ) if ( abs(instlist[k]) == abs(instlist[n]) ) { sprintf(msg,"%s %s has method %s twice.\n", typenames[type],ELNAME(id),METH_INSTANCE(abs(instlist[k]))->name); outstring(msg); count++; } } } return count; } /* end method_check() */ /********************************************************************** * * Function: list_check() * * Purpose: Check element linked lists. * */ int list_check() { int type; element_id id; int numerr = 0; /* free_discards(DISCARDS_ALL); */ for ( type = 0 ; type < NUMELEMENTS ; type++ ) { element_id backid = NULLID; if ( (web.representation == SIMPLEX) && (type == EDGE) ) continue; #ifndef HASH_ID /* check free list */ { int freecount,maxfree; maxfree = web.skel[type].maxcount - web.skel[type].count - web.skel[type].discard_count; id = web.skel[type].free; freecount = 0; while ( id != NULLID ) { freecount++; if ( freecount > maxfree ) { sprintf(msg,"Type %d freelist has too many elements: %d.\n", type,freecount); erroutstring(msg); if ( ++numerr > MAXERR ) {erroutstring("Too many errors.\n"); return numerr;} break; } if ( id_type(id) != type ) { sprintf(msg,"Type %d freelist has bad id %lX\n",type,(unsigned long)id); erroutstring(msg); if ( ++numerr > MAXERR ) {erroutstring("Too many errors.\n"); return numerr;} break; } if ( elptr(id)->backchain != backid ) { sprintf(msg, "Type %d freelist has bad backchain %X instead of %X for id %lX\n", type, (unsigned long)elptr(id)->backchain,(unsigned long)backid, (unsigned long)id); erroutstring(msg); if ( ++numerr > MAXERR ) {erroutstring("Too many errors.\n"); return numerr;} } backid = id; id = elptr(id)->forechain; } /* end while */ if ( backid != web.skel[type].freelast ) { sprintf(msg,"Type %d freelist has bad freelast %lX instead of %lX.\n", type, (unsigned long)web.skel[type].freelast,(unsigned long)backid); erroutstring(msg); } if ( freecount != maxfree ) { sprintf(msg,"Type %d freelist has %d elements instead of %d.\n", type,freecount,maxfree); erroutstring(msg); } if ( !equal_id(id,NULLID) ) { sprintf(msg,"Type %d freelist last id is non-null: %lX\n",type, (unsigned long)id); erroutstring(msg); } } #endif #ifndef MPI_EVOLVER { int usedcount,maxused,discards; element_id prev_id; /* check used list */ maxused = web.skel[type].count; id = web.skel[type].used; prev_id = NULLID; usedcount = 0; discards = 0; while ( valid_id(id) ) { if ( valid_element(id) ) usedcount++; else discards++; if ( usedcount > maxused ) { sprintf(msg,"Type %d usedlist has too many elements: %d.\n", type,usedcount); erroutstring(msg); if ( ++numerr > MAXERR ) {erroutstring("Too many errors.\n"); return numerr;} break; } if ( id_type(id) != type ) { sprintf(msg,"Type %d usedlist has bad id %lX of type %d\n", type,(unsigned long)id,id_type(id)); erroutstring(msg); if ( ++numerr > MAXERR ) { erroutstring("Too many errors.\n"); return numerr;} break; } if ( !equal_id(prev_id,elptr(id)->backchain) ) { sprintf(msg,"Type %d used list id %lX has backchain %lX instead of %lX\n", type,(unsigned long)id,(unsigned long)elptr(id)->backchain, (unsigned long)prev_id); erroutstring(msg); if ( ++numerr > MAXERR ) {erroutstring("Too many errors.\n"); return numerr;} } prev_id = id; id = elptr(id)->forechain; } /* end while */ if ( usedcount != maxused ) { sprintf(msg,"Type %d usedlist has %d elements.\n",type,usedcount); erroutstring(msg); } if ( discards != web.skel[type].discard_count ) { sprintf(msg,"Type %d usedlist has %d elements.\n",type,usedcount); erroutstring(msg); } if ( !equal_id(id,NULLID) ) { sprintf(msg,"Type %d usedlist last id is non-null: %lX\n",type, (unsigned long)id); erroutstring(msg); } } #endif } /* end for loop */ #ifdef MPI_EVOLVER for ( type = VERTEX ; type < NUMELEMENTS ; type++ ) { int k; for ( k = 0 ; k < web.skel[type].maxcount ; k++ ) if ( (web.skel[type].ibase[k]->local_id & OFFSETMASK) != k ) { sprintf(msg,"Task %d: local_id is %08X on %s ibase[0x%X], self id %08X\n", this_task,(int)(web.skel[type].ibase[k]->local_id), typenames[type],k, (int)(web.skel[type].ibase[k]->self_id)); erroutstring(msg); } } #endif return numerr; } /* end list_check() */ /********************************************************************** * * Function: facetedge_check() * * Purpose: Check integrity of facetedge chains both ways. */ int facetedge_check(flag) int flag; /* PRELIMCHECK for check before subdivision, else REGCHECK */ { vertex_id v_id; edge_id e_id; facet_id f_id; facetedge_id fe_id,last_fe; int count; int numerr = 0; int n; /* check facetedge chain consistencies */ if ( numerr >= MAXERR ) numerr = 0; count = 0; MFOR_ALL_FACETEDGES(fe_id) { facetedge_id fe; f_id = get_fe_facet(fe_id); #ifdef MPI_EVOLVER if ( (web.representation==SOAPFILM) || (id_task(f_id)==this_task) ) #endif if ( valid_id(f_id) && !valid_element(f_id) ) { sprintf(msg,"Facetedge %s links to invalid facet %08lX.\n", ELNAME(fe_id), (unsigned long)f_id); erroutstring(msg); } #ifdef MPI_EVOLVER if ( (web.representation==SOAPFILM) || (id_task(f_id)==this_task) ) #endif if ( valid_id(f_id) && !valid_element(get_facet_fe(f_id)) ) { sprintf(msg,"Facetedge %s links to facet %s with invalid facetedge %s.\n", ELNAME(fe_id),ELNAME1(f_id),ELNAME2(get_facet_fe(f_id))); erroutstring(msg); } e_id = get_fe_edge(fe_id); #ifdef MPI_EVOLVER if ( mpi_corona_state > NO_CORONA || id_task(e_id) == this_task ) #endif { if ( valid_id(e_id) && !valid_element(e_id) ) { sprintf(msg,"Facetedge %s links to invalid edge %08lX.\n", ELNAME(fe_id),(unsigned long)e_id); erroutstring(msg); } if ( valid_id(e_id) && !valid_element(get_edge_fe(e_id)) ) { sprintf(msg,"Facetedge %s links to edge %s with invalid facetedge %s.\n", ELNAME(fe_id),ELNAME1(e_id),ELNAME2(get_edge_fe(e_id))); erroutstring(msg); } } fe = get_prev_edge(fe_id); if ( valid_id(fe) && !equal_id(get_next_edge(fe),fe_id) ) { sprintf(msg,"Facetedge %s has bad prev edge link\n",ELNAME(fe_id)); erroutstring(msg); if ( ++numerr > MAXERR ) {erroutstring("Too many errors.\n"); return numerr;} } fe = get_next_edge(fe_id); if ( valid_id(fe) && !equal_id(get_prev_edge(fe),fe_id) ) { sprintf(msg,"Facetedge %s has bad next edge link\n",ELNAME(fe_id)); erroutstring(msg); if ( ++numerr > MAXERR ) {erroutstring("Too many errors.\n"); return numerr;} } fe = get_next_facet(fe_id); if ( valid_id(fe) && !equal_id(get_prev_facet(fe),fe_id) ) { sprintf(msg,"Facetedge %s has bad next facet link\n",ELNAME(fe_id)); erroutstring(msg); if ( ++numerr > MAXERR ) {erroutstring("Too many errors.\n"); return numerr;} } fe = get_prev_facet(fe_id); if ( valid_id(fe) && !equal_id(get_next_facet(fe),fe_id) ) { sprintf(msg,"Facetedge %s has bad prev facet link\n",ELNAME(fe_id)); erroutstring(msg); if ( ++numerr > MAXERR ) {erroutstring("Too many errors.\n"); return numerr;} } fe = get_next_edge(fe_id); #ifndef MPI_EVOLVER if ( valid_id(fe) && !equal_id(get_fe_headv(fe_id),get_fe_tailv(fe)) ) { sprintf(msg,"Facetedge %s head vertex disagrees with next tail.\n", ELNAME(fe_id)); erroutstring(msg); if ( ++numerr > MAXERR ) {erroutstring("Too many errors.\n"); return numerr;} } #endif fe = get_prev_edge(fe_id); #ifndef MPI_EVOLVER if ( valid_id(fe) && !equal_id(get_fe_tailv(fe_id),get_fe_headv(fe)) ) { sprintf(msg,"Facetedge %s tail vertex disagrees with prev head.\n", ELNAME(fe_id)); erroutstring(msg); if ( ++numerr > MAXERR ) {erroutstring("Too many errors.\n"); return numerr;} } #endif count++; } if ( count != web.skel[FACETEDGE].count ) { sprintf(msg,"Only %d facetedges out of %ld generated.\n", count,web.skel[FACETEDGE].count); erroutstring(msg); } /* check that vertices have legit edge link */ FOR_ALL_VERTICES(v_id) { e_id = get_vertex_edge(v_id); if ( !valid_id(e_id) ) continue; if ( !valid_element(e_id) ) { sprintf(errmsg,"Vertex %s has invalid edge link.\n", ELNAME(v_id)+1); kb_error(1829,errmsg,WARNING); continue; } else if (get_vattr(v_id) & Q_MIDPOINT) { if ( !equal_id(v_id,get_edge_midv(e_id)) ) { sprintf(errmsg,"Vertex %s has bad midpoint edge link.\n",ELNAME(v_id)); kb_error(1830,errmsg,WARNING); continue; } } else if (get_vattr(v_id) & Q_MIDEDGE) { vertex_id *v = get_edge_vertices(e_id); int i; for ( i = 0 ; i <= web.lagrange_order ; i++ ) if ( equal_id(v_id,v[i]) ) break; if ( i > web.lagrange_order ) { sprintf(errmsg,"Vertex %s has bad edge link.\n",ELNAME(v_id)); kb_error(1831,errmsg,WARNING); } } else if (get_vattr(v_id) & Q_MIDFACET) { } else if ( !equal_id(v_id,get_edge_tailv(e_id)) ) { sprintf(errmsg,"Vertex %s has bad edge link.\n",ELNAME(v_id)); kb_error(1832,errmsg,WARNING); continue; } } /* end vertices */ FOR_ALL_EDGES(e_id) { edge_id ee_id; v_id = get_edge_tailv(e_id); n = 0; ee_id = e_id; do { ee_id = get_next_tail_edge(ee_id); if ( ++n > 2*web.skel[EDGE].count ) { sprintf(errmsg,"Vertex %s has bad edge loop from edge %s.\n", ELNAME(v_id),ELNAME1(e_id)); kb_error(1833,errmsg,WARNING); break; } } while ( !equal_id(ee_id,e_id) ); v_id = get_edge_headv(e_id); n = 0; ee_id = e_id; do { ee_id = get_next_head_edge(ee_id); if ( ++n > 2*web.skel[EDGE].count ) { sprintf(errmsg,"Vertex %s has bad edge loop from edge %s.\n", ELNAME(v_id),ELNAME1(e_id)); kb_error(1834,errmsg,WARNING); break; } } while ( !equal_id(ee_id,e_id) ); } /* end edges */ /* some more checks on edges */ count = 0; bare_edge_count = 0; FOR_ALL_EDGES(e_id) { vertex_id v_id = get_edge_headv(e_id); facetedge_id first_fe; #ifdef MPI_EVOLVER if ( mpi_corona_state > NO_CORONA || id_task(v_id) == this_task ) #endif if ( get_vattr(v_id) & AXIAL_POINT ) { sprintf(msg,"Vertex %s is axial and not first vertex of edge %s.\n", ELNAME(v_id),ELNAME1(e_id)); erroutstring(msg); } last_fe = NULLID; fe_id = first_fe = get_edge_fe(e_id); if ( valid_id(fe_id) ) do { edge_id ee_id = get_fe_edge(fe_id); if ( !equal_id(e_id,ee_id) ) { sprintf(msg,"Facetedge %s on edge %s instead of %s.\n", ELNAME(fe_id),SELNAME1(ee_id), SELNAME2(e_id)); erroutstring(msg); if ( ++numerr > MAXERR ) {erroutstring("Too many errors.\n"); goto facet_check;} } count++; if ( count > web.skel[FACETEDGE].count ) { sprintf(msg,"Bad chain of facetedges around edge %s.\n", ELNAME(e_id)); erroutstring(msg); if ( ++numerr > MAXERR ) {erroutstring("Too many errors.\n"); goto facet_check;} } last_fe = fe_id; fe_id = get_next_facet(fe_id); } while ( valid_id(fe_id) && !equal_id(fe_id,first_fe) ); if ( last_fe == NULLID ) { if ( (web.representation == SOAPFILM) && (flag == REGCHECK) ) { edge_id orig; bare_edge_count++; if ( get_eattr(e_id) & BARE_NAKED ) continue; sprintf(msg,"Edge %s has no facets.\n",ELNAME(e_id)); erroutstring(msg); orig = get_original(e_id); if ( valid_id(orig) && !equal_element(orig,e_id) ) { sprintf(msg," (originally edge %s)\n",ELNAME(e_id)); erroutstring(msg); } } } else if ( !equal_id(get_edge_fe(e_id),get_next_facet(last_fe)) ) { edge_id orig; sprintf(msg,"Facets around edge %s do not link up.\n", ELNAME(e_id)); erroutstring(msg); orig = get_original(e_id); if ( valid_id(orig) && !equal_element(orig,e_id) ) { sprintf(msg," (originally edge %s)\n",ELNAME(e_id)); erroutstring(msg); } } } #ifndef MPI_EVOLVER if ( count != web.skel[FACETEDGE].count ) { sprintf(msg,"Edges have %d facetedges out of %ld used.\n", count,web.skel[FACETEDGE].count); erroutstring(msg); ++numerr; } #endif facet_check: if ( numerr >= MAXERR ) numerr = 0; if ( web.representation == SOAPFILM ) { int i; count = 0; MFOR_ALL_FACETS(f_id) { facetedge_id first_fe; int thiscount = 0; last_fe = NULLID; fe_id = first_fe = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++, fe_id = get_next_edge(fe_id) ) { vertex_id v_id; edge_id e_id = get_fe_edge(fe_id); facet_id ff_id; #ifdef MPI_EVOLVER if ( mpi_corona_state == NO_CORONA ) { if ( id_task(e_id) == this_task ) { v_id = get_edge_tailv(e_id); if ( id_task(v_id) == this_task ) { if ( (thiscount != 0) && (get_vattr(v_id) & AXIAL_POINT)) { set_facet_fe(f_id,fe_id); } } } } else #endif { v_id = get_edge_tailv(e_id); if ( (thiscount != 0) && (get_vattr(v_id) & AXIAL_POINT)) { set_facet_fe(f_id,fe_id); } } ff_id = get_fe_facet(fe_id); if ( !equal_id(f_id,ff_id) ) { sprintf(msg,"Facetedge %s on facet %s instead of %s.\n", ELNAME(fe_id),SELNAME1(ff_id), SELNAME2(f_id)); erroutstring(msg); if ( ++numerr > MAXERR ) {erroutstring("Too many errors.\n"); break;} } count++; if ( ++thiscount > web.skel[FACETEDGE].count ) { facet_id orig; sprintf(msg,"Facetedge loop not closed on facet %s.\n", ELNAME(f_id)); erroutstring(msg); orig = get_original(f_id); if ( valid_id(orig) && !equal_element(orig,f_id) ) { sprintf(msg," (originally facet %s)\n", ELNAME(orig)); erroutstring(msg); } break; } last_fe = fe_id; } /* end while */ fe_id = get_next_edge(last_fe); if ( !equal_id(first_fe,fe_id) ) { facet_id orig; sprintf(msg,"Edges around facet %s do not link up.\n", ELNAME(f_id)); erroutstring(msg); orig = get_original(f_id); if ( valid_id(orig) && !equal_element(orig,f_id) ) { sprintf(msg," (originally facet %s)\n", ELNAME(orig)); erroutstring(msg); } if ( ++numerr > MAXERR ) {erroutstring("Too many errors.\n"); break;} } if ( (thiscount != 3) && (flag == REGCHECK) ) { facet_id orig; sprintf(msg,"Facet %s has %d edges.\n",ELNAME(f_id),thiscount); erroutstring(msg); orig = get_original(f_id); if ( valid_id(orig) && !equal_element(orig,f_id) ) { sprintf(msg," (originally facet %s)\n", ELNAME(orig)); erroutstring(msg); } if ( ++numerr > MAXERR ) {erroutstring("Too many errors.\n"); break;} } } if ( count != web.skel[FACETEDGE].count ) { sprintf(msg,"Facets have %d facet-edges out of %ld used.\n", count,web.skel[FACETEDGE].count); erroutstring(msg); ++numerr; } } /* end SOAPFILM */ return numerr; } /* end facetedge_check() */ /************************************************************************ * * function: vertex_facet_check() * * purpose: Check the vertex facet circularly linked lists. */ int vertex_facet_check() { vertex_id v_id; int numerr = 0; MFOR_ALL_VERTICES(v_id) { element_id f_id, start_f_id; int count = 0; start_f_id = get_vertex_first_facet(v_id); if ( !valid_id(start_f_id) ) continue; f_id = start_f_id; do { f_id = get_next_vertex_facet(v_id,f_id); count++; if ( count > web.skel[FACET].count ) { sprintf(msg,"Vertex %s has bad facet list.\n",ELNAME(v_id)); erroutstring(msg); ++numerr; break; } } while ( !equal_id(f_id,start_f_id) ); } if ( (numerr > 0) && (web.representation == SIMPLEX) ) { erroutstring("Remaking vertex facet lists.\n"); make_vfacet_lists(); } return numerr; } /************************************************************************ * * Function: facet_body_check() * * Purpose: Check whether adjacent facets have same bodies. * Should be used only for complete bodies. * Also checks body facet lists. */ int facet_body_check() { facetedge_id fe; facet_id f_id; body_id b_id; int numerr = 0; element_id orig; FOR_ALL_BODIES(b_id) { f_id = get_body_facet(b_id); if ( !valid_id(f_id) ) continue; if ( !valid_element(f_id) ) { sprintf(msg,"Body %s has invalid facet link to facet %s.\n", ELNAME(b_id),ELNAME1(f_id)); erroutstring(msg); numerr++; continue; } if ( !equal_id(b_id,get_facet_body(f_id) ) ) { sprintf(msg,"Body %s has link to facet %s, which is on body %s.\n", ELNAME(b_id),SELNAME1(f_id),ELNAME2(get_facet_body(f_id))); erroutstring(msg); numerr++; } } if ( web.representation != SOAPFILM ) return numerr; FOR_ALL_FACETEDGES(fe) { body_id bb_id; facet_id ff_id; edge_id e_id; int k; for ( k = 0 ; k < 2 ; k++ ) { fe = inverse_id(fe); f_id = get_fe_facet(fe); b_id = get_facet_body(f_id); if ( equal_id(fe,get_prev_facet(fe)) ) continue; ff_id = get_fe_facet(fe_inverse(get_prev_facet(fe))); bb_id = get_facet_body(ff_id); e_id = get_fe_edge(fe); if ( /* !equal_id(b_id,bb_id) */ (ordinal(b_id) != ordinal(bb_id)) /* for MPI, temp kludge */ && !(get_attr(e_id)&(CONSTRAINT|FIXED|BOUNDARY)) ) { sprintf(msg,"Inconsistent bodies for facets on edge %s.", ELNAME(e_id) ); erroutstring(msg); numerr++; orig = get_original(e_id); if ( valid_id(orig) && !equal_element(orig,e_id) ) { sprintf(msg," (originally edge %s)",ELNAME(orig)); erroutstring(msg); } sprintf(msg,"\n facet %d",oid(f_id)); erroutstring(msg); orig = get_original(f_id); if ( valid_id(orig) && !equal_element(orig,f_id) ) { sprintf(msg," (orig. %s)",ELNAME(orig)); erroutstring(msg); } if ( valid_id(b_id) ) { sprintf(msg," has body %s",ELNAME(b_id)); erroutstring(msg); orig = get_original(b_id); if ( valid_id(orig) && !equal_element(orig,b_id) ) { sprintf(msg," (orig. %s)",ELNAME(orig)); erroutstring(msg); } } else erroutstring(" has no body"); sprintf(msg,"; facet %d",oid(ff_id)); erroutstring(msg); orig = get_original(ff_id); if ( valid_id(orig) && !equal_element(orig,ff_id) ) { sprintf(msg," (orig. %s)",ELNAME(orig)); erroutstring(msg); } if ( valid_id(bb_id) ) { sprintf(msg," has body %s",ELNAME(bb_id)); erroutstring(msg); orig = get_original(bb_id); if ( valid_id(orig) && !equal_element(orig,bb_id) ) { sprintf(msg," (orig. %s)",ELNAME(orig)); erroutstring(msg); } } else erroutstring(" has no body"); erroutstring(".\n"); /* just a warning, not an error */ } } } /* end FACETEDGES */ numerr += check_body_facets(); return numerr; } /* end facet_body_check() */ /************************************************************************ * * function: check_body_facets() * * purpose: check body facet lists */ int check_body_facets() { int numerr = 0; body_id b_id; FOR_ALL_BODIES(b_id) { facet_id start_f,next_f; facet_id f_id = get_body_facet(b_id); if ( !valid_id(f_id) ) continue; start_f = f_id; do { if ( !valid_element(f_id) ) { sprintf(msg,"Invalid facet %s on body facet list of body %s.\n", ELNAME(f_id),ELNAME1(b_id)); erroutstring(msg); numerr++; } if ( !equal_id(b_id,get_facet_body(f_id)) ) { sprintf(msg,"Facet %s on body %s facet list, but is on body %s.\n", SELNAME(f_id),ELNAME1(b_id),ELNAME2(get_facet_body(f_id))); erroutstring(msg); numerr++; } next_f = get_next_body_facet(f_id); if ( !equal_id(f_id,get_prev_body_facet(next_f)) ) { sprintf(msg,"Facet %s next body facet has bad prev_body_facet.\n", SELNAME(f_id)); erroutstring(msg); numerr++; break; } f_id = next_f; } while ( !equal_id(f_id,start_f) ); } return numerr; } /********************************************************************** * * Function: collapse_check() * * Purpose: Checks whether adjacent facets have collapsed. * Tests whether they have the same third vertex. * Also checks for edges that have the same endpoints. */ int vvvvcomp(a,b) struct vvvv *a,*b; { int i; for ( i = 0 ; i < 3 ; i++ ) { if ( a->v[i] < b->v[i] ) return -1; if ( a->v[i] > b->v[i] ) return 1; } return 0; } int collapse_check() { struct vvvv *vvlist,*current; facet_id f_id; edge_id e_id; vertex_id tmp; vertex_id v1,v2,v3; int i; int count; int numerr = 0; element_id orig; /* check edges */ if ( web.skel[EDGE].count == 0 ) return numerr; vvlist = (struct vvvv *)temp_calloc(web.skel[EDGE].count,sizeof(struct vvvv)); current = vvlist; count = 0; FOR_ALL_EDGES(e_id) { v1 = get_edge_tailv(e_id); v2 = get_edge_headv(e_id); if ( equal_element(v1,v2) && !(get_vattr(v1) & AXIAL_POINT) ) { sprintf(msg,"Edge %s",ELNAME(e_id)); erroutstring(msg); orig = get_original(e_id); if ( valid_id(orig) && !equal_element(orig,e_id) ) { sprintf(msg," (orig. %s)",ELNAME(orig)); erroutstring(msg); } sprintf(msg," is loop on vertex %s",ELNAME(v1)); erroutstring(msg); orig = get_original(get_edge_tailv(e_id)); if ( valid_id(orig) && !equal_element(orig,v1) ) { sprintf(msg," (orig. %s)",ELNAME(orig)); erroutstring(msg); } erroutstring(".\n"); numerr++; } if ( v1 <= v2 ) { current->id = e_id; current->v[0] = v1; current->v[1] = v2; } else { current->id = inverse_id(e_id); current->v[0] = v2; current->v[1] = v1; } if ( ++count > web.skel[EDGE].count ) { erroutstring("Edge count disagrees with supposed value."); count--; numerr++; break; } current++; } qsort((char *)vvlist,count,sizeof(struct vvvv),FCAST vvvvcomp); /* scan list for duplicates */ for ( i = 0 ; i < count-1 ; i++ ) { if ( vvvvcomp(vvlist+i,vvlist+i+1) == 0 ) if ( !web.symmetry_flag || (get_edge_wrap(vvlist[i].id) == get_edge_wrap(vvlist[i+1].id)) ) { sprintf(msg,"Edges %s and %s have same endpoints: %s %s.\n", ELNAME(vvlist[i].id),ELNAME1(vvlist[i+1].id), ELNAME2(vvlist[i].v[0]), ELNAME3(vvlist[i].v[1])); erroutstring(msg); numerr++; } } temp_free((char*)vvlist); if ( web.representation == STRING ) return numerr; #ifdef MPI_EVOLVER if ( mpi_corona_state == NO_CORONA ) return numerr; #endif /* check facets */ if ( web.skel[FACET].count == 0 ) return numerr; vvlist = (struct vvvv *)temp_calloc(web.skel[FACET].count,sizeof(struct vvvv)); current = vvlist; count = 0; FOR_ALL_FACETS(f_id) { facetedge_id fe; fe = get_facet_fe(f_id); v1 = positive_id(get_fe_tailv(fe)); v2 = positive_id(get_fe_headv(fe)); v3 = positive_id(get_fe_headv(get_next_edge(fe))); /* bubble sort */ if ( v1 > v2 ) { tmp = v1; v1 = v2; v2 = tmp; invert(f_id); } if ( v2 > v3 ) { tmp = v2; v2 = v3; v3 = tmp; invert(f_id); } if ( v1 > v2 ) { tmp = v1; v1 = v2; v2 = tmp; invert(f_id); } current->id = f_id; current->v[0] = v1; current->v[1] = v2; current->v[2] = v3; if ( ++count > web.skel[FACET].count ) { erroutstring("Facet count disagrees with supposed value."); count--; if ( ++numerr > MAXERR ) {erroutstring("Too many errors.\n"); return numerr;} break; } current++; } qsort((char *)vvlist,count,sizeof(struct vvvv),FCAST vvvvcomp); /* scan list for duplicates */ for ( i = 0 ; i < count-1 ; i++ ) { if ( (vvvvcomp(vvlist+i,vvlist+i+1) == 0) && !(get_vattr(vvlist[i].v[0]) & AXIAL_POINT) && !(get_vattr(vvlist[i].v[1]) & AXIAL_POINT) && !(get_vattr(vvlist[i].v[2]) & AXIAL_POINT) ) { sprintf(msg,"Facets %s and %s have same vertices: %s %s %s.\n", ELNAME(vvlist[i].id),ELNAME1(vvlist[i+1].id), ELNAME2(vvlist[i].v[0]), ELNAME3(vvlist[i].v[1]),ELNAME4(vvlist[i].v[2])); erroutstring(msg); numerr++; } } temp_free((char*)vvlist); return numerr; } /* end collapse_check() */ /********************************************************************** * * Function: normal_change_check() * * Purpose: Checks how much the normal of facets have changed * during motion. Returns the largest (delta normal)/(normal) */ REAL normal_change_check() { facet_id f_id; facetedge_id fe; vertex_id v_id; REAL side[2][MAXCOORD]; REAL max_delta; REAL old_normal[MAXCOORD]; REAL new_normal[MAXCOORD]; REAL x[3][MAXCOORD]; int ord_v; REAL diff; struct boundary *bdry; int i,n; max_delta = 0.0; FOR_ALL_FACETS(f_id) { int ii; if ( get_fattr(f_id) & FIXED ) continue; /* get old normal */ fe = get_facet_fe(f_id); for ( ii = 0 ; ii < FACET_EDGES ; ii++, fe = get_next_edge(fe) ) { v_id = get_fe_tailv(fe); ord_v = loc_ordinal(v_id); if ( get_vattr(v_id) & BOUNDARY ) { bdry = get_boundary(v_id); for ( i = 0 ; i < SDIM ; i++ ) x[ii][i] = eval(bdry->coordf[i],saved.coord[ord_v],v_id,NULL); } else { for ( i = 0 ; i < SDIM ; i++ ) x[ii][i] = saved.coord[ord_v][i]; } } for ( i = 0 ; i < SDIM ; i++ ) { side[0][i] = x[1][i] - x[0][i]; side[1][i] = x[2][i] - x[0][i]; } cross_prod(side[0],side[1],old_normal); /* get new normal */ fe = get_facet_fe(f_id); for ( n = 0 ; n < FACET_EDGES ; n++, fe = get_next_edge(fe) ) { v_id = get_fe_tailv(fe); for ( i = 0 ; i < SDIM ; i++ ) x[n][i] = get_coord(v_id)[i]; } for ( i = 0 ; i < SDIM ; i++ ) { side[0][i] = x[1][i] - x[0][i]; side[1][i] = x[2][i] - x[0][i]; } cross_prod(side[0],side[1],new_normal); /* test difference */ for ( i = 0 ; i < SDIM ; i++ ) side[0][i] = new_normal[i] - old_normal[i]; diff = sqrt(SDIM_dot(side[0],side[0])/ SDIM_dot(old_normal,old_normal)); if ( diff > max_delta ) max_delta = diff; } return max_delta; } /* end normal_change_check() */ /******************************************************************** * * function: wrap_check() * * purpose: check wraps around facet make identity, except for axial * point. And checks torus wraps for validity. * * return: number of bad facets. */ int wrap_check() { edge_id e_id; facet_id f_id; int numerr = 0; int count = 0; int i; if ( web.torus_flag ) FOR_ALL_EDGES(e_id) { WRAPTYPE wrap = get_edge_wrap(e_id); for ( i = 0 ; i < SDIM ; i++, wrap >>= TWRAPBITS ) switch ( wrap & WRAPMASK ) { case NEGWRAP : break; case 0 : break; case POSWRAP : break; default : sprintf(errmsg,"Big wrap %d on edge %s period %d\n", WRAPNUM(wrap&WRAPMASK),ELNAME(e_id),i+1); erroutstring(errmsg); numerr++; break; } } FOR_ALL_FACETS(f_id) { facetedge_id fe,start_fe; int wrap = 0; fe = start_fe = get_facet_fe(f_id); count = 0; if ( valid_id(fe) ) do { if ( get_vattr(get_fe_tailv(fe)) & AXIAL_POINT ) { wrap = 0; break; } wrap = (*sym_compose)(wrap,get_fe_wrap(fe)); fe = get_next_edge(fe); count++; if ( count > 2*web.skel[EDGE].count ) { sprintf(errmsg,"Facet %s has unclosed edge loop.\n",ELNAME(f_id)); erroutstring(errmsg); numerr++; break; } } while ( valid_id(fe) && !equal_id(fe,start_fe) ); if ( valid_id(fe) && (wrap != 0) ) { sprintf(errmsg,"Wraps around facet %s not consistent.\n",ELNAME(f_id)); kb_error(2000,errmsg,WARNING); numerr++; } } return numerr; } /* end wrap_check() */ evolver-2.30c.dfsg/src/mvgraph.c0000644000175300017530000000307111410765113017026 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************** * * File: MVgraph.c * * Contents: Routines for interface with shared memory * MinneView. */ #include "include.h" #ifndef OOGL void Begin_OOGL() { kb_error(1251,"This Evolver not compiled with the OOGL option.\n",WARNING); } void UpdateOOGL() { } void End_OOGL() { } #else void Begin_OOGL() {} void End_OOGL() { OOGL_flag = 0; if ( geomview_flag ) End_geomview(); } void UpdateOOGL() { void (*old_start)ARGS((void)); void (*old_end)ARGS((void)); void (*old_gedge)ARGS((struct graphdata *,edge_id)); void (*old_gfacet)ARGS((struct graphdata*,facet_id)); /* if user has asked us to quit, don't bother redisplaying */ /* (especially since graphgen resets breakflag!) */ if (breakflag) return; /* save current screen graphics pointers */ old_start = graph_start; old_end = graph_end; old_gfacet = graph_facet; old_gedge = graph_edge; /* OOGL pointers */ if ( geomview_flag ) { graph_start = geomview_start; graph_facet = geomview_facet; graph_edge = geomview_edge; graph_end = geomview_end; } /* do output */ graphgen(); /* restore old graphics */ graph_start = old_start; graph_end = old_end; graph_edge = old_gedge; graph_facet = old_gfacet; } #endif evolver-2.30c.dfsg/src/odrv.c0000644000175300017530000007307611410765113016350 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /* for making FORTRAN local variables stack instead of static */ #define STATIC /************************************************************************/ /* odrv.f -- translated by f2c (version of 8 February 1991 13:22:30). */ /* For YSMP sparse matrix package */ /************************************************************************/ #include "include.h" #include "f2c.h" int odrv_ ARGS(( integer *, integer *,integer *,REAL *, integer *,integer *, integer *,integer *, integer *, integer *)); /* *********************************************************************** */ /* 1/15/81 */ /* *********************************************************************** */ /* Function: ODRV -- DRIVER FOR SPARSE MATRIX REORDERING ROUTINES */ /* *********************************************************************** */ int odrv_(n, ia, ja, a, p, ip, nsp, isp, path, flag_) integer *n, *ia, *ja; doublereal *a; integer *p, *ip, *nsp, *isp, *path, *flag_; { STATIC integer head, l; STATIC logical dflag; STATIC integer q, v; STATIC integer max_, tmp; #ifdef NOPROTO extern /* Subroutine */ int md_(); extern /* Subroutine */ int sro_(); #endif /* DESCRIPTION */ /* ODRV FINDS A MINIMUM DEGREE ORDERING OF THE ROWS AND COLUMNS OF A */ /* SYMMETRIC MATRIX M STORED IN (IA,JA,A) FORMAT (SEE BELOW). FOR THE */ /* REORDERED MATRIX, THE WORK AND STORAGE REQUIRED TO PERFORM GAUSSIAN */ /* ELIMINATION IS (USUALLY) SIGNIFICANTLY LESS. */ /* IF ONLY THE NONZERO ENTRIES IN THE UPPER TRIANGLE OF M ARE BEING */ /* STORED, THEN ODRV SYMMETRICALLY REORDERS (IA,JA,A), (OPTIONALLY) */ /* WITH THE DIAGONAL ENTRIES PLACED FIRST IN EACH ROW. THIS IS TO */ /* ENSURE THAT IF M(I,J) WILL BE IN THE UPPER TRIANGLE OF M WITH */ /* RESPECT TO THE NEW ORDERING, THEN M(I,J) IS STORED IN ROW I (AND */ /* THUS M(J,I) IS NOT STORED); WHEREAS IF M(I,J) WILL BE IN THE */ /* STRICT LOWER TRIANGLE OF M, THEN M(J,I) IS STORED IN ROW J (AND */ /* THUS M(I,J) IS NOT STORED). */ /* STORAGE OF SPARSE MATRICES */ /* THE NONZERO ENTRIES OF THE MATRIX M ARE STORED ROW-BY-ROW IN THE */ /* ARRAY A. TO IDENTIFY THE INDIVIDUAL NONZERO ENTRIES IN EACH ROW, */ /* WE NEED TO KNOW IN WHICH COLUMN EACH ENTRY LIES. THESE COLUMN */ /* INDICES ARE STORED IN THE ARRAY JA; I.E., IF A(K) = M(I,J), THEN */ /* JA(K) = J. TO IDENTIFY THE INDIVIDUAL ROWS, WE NEED TO KNOW WHERE */ /* EACH ROW STARTS. THESE ROW POINTERS ARE STORED IN THE ARRAY IA; */ /* I.E., IF M(I,J) IS THE FIRST NONZERO ENTRY (STORED) IN THE I-TH ROW */ /* AND A(K) = M(I,J), THEN IA(I) = K. MOREOVER, IA(N+1) POINTS TO */ /* THE FIRST LOCATION FOLLOWING THE LAST ELEMENT IN THE LAST ROW. */ /* THUS, THE NUMBER OF ENTRIES IN THE I-TH ROW IS IA(I+1) - IA(I), */ /* THE NONZERO ENTRIES IN THE I-TH ROW ARE STORED CONSECUTIVELY IN */ /* A(IA(I)), A(IA(I)+1), ..., A(IA(I+1)-1), */ /* AND THE CORRESPONDING COLUMN INDICES ARE STORED CONSECUTIVELY IN */ /* JA(IA(I)), JA(IA(I)+1), ..., JA(IA(I+1)-1). */ /* SINCE THE COEFFICIENT MATRIX IS SYMMETRIC, ONLY THE NONZERO ENTRIES */ /* IN THE UPPER TRIANGLE NEED BE STORED. FOR EXAMPLE, THE MATRIX */ /* ( 1 0 2 3 0 ) */ /* ( 0 4 0 0 0 ) */ /* M = ( 2 0 5 6 0 ) */ /* ( 3 0 6 7 8 ) */ /* ( 0 0 0 8 9 ) */ /* COULD BE STORED AS */ /* \ 1 2 3 4 5 6 7 8 9 10 11 12 13 */ /* ---+-------------------------------------- */ /* IA \ 1 4 5 8 12 14 */ /* JA \ 1 3 4 2 1 3 4 1 3 4 5 4 5 */ /* A \ 1 2 3 4 2 5 6 3 6 7 8 8 9 */ /* OR (SYMMETRICALLY) AS */ /* \ 1 2 3 4 5 6 7 8 9 */ /* ---+-------------------------- */ /* IA \ 1 4 5 7 9 10 */ /* JA \ 1 3 4 2 3 4 4 5 5 */ /* A \ 1 2 3 4 5 6 7 8 9 . */ /* PARAMETERS */ /* N - ORDER OF THE MATRIX */ /* IA - INTEGER ONE-DIMENSIONAL ARRAY CONTAINING POINTERS TO DELIMIT */ /* ROWS IN JA AND A; DIMENSION = N+1 */ /* JA - INTEGER ONE-DIMENSIONAL ARRAY CONTAINING THE COLUMN INDICES */ /* CORRESPONDING TO THE ELEMENTS OF A; DIMENSION = NUMBER OF */ /* NONZERO ENTRIES IN (THE UPPER TRIANGLE OF) M */ /* A - REAL ONE-DIMENSIONAL ARRAY CONTAINING THE NONZERO ENTRIES IN */ /* (THE UPPER TRIANGLE OF) M, STORED BY ROWS; DIMENSION = */ /* NUMBER OF NONZERO ENTRIES IN (THE UPPER TRIANGLE OF) M */ /* P - INTEGER ONE-DIMENSIONAL ARRAY USED TO RETURN THE PERMUTATION */ /* OF THE ROWS AND COLUMNS OF M CORRESPONDING TO THE MINIMUM */ /* DEGREE ORDERING; DIMENSION = N */ /* IP - INTEGER ONE-DIMENSIONAL ARRAY USED TO RETURN THE INVERSE OF */ /* THE PERMUTATION RETURNED IN P; DIMENSION = N */ /* NSP - DECLARED DIMENSION OF THE ONE-DIMENSIONAL ARRAY ISP; NSP */ /* MUST BE AT LEAST 3N+4K, WHERE K IS THE NUMBER OF NONZEROES */ /* IN THE STRICT UPPER TRIANGLE OF M */ /* ISP - INTEGER ONE-DIMENSIONAL ARRAY USED FOR WORKING STORAGE; */ /* DIMENSION = NSP */ /* PATH - INTEGER PATH SPECIFICATION; VALUES AND THEIR MEANINGS ARE - */ /* 1 FIND MINIMUM DEGREE ORDERING ONLY */ /* 2 FIND MINIMUM DEGREE ORDERING AND REORDER SYMMETRICALLY */ /* STORED MATRIX (USED WHEN ONLY THE NONZERO ENTRIES IN */ /* THE UPPER TRIANGLE OF M ARE BEING STORED) */ /* 3 REORDER SYMMETRICALLY STORED MATRIX AS SPECIFIED BY */ /* INPUT PERMUTATION (USED WHEN AN ORDERING HAS ALREADY */ /* BEEN DETERMINED AND ONLY THE NONZERO ENTRIES IN THE */ /* UPPER TRIANGLE OF M ARE BEING STORED) */ /* 4 SAME AS 2 BUT PUT DIAGONAL ENTRIES AT START OF EACH ROW */ /* 5 SAME AS 3 BUT PUT DIAGONAL ENTRIES AT START OF EACH ROW */ /* FLAG - INTEGER ERROR FLAG; VALUES AND THEIR MEANINGS ARE - */ /* 0 NO ERRORS DETECTED */ /* 9N+K INSUFFICIENT STORAGE IN MD */ /* 10N+1 INSUFFICIENT STORAGE IN ODRV */ /* 11N+1 ILLEGAL PATH SPECIFICATION */ /* CONVERSION FROM REAL TO DOUBLE PRECISION */ /* CHANGE THE REAL DECLARATIONS IN ODRV AND SRO TO DOUBLE PRECISION */ /* DECLARATIONS. */ /* ----------------------------------------------------------------------- */ /* .... REAL A(1) */ /* ----INITIALIZE ERROR FLAG AND VALIDATE PATH SPECIFICATION */ /* Parameter adjustments */ --isp; --ip; --p; --a; --ja; --ia; /* Function Body */ *flag_ = 0; if (*path < 1 || 5 < *path) { goto L111; } /* ----ALLOCATE STORAGE AND FIND MINIMUM DEGREE ORDERING */ if ((*path - 1) * (*path - 2) * (*path - 4) != 0) { goto L1; } max_ = (*nsp - *n) / 2; v = 1; l = v + max_; head = l + max_; if (max_ < *n) { goto L110; } md_(n, &ia[1], &ja[1], &max_, &isp[v], &isp[l], &isp[head], &p[1], &ip[1], &isp[v], flag_); if (*flag_ != 0) { goto L100; } /* ----ALLOCATE STORAGE AND SYMMETRICALLY REORDER MATRIX */ L1: if ((*path - 2) * (*path - 3) * (*path - 4) * (*path - 5) != 0) { goto L2; } tmp = *nsp + 1 - *n; q = tmp - (ia[*n + 1] - 1); if (q < 1) { goto L110; } dflag = *path == 4 || *path == 5; sro_(n, &ip[1], &ia[1], &ja[1], &a[1], &isp[tmp], &isp[q], &dflag); L2: return 0; /* ** ERROR -- ERROR DETECTED IN MD */ L100: return 0; /* ** ERROR -- INSUFFICIENT STORAGE */ L110: *flag_ = *n * 10 + 1; return 0; /* ** ERROR -- ILLEGAL PATH SPECIFIED */ L111: *flag_ = *n * 11 + 1; return 0; } /* odrv_ */ /* *********************************************************************** */ /* *********************************************************************** */ /* Function: MD -- MINIMUM DEGREE ALGORITHM (BASED ON ELEMENT MODEL) */ /* *********************************************************************** */ int md_(n, ia, ja, max_, v, l, head, last, next, mark, flag_) integer *n, *ia, *ja, *max_, *v, *l, *head, *last, *next, *mark, *flag_; { /* System generated locals */ integer i__1; STATIC integer equiv_0[1]; /* Local variables */ STATIC integer dmin_, tail, k; #define ek (equiv_0) #define vk (equiv_0) STATIC integer tag; #ifdef NOPROTO extern /* Subroutine */ int mdi_(); extern /* Subroutine */ int mdm_(), mdp_(), mdu_(); #endif /* DESCRIPTION */ /* MD FINDS A MINIMUM DEGREE ORDERING OF THE ROWS AND COLUMNS OF A */ /* SYMMETRIC MATRIX M STORED IN (IA,JA,A) FORMAT. */ /* ADDITIONAL PARAMETERS */ /* MAX - DECLARED DIMENSION OF THE ONE-DIMENSIONAL ARRAYS V AND L; */ /* MAX MUST BE AT LEAST N+2K, WHERE K IS THE NUMBER OF */ /* NONZEROES IN THE STRICT UPPER TRIANGLE OF M */ /* V - INTEGER ONE-DIMENSIONAL WORK ARRAY; DIMENSION = MAX */ /* L - INTEGER ONE-DIMENSIONAL WORK ARRAY; DIMENSION = MAX */ /* HEAD - INTEGER ONE-DIMENSIONAL WORK ARRAY; DIMENSION = N */ /* LAST - INTEGER ONE-DIMENSIONAL ARRAY USED TO RETURN THE PERMUTATION */ /* OF THE ROWS AND COLUMNS OF M CORRESPONDING TO THE MINIMUM */ /* DEGREE ORDERING; DIMENSION = N */ /* NEXT - INTEGER ONE-DIMENSIONAL ARRAY USED TO RETURN THE INVERSE OF */ /* THE PERMUTATION RETURNED IN LAST; DIMENSION = N */ /* MARK - INTEGER ONE-DIMENSIONAL WORK ARRAY (MAY BE THE SAME AS V); */ /* DIMENSION = N */ /* FLAG - INTEGER ERROR FLAG; VALUES AND THEIR MEANINGS ARE - */ /* 0 NO ERRORS DETECTED */ /* 11N+1 INSUFFICIENT STORAGE IN MD */ /* DEFINITIONS OF INTERNAL PARAMETERS */ /* ---------+--------------------------------------------------------- */ /* V(S) \ VALUE FIELD OF LIST ENTRY */ /* ---------+--------------------------------------------------------- */ /* L(S) \ LINK FIELD OF LIST ENTRY (0 => END OF LIST) */ /* ---------+--------------------------------------------------------- */ /* L(VI) \ POINTER TO ELEMENT LIST OF UNELIMINATED VERTEX VI */ /* ---------+--------------------------------------------------------- */ /* L(EJ) \ POINTER TO BOUNDARY LIST OF ACTIVE ELEMENT EJ */ /* ---------+--------------------------------------------------------- */ /* HEAD(D) \ VJ => VJ HEAD OF D-LIST D */ /* \ 0 => NO VERTEX IN D-LIST D */ /* \ VI UNELIMINATED VERTEX */ /* \ VI IN EK \ VI NOT IN EK */ /* ---------+-----------------------------+--------------------------- */ /* NEXT(VI) \ UNDEFINED BUT NONNEGATIVE \ VJ => VJ NEXT IN D-LIST */ /* \ \ 0 => VI TAIL OF D-LIST */ /* ---------+-----------------------------+--------------------------- */ /* LAST(VI) \ (NOT SET UNTIL MDP) \ -D => VI HEAD OF D-LIST D */ /* \-VK => COMPUTE DEGREE \ VJ => VJ LAST IN D-LIST */ /* \ EJ => VI PROTOTYPE OF EJ \ 0 => VI NOT IN ANY D-LIST */ /* \ 0 => DO NOT COMPUTE DEGREE \ */ /* ---------+-----------------------------+--------------------------- */ /* MARK(VI) \ MARK(VK) \ NONNEGATIVE TAG < MARK(VK) */ /* \ VI ELIMINATED VERTEX */ /* \ EI ACTIVE ELEMENT \ OTHERWISE */ /* ---------+-----------------------------+--------------------------- */ /* NEXT(VI) \ -J => VI WAS J-TH VERTEX \ -J => VI WAS J-TH VERTEX */ /* \ TO BE ELIMINATED \ TO BE ELIMINATED */ /* ---------+-----------------------------+--------------------------- */ /* LAST(VI) \ M => SIZE OF EI = M \ UNDEFINED */ /* ---------+-----------------------------+--------------------------- */ /* MARK(VI) \ -M => OVERLAP COUNT OF EI \ UNDEFINED */ /* \ WITH EK = M \ */ /* \ OTHERWISE NONNEGATIVE TAG \ */ /* \ < MARK(VK) \ */ /* ----------------------------------------------------------------------- */ /* ----INITIALIZATION */ /* Parameter adjustments */ --mark; --next; --last; --head; --l; --v; --ja; --ia; /* Function Body */ tag = 0; mdi_(n, &ia[1], &ja[1], max_, &v[1], &l[1], &head[1], &last[1], &next[1], &mark[1], &tag, flag_); if (*flag_ != 0) { return 0; } k = 0; dmin_ = 1; /* ----WHILE K < N DO */ L1: if (k >= *n) { goto L4; } /* ------SEARCH FOR VERTEX OF MINIMUM DEGREE */ L2: if (head[dmin_] > 0) { goto L3; } ++dmin_; goto L2; /* ------REMOVE VERTEX VK OF MINIMUM DEGREE FROM DEGREE LIST */ L3: *vk = head[dmin_]; head[dmin_] = next[*vk]; if (head[dmin_] > 0) { last[head[dmin_]] = -dmin_; } /* ------NUMBER VERTEX VK, ADJUST TAG, AND TAG VK */ ++k; next[*vk] = -k; last[*ek] = dmin_ - 1; tag += last[*ek]; mark[*vk] = tag; /* ------FORM ELEMENT EK FROM UNELIMINATED NEIGHBORS OF VK */ mdm_(vk, &tail, &v[1], &l[1], &last[1], &next[1], &mark[1]); /* ------PURGE INACTIVE ELEMENTS AND DO MASS ELIMINATION */ mdp_(&k, ek, &tail, &v[1], &l[1], &head[1], &last[1], &next[1], &mark[1]); /* ------UPDATE DEGREES OF UNELIMINATED VERTICES IN EK */ mdu_(ek, &dmin_, &v[1], &l[1], &head[1], &last[1], &next[1], &mark[1]); goto L1; /* ----GENERATE INVERSE PERMUTATION FROM PERMUTATION */ L4: i__1 = *n; for (k = 1; k <= i__1; ++k) { next[k] = -next[k]; /* L5: */ last[next[k]] = k; } return 0; } /* md_ */ #undef vk #undef ek /* *********************************************************************** */ /* MDI -- INITIALIZATION */ /* *********************************************************************** */ /* Subroutine */ int mdi_(n, ia, ja, max_, v, l, head, last, next, mark, tag, flag_) integer *n, *ia, *ja, *max_, *v, *l, *head, *last, *next, *mark, *tag, *flag_; { /* System generated locals */ integer i__1, i__2; /* Local variables */ STATIC integer jmin, jmax, j, vi, vj, dvi, sfs; /* ----INITIALIZE DEGREES, ELEMENT LISTS, AND DEGREE LISTS */ /* Parameter adjustments */ --mark; --next; --last; --head; --l; --v; --ja; --ia; /* Function Body */ i__1 = *n; for (vi = 1; vi <= i__1; ++vi) { mark[vi] = 1; l[vi] = 0; /* L1: */ head[vi] = 0; } sfs = *n + 1; /* ----CREATE NONZERO STRUCTURE */ /* ----FOR EACH NONZERO ENTRY A(VI,VJ) IN STRICT UPPER TRIANGLE */ i__1 = *n; for (vi = 1; vi <= i__1; ++vi) { jmin = ia[vi]; jmax = ia[vi + 1] - 1; if (jmin > jmax) { goto L3; } i__2 = jmax; for (j = jmin; j <= i__2; ++j) { vj = ja[j]; if (vi >= vj) { goto L2; } if (sfs >= *max_) { goto L101; } /* ------ENTER VJ IN ELEMENT LIST FOR VI */ ++mark[vi]; v[sfs] = vj; l[sfs] = l[vi]; l[vi] = sfs; ++sfs; /* ------ENTER VI IN ELEMENT LIST FOR VJ */ ++mark[vj]; v[sfs] = vi; l[sfs] = l[vj]; l[vj] = sfs; ++sfs; L2: ; } L3: ; } /* ----CREATE DEGREE LISTS AND INITIALIZE MARK VECTOR */ i__1 = *n; for (vi = 1; vi <= i__1; ++vi) { dvi = mark[vi]; next[vi] = head[dvi]; head[dvi] = vi; last[vi] = -dvi; if (next[vi] > 0) { last[next[vi]] = vi; } /* L4: */ mark[vi] = *tag; } return 0; /* ** ERROR -- INSUFFICIENT STORAGE */ L101: *flag_ = *n * 9 + vi; return 0; } /* mdi_ */ /* *********************************************************************** */ /* MDM -- FORM ELEMENT FROM UNELIMINATED NEIGHBORS OF VK */ /* *********************************************************************** */ /* Subroutine */ int mdm_(vk, tail, v, l, last, next, mark) integer *vk, *tail, *v, *l, *last, *next, *mark; { /* System generated locals */ integer i__1; STATIC integer equiv_0[1]; /* Local variables */ STATIC integer b, s, lb; #define es (equiv_0) STATIC integer vb, ls; #define vs (equiv_0) STATIC integer blpmax, tag, blp; /* ----INITIALIZE TAG AND LIST OF UNELIMINATED NEIGHBORS */ /* Parameter adjustments */ --mark; --next; --last; --l; --v; /* Function Body */ tag = mark[*vk]; *tail = *vk; /* ----FOR EACH VERTEX/ELEMENT VS/ES IN ELEMENT LIST OF VK */ ls = l[*vk]; L1: s = ls; if (s == 0) { goto L5; } ls = l[s]; *vs = v[s]; if (next[*vs] < 0) { goto L2; } /* ------IF VS IS UNELIMINATED VERTEX, THEN TAG AND APPEND TO LIST OF */ /* ------UNELIMINATED NEIGHBORS */ mark[*vs] = tag; l[*tail] = s; *tail = s; goto L4; /* ------IF ES IS ACTIVE ELEMENT, THEN ... */ /* --------FOR EACH VERTEX VB IN BOUNDARY LIST OF ELEMENT ES */ L2: lb = l[*es]; blpmax = last[*es]; i__1 = blpmax; for (blp = 1; blp <= i__1; ++blp) { b = lb; lb = l[b]; vb = v[b]; /* ----------IF VB IS UNTAGGED VERTEX, THEN TAG AND APPEND TO LIST OF */ /* ----------UNELIMINATED NEIGHBORS */ if (mark[vb] >= tag) { goto L3; } mark[vb] = tag; l[*tail] = b; *tail = b; L3: ; } /* --------MARK ES INACTIVE */ mark[*es] = tag; L4: goto L1; /* ----TERMINATE LIST OF UNELIMINATED NEIGHBORS */ L5: l[*tail] = 0; return 0; } /* mdm_ */ #undef vs #undef es /* *********************************************************************** */ /* MDP -- PURGE INACTIVE ELEMENTS AND DO MASS ELIMINATION */ /* *********************************************************************** */ /* Subroutine */ int mdp_(k, ek, tail, v, l, head, last, next, mark) integer *k, *ek, *tail, *v, *l, *head, *last, *next, *mark; { /* System generated locals */ integer i__1; /* Local variables */ STATIC integer ofree, i, s, li, es, vi, ls, ilpmax, tag, evi, ilp, lvi; /* ----INITIALIZE TAG */ /* Parameter adjustments */ --mark; --next; --last; --head; --l; --v; /* Function Body */ tag = mark[*ek]; /* ----FOR EACH VERTEX VI IN EK */ li = *ek; ilpmax = last[*ek]; if (ilpmax <= 0) { goto L12; } i__1 = ilpmax; for (ilp = 1; ilp <= i__1; ++ilp) { i = li; li = l[i]; vi = v[li]; /* ------REMOVE VI FROM DEGREE LIST */ if (last[vi] == 0) { goto L3; } if (last[vi] > 0) { goto L1; } head[-last[vi]] = next[vi]; goto L2; L1: next[last[vi]] = next[vi]; L2: if (next[vi] > 0) { last[next[vi]] = last[vi]; } /* ------REMOVE INACTIVE ITEMS FROM ELEMENT LIST OF VI */ L3: ls = vi; L4: s = ls; ls = l[s]; if (ls == 0) { goto L6; } es = v[ls]; if (mark[es] < tag) { goto L5; } ofree = ls; l[s] = l[ls]; ls = s; L5: goto L4; /* ------IF VI IS INTERIOR VERTEX, THEN REMOVE FROM LIST AND ELIMINATE */ L6: lvi = l[vi]; if (lvi != 0) { goto L7; } l[i] = l[li]; li = i; ++(*k); next[vi] = -(*k); --last[*ek]; goto L11; /* ------ELSE ... */ /* --------CLASSIFY VERTEX VI */ L7: if (l[lvi] != 0) { goto L9; } evi = v[lvi]; if (next[evi] >= 0) { goto L9; } if (mark[evi] < 0) { goto L8; } /* ----------IF VI IS PROTOTYPE VERTEX, THEN MARK AS SUCH, INITIALIZE */ /* ----------OVERLAP COUNT FOR CORRESPONDING ELEMENT, AND MOVE VI TO E ND */ /* ----------OF BOUNDARY LIST */ last[vi] = evi; mark[evi] = -1; l[*tail] = li; *tail = li; l[i] = l[li]; li = i; goto L10; /* ----------ELSE IF VI IS DUPLICATE VERTEX, THEN MARK AS SUCH AND ADJ UST */ /* ----------OVERLAP COUNT FOR CORRESPONDING ELEMENT */ L8: last[vi] = 0; --mark[evi]; goto L10; /* ----------ELSE MARK VI TO COMPUTE DEGREE */ L9: last[vi] = -(*ek); /* --------INSERT EK IN ELEMENT LIST OF VI */ L10: v[ofree] = *ek; l[ofree] = l[vi]; l[vi] = ofree; L11: ; } /* ----TERMINATE BOUNDARY LIST */ L12: l[*tail] = 0; return 0; } /* mdp_ */ /* *********************************************************************** */ /* MDU -- UPDATE DEGREES OF UNELIMINATED VERTICES IN EK */ /* *********************************************************************** */ /* Subroutine */ int mdu_(ek, dmin_, v, l, head, last, next, mark) integer *ek, *dmin_, *v, *l, *head, *last, *next, *mark; { /* System generated locals */ integer i__1, i__2; STATIC integer equiv_0[1]; /* Local variables */ STATIC integer b, i, s; #define es (equiv_0) STATIC integer vb, vi; #define vs (equiv_0) STATIC integer blpmax, ilpmax, tag, blp, dvi, evi, ilp; /* ----INITIALIZE TAG */ /* Parameter adjustments */ --mark; --next; --last; --head; --l; --v; /* Function Body */ tag = mark[*ek] - last[*ek]; /* ----FOR EACH VERTEX VI IN EK */ i = *ek; ilpmax = last[*ek]; if (ilpmax <= 0) { goto L11; } i__1 = ilpmax; for (ilp = 1; ilp <= i__1; ++ilp) { i = l[i]; vi = v[i]; if ((i__2 = last[vi]) < 0) { goto L1; } else if (i__2 == 0) { goto L10; } else { goto L8; } /* ------IF VI NEITHER PROTOTYPE NOR DUPLICATE VERTEX, THEN MERGE ELEM ENTS */ /* ------TO COMPUTE DEGREE */ L1: ++tag; dvi = last[*ek]; /* --------FOR EACH VERTEX/ELEMENT VS/ES IN ELEMENT LIST OF VI */ s = l[vi]; L2: s = l[s]; if (s == 0) { goto L9; } *vs = v[s]; if (next[*vs] < 0) { goto L3; } /* ----------IF VS IS UNELIMINATED VERTEX, THEN TAG AND ADJUST DEGREE */ mark[*vs] = tag; ++dvi; goto L5; /* ----------IF ES IS ACTIVE ELEMENT, THEN EXPAND */ /* ------------CHECK FOR OUTMATCHED VERTEX */ L3: if (mark[*es] < 0) { goto L6; } /* ------------FOR EACH VERTEX VB IN ES */ b = *es; blpmax = last[*es]; i__2 = blpmax; for (blp = 1; blp <= i__2; ++blp) { b = l[b]; vb = v[b]; /* --------------IF VB IS UNTAGGED, THEN TAG AND ADJUST DEGREE */ if (mark[vb] >= tag) { goto L4; } mark[vb] = tag; ++dvi; L4: ; } L5: goto L2; /* ------ELSE IF VI IS OUTMATCHED VERTEX, THEN ADJUST OVERLAPS BUT DO NOT */ /* ------COMPUTE DEGREE */ L6: last[vi] = 0; --mark[*es]; L7: s = l[s]; if (s == 0) { goto L10; } *es = v[s]; if (mark[*es] < 0) { --mark[*es]; } goto L7; /* ------ELSE IF VI IS PROTOTYPE VERTEX, THEN CALCULATE DEGREE BY */ /* ------INCLUSION/EXCLUSION AND RESET OVERLAP COUNT */ L8: evi = last[vi]; dvi = last[*ek] + last[evi] + mark[evi]; mark[evi] = 0; /* ------INSERT VI IN APPROPRIATE DEGREE LIST */ L9: next[vi] = head[dvi]; head[dvi] = vi; last[vi] = -dvi; if (next[vi] > 0) { last[next[vi]] = vi; } if (dvi < *dmin_) { *dmin_ = dvi; } L10: ; } L11: return 0; } /* mdu_ */ #undef vs #undef es /* *********************************************************************** */ /* *********************************************************************** */ /* *********************************************************************** */ /* SRO -- SYMMETRIC REORDERING OF SPARSE SYMMETRIC MATRIX */ /* *********************************************************************** */ /* Subroutine */ int sro_(n, ip, ia, ja, a, q, r, dflag) integer *n, *ip, *ia, *ja; doublereal *a; integer *q, *r; logical *dflag; { /* System generated locals */ integer i__1, i__2; /* Local variables */ STATIC integer jmin, jmax, i, j, k, ilast; STATIC doublereal ak; STATIC integer jdummy, jak; /* DESCRIPTION */ /* THE NONZERO ENTRIES OF THE MATRIX M ARE ASSUMED TO BE STORED */ /* SYMMETRICALLY IN (IA,JA,A) FORMAT (I.E., NOT BOTH M(I,J) AND M(J,I) */ /* ARE STORED IF I NE J). */ /* SRO DOES NOT REARRANGE THE ORDER OF THE ROWS, BUT DOES MOVE */ /* NONZEROES FROM ONE ROW TO ANOTHER TO ENSURE THAT IF M(I,J) WILL BE */ /* IN THE UPPER TRIANGLE OF M WITH RESPECT TO THE NEW ORDERING, THEN */ /* M(I,J) IS STORED IN ROW I (AND THUS M(J,I) IS NOT STORED); WHEREAS */ /* IF M(I,J) WILL BE IN THE STRICT LOWER TRIANGLE OF M, THEN M(J,I) IS */ /* STORED IN ROW J (AND THUS M(I,J) IS NOT STORED). */ /* ADDITIONAL PARAMETERS */ /* Q - INTEGER ONE-DIMENSIONAL WORK ARRAY; DIMENSION = N */ /* R - INTEGER ONE-DIMENSIONAL WORK ARRAY; DIMENSION = NUMBER OF */ /* NONZERO ENTRIES IN THE UPPER TRIANGLE OF M */ /* DFLAG - LOGICAL VARIABLE; IF DFLAG = .TRUE., THEN STORE NONZERO */ /* DIAGONAL ELEMENTS AT THE BEGINNING OF THE ROW */ /* ----------------------------------------------------------------------- */ /* REAL A(1), AK */ /* --PHASE 1 -- FIND ROW IN WHICH TO STORE EACH NONZERO */ /* ----INITIALIZE COUNT OF NONZEROES TO BE STORED IN EACH ROW */ /* Parameter adjustments */ --r; --q; --a; --ja; --ia; --ip; /* Function Body */ i__1 = *n; for (i = 1; i <= i__1; ++i) { /* L1: */ q[i] = 0; } /* ----FOR EACH NONZERO ELEMENT A(J) */ i__1 = *n; for (i = 1; i <= i__1; ++i) { jmin = ia[i]; jmax = ia[i + 1] - 1; if (jmin > jmax) { goto L3; } i__2 = jmax; for (j = jmin; j <= i__2; ++j) { /* --------FIND ROW (=R(J)) AND COLUMN (=JA(J)) IN WHICH TO STORE A(J) ... */ k = ja[j]; if (ip[k] < ip[i]) { ja[j] = i; } if (ip[k] >= ip[i]) { k = i; } r[j] = k; /* --------... AND INCREMENT COUNT OF NONZEROES (=Q(R(J)) IN THAT ROW */ /* L2: */ ++q[k]; } L3: ; } /* --PHASE 2 -- FIND NEW IA AND PERMUTATION TO APPLY TO (JA,A) */ /* ----DETERMINE POINTERS TO DELIMIT ROWS IN PERMUTED (JA,A) */ i__1 = *n; for (i = 1; i <= i__1; ++i) { ia[i + 1] = ia[i] + q[i]; /* L4: */ q[i] = ia[i + 1]; } /* ----DETERMINE WHERE EACH (JA(J),A(J)) IS STORED IN PERMUTED (JA,A) */ /* ----FOR EACH NONZERO ELEMENT (IN REVERSE ORDER) */ ilast = 0; jmin = ia[1]; jmax = ia[*n + 1] - 1; j = jmax; i__1 = jmax; for (jdummy = jmin; jdummy <= i__1; ++jdummy) { i = r[j]; if (! (*dflag) || ja[j] != i || i == ilast) { goto L5; } /* ------IF DFLAG, THEN PUT DIAGONAL NONZERO AT BEGINNING OF ROW */ r[j] = ia[i]; ilast = i; goto L6; /* ------PUT (OFF-DIAGONAL) NONZERO IN LAST UNUSED LOCATION IN ROW */ L5: --q[i]; r[j] = q[i]; L6: --j; } /* --PHASE 3 -- PERMUTE (JA,A) TO UPPER TRIANGULAR FORM (WRT NEW ORDERING) */ i__1 = jmax; for (j = jmin; j <= i__1; ++j) { L7: if (r[j] == j) { goto L8; } k = r[j]; r[j] = r[k]; r[k] = k; jak = ja[k]; ja[k] = ja[j]; ja[j] = jak; ak = a[k]; a[k] = a[j]; a[j] = ak; goto L7; L8: ; } return 0; } /* sro_ */ evolver-2.30c.dfsg/src/storage.h0000644000175300017530000003070011410765113017032 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /******************************************************************** * * File: storage.h * * Purpose: Header file defining details of storage implementation. * All machine-dependent gory details should be here * (for inclusion in other source files that need to know) * or in storage.c (purely private details). * * This version has element ids typed as longs. * Also implements id as offset from element base. * All elements of same type in one memory block, extended * as needed. To be used on at least 32 bit machines. */ /* using offset as id is 15% faster overall than using ordinal */ #define NUMELEMENTS 5 /* these values used for identification and as index into skeleton info */ #define VERTEX 0 #define EDGE 1 #define FACET 2 #define BODY 3 #define FACETEDGE 4 /* other object type ids, used in mpi transfers for example */ #define QUANTITY_OBJECT 5 #define INSTANCE_OBJECT 6 #ifdef xelement /***************************************************************** * * Universal element identifier. Don't want to use straight * pointer since base changes when realloced. * (not actually used; just for documentation) */ typedef struct xelement_id { unsigned int type : 3; /* see enum below */ unsigned int valid: 1; /* valid id bit */ unsigned int sign : 1; /* set for reverse orientation */ unsigned int offset: 27; /* offset from block start */ } xelement_id; #endif #ifndef TASK_ID_BITS #define TASK_ID_BITS 0 #endif #define MAX_TASK ((1<> TYPESHIFT) /* to give switched orientation of first if that of second is inverted */ #define same_sign(id1,id2) ((id1) ^ ((id2) & SIGNMASK)) /* to test whether two elements have the same orientation */ #define like_sign(id1,id2) (((id1) & SIGNMASK) == ((id2) & SIGNMASK)) /* number of elements to allocate memory for at one time */ #define BATCHSIZE 100 /* machine number of id, for MPI */ #define id_task(id) (int)(((id)&TASKMASK) >> TASK_ID_SHIFT) /* for denoting freed spot in hash table */ #define ELHASH_FREE ((struct element *)1) /* Macros for getting structure pointer from id */ #ifdef HASH_ID #define elptr(id) ((struct element *)(elhash_lookup(id))) #define vptr(v_id) ((struct vertex *)(elhash_lookup(v_id))) #define eptr(e_id) ((struct edge *)(elhash_lookup(e_id))) #define fptr(f_id) ((struct facet *)(elhash_lookup(f_id))) #define bptr(b_id) ((struct body *)(elhash_lookup(b_id))) #define feptr(fe_id) ((struct facetedge *)(elhash_lookup(fe_id))) #else /* lookup in ibase pointer list for each element */ #ifdef MPI_EVOLVER #define elptr(id) ((struct element *)((id_task(id)==this_task?web.skel[id_type(id)].ibase[(id)&OFFSETMASK]:mpi_remote_elptr(id)))) #define vptr(v_id) ((struct vertex *)((id_task(v_id)==this_task?web.skel[VERTEX].ibase[(v_id)&OFFSETMASK]:mpi_remote_elptr(v_id)))) #define eptr(e_id) ((struct edge *)((id_task(e_id)==this_task?web.skel[EDGE].ibase[(e_id)&OFFSETMASK]:mpi_remote_elptr(e_id)))) #define fptr(f_id) ((struct facet *)((id_task(f_id)==this_task?web.skel[FACET].ibase[(f_id)&OFFSETMASK]:mpi_remote_elptr(f_id)))) #define xxbptr(b_id) ((struct body *)((id_task(b_id)==this_task?web.skel[BODY].ibase[(b_id)&OFFSETMASK]:mpi_remote_elptr(b_id)))) #define bptr(b_id) ((struct body *)(web.skel[BODY].ibase[(b_id)&OFFSETMASK])) #define feptr(fe_id) ((struct facetedge *)((id_task(fe_id)==this_task?web.skel[FACETEDGE].ibase[(fe_id)&OFFSETMASK]:mpi_remote_elptr(fe_id)))) #else #define elptr(id) ((struct element *)(web.skel[id_type(id)].ibase[(id)&OFFSETMASK])) #define vptr(v_id) ((struct vertex *)(web.skel[VERTEX].ibase[(v_id)&OFFSETMASK])) #define eptr(e_id) ((struct edge *)(web.skel[EDGE].ibase[(e_id)&OFFSETMASK])) #define fptr(f_id) ((struct facet *)(web.skel[FACET].ibase[(f_id)&OFFSETMASK])) #define bptr(b_id) ((struct body *)(web.skel[BODY].ibase[(b_id)&OFFSETMASK])) #define feptr(fe_id) ((struct facetedge *)(web.skel[FACETEDGE].ibase[(fe_id)&OFFSETMASK])) #endif /* end HASH_ID */ #endif #define ordinal(id) (valid_id(id) ? (int)((id) & OFFSETMASK) : -1 ) #ifdef MPI_EVOLVER #define loc_ordinal(id) (valid_id(id) ? (int)(elptr(id)->local_id & OFFSETMASK) : -1 ) #else #define loc_ordinal(id) (valid_id(id) ? (int)((id) & OFFSETMASK) : -1 ) #endif /* Macros for producing element name strings for printing. */ /* Note these use global permanent strings to store return string */ /* so printf's with multiple names should use different ELNAMEs. */ /* ELNAME is unsigned, SELNAME is signed */ extern char elnames[10][30]; #ifdef MPI_EVOLVER #define ELNAME(id) (valid_id(id) ? (sprintf(elnames[0],"%d@%d",(int)((id)&OFFSETMASK)+1,id_task(id)),elnames[0]) : "") #define ELNAME1(id) (valid_id(id) ? (sprintf(elnames[1],"%d@%d",(int)((id)&OFFSETMASK)+1,id_task(id)),elnames[1]) : "") #define ELNAME2(id) (valid_id(id) ? (sprintf(elnames[2],"%d@%d",(int)((id)&OFFSETMASK)+1,id_task(id)),elnames[2]) : "") #define ELNAME3(id) (valid_id(id) ? (sprintf(elnames[3],"%d@%d",(int)((id)&OFFSETMASK)+1,id_task(id)),elnames[3]) : "") #define ELNAME4(id) (valid_id(id) ? (sprintf(elnames[4],"%d@%d",(int)((id)&OFFSETMASK)+1,id_task(id)),elnames[4]) : "") #define ELNAME5(id) (valid_id(id) ? (sprintf(elnames[5],"%d@%d",(int)((id)&OFFSETMASK)+1,id_task(id)),elnames[5]) : "") #define ELNAME6(id) (valid_id(id) ? (sprintf(elnames[6],"%d@%d",(int)((id)&OFFSETMASK)+1,id_task(id)),elnames[6]) : "") #define ELNAME7(id) (valid_id(id) ? (sprintf(elnames[7],"%d@%d",(int)((id)&OFFSETMASK)+1,id_task(id)),elnames[7]) : "") #define ELNAME8(id) (valid_id(id) ? (sprintf(elnames[8],"%d@%d",(int)((id)&OFFSETMASK)+1,id_task(id)),elnames[8]) : "") #define ELNAME9(id) (valid_id(id) ? (sprintf(elnames[9],"%d@%d",(int)((id)&OFFSETMASK)+1,id_task(id)),elnames[9]) : "") #define SELNAME(id) (valid_id(id) ? (sprintf(elnames[0],"%s%d@%d",(inverted(id)?"-":""),(int)((id)&OFFSETMASK)+1,id_task(id)),elnames[0]) : "") #define SELNAME1(id) (valid_id(id) ? (sprintf(elnames[1],"%s%d@%d",(inverted(id)?"-":""),(int)((id)&OFFSETMASK)+1,id_task(id)),elnames[1]) : "") #define SELNAME2(id) (valid_id(id) ? (sprintf(elnames[2],"%s%d@%d",(inverted(id)?"-":""),(int)((id)&OFFSETMASK)+1,id_task(id)),elnames[2]) : "") #define SELNAME3(id) (valid_id(id) ? (sprintf(elnames[3],"%s%d@%d",(inverted(id)?"-":""),(int)((id)&OFFSETMASK)+1,id_task(id)),elnames[3]) : "") #define SELNAME4(id) (valid_id(id) ? (sprintf(elnames[4],"%s%d@%d",(inverted(id)?"-":""),(int)((id)&OFFSETMASK)+1,id_task(id)),elnames[4]) : "") #define SELNAME5(id) (valid_id(id) ? (sprintf(elnames[5],"%s%d@%d",(inverted(id)?"-":""),(int)((id)&OFFSETMASK)+1,id_task(id)),elnames[5]) : "") #define SELNAME6(id) (valid_id(id) ? (sprintf(elnames[6],"%s%d@%d",(inverted(id)?"-":""),(int)((id)&OFFSETMASK)+1,id_task(id)),elnames[6]) : "") #define SELNAME7(id) (valid_id(id) ? (sprintf(elnames[7],"%s%d@%d",(inverted(id)?"-":""),(int)((id)&OFFSETMASK)+1,id_task(id)),elnames[7]) : "") #define SELNAME8(id) (valid_id(id) ? (sprintf(elnames[8],"%s%d@%d",(inverted(id)?"-":""),(int)((id)&OFFSETMASK)+1,id_task(id)),elnames[8]) : "") #define SELNAME9(id) (valid_id(id) ? (sprintf(elnames[9],"%s%d@%d",(inverted(id)?"-":""),(int)((id)&OFFSETMASK)+1,id_task(id)),elnames[9]) : "") #else #define ELNAME(id) (valid_id(id) ? (sprintf(elnames[0],"%d",(int)((id)&OFFSETMASK)+1),elnames[0]) : "") #define ELNAME1(id) (valid_id(id) ? (sprintf(elnames[1],"%d",(int)((id)&OFFSETMASK)+1),elnames[1]) : "") #define ELNAME2(id) (valid_id(id) ? (sprintf(elnames[2],"%d",(int)((id)&OFFSETMASK)+1),elnames[2]) : "") #define ELNAME3(id) (valid_id(id) ? (sprintf(elnames[3],"%d",(int)((id)&OFFSETMASK)+1),elnames[3]) : "") #define ELNAME4(id) (valid_id(id) ? (sprintf(elnames[4],"%d",(int)((id)&OFFSETMASK)+1),elnames[4]) : "") #define ELNAME5(id) (valid_id(id) ? (sprintf(elnames[5],"%d",(int)((id)&OFFSETMASK)+1),elnames[5]) : "") #define ELNAME6(id) (valid_id(id) ? (sprintf(elnames[6],"%d",(int)((id)&OFFSETMASK)+1),elnames[6]) : "") #define ELNAME7(id) (valid_id(id) ? (sprintf(elnames[7],"%d",(int)((id)&OFFSETMASK)+1),elnames[7]) : "") #define ELNAME8(id) (valid_id(id) ? (sprintf(elnames[8],"%d",(int)((id)&OFFSETMASK)+1),elnames[8]) : "") #define ELNAME9(id) (valid_id(id) ? (sprintf(elnames[9],"%d",(int)((id)&OFFSETMASK)+1),elnames[9]) : "") #define SELNAME(id) (valid_id(id) ? (sprintf(elnames[0],"%s%d",(inverted(id)?"-":""),(int)((id)&OFFSETMASK)+1),elnames[0]) : "") #define SELNAME1(id) (valid_id(id) ? (sprintf(elnames[1],"%s%d",(inverted(id)?"-":""),(int)((id)&OFFSETMASK)+1),elnames[1]) : "") #define SELNAME2(id) (valid_id(id) ? (sprintf(elnames[2],"%s%d",(inverted(id)?"-":""),(int)((id)&OFFSETMASK)+1),elnames[2]) : "") #define SELNAME3(id) (valid_id(id) ? (sprintf(elnames[3],"%s%d",(inverted(id)?"-":""),(int)((id)&OFFSETMASK)+1),elnames[3]) : "") #define SELNAME4(id) (valid_id(id) ? (sprintf(elnames[4],"%s%d",(inverted(id)?"-":""),(int)((id)&OFFSETMASK)+1),elnames[4]) : "") #define SELNAME5(id) (valid_id(id) ? (sprintf(elnames[5],"%s%d",(inverted(id)?"-":""),(int)((id)&OFFSETMASK)+1),elnames[5]) : "") #define SELNAME6(id) (valid_id(id) ? (sprintf(elnames[6],"%s%d",(inverted(id)?"-":""),(int)((id)&OFFSETMASK)+1),elnames[6]) : "") #define SELNAME7(id) (valid_id(id) ? (sprintf(elnames[7],"%s%d",(inverted(id)?"-":""),(int)((id)&OFFSETMASK)+1),elnames[7]) : "") #define SELNAME8(id) (valid_id(id) ? (sprintf(elnames[8],"%s%d",(inverted(id)?"-":""),(int)((id)&OFFSETMASK)+1),elnames[8]) : "") #define SELNAME9(id) (valid_id(id) ? (sprintf(elnames[9],"%s%d",(inverted(id)?"-":""),(int)((id)&OFFSETMASK)+1),elnames[9]) : "") #endif /* Macros for manipulating and testing element ids */ #define edge_inverse(id) inverse_id(id) #define facet_inverse(id) inverse_id(id) #define fe_inverse(id) inverse_id(id) #define invert(id) ((id) ^= SIGNMASK) #define equal_id(a,b) ((a)==(b)) #define equal_element(a,b) (((a)|SIGNMASK) == ((b)|SIGNMASK)) #define valid_id(id) ((int)(((id)&VALIDMASK) >> VALIDSHIFT)) #define inverted(id) ((int)(((id)&SIGNMASK) >> SIGNSHIFT)) #define inverse_id(id) ((id) ^ SIGNMASK) #define positive_id(id) ((id) & ~SIGNMASK) typedef int ORDTYPE; /* element numbering type */ typedef struct element *INDIRECT_TYPE; extern struct blocklist_struct { struct element *blockptr; /* allocated block */ int start_ord; /* ordinal of first element */ int count; /* elements in block */ } *blocklist[NUMELEMENTS]; extern int blockcount[NUMELEMENTS]; /* how many blocks allocated */ extern int blockmax[NUMELEMENTS]; /* length of blocklist */ /* individual indirect block pointer arrays */ extern INDIRECT_TYPE *vibase; extern INDIRECT_TYPE *eibase; extern INDIRECT_TYPE *fibase; extern INDIRECT_TYPE *bibase; extern INDIRECT_TYPE *feibase; /* for getting id1 with orientation sign of id2 */ #define copy_sign(id1,id2) (((id1)&~SIGNMASK) | ((id2) & SIGNMASK)) evolver-2.30c.dfsg/src/rl_head.h0000644000175300017530000001060711410765113016770 0ustar hazelscthazelsct"actual_volume", "alice", "approx_curvature", "approximate_curvature", "area", "area_fixed", "area_method_name", "area_normalization", "areaweed", "attribute", //"attributes", "autochop", "autodisplay", "autopop", "autorecalc", "axial_point", "backbody", "backcolor", "bare", "bezier_basis", "bodies", "body", "bottominfo", //"boundaries", "boundary", "boundary_curvature", "break", "burchard", "bye", "chdir", "check", "clipped_cells", "close_show", "color", "colorfile", "conducting_knot_energy", "conformal_metric", "connected_cells", "conserved", "constraint", "constraint_tolerance", //"constraints", "content", "continue", "convert_to_quantities", "convex", "count", "counts", "datafilename", "date_and_time", "debug", "define", "delete", "density", "diffusion", "dihedral", "dirichlet", "dirichlet_mode", "dissolve", "do", "dump", //"e", "edge", "edge_divide", //"edges", "edgeswap", "edgeweed", "effective_area", "efixed", "eigenprobe", "element_modulus", "else", "energy", "eprint", "equiangulate", "errprintf", "estimate", "everything_quantities", "evolver_version", "exec", "exit", "exprint", "extrapolate", "face", //"faces", //"facet", "facet_edge", //"facet_edges", //"facetedge", "facetedges", //"facets", "fix", "fixed", "fixed_area", "for", "foreach", "form_integrand", "formula", "frontbody", "frontcolor", "function", //"g", "gap_constant", "gauss_curvature", "geompipe", "geomview", "global", "global_method", "go", "gravity", "gravity_constant", "gridflag", "help", "hessian", "hessian_menu", "hessian_seek", "hessian_special_normal_vector", "histogram", "history", "hit_constraint", "hit_partner", "homothety", "id", "if", "ignore_constraints", "ignore_fixed", //"imod", "info_only", "insulating_knot_energy", "integer", "integral_order", "integral_order_1d", "integral_order_2d", //"integration_order", //"integration_order_1d", //"integration_order_2d", "interp_bdry_param", "is_defined", "jiggle", "k_vector_order", "keep_macros", "keep_originals", "keylogfile", "klein_metric", "kmetis", "labelflag", "lagrange", "lagrange_multiplier", "lagrange_order", "lanczos", "length", "length_method_name", "linear", "list", "load", "load_library", "local", "logfile", "loghistogram", "longj", "mean_curvature", "mean_curvature_integral", "merit_factor", "method", "method_instance", "metis", "metis_factor", "metric", "midv", "mobility", "mobility_tensor", "move", "new_body", "new_edge", "new_facet", "new_vertex", "no_display", "no_refine", "nodisplay", "noncontent", "nonnegative", "nonpositive", "nonwall", "normal_curvature", "notch", "oid", "ometis", "on_boundary", "on_constraint", "ooglfile", "opacity", //"optimise", //"optimising_parameter", "optimize", "optimizing_parameter", "orientation", "original", "parameter", "parameter_1", "parameter_file", "parameters", "partner_hitting", "pause", "pdelta", "periods", "permload", "phase", "phasefile", //"pi", "pop", "pop_edge_to_tri", "pop_quad_to_quad", "pop_tri_to_edge", "postscript", "pressure", "print", "printf", "procedure", "procedures", "pscale", "pscolorflag", "quadratic", "quantity", "quiet", "quietgo", "quietload", "quit", "raw_vertex_average", "rawest_vertex_average", "rawestv", "rawv", "read", "real", "rebody", "recalc", "refine", "renumber_all", "reorder_storage", "return", "ritz", "runge_kutta", "saddle", "scalar_integrand", "scale", "scale_limit", "self", "set", "shading", "shell", "show", "show_all_quantities", "show_expr", "show_inner", "show_off", "show_outer", "show_trans", "show_vol", "showq", "simplex_representation", "sizeof", "soapfilm", "sobolev_mode", "sobolev_seek", "space_dimension", "spring_constant", "sprintf", "sqcurve", "square_curvature", "square_gaussian_curvature", "squared_curvature", "squared_gaussian_curvature", "stability_test", "string", //"sum", "surface_dimension", "surface_energy", "swap_colors", "symmetric_content", "symmetry_group", "system", "tag", "target", "temperature", "tension", "tetra_point", "then", "thicken", "tolerance", "topinfo", "torus", "torus_filled", "torus_periods", "total", "total_time", "transform_depth", "transform_expr", "triple_point", "ulong", "unfix", "unset", "utest", "valence", "value", "vector_integrand", "verbose", "vertex", "vertex_average", "vertexnormal", "vertices", "view_matrix", "view_transform_generators", "view_transforms", "visibility_test", "volconst", "volfixed", "volume", "volume_method_name", "warning_messages", "where", "while", "wrap", "wrap_vertex", "wulff", "zoom", "zoom_radius", "zoom_vertex", evolver-2.30c.dfsg/src/alloca.c0000644000175300017530000000173111410765113016616 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************** * * File: alloca.c * * Contents: Kludge substitute for alloca() for those * systems that do not have it. alloca() is * an old unix function that allocates memory * from the stack. The version of YACC I use * uses alloca in case of parsing stack overflow, * so alloca should almost never be called. * Link with this file only if the linker * complains about not finding alloca. * This file simply substitutes a call to * malloc(), so represents a potential * memory leak. */ #include "include.h" void * alloca(size_t size) { return (void*)malloc(size); } evolver-2.30c.dfsg/src/calcforc.c0000644000175300017530000005721511410765113017147 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************* * * file: calcforc.c * * Contents: Functions calculating energy, volume, and * their gradients at vertices. */ #include "include.h" #ifdef THREADS /****************************************************************** * * function: thread_calc_facet_energy() * * purpose: thread-friendly calculation of classic facet energy. */ void thread_calc_facet_energy() { #ifdef OLDTHNEXT facet_id f_id; THREAD_FOR_ALL_FACETS(f_id) (*calc_facet_energy)(f_id,ALL_ENERGIES); #else THREAD_FOR_ALL_NEW(FACET, {(*calc_facet_energy)(*idptr,ALL_ENERGIES);}); #endif } /****************************************************************** * * function: thread_calc_facet_forces() * * purpose: thread-friendly calculation of classic facet forces. */ void thread_calc_facet_forces() { #ifdef OLDTHNEXT facet_id f_id; THREAD_FOR_ALL_FACETS(f_id) { (*calc_facet_forces)(f_id); } #else THREAD_FOR_ALL_NEW(FACET,(*calc_facet_forces)(*idptr)) #endif } #endif /* THREADS */ /******************************************************************* * * Function: calc_energy() * * Purpose: Wrapper for total energy calculation. * * */ void calc_energy() { #ifdef MPI_EVOLVER mpi_calc_energy(); #else local_calc_energy(); #endif } /******************************************************************* * * Function: local_calc_energy() * * Purpose: Finds total energy of configuration. * Requires volumes set to calculate pressure energy. * */ void local_calc_energy() { int i; body_id b_id; edge_id e_id; web.total_energy = 0.0; web.total_area = 0.0; for ( i = 0 ; i < MAXADDENDS ; i++ ) { web.total_area_addends[i] = 0.0; web.total_energy_addends[i] = 0.0; } web.spring_energy = 0.0; euclidean_area = 0.0; if ( web.modeltype == LAGRANGE && !quantities_only_flag ) { if ( auto_convert_flag ) convert_to_quantities(); else kb_error(1774,"LAGRANGE model needs convert_to_quantities.\n",RECOVERABLE); } if ( min_square_grad_flag ) { web.total_energy = square_grad(); return; } if ( quantities_only_flag ) goto quantities_only; if ( square_curvature_flag ) { if ( globals(square_curvature_param)->value.real == 0.0 ) square_curvature_flag &= ~EVALUATE; else square_curvature_flag |= EVALUATE; } if ( mean_curv_int_flag ) { if ( globals(square_curvature_param)->value.real == 0.0 ) mean_curv_int_flag &= ~EVALUATE; else mean_curv_int_flag |= EVALUATE; } if ( sqgauss_flag ) { if ( globals(sqgauss_param)->value.real == 0.0 ) sqgauss_flag &= ~EVALUATE; else sqgauss_flag |= EVALUATE; } if ( ((square_curvature_flag | mean_curv_int_flag) & EVALUATE) && !kusner_flag && !conf_edge_curv_flag ) sqcurve_energy_init(); if ( web.representation == SIMPLEX ) { facet_id f_id; FOR_ALL_FACETS(f_id) calc_simplex_energy(f_id); FOR_ALL_EDGES(e_id) calc_simplex_edge_energy(e_id); } else if ( web.representation == STRING ) { vertex_id v_id; FOR_ALL_EDGES(e_id) { ATTR attr = get_eattr(e_id); (*calc_edge_energy)(e_id); if ( attr & BDRY_ENERGY ) calc_constr_energy_e(e_id); /* substitute surface energy */ } /* boundary energy and square curvature */ if ( ((square_curvature_flag | mean_curv_int_flag) & EVALUATE) && !kusner_flag && !conf_edge_curv_flag ) sqcurve_energy_string_init(); FOR_ALL_VERTICES(v_id) { ATTR attr = get_vattr(v_id); if ( ((square_curvature_flag | mean_curv_int_flag) & EVALUATE) && !kusner_flag && !conf_edge_curv_flag ) sqcurve_energy_string(v_id); if ( attr & BDRY_ENERGY ) { if ( attr & CONSTRAINT ) calc_constr_energy_v(v_id); } } } /* end STRING */ else /* web.representation == SOAPFILM */ { facet_id f_id; if ( threadflag ) thread_launch(TH_CALC_FACET_ENERGY,FACET); else { FOR_ALL_FACETS(f_id) (*calc_facet_energy)(f_id,ALL_ENERGIES); } FOR_ALL_EDGES(e_id) { ATTR attr = get_eattr(e_id); if ( attr & BDRY_ENERGY ) { if ( attr & CONSTRAINT ) calc_constr_energy_e(e_id); } if ( attr & DENSITY ) (*calc_edge_energy)(e_id); /* for triple line energies */ } } /* end SOAPFILM */ if ( ((square_curvature_flag | mean_curv_int_flag) & EVALUATE) && !kusner_flag && !conf_edge_curv_flag ) sqcurve_energy_end(); if ( (square_curvature_flag & EVALUATE) && kusner_flag ) kusner_energy(); if ( (square_curvature_flag & EVALUATE) && conf_edge_curv_flag ) conf_edge_curv_energy(); if ( sqgauss_flag & EVALUATE ) sqgauss_energy(); /* Add kludge forces on boundary edges to prevent pulling away */ /* Could use more exact test to see if worth calling these */ if ( (web.modeltype == LINEAR) && (web.convex_flag) ) { if ( web.bdrymax > 0 ) FOR_ALL_EDGES(e_id) bdry_spring_energy(e_id); if ( web.maxcon > 0 ) FOR_ALL_EDGES(e_id) constr_spring_energy(e_id); } quantities_only: FOR_ALL_BODIES(b_id) { REAL fix,vol; fix = get_body_fixvol(b_id); vol = get_body_volume(b_id); if ( web.pressure_flag && (get_battr(b_id) & FIXEDVOL) && !quantities_only_flag ) { if ( !equal_id(b_id,web.outside_body) ) { binary_tree_add(web.total_energy_addends, -web.pressure*(fix*log(vol/fix)-(vol-fix))); if ( valid_id(web.outside_body) ) binary_tree_add(web.total_energy_addends,web.pressure*vol); set_body_pressure(b_id,web.pressure*fix/vol); } } else if ( (get_battr(b_id) & PRESSURE) && !quantities_only_flag ) binary_tree_add(web.total_energy_addends,-get_body_pressure(b_id)*vol); } if ( gen_quant_count ) web.total_energy += calc_quants(Q_ENERGY); /* finalize web.total_area */ for ( i = 0 ; i < MAXADDENDS ; i++ ) { web.total_area += web.total_area_addends[i]; web.total_energy += web.total_energy_addends[i]; } extrap_val[reflevel] = web.total_energy; } /* end calc_energy() */ /**************************************************************** * * Function: calc_force() * * Purpose: Calls proper energy gradient calculator. * */ void calc_force() { #ifdef MPI_EVOLVER mpi_calc_force(); #else local_calc_force(); #endif } /**************************************************************** * * Function: calc_force() * * Purpose: calculates net force at triangulation vertices due * to surface tension and constraints * */ void local_calc_force() { vertex_id v_id; facet_id f_id; edge_id e_id; int i; /* zero out vertex cumulative quantities */ MFOR_ALL_VERTICES(v_id) { REAL *f = get_force(v_id); for ( i = 0 ; i < SDIM ; i ++ ) f[i] = 0.0; set_vertex_star(v_id,0.0); set_vertex_valence(v_id,0); } if ( min_square_grad_flag ) { square_grad_forces(); return; } if ( quantities_only_flag ) goto grad_quantities_only; if ( square_curvature_flag ) { if ( globals(square_curvature_param)->value.real == 0.0 ) square_curvature_flag &= ~EVALUATE; else square_curvature_flag |= EVALUATE; } if ( mean_curv_int_flag ) { if ( globals(mean_curvature_param)->value.real == 0.0 ) mean_curv_int_flag &= ~EVALUATE; else mean_curv_int_flag |= EVALUATE; } if ( sqgauss_flag ) { if ( globals(sqgauss_param)->value.real == 0.0 ) sqgauss_flag &= ~EVALUATE; else sqgauss_flag |= EVALUATE; } if ( ((square_curvature_flag | mean_curv_int_flag) & EVALUATE) && !kusner_flag && !conf_edge_curv_flag ) sqcurve_force_init(); if ( unit_normal_flag ) /* for Dennis DeTurck */ { FOR_ALL_VERTICES(v_id) { REAL *f = get_force(v_id); facetedge_id fe = get_vertex_fe(v_id); if ( inverted(get_fe_facet(fe)) ) fe = get_next_facet(fe); calc_vertex_normal(v_id,fe,f); for ( i = 0 ; i < SDIM ; i++ ) f[i] *= deturck_factor; } /* continue with other forces */ } /* boundary and constraint forces */ if ( web.representation == SIMPLEX ) { FOR_ALL_EDGES(e_id) calc_simplex_edge_force(e_id); } else if ( web.representation == STRING ) { if ( ((square_curvature_flag | mean_curv_int_flag) & EVALUATE) && !kusner_flag && !conf_edge_curv_flag ) sqcurve_energy_string_init(); FOR_ALL_VERTICES(v_id) { ATTR attr = get_vattr(v_id); if ( attr & FIXED ) continue; if ( ((square_curvature_flag | mean_curv_int_flag) & EVALUATE) && !kusner_flag && !conf_edge_curv_flag ) sqcurve_force_string(v_id); if ( !(attr & BDRY_ENERGY) ) continue; if ( attr & CONSTRAINT ) calc_constr_force_v(v_id); } } else /* SOAPFILM */ { FOR_ALL_EDGES(e_id) { ATTR attr = get_eattr(e_id); if ( attr & DENSITY ) (*calc_edge_forces)(e_id); /* for triple line energies */ if ( !(attr & BDRY_ENERGY) ) continue; if ( attr & CONSTRAINT ) calc_constr_force_e(e_id); } } if ( web.representation == SIMPLEX ) { FOR_ALL_FACETS(f_id) calc_simplex_forces(f_id); } else if ( web.representation == STRING ) { /* tension forces */ /* add each edge's contribution to its endpoints */ FOR_ALL_EDGES(e_id) { ATTR attr = get_eattr(e_id); (*calc_edge_forces)(e_id); if ( attr & BDRY_ENERGY ) calc_constr_force_e(e_id); /* substitute surface energy */ } } else /* get here only for SOAPFILM */ { /* tension forces */ /* find each triangle's contribution to forces on its vertices */ if ( threadflag ) thread_launch(TH_CALC_FACET_FORCES,FACET); else FOR_ALL_FACETS(f_id) (*calc_facet_forces)(f_id); } if ( ((square_curvature_flag | mean_curv_int_flag) & EVALUATE ) && !kusner_flag && !conf_edge_curv_flag ) sqcurve_force_end(); if ( (square_curvature_flag & EVALUATE) && kusner_flag ) kusner_force(); if ( (square_curvature_flag & EVALUATE) && conf_edge_curv_flag) conf_edge_curv_force(); if ( sqgauss_flag & EVALUATE ) sqgauss_force(); /* Add kludge forces on boundary edges to prevent pulling away */ if ( (web.modeltype == LINEAR) & web.convex_flag ) { FOR_ALL_EDGES(e_id) { bdry_force(e_id); constr_springs(e_id); } } grad_quantities_only: /* general quantity forces */ calc_quant_grads(Q_ENERGY); /* derivatives with respect to optimizing parameters */ if ( optparamcount > 0 ) { struct oldcoord csaved; csaved.coord = NULL; save_coords(&csaved,SAVE_SEPARATE); for ( i = 0 ; i < optparamcount ; i++ ) { REAL dp; REAL emid = web.total_energy; REAL eleft,eright; dp = globals(optparam[i].pnum)->attr.varstuff.delta; /* right difference */ globals(optparam[i].pnum)->value.real += dp; project_all(0, TEST_MOVE); if ( fixed_constraint_flag || web.pressure_flag || web.pressflag ) { calc_content(Q_FIXED); /*volume_restore();*/ /*calc_pressure();*/ } calc_energy(); /* energy after motion */ eright = web.total_energy; restore_coords(&csaved,SAVE_SEPARATE); /* also restores opt params */ /* left difference */ globals(optparam[i].pnum)->value.real -= dp; project_all(0, TEST_MOVE); if ( fixed_constraint_flag || web.pressure_flag || web.pressflag ) { calc_content(Q_FIXED); /*volume_restore(); */ /*calc_pressure(); */ } calc_energy(); /* energy after motion */ eleft = web.total_energy; restore_coords(&csaved,SAVE_SEPARATE); /* also restores opt params */ web.total_energy = emid; /* restore */ optparam[i].grad = (eright - eleft)/2/dp; } unsave_coords(&csaved,SAVE_SEPARATE); } } /* end calc_force() */ /********************************************************************* * * function force_normalization() * * purpose: now have total energy gradient (which is a covector) and * need to convert to a velocity vector, which requires * multiplication by the inverse of a metric matrix. * Default is Euclidean (identity matrix). */ void force_normalization() { vertex_id v_id; int i,j; MAT2D(a,MAXPARAM,MAXCOORD); if ( web.metric_flag && metric_convert_flag ) FOR_ALL_VERTICES(v_id) { if ( !(get_attr(v_id) & FIXED) ) metric_form_to_vector(get_coord(v_id),get_force(v_id)); } /* do area normalization */ if ( web.area_norm_flag && !approx_curve_flag) { FOR_ALL_VERTICES(v_id) { REAL area = ((web.representation==STRING)?get_vertex_length_star(v_id): get_vertex_area_star(v_id))/star_fraction; REAL *force = get_force(v_id); REAL ff; if ( get_vattr(v_id) & FIXED ) continue; if ( effective_area_flag && (web.representation == STRING) ) { /* calculate effective area */ REAL d; int valence = get_vertex_valence(v_id); ff = SDIM_dot(force,force); if ( ff == 0.0 ) continue; d = get_edge_density(get_vertex_edge(v_id)); ff /= d*d; if ( valence == 2 ) { REAL f2; f2 = sqrt(ff)/2; area *= sqrt(1 - ff/4)*f2/asin(f2); } else if ( valence == 1 ) { if ( !(get_vattr(v_id) & (FIXED|CONSTRAINT|BOUNDARY) ) ) { edge_id e_id = get_vertex_edge(v_id); vertex_id other_v = get_edge_headv(e_id); eliminate_edge(e_id); free_element(e_id); add_vertex_valence(other_v,-1); continue; } area *= sqrt(1 - ff/4); } else if ( valence == 0 ) area = 1.0; /* disconnected pt; no force anyway */ else if ( (valence == 3) && !old_area_flag ) { /* for density 1 edges only */ edge_id e_id; REAL ss[3],f[MAXCOORD],side[3][MAXCOORD]; REAL ang12,ang13,det,leg[2][MAXCOORD]; area = 0.0; e_id = get_vertex_edge(v_id); for ( i = 0 ; i < 3 ; i++ ) { get_edge_side(e_id,side[i]); ss[i] = sqrt(SDIM_dot(side[i],side[i])); e_id = get_next_tail_edge(e_id); } ang12 = acos(SDIM_dot(side[0],side[1])/ss[0]/ss[1]) - 2*M_PI/3; ang13 = acos(SDIM_dot(side[0],side[2])/ss[0]/ss[2]) - 2*M_PI/3; for ( j = 0 ; j < SDIM ; j++ ) { leg[0][j] = side[1][j] - side[0][j]; leg[1][j] = side[0][j] - side[2][j]; } det = fabs(leg[0][0]*leg[1][1] - leg[0][1]*leg[1][0]); if ( det != 0.0 ) { f[0] = (leg[1][0]*ang12 - leg[0][0]*ang13)/det; f[1] = (leg[1][1]*ang12 - leg[0][1]*ang13)/det; area = -0.5*(f[0]*force[0]+f[1]*force[1])/ (f[0]*f[0]+f[1]*f[1]); } else area = f[0] = f[1] = 0.0; set_vertex_star(v_id,star_fraction*area); for ( i = 0 ; i < 2 ; i++ ) force[i] = -2*f[i]; continue; } else /* triple point at least */ { edge_id e_id,start_e; REAL ss,fs,side[MAXCOORD]; area = 0.0; e_id = start_e = get_vertex_edge(v_id); do { get_edge_side(e_id,side); ss = SDIM_dot(side,side); fs = SDIM_dot(force,side); ff = SDIM_dot(force,force); area += 0.5*sqrt(ff*ss - fs*fs); e_id = get_next_tail_edge(e_id); } while ( !equal_id(e_id,start_e) ); } set_vertex_star(v_id,star_fraction*area); } else if ( effective_area_flag && (web.representation == SOAPFILM) ) { /* crude correction for triple edges and tetra points */ if ( get_vattr(v_id) & TRIPLE_PT ) area /= sqrt(3.); else if ( get_vattr(v_id) & TETRA_PT ) area /= sqrt(6.); } if ( area == 0.0 ) { sprintf(errmsg,"Zero area around vertex %s.\n",ELNAME(v_id)); kb_error(1453,errmsg,RECOVERABLE); } for ( i = 0 ; i < SDIM ; i++ ) force[i] /= area; } } /* end web.area_norm_flag && !approx_curve_flag */ /* project to parameter space for boundary points */ FOR_ALL_VERTICES(v_id) { int pcount; REAL *f = get_force(v_id); REAL tmp[MAXCOORD]; struct boundary *bdry; int m; if ( get_vattr(v_id) & FIXED ) continue; if ( !(get_vattr(v_id) & BOUNDARY) ) continue; bdry = get_boundary(v_id); pcount = bdry->pcount; b_proj(bdry,get_param(v_id),a,PARAMPROJ,v_id); matvec_mul(a,f,tmp,pcount,SDIM); for ( m = 0 ; m < pcount ; m++ ) f[m] = tmp[m]; for ( m = pcount ; m < SDIM ; m++ ) f[m] = 0.0; } return; } /* end force_normalization */ /************************************************************* * * Function: calc_content() * * Purpose: wrapper for local_calc_content, and * calculates body volumes diffs * * Return value: total difference from fixed value constraints */ REAL calc_content ARGS1((mode), int mode) { body_id b_id,bi_id; int k; REAL diff = 0.0; struct gen_quant *gq; facet_id f_id; /* starting body volumes */ FOR_ALL_BODIES(b_id) { int attr = get_battr(b_id); if ( !everything_quantities_flag || ((attr & FIXEDVOL) && (mode & Q_FIXED)) || ((attr & PRESSURE) && (mode & (Q_ENERGY|Q_INFO)) ) || (!(attr & (FIXEDVOL|PRESSURE)) && (mode & Q_INFO)) ) { set_body_volume(b_id,get_body_volconst(b_id),NOSETSTAMP); if ( web.representation == STRING ) { /* kludge for calculating separate facet areas */ facet_id start_f = get_body_facet(b_id); f_id = start_f; if ( valid_id(f_id) ) { do { set_facet_area(f_id,0.0); f_id = get_next_body_facet(f_id); } while ( !equal_id(f_id,start_f)); } } } } #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) mpi_calc_content(mode); else #endif local_calc_content(mode); if ( web.torus_flag ) { /* do volume adjust to get continuity with old volume, or fixed volume */ FOR_ALL_BODIES(b_id) { REAL vol = get_body_volume(b_id); REAL dvol = 0.0; REAL new_vc; if ( (get_battr(b_id) & FIXEDVOL) && !web.pressure_flag ) { REAL ovol = get_body_fixvol(b_id); dvol = web.torusv*floor((ovol-vol)/web.torusv+0.5); add_body_volume_plain(b_id,dvol); save_body_volume(b_id); } else if (mode & Q_RENORMALIZE) /* set between 0 and 1 */ { dvol = -web.torusv*floor(vol/web.torusv); add_body_volume_plain(b_id,dvol); save_body_volume(b_id); } else /* enforce continuity */ { REAL ovol = get_body_oldvolume(b_id); dvol = web.torusv*floor((ovol-vol)/web.torusv+0.5); add_body_volume_plain(b_id,dvol); } new_vc = get_body_volconst(b_id)+dvol; set_body_volconst(b_id,new_vc); if ( (web.representation == STRING) && (new_vc != 0.0) ) { /* apply volconst to most worthy facet */ facet_id f_id = get_body_facet(b_id); facet_id start_f = f_id; REAL worst_a = new_vc > 0.0 ? 1e30 : -1e30; facet_id best_f = NULLID; if ( valid_id(f_id) ) do { REAL a = get_facet_area(f_id); if ( inverted(f_id) ) a = -a; if ( ((new_vc > 0.0) && (a < worst_a)) || ((new_vc < 0.0) && (a > worst_a)) ) { best_f = f_id; worst_a = a; } f_id = get_next_body_facet(f_id); } while ( !equal_id(f_id,start_f) ); if ( valid_id(best_f) ) add_facet_area(best_f,new_vc); } } } if ( mode & Q_RENORMALIZE ) { /* take care of bodies with declared actual volume */ FOR_ALL_BODIES(b_id) if ( get_battr(b_id) & ACTUALVOL ) { REAL calcvol = get_body_volume(b_id); REAL actual = get_body_actualvolume(b_id); set_body_volume(b_id,actual,SETSTAMP); set_body_volconst(b_id,get_body_volconst(b_id)+actual-calcvol); save_body_volume(b_id); } } web.vol_flag = 1; /* get total deviation from constraints */ if ( !everything_quantities_flag && !web.pressure_flag ) FOR_ALL_BODIES(bi_id) { REAL absvol = get_body_abstotal(b_id); REAL target = get_body_fixvol(bi_id); REAL actual = get_body_volume(bi_id); if ( !(get_battr(bi_id) & FIXEDVOL) ) continue; diff += fabs(target - actual)/ (web.target_tolerance*(absvol?absvol:1.0)); } for ( k = 0 ; k < gen_quant_count ; k++ ) { gq = GEN_QUANT(k); if ( !(gq->flags & Q_FIXED) ) continue; if ( gq->tolerance > 0.0 ) diff += fabs(gq->target - gq->value)/(gq->tolerance*gq->abstotal); else diff += fabs(gq->target - gq->value)/(web.target_tolerance*gq->abstotal); } if ( mode & Q_FIXED ) fixed_volume_timestamp = global_timestamp; if ( mode & Q_INFO ) info_volume_timestamp = global_timestamp; return diff; } /************************************************************* * * Function: local_calc_content() * * Purpose: calculates body volumes on local piece of surface. * */ void local_calc_content ARGS1((mode), int mode) /* Q_FIXED | Q_INFO | Q_RENORMALIZE */ { facet_id f_id; facetedge_id fe_id; body_id b_id; FOR_ALL_BODIES(b_id) { /* initialize binary tree add */ struct body *ptr; int k; ptr = bptr(b_id); for ( k = 0 ; k < MAXADDENDS ; k++ ) ptr->volume_addends[k] = 0; } if ( gen_quant_count ) calc_quants(mode); if ( quantities_only_flag ) return; if ( web.bodycount == 0 ) return; if ( web.representation == SIMPLEX ) { FOR_ALL_FACETS(f_id) calc_simplex_volume(f_id); } else if ( web.representation == STRING ) { vertex_id v_id; FOR_ALL_FACETEDGES(fe_id) (*calc_edge_area)(fe_id); FOR_ALL_VERTICES(v_id) { ATTR attr = get_vattr(v_id); if ( !(attr & BDRY_CONTENT) ) continue; if ( attr & CONSTRAINT ) calc_constr_content_v(v_id); } } else if ( web.representation == SOAPFILM ) { if ( web.torus_flag ) torvol(); else { edge_id e_id; FOR_ALL_FACETS(f_id) (*calc_facet_volume)(f_id); FOR_ALL_EDGES(e_id) { ATTR attr = get_eattr(e_id); if ( !(attr & BDRY_CONTENT) ) continue; if ( attr & CONSTRAINT ) calc_constr_content_e(e_id); } } } /* finish binary tree accumulation */ FOR_ALL_BODIES(b_id) { REAL sum = 0.0; struct body *ptr = bptr(b_id); int k; for ( k = 0 ; k < MAXADDENDS ; k++ ) sum += ptr->volume_addends[k]; add_body_volume_plain(b_id,sum); } } /* end local_calc_content() */ /************************************************************* * * Function: calc_pressure() * * Purpose: calculates body volumes and pressures on faces */ void calc_pressure() { body_id b_id; if ( !web.pressure_flag ) return; calc_content(Q_FIXED); /* now compute pressure in each body */ FOR_ALL_BODIES(b_id) { REAL p; if ( equal_id(b_id,web.outside_body) ) p = web.pressure; else if ( get_battr(b_id) & FIXEDVOL ) { if ( get_body_volume(b_id) > 0.0 ) p = web.pressure*get_body_fixvol(b_id)/get_body_volume(b_id); else { sprintf(errmsg,"Body %s has volume %f\n",ELNAME(b_id), (DOUBLE)get_body_volume(b_id)); kb_error(1776,errmsg,WARNING); p = 0.0; } } else { p = 0.0; } set_body_pressure(b_id,p); } } /* end calc_pressure() */ evolver-2.30c.dfsg/src/sqcurve3.c0000644000175300017530000016231611410765113017145 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************** * * File: sqcurve3.c * * Purpose: Does calculations needed for including square curvature * in energy. Linear model only. * Named quantity methods. * * Vertex star approach. * * Assumes complete star around vertex. v_info comes in with * central vertex first, followed by ring in order. Sides * are in same order. */ #include "include.h" /* some timing results: 4-processor 4400 SGI (polyphemus) Old methods used processors in 2:1:1:1 ratio. New are 1:1:1:1 So would scale better to more procs. PLAIN: Star methods take about same time as old methods, but better load balance EFFECTIVE AREA, NORMAL_SQ New methods take same clock time, more total time. */ /* local storage max */ #define MAXV 20 REAL star_sqcurve_method_all ARGS((struct qinfo*,int)); static int h0_flag; /* set to use (H - H_0)^2 */ static REAL h0_value; /* value of H_0 */ /*********************************************************************** * * Function: star_sqcurve_method_init() * * Purpose: Initializes data structures for square curvature. */ void star_sqcurve_method_init(mode,mi) int mode; /* METHOD_VALUE or METHOD_GRADIENT */ struct method_instance *mi; { int k,n; struct gen_quant_method *gm; int eltype; if ( web.dimension != 2 ) kb_error(1610,"sq_mean_curvature method only for 2D facets.\n",RECOVERABLE); if ( web.modeltype != LINEAR ) kb_error(1611,"sq_mean_curvature method only for LINEAR model.\n", RECOVERABLE); if ( everything_quantities_flag & square_curvature_flag ) GEN_QUANT(sq_mean_curv_quantity_num)->modulus = globals(square_curvature_param)->value.real; /* see if using (H - H_0)^2 adjustment */ h0_flag = 0; h0_attr = find_extra("h_zero",&eltype); if ( h0_attr >= 0 ) h0_flag = H0_IN_ATTR; else { k = lookup_global("h_zero"); if ( k >= 0 ) { h0_flag = H0_IN_GLOBAL; h0_value = globals(k)->value.real; } } if ( star_sq_mean_curvature_mi < 0 ) { /* see what method indices correspond to what methods */ for ( n=0,gm = basic_gen_methods ; gm->name[0] != ' ' ; gm++,n++ ) { if ( stricmp(gm->name,"star_sq_mean_curvature") == 0 ) star_sq_mean_curvature_mi = n; if ( stricmp(gm->name,"star_eff_area_sq_mean_curvature") == 0 ) star_eff_area_sq_mean_curvature_mi = n; if ( stricmp(gm->name,"star_normal_sq_mean_curvature") == 0 ) star_normal_sq_mean_curvature_mi = n; if ( stricmp(gm->name,"star_perp_sq_mean_curvature") == 0 ) star_perp_sq_mean_curvature_mi = n; } } if ( mi->gen_method == star_eff_area_sq_mean_curvature_mi ) if ( SDIM != 3 ) kb_error(1612,"star_eff_area_sq_mean_curvature method only for 3D space.\n", RECOVERABLE); if ( mi->gen_method == star_normal_sq_mean_curvature_mi ) if ( SDIM != 3 ) kb_error(1613,"star_normal_sq_mean_curvature method only for 3D space.\n", RECOVERABLE); if ( mi->gen_method == star_perp_sq_mean_curvature_mi ) if ( SDIM != 3 ) kb_error(2810,"star_perp_sq_mean_curvature method only for 3D space.\n", RECOVERABLE); if ( self_similar_flag ) { int param = lookup_global(SELFSIM_NAME); kb_error(1614,"Cannot use self-similarity with star_sq_... methods.\n", RECOVERABLE); if ( param < 0 ) /* missing, so add */ { param = add_global(SELFSIM_NAME); globals(param)->value.real = 1.0; /* default */ globals(param)->flags |= ORDINARY_PARAM | RECALC_PARAMETER | ALWAYS_RECALC; } if ( h0_flag == 0 ) { h0_flag = H0_IN_GLOBAL; h0_value = 0.0; } } } /*********************************************************************** * * function: star_sqcurve_method_all() * * purpose: value and gradient calculations using common code. * */ REAL star_sqcurve_method_all(v_info,mode) struct qinfo *v_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ { int variety; /* PLAIN_SQ, EFF_SQ, NORMAL_SQ, PERP_SQ */ int pairs; int i,j,k,ii,kk; REAL energy=0.0,ff,fn=0.0,nn=0.0,area,g,h=0.0; REAL dAdv[MAXCOORD], *a, *d, *s1s1, *s1s2, *s2s2; REAL vnorm[MAXCOORD]; REAL **dAdv1,**dAdv2,***dvnorm1,***dvnorm2,**ds2; REAL ***ddAdv1dv1,***ddAdv2dv1,***ddAdv1dv2,***ddAdv2dv2; REAL ***ddss12,***ddss21,***ddss22; REAL **dfndv1,**dfndv2,**dffdv1,**dffdv2,**dnndv1,**dnndv2; REAL ***dfdv1,***dfdv2; REAL *s1,*s2; REAL **s = v_info->sides[0]; REAL temp[MAXCOORD]; REAL antisymnorm[MAXCOORD][MAXCOORD]; REAL antisymf[MAXCOORD][MAXCOORD]; REAL aa75=0.0,a75=0.0,ffaaa=0.0,ffaa=0.0,an75=0.0,ann75=0.0,fnnna=0.0; REAL fnna=0.0,fn75=0.0,fnn75=0.0,ffn75=0.0,afnfn=0.0,afffnfn=0.0,fffnfn=0.0; REAL ffafnfnfn=0.0,afffffnfnfnfn=0.0,fffffnfnfna=0.0,fffffnfnfn=0.0, fffffnfn=0.0; /* common subexpressions */ REAL aa[MAXV*5]; MAT2D(ds1,12*MAXV,MAXCOORD); MAT3D(ddss11,16*MAXV,MAXCOORD,MAXCOORD); REAL vertex_h0; REAL hh0=0.0; /* curvature adjusted for h-h0 */ int concount; /* number of constraints vertex is on */ int final_edge; /* of pairs; wrap to first for complete star */ MAT2D(proj,MAXCOORD,MAXCOORD); struct method_instance *mi = METH_INSTANCE(v_info->method); if ( mi->gen_method == star_normal_sq_mean_curvature_mi ) variety = NORMAL_SQ; else if ( mi->gen_method == star_perp_sq_mean_curvature_mi ) variety = PERP_SQ; else if ( mi->gen_method == star_eff_area_sq_mean_curvature_mi ) variety = EFF_SQ; else variety = PLAIN_SQ; pairs = (v_info->vcount - 1); if ( pairs <= 0 ) return 0.0; if ( v_info->flags & INCOMPLETE_STAR ) { pairs--; final_edge = pairs; } else final_edge = 0; if ( v_info->vcount > MAXV ) { a = (REAL*)mycalloc(5*pairs,sizeof(REAL)); ds1 = dmatrix(0,12*v_info->vcount,0,SDIM-1); } else { memset((char*)aa,0,sizeof(REAL)*5*pairs); a = aa; memset((char*)ds1[0],0,sizeof(REAL)*12*v_info->vcount*MAXCOORD); } d = a+pairs; s1s1 = d + pairs; s1s2 = s1s1 + pairs; s2s2 = s1s2 + pairs; ds2 = ds1 + pairs; dAdv1 = ds2 + pairs; dAdv2 = dAdv1 + pairs; dfndv1 = dAdv2 + pairs; dfndv2 = dfndv1 + pairs; dffdv1 = dfndv2 + pairs; dffdv2 = dffdv1 + pairs; dnndv1 = dffdv2 + pairs; dnndv2 = dnndv1 + pairs; /* basic dot products */ area = 0.0; for ( j = 0 ; j < SDIM ; j++ ) dAdv[j] = vnorm[j] = 0.0; for ( k = 0 ; k < pairs ; k++ ) { s1 = s[k]; s2 = s[(k+1==pairs)?final_edge:k+1]; s1s1[k] = SDIM_dot(s1,s1); s1s2[k] = SDIM_dot(s1,s2); s2s2[k] = SDIM_dot(s2,s2); d[k] = s1s1[k]*s2s2[k] - s1s2[k]*s1s2[k]; a[k] = 0.5*sqrt(d[k]); area += a[k]; for ( j = 0 ; j < SDIM ; j++ ) { ds1[k][j] = 2*(s2s2[k]*s1[j] - s1s2[k]*s2[j]); ds2[k][j] = 2*(s1s1[k]*s2[j] - s1s2[k]*s1[j]); dAdv1[k][j] = 0.125/a[k]*ds1[k][j]; dAdv2[k][j] = 0.125/a[k]*ds2[k][j]; dAdv[j] -= dAdv1[k][j] + dAdv2[k][j]; } if ( variety != PLAIN_SQ ) { cross_prod(s1,s2,temp); for ( j = 0 ; j < SDIM ; j++ ) vnorm[j] += 0.5*temp[j]; } } /* constraint projection */ concount = constr_proj_matrix_wall(v_info->id,proj); if ( concount && (v_info->flags & INCOMPLETE_STAR) ) { /* project force and normal to constraints. This is enough to take care of energy and gradient, since force and energy occur only in dot products. */ int i; for ( i = 0 ; i < SDIM ; i++ ) temp[i] = dAdv[i]; matvec_mul(proj,temp,dAdv,SDIM,SDIM); for ( i = 0 ; i < SDIM ; i++ ) temp[i] = vnorm[i]; matvec_mul(proj,temp,vnorm,SDIM,SDIM); } /* energy */ ff = SDIM_dot(dAdv,dAdv); switch ( variety ) { case TEST_SQ: nn = SDIM_dot(vnorm,vnorm); energy = nn; break; case PLAIN_SQ: energy = 0.75/area*ff; break; case EFF_SQ: nn = SDIM_dot(vnorm,vnorm); if ( nn <= 0.0 ) return 0.0; energy = 0.75*area*ff/nn; break; case NORMAL_SQ: fn = SDIM_dot(dAdv,vnorm); if ( fn == 0.0 ) { ff = 0.0; fn = 1e-6; } switch ( h0_flag ) { case H0_IN_GLOBAL: hh0 = ff/fn - 2*h0_value/3; break; case H0_IN_ATTR: hh0 = ff/fn-2*(*VREAL(v_info->id,h0_attr))/3; break; default: hh0 = ff/fn; break; } energy = 0.75*area*hh0*hh0; break; case PERP_SQ: fn = SDIM_dot(dAdv,vnorm); nn = SDIM_dot(vnorm,vnorm); switch ( h0_flag ) { case H0_IN_GLOBAL: vertex_h0 = h0_value; break; case H0_IN_ATTR: vertex_h0 = (*VREAL(v_info->id,h0_attr)); break; default: vertex_h0 = 0.0; break; } if ( nn == 0.0 || (concount == 2 && nn < 0.01*area*area) ) { /* C2 line, probably, so h = 0 */ fn = 0.0; nn = 1.0; } hh0 = (fn/nn - 2*vertex_h0/3); energy = 0.75*area*hh0*hh0; break; } if ( get_vattr(v_info->id) & AXIAL_POINT ) { if ( sym_flags & DOUBLE_AXIAL ) energy /= rotorder/2; else energy /= rotorder; energy *= rotorder/v_info->axial_order; } if ( mode == METHOD_VALUE ) goto all_exit; /* Gradient */ if ( v_info->vcount > MAXV ) ddss11 = dmatrix3(16*pairs,SDIM,SDIM); else memset((char*)ddss11[0][0],0,sizeof(REAL)*16*pairs*MAXCOORD*MAXCOORD); ddss12 = ddss11 + pairs; ddss21 = ddss12 + pairs; ddss22 = ddss21 + pairs; ddAdv1dv1 = ddss22 + pairs; ddAdv1dv2 = ddAdv1dv1 + pairs; ddAdv2dv1 = ddAdv1dv2 + pairs; ddAdv2dv2 = ddAdv2dv1 + pairs; dvnorm1 = ddAdv2dv2 + pairs; dvnorm2 = dvnorm1 + pairs; dfdv1 = dvnorm2 + pairs; dfdv2 = dfdv1 + pairs; /* first, some more common terms */ for ( k = 0 ; k < pairs ; k++ ) { s1 = s[k]; s2 = s[(k+1==pairs)?final_edge:k+1]; for ( i = 0 ; i < SDIM ; i++ ) { ddss11[k][i][i] = 2*s2s2[k]; ddss12[k][i][i] = -2*s1s2[k]; ddss21[k][i][i] = -2*s1s2[k]; ddss22[k][i][i] = 2*s1s1[k]; for ( j = 0 ; j < SDIM ; j++ ) { ddss11[k][i][j] -= 2*s2[i]*s2[j]; ddss12[k][i][j] += 4*s1[i]*s2[j] - 2*s2[i]*s1[j]; ddss21[k][i][j] += 4*s2[i]*s1[j] - 2*s1[i]*s2[j]; ddss22[k][i][j] -= 2*s1[i]*s1[j]; ddAdv1dv1[k][i][j] = -0.125/a[k]/a[k]*dAdv1[k][i]*ds1[k][j] + 0.125/a[k]*ddss11[k][i][j]; ddAdv1dv2[k][i][j] = -0.125/a[k]/a[k]*dAdv1[k][i]*ds2[k][j] + 0.125/a[k]*ddss12[k][i][j]; dfdv1[k][i][j] = -ddAdv1dv1[k][i][j] - ddAdv1dv2[k][i][j]; ddAdv2dv1[k][i][j] = -0.125/a[k]/a[k]*dAdv2[k][i]*ds1[k][j] + 0.125/a[k]*ddss21[k][i][j]; ddAdv2dv2[k][i][j] = -0.125/a[k]/a[k]*dAdv2[k][i]*ds2[k][j] + 0.125/a[k]*ddss22[k][i][j]; dfdv2[k][i][j] = -ddAdv2dv1[k][i][j] - ddAdv2dv2[k][i][j]; } if ( concount ) { /* project force and normal to constraints. */ for ( j = 0 ; j < SDIM ; j++ ) temp[j] = dfdv1[k][i][j]; matvec_mul(proj,temp,dfdv1[k][i],SDIM,SDIM); for ( j = 0 ; j < SDIM ; j++ ) temp[j] = dfdv2[k][i][j]; matvec_mul(proj,temp,dfdv2[k][i],SDIM,SDIM); } dffdv1[k][i] = 2*SDIM_dot(dfdv1[k][i],dAdv); dffdv2[k][i] = 2*SDIM_dot(dfdv2[k][i],dAdv); } if ( variety == PLAIN_SQ ) continue; dvnorm1[k][0][1] = -0.5*s2[2]; dvnorm1[k][0][2] = 0.5*s2[1]; dvnorm1[k][1][0] = 0.5*s2[2]; dvnorm1[k][1][2] = -0.5*s2[0]; dvnorm1[k][2][0] = -0.5*s2[1]; dvnorm1[k][2][1] = 0.5*s2[0]; dvnorm2[k][0][1] = 0.5*s1[2]; dvnorm2[k][0][2] = -0.5*s1[1]; dvnorm2[k][1][0] = -0.5*s1[2]; dvnorm2[k][1][2] = 0.5*s1[0]; dvnorm2[k][2][0] = 0.5*s1[1]; dvnorm2[k][2][1] = -0.5*s1[0]; if ( concount ) for ( i = 0 ; i < SDIM ; i++ ) { /* project force and normal to constraints. */ for ( j = 0 ; j < SDIM ; j++ ) temp[j] = dvnorm1[k][i][j]; matvec_mul(proj,temp,dvnorm1[k][i],SDIM,SDIM); for ( j = 0 ; j < SDIM ; j++ ) temp[j] = dvnorm2[k][i][j]; matvec_mul(proj,temp,dvnorm2[k][i],SDIM,SDIM); } for ( i = 0 ; i < SDIM ; i++ ) { dfndv1[k][i] = SDIM_dot(dfdv1[k][i],vnorm); dfndv1[k][i] += SDIM_dot(dAdv,dvnorm1[k][i]); dfndv2[k][i] = SDIM_dot(dfdv2[k][i],vnorm); dfndv2[k][i] += SDIM_dot(dAdv,dvnorm2[k][i]); dnndv1[k][i] = 2*SDIM_dot(vnorm,dvnorm1[k][i]); dnndv2[k][i] = 2*SDIM_dot(vnorm,dvnorm2[k][i]); } } /* now, the actual gradients */ for ( k = 0 ; k < v_info->vcount ; k++ ) for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[k][i] = 0.0; switch ( variety ) { case TEST_SQ: for ( k = 0 ; k < pairs ; k++ ) { REAL *grad2 = (k+1==pairs)?v_info->grad[final_edge+1]:v_info->grad[k+2]; for ( i = 0 ; i < SDIM ; i++ ) { g = dnndv1[k][i]; v_info->grad[k+1][i] += g; v_info->grad[0][i] -= g; g = dnndv2[k][i]; grad2[i] += g; v_info->grad[0][i] -= g; } } break; case PLAIN_SQ: for ( k = 0 ; k < pairs ; k++ ) { REAL *grad2 = (k+1==pairs)?v_info->grad[final_edge+1]:v_info->grad[k+2]; for ( i = 0 ; i < SDIM ; i++ ) { g = -0.75*ff/area/area*dAdv1[k][i] + 0.75/area*dffdv1[k][i]; v_info->grad[k+1][i] += g; v_info->grad[0][i] -= g; g = -0.75*ff/area/area*dAdv2[k][i] + 0.75/area*dffdv2[k][i]; grad2[i] += g; v_info->grad[0][i] -= g; } } break; case EFF_SQ: for ( k = 0 ; k < pairs ; k++ ) { REAL *grad2 = (k+1==pairs)?v_info->grad[final_edge+1]:v_info->grad[k+2]; for ( i = 0 ; i < SDIM ; i++ ) { g = 0.75*ff/nn*dAdv1[k][i] + 0.75*area/nn*dffdv1[k][i] - 0.75*area/nn/nn*ff*dnndv1[k][i]; v_info->grad[k+1][i] += g; v_info->grad[0][i] -= g; g = 0.75*ff/nn*dAdv2[k][i] + 0.75*area/nn*dffdv2[k][i] - 0.75*area/nn/nn*ff*dnndv2[k][i]; grad2[i] += g; v_info->grad[0][i] -= g; } } break; case NORMAL_SQ: if ( fn != 0.0 ) for ( k = 0 ; k < pairs ; k++ ) { REAL *grad2 = (k+1==pairs)?v_info->grad[final_edge+1]:v_info->grad[k+2]; REAL g1,g2,g3; for ( i = 0 ; i < SDIM ; i++ ) { g1 = 0.75*hh0*hh0*dAdv1[k][i]; g2 = 1.5*area/fn*hh0*dffdv1[k][i]; g3 = - 1.5*area*ff*hh0/fn/fn*dfndv1[k][i]; g = g1 + g2 + g3; v_info->grad[k+1][i] += g; v_info->grad[0][i] -= g; g1 = 0.75*hh0*hh0*dAdv2[k][i] ; g2 = 1.5*area/fn*hh0*dffdv2[k][i]; g3 = - 1.5*area*ff*hh0/fn/fn*dfndv2[k][i]; g = g1 + g2 + g3; grad2[i] += g; v_info->grad[0][i] -= g; } } break; case PERP_SQ: for ( k = 0 ; k < pairs ; k++ ) { REAL *grad2 = (k+1==pairs)?v_info->grad[final_edge+1]:v_info->grad[k+2]; for ( i = 0 ; i < SDIM ; i++ ) { g = 0.75*hh0*hh0*dAdv1[k][i] + 1.5*area/nn*hh0*dfndv1[k][i] - 1.5*area*fn*hh0/nn/nn*dnndv1[k][i]; v_info->grad[k+1][i] += g; v_info->grad[0][i] -= g; g = 0.75*hh0*hh0*dAdv2[k][i] + 1.5*area/nn*hh0*dfndv2[k][i] - 1.5*area*fn*hh0/nn/nn*dnndv2[k][i]; grad2[i] += g; v_info->grad[0][i] -= g; } } break; } if ( get_vattr(v_info->id) & AXIAL_POINT ) { REAL fudge = 1./rotorder; if ( sym_flags & DOUBLE_AXIAL ) fudge *= 2; fudge *= rotorder/v_info->axial_order; for ( i = 0 ; i < v_info->vcount ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) v_info->grad[i][j] *= fudge; } if ( mode == METHOD_GRADIENT ) goto all_exit; /* Hessian */ if ( (variety == EFF_SQ) || (variety==PERP_SQ) || (variety == TEST_SQ) ) { for ( i = 0 ; i < SDIM ; i++ ) { antisymnorm[i][(i+1)%SDIM] = vnorm[(i+2)%SDIM]/2; antisymnorm[(i+1)%SDIM][i] = -vnorm[(i+2)%SDIM]/2; antisymnorm[i][i] = 0.0; } } if ( (variety == NORMAL_SQ) || (variety==PERP_SQ) || (variety == TEST_SQ) ) { for ( i = 0 ; i < SDIM ; i++ ) { antisymf[i][(i+1)%SDIM] = dAdv[(i+2)%SDIM]/2; antisymf[(i+1)%SDIM][i] = -dAdv[(i+2)%SDIM]/2; antisymf[i][i] = 0.0; } } if ( h0_flag && (variety != NORMAL_SQ) && (variety != PERP_SQ) ) kb_error(1615, "Can only use h_zero with star_normal_sq_curve.\n",RECOVERABLE); switch ( variety ) { case PLAIN_SQ: aa75 = 0.75/area/area; a75 = 0.75/area; ffaaa = 1.5*ff/area/area/area; ffaa = 0.75*ff/area/area; break; case EFF_SQ: an75 = 0.75*area/nn; ann75 = 0.75*area/nn/nn; fnnna = 1.5*ff/nn/nn/nn*area; fnna = 0.75*ff/nn/nn*area; fn75 = 0.75/nn; fnn75 = 0.75*ff/nn/nn; ffn75 = 0.75*ff/nn; break; case NORMAL_SQ: afnfn = 1.5*area/fn/fn; fffnfn = 1.5*hh0/fn; afffnfn = 1.5*area*hh0/fn; ffafnfnfn = 1.5*area*(ff/fn + hh0)/fn/fn; afffffnfnfnfn = 1.5*area*ff*(ff/fn+2*hh0)/fn/fn/fn; fffffnfnfna = 1.5*ff*hh0/fn/fn*area; fffffnfnfn = 1.5*ff*hh0/fn/fn; fffffnfn = 0.75*hh0*hh0; break; } for ( k = 0 ; k < pairs ; k++ ) { REAL co1 = 0.25/a[k]/a[k]/a[k]; REAL co2 = 0.125/a[k]/a[k]; REAL co3 = 0.125/a[k]; REAL dssf = (SDIM_dot(ds1[k],dAdv)+SDIM_dot(ds2[k],dAdv)); REAL s1f,s2f; REAL ddssf1[MAXCOORD],ddssf2[MAXCOORD]; for ( i = 0 ; i < SDIM ; i++ ) { ddssf1[i] = (SDIM_dot(ddss11[k][i],dAdv)+SDIM_dot(ddss12[k][i],dAdv)); ddssf2[i] = (SDIM_dot(ddss21[k][i],dAdv)+SDIM_dot(ddss22[k][i],dAdv)); } s1 = s[k]; s2 = s[(k+1==pairs)?final_edge:k+1]; s1f = SDIM_dot(s1,dAdv); s2f = SDIM_dot(s2,dAdv); for ( kk = 0 ; kk < pairs ; kk++ ) { int nextk = (k+1==pairs) ? final_edge : k+1 ; int nextkk = (kk+1==pairs) ? final_edge : kk+1 ; REAL **h11 = v_info->hess[k+1][kk+1]; REAL **h12 = v_info->hess[k+1][nextkk+1]; REAL **h21 = v_info->hess[nextk+1][kk+1]; REAL **h22 = v_info->hess[nextk+1][nextkk+1]; REAL **h00 = v_info->hess[0][0]; REAL **h10 = v_info->hess[k+1][0]; REAL **h02 = v_info->hess[0][nextkk+1]; REAL **h20 = v_info->hess[nextk+1][0]; REAL **h01 = v_info->hess[0][kk+1]; REAL ddff,dddsf,ddnn,ddfn; for ( i = 0 ; i < SDIM ; i++ ) for ( ii = 0 ; ii < SDIM ; ii++ ) { /* 11 term */ ddff = SDIM_dot(dfdv1[k][i],dfdv1[kk][ii]); if ( k == kk ) { ddff += (-co1*dAdv1[k][i]*dAdv1[k][ii] + co2*ddAdv1dv1[k][i][ii]) *dssf; ddff += co2*dAdv1[k][i]*ddssf1[ii]; ddff += co2*dAdv1[kk][ii]*ddssf1[i]; dddsf = ((i==ii) ? 4*s2f : 0. ) - 2*s2[i]*dAdv[ii] - 2*s2[ii]*dAdv[i]; ddff -= co3*dddsf; } ddff *= 2; switch ( variety ) { case TEST_SQ: h = ddnn = 2*SDIM_dot(dvnorm1[k][i],dvnorm1[kk][ii]); break; case PLAIN_SQ: h = -aa75*(dffdv1[k][i]*dAdv1[kk][ii] + dffdv1[kk][ii]*dAdv1[k][i]) + ffaaa*dAdv1[k][i]*dAdv1[kk][ii] + a75*ddff; if ( k == kk ) h -= ffaa*ddAdv1dv1[k][i][ii]; break; case EFF_SQ: ddnn = 2*SDIM_dot(dvnorm1[k][i],dvnorm1[kk][ii]); h = an75*ddff - ann75*dffdv1[k][i]*dnndv1[kk][ii] - ann75*dffdv1[kk][ii]*dnndv1[k][i] + fnnna*dnndv1[k][i]*dnndv1[kk][ii] - fnna*ddnn + fn75*dffdv1[k][i]*dAdv1[kk][ii] + fn75*dffdv1[kk][ii]*dAdv1[k][i] - fnn75*dAdv1[kk][ii]*dnndv1[k][i] - fnn75*dnndv1[kk][ii]*dAdv1[k][i]; if ( k == kk ) h += ffn75*ddAdv1dv1[k][i][ii]; break; case NORMAL_SQ: ddfn = SDIM_dot(dfdv1[k][i],dvnorm1[kk][ii]) + SDIM_dot(dvnorm1[k][i],dfdv1[kk][ii]); if ( k == kk ) { ddfn += (-co1*dAdv1[k][i]*dAdv1[k][ii] + co2*ddAdv1dv1[k][i][ii]) *(SDIM_dot(ds1[k],vnorm)+SDIM_dot(ds2[k],vnorm)); ddfn += co2*dAdv1[k][i]* (SDIM_dot(ddss11[kk][ii],vnorm) +SDIM_dot(ddss12[kk][ii],vnorm)); ddfn += co2*dAdv1[kk][ii]* (SDIM_dot(ddss11[k][i],vnorm) +SDIM_dot(ddss12[k][i],vnorm)); dddsf = ((i==ii) ? 4*SDIM_dot(s2,vnorm) : 0. ) - 2*s2[i]*vnorm[ii] - 2*s2[ii]*vnorm[i]; ddfn -= co3*dddsf; } h = afnfn*dffdv1[k][i]*dffdv1[kk][ii] + afffnfn*ddff + fffnfn*dffdv1[k][i]*dAdv1[kk][ii] + fffnfn*dAdv1[k][i]*dffdv1[kk][ii] - ffafnfnfn*dffdv1[k][i]*dfndv1[kk][ii] - ffafnfnfn*dfndv1[k][i]*dffdv1[kk][ii] + afffffnfnfnfn*dfndv1[k][i]*dfndv1[kk][ii] - fffffnfnfna*ddfn - fffffnfnfn*dfndv1[k][i]*dAdv1[kk][ii] - fffffnfnfn*dAdv1[k][i]*dfndv1[kk][ii]; if ( k == kk ) h += fffffnfn*ddAdv1dv1[k][i][ii]; break; case PERP_SQ: ddnn = 2*SDIM_dot(dvnorm1[k][i],dvnorm1[kk][ii]); ddfn = SDIM_dot(dfdv1[k][i],dvnorm1[kk][ii]) + SDIM_dot(dvnorm1[k][i],dfdv1[kk][ii]); if ( k == kk ) { ddfn += (-co1*dAdv1[k][i]*dAdv1[k][ii] + co2*ddAdv1dv1[k][i][ii]) *(SDIM_dot(ds1[k],vnorm)+SDIM_dot(ds2[k],vnorm)); ddfn += co2*dAdv1[k][i]* (SDIM_dot(ddss11[kk][ii],vnorm) +SDIM_dot(ddss12[kk][ii],vnorm)); ddfn += co2*dAdv1[kk][ii]* (SDIM_dot(ddss11[k][i],vnorm) +SDIM_dot(ddss12[k][i],vnorm)); dddsf = ((i==ii) ? 4*SDIM_dot(s2,vnorm) : 0. ) - 2*s2[i]*vnorm[ii] - 2*s2[ii]*vnorm[i]; ddfn -= co3*dddsf; } h = 1.5*hh0*dAdv1[k][i]*(dfndv1[kk][ii]/nn-fn/nn/nn*dnndv1[kk][ii]) + 1.5*hh0*dAdv1[kk][ii]*(dfndv1[k][i]/nn-fn/nn/nn*dnndv1[k][i]) + 1.5*area*(dfndv1[k][i]/nn-fn/nn/nn*dnndv1[k][i])* (dfndv1[kk][ii]/nn-fn/nn/nn*dnndv1[kk][ii]) + 1.5*area*hh0*(ddfn/nn - dfndv1[k][i]*dnndv1[kk][ii]/nn/nn - dfndv1[kk][ii]*dnndv1[k][i]/nn/nn +2*fn*dnndv1[k][i]*dnndv1[kk][ii]/nn/nn/nn - fn*ddnn/nn/nn); if ( k == kk ) h += 0.75*ddAdv1dv1[k][i][ii]*hh0*hh0; break; } if ( !is_finite(h) ) { sprintf(errmsg, "Star sq curvature hessian infinite at vertex %s\n", ELNAME(v_info->id)); kb_error(3617,errmsg,RECOVERABLE); } h11[i][ii] += h; h01[i][ii] -= h; h10[i][ii] -= h; h00[i][ii] += h; /* 12 term */ ddff = SDIM_dot(dfdv1[k][i],dfdv2[kk][ii]); if ( k == kk ) { ddff += (-co1*dAdv1[k][i]*dAdv2[k][ii] + co2*ddAdv1dv2[k][i][ii]) *dssf; ddff += co2*dAdv1[k][i]*ddssf2[ii]; ddff += co2*dAdv2[kk][ii]*ddssf1[i]; dddsf = 4*dAdv[i]*s2[ii] -2*dAdv[ii]*s2[i] - 2*((i==ii) ? s2f : 0.0) + 4*dAdv[ii]*s1[i] - 2*dAdv[i]*s1[ii] - 2*((i==ii) ? s1f : 0.0); ddff -= co3*dddsf; } ddff *= 2; switch ( variety ) { case TEST_SQ: ddnn = 2*SDIM_dot(dvnorm1[k][i],dvnorm2[kk][ii]); if ( k == kk ) ddnn += 2*antisymnorm[i][ii]; h = ddnn; break; case PLAIN_SQ: h = -aa75*dffdv1[k][i]*dAdv2[kk][ii] - aa75*dffdv2[kk][ii]*dAdv1[k][i] + ffaaa*dAdv1[k][i]*dAdv2[kk][ii] + a75*ddff; if ( k == kk ) h -= ffaa*ddAdv1dv2[k][i][ii]; break; case EFF_SQ: ddnn = 2*SDIM_dot(dvnorm1[k][i],dvnorm2[kk][ii]); if ( k == kk ) ddnn += 2*antisymnorm[i][ii]; h = an75*ddff - ann75*dffdv1[k][i]*dnndv2[kk][ii] - ann75*dffdv2[kk][ii]*dnndv1[k][i] + fnnna*dnndv1[k][i]*dnndv2[kk][ii] - fnna*ddnn + fn75*dffdv1[k][i]*dAdv2[kk][ii] + fn75*dffdv2[kk][ii]*dAdv1[k][i] - fnn75*dAdv2[kk][ii]*dnndv1[k][i] - fnn75*dnndv2[kk][ii]*dAdv1[k][i]; if ( k == kk ) h += ffn75*ddAdv1dv2[k][i][ii]; break; case NORMAL_SQ: ddfn = SDIM_dot(dfdv1[k][i],dvnorm2[kk][ii]) + SDIM_dot(dvnorm1[k][i],dfdv2[kk][ii]); if ( k == kk ) ddfn += antisymf[i][ii]; if ( k == kk ) { ddfn += (-co1*dAdv1[k][i]*dAdv2[k][ii] + co2*ddAdv1dv2[k][i][ii]) *(SDIM_dot(ds1[k],vnorm)+SDIM_dot(ds2[k],vnorm)); ddfn += co2*dAdv1[k][i]* (SDIM_dot(ddss21[kk][ii],vnorm) +SDIM_dot(ddss22[kk][ii],vnorm)); ddfn += co2*dAdv2[kk][ii]* (SDIM_dot(ddss11[k][i],vnorm) +SDIM_dot(ddss12[k][i],vnorm)); dddsf = 4*vnorm[i]*s2[ii] -2*vnorm[ii]*s2[i] - 2*((i==ii) ? SDIM_dot(s2,vnorm) : 0.0) + 4*vnorm[ii]*s1[i] - 2*vnorm[i]*s1[ii] - 2*((i==ii) ? SDIM_dot(s1,vnorm) : 0.0); ddfn -= co3*dddsf; } h = afnfn*dffdv1[k][i]*dffdv2[kk][ii] + afffnfn*ddff - ffafnfnfn*dffdv1[k][i]*dfndv2[kk][ii] + fffnfn*dffdv1[k][i]*dAdv2[kk][ii] + fffnfn*dAdv1[k][i]*dffdv2[kk][ii] - ffafnfnfn*dfndv1[k][i]*dffdv2[kk][ii] + afffffnfnfnfn*dfndv1[k][i]*dfndv2[kk][ii] - fffffnfnfna*ddfn - fffffnfnfn*dfndv1[k][i]*dAdv2[kk][ii] - fffffnfnfn*dAdv1[k][i]*dfndv2[kk][ii]; if ( k == kk ) h += fffffnfn*ddAdv1dv2[k][i][ii]; break; case PERP_SQ: ddnn = 2*SDIM_dot(dvnorm1[k][i],dvnorm2[kk][ii]); if ( k == kk ) ddnn += 2*antisymnorm[i][ii]; ddfn = SDIM_dot(dfdv1[k][i],dvnorm2[kk][ii]) + SDIM_dot(dvnorm1[k][i],dfdv2[kk][ii]); if ( k == kk ) ddfn += antisymf[i][ii]; if ( k == kk ) { ddfn += (-co1*dAdv1[k][i]*dAdv2[k][ii] + co2*ddAdv1dv2[k][i][ii]) *(SDIM_dot(ds1[k],vnorm)+SDIM_dot(ds2[k],vnorm)); ddfn += co2*dAdv1[k][i]* (SDIM_dot(ddss21[kk][ii],vnorm) +SDIM_dot(ddss22[kk][ii],vnorm)); ddfn += co2*dAdv2[kk][ii]* (SDIM_dot(ddss11[k][i],vnorm) +SDIM_dot(ddss12[k][i],vnorm)); dddsf = 4*vnorm[i]*s2[ii] -2*vnorm[ii]*s2[i] - 2*((i==ii) ? SDIM_dot(s2,vnorm) : 0.0) + 4*vnorm[ii]*s1[i] - 2*vnorm[i]*s1[ii] - 2*((i==ii) ? SDIM_dot(s1,vnorm) : 0.0); ddfn -= co3*dddsf; } h = 1.5*hh0*dAdv1[k][i]*(dfndv2[kk][ii]/nn-fn/nn/nn*dnndv2[kk][ii]) + 1.5*hh0*dAdv2[kk][ii]*(dfndv1[k][i]/nn-fn/nn/nn*dnndv1[k][i]) + 1.5*area*(dfndv1[k][i]/nn-fn/nn/nn*dnndv1[k][i])* (dfndv2[kk][ii]/nn-fn/nn/nn*dnndv2[kk][ii]) + 1.5*area*hh0*(ddfn/nn - dfndv1[k][i]*dnndv2[kk][ii]/nn/nn - dfndv2[kk][ii]*dnndv1[k][i]/nn/nn +2*fn*dnndv1[k][i]*dnndv2[kk][ii]/nn/nn/nn - fn*ddnn/nn/nn); if ( k == kk ) h += 0.75*ddAdv1dv2[k][i][ii]*hh0*hh0; break; } if ( !is_finite(h) ) { sprintf(errmsg, "Star sq curvature hessian infinite at vertex %s\n", ELNAME(v_info->id)); kb_error(3618,errmsg,RECOVERABLE); } h12[i][ii] += h; h02[i][ii] -= h; h10[i][ii] -= h; h00[i][ii] += h; /* 21 term */ ddff = SDIM_dot(dfdv2[k][i],dfdv1[kk][ii]); if ( k == kk ) { ddff += (-co1*dAdv2[k][i]*dAdv1[k][ii] + co2*ddAdv2dv1[k][i][ii]) *dssf; ddff += co2*dAdv2[k][i]*ddssf1[ii]; ddff += co2*dAdv1[kk][ii]*ddssf2[i]; dddsf = 4*dAdv[i]*s1[ii] -2*dAdv[ii]*s1[i] - 2*((i==ii) ? s1f: 0.0) + 4*dAdv[ii]*s2[i] - 2*dAdv[i]*s2[ii] - 2*((i==ii) ? s2f : 0.0); ddff -= co3*dddsf; } ddff *= 2; switch ( variety ) { case TEST_SQ: ddnn = 2*SDIM_dot(dvnorm2[k][i],dvnorm1[kk][ii]); if ( k == kk ) ddnn += 2*antisymnorm[ii][i]; h = ddnn; break; case PLAIN_SQ: h = -aa75*dffdv2[k][i]*dAdv1[kk][ii] - aa75*dffdv1[kk][ii]*dAdv2[k][i] + ffaaa*dAdv2[k][i]*dAdv1[kk][ii] + a75*ddff; if ( k == kk ) h -= ffaa*ddAdv2dv1[k][i][ii]; break; case EFF_SQ: ddnn = 2*SDIM_dot(dvnorm2[k][i],dvnorm1[kk][ii]); if ( k == kk ) ddnn += 2*antisymnorm[ii][i]; h = an75*ddff - ann75*dffdv2[k][i]*dnndv1[kk][ii] - ann75*dffdv1[kk][ii]*dnndv2[k][i] + fnnna*dnndv2[k][i]*dnndv1[kk][ii] - fnna*ddnn + fn75*dffdv2[k][i]*dAdv1[kk][ii] + fn75*dffdv1[kk][ii]*dAdv2[k][i] - fnn75*dAdv1[kk][ii]*dnndv2[k][i] - fnn75*dnndv1[kk][ii]*dAdv2[k][i]; if ( k == kk ) h += ffn75*ddAdv2dv1[k][i][ii]; break; case NORMAL_SQ: ddfn = SDIM_dot(dfdv2[k][i],dvnorm1[kk][ii]) + SDIM_dot(dvnorm2[k][i],dfdv1[kk][ii]); if ( k == kk ) ddfn += antisymf[ii][i]; if ( k == kk ) { ddfn += (-co1*dAdv2[k][i]*dAdv1[k][ii] + co2*ddAdv2dv1[k][i][ii]) *(SDIM_dot(ds1[k],vnorm)+SDIM_dot(ds2[k],vnorm)); ddfn += co2*dAdv2[k][i]* (SDIM_dot(ddss11[kk][ii],vnorm) +SDIM_dot(ddss12[kk][ii],vnorm)); ddfn += co2*dAdv1[kk][ii]* (SDIM_dot(ddss21[k][i],vnorm) +SDIM_dot(ddss22[k][i],vnorm)); dddsf = 4*vnorm[i]*s1[ii] -2*vnorm[ii]*s1[i] - 2*((i==ii) ? SDIM_dot(s1,vnorm) : 0.0) + 4*vnorm[ii]*s2[i] - 2*vnorm[i]*s2[ii] - 2*((i==ii) ? SDIM_dot(s2,vnorm) : 0.0); ddfn -= co3*dddsf; } h = afnfn*dffdv2[k][i]*dffdv1[kk][ii] + afffnfn*ddff - ffafnfnfn*dffdv2[k][i]*dfndv1[kk][ii] + fffnfn*dffdv2[k][i]*dAdv1[kk][ii] - ffafnfnfn*dfndv2[k][i]*dffdv1[kk][ii] + afffffnfnfnfn*dfndv2[k][i]*dfndv1[kk][ii] - fffffnfnfna*ddfn - fffffnfnfn*dfndv2[k][i]*dAdv1[kk][ii] + fffnfn*dAdv2[k][i]*dffdv1[kk][ii] - fffffnfnfn*dAdv2[k][i]*dfndv1[kk][ii]; if ( k == kk ) h += fffffnfn*ddAdv2dv1[k][i][ii]; break; case PERP_SQ: ddnn = 2*SDIM_dot(dvnorm2[k][i],dvnorm1[kk][ii]); if ( k == kk ) ddnn += 2*antisymnorm[ii][i]; ddfn = SDIM_dot(dfdv2[k][i],dvnorm1[kk][ii]) + SDIM_dot(dvnorm2[k][i],dfdv1[kk][ii]); if ( k == kk ) ddfn += antisymf[ii][i]; if ( k == kk ) { ddfn += (-co1*dAdv2[k][i]*dAdv1[k][ii] + co2*ddAdv2dv1[k][i][ii]) *(SDIM_dot(ds1[k],vnorm)+SDIM_dot(ds2[k],vnorm)); ddfn += co2*dAdv2[k][i]* (SDIM_dot(ddss11[kk][ii],vnorm) +SDIM_dot(ddss12[kk][ii],vnorm)); ddfn += co2*dAdv1[kk][ii]* (SDIM_dot(ddss21[k][i],vnorm) +SDIM_dot(ddss22[k][i],vnorm)); dddsf = 4*vnorm[i]*s1[ii] -2*vnorm[ii]*s1[i] - 2*((i==ii) ? SDIM_dot(s1,vnorm) : 0.0) + 4*vnorm[ii]*s2[i] - 2*vnorm[i]*s2[ii] - 2*((i==ii) ? SDIM_dot(s2,vnorm) : 0.0); ddfn -= co3*dddsf; } h = 1.5*hh0*dAdv2[k][i]*(dfndv1[kk][ii]/nn-fn/nn/nn*dnndv1[kk][ii]) + 1.5*hh0*dAdv1[kk][ii]*(dfndv2[k][i]/nn-fn/nn/nn*dnndv2[k][i]) + 1.5*area*(dfndv2[k][i]/nn-fn/nn/nn*dnndv2[k][i])* (dfndv1[kk][ii]/nn-fn/nn/nn*dnndv1[kk][ii]) + 1.5*area*hh0*(ddfn/nn - dfndv2[k][i]*dnndv1[kk][ii]/nn/nn - dfndv1[kk][ii]*dnndv2[k][i]/nn/nn +2*fn*dnndv2[k][i]*dnndv1[kk][ii]/nn/nn/nn - fn*ddnn/nn/nn); if ( k == kk ) h += 0.75*ddAdv2dv1[k][i][ii]*hh0*hh0; break; } if ( !is_finite(h) ) { sprintf(errmsg, "Star sq curvature hessian infinite at vertex %s\n", ELNAME(v_info->id)); kb_error(3619,errmsg,RECOVERABLE); } h21[i][ii] += h; h01[i][ii] -= h; h20[i][ii] -= h; h00[i][ii] += h; /* 22 term */ ddff = SDIM_dot(dfdv2[k][i],dfdv2[kk][ii]); if ( k == kk ) { ddff += (-co1*dAdv2[k][i]*dAdv2[k][ii] + co2*ddAdv2dv2[k][i][ii]) *dssf; ddff += co2*dAdv2[k][i]*ddssf2[ii]; ddff += co2*dAdv2[kk][ii]*ddssf2[i]; dddsf = ((i==ii) ? 4*s1f : 0. ) - 2*s1[i]*dAdv[ii] - 2*s1[ii]*dAdv[i]; ddff -= co3*dddsf; } ddff *= 2; switch ( variety ) { case TEST_SQ: ddnn = 2*SDIM_dot(dvnorm2[k][i],dvnorm2[kk][ii]); h = ddnn; break; case PLAIN_SQ: h = -aa75*dffdv2[k][i]*dAdv2[kk][ii] - aa75*dffdv2[kk][ii]*dAdv2[k][i] + ffaaa*dAdv2[k][i]*dAdv2[kk][ii] + a75*ddff; if ( k == kk ) h -= ffaa*ddAdv2dv2[k][i][ii]; break; case EFF_SQ: ddnn = 2*SDIM_dot(dvnorm2[k][i],dvnorm2[kk][ii]); h = an75*ddff - ann75*dffdv2[k][i]*dnndv2[kk][ii] - ann75*dffdv2[kk][ii]*dnndv2[k][i] + fnnna*dnndv2[k][i]*dnndv2[kk][ii] - fnna*ddnn + fn75*dffdv2[k][i]*dAdv2[kk][ii] + fn75*dffdv2[kk][ii]*dAdv2[k][i] - fnn75*dAdv2[kk][ii]*dnndv2[k][i] - fnn75*dnndv2[kk][ii]*dAdv2[k][i]; if ( k == kk ) h += ffn75*ddAdv2dv2[k][i][ii]; break; case NORMAL_SQ: ddfn = SDIM_dot(dfdv2[k][i],dvnorm2[kk][ii]) + SDIM_dot(dvnorm2[k][i],dfdv2[kk][ii]); if ( k == kk ) { ddfn += (-co1*dAdv2[k][i]*dAdv2[k][ii] + co2*ddAdv2dv2[k][i][ii]) *(SDIM_dot(ds1[k],vnorm)+SDIM_dot(ds2[k],vnorm)); ddfn += co2*dAdv2[k][i]* (SDIM_dot(ddss21[kk][ii],vnorm) +SDIM_dot(ddss22[kk][ii],vnorm)); ddfn += co2*dAdv2[kk][ii]* (SDIM_dot(ddss21[k][i],vnorm) +SDIM_dot(ddss22[k][i],vnorm)); dddsf = ((i==ii) ? 4*SDIM_dot(s1,vnorm) : 0. ) - 2*s1[i]*vnorm[ii] - 2*s1[ii]*vnorm[i]; ddfn -= co3*dddsf; } h = afnfn*dffdv2[k][i]*dffdv2[kk][ii] + afffnfn*ddff - ffafnfnfn*dffdv2[k][i]*dfndv2[kk][ii] + fffnfn*dffdv2[k][i]*dAdv2[kk][ii] - ffafnfnfn*dfndv2[k][i]*dffdv2[kk][ii] + afffffnfnfnfn*dfndv2[k][i]*dfndv2[kk][ii] - fffffnfnfna*ddfn - fffffnfnfn*dfndv2[k][i]*dAdv2[kk][ii] + fffnfn*dAdv2[k][i]*dffdv2[kk][ii] - fffffnfnfn*dAdv2[k][i]*dfndv2[kk][ii]; if ( k == kk ) h += fffffnfn*ddAdv2dv2[k][i][ii]; break; case PERP_SQ: ddnn = 2*SDIM_dot(dvnorm2[k][i],dvnorm2[kk][ii]); ddfn = SDIM_dot(dfdv2[k][i],dvnorm2[kk][ii]) + SDIM_dot(dvnorm2[k][i],dfdv2[kk][ii]); if ( k == kk ) { ddfn += (-co1*dAdv2[k][i]*dAdv2[k][ii] + co2*ddAdv2dv2[k][i][ii]) *(SDIM_dot(ds1[k],vnorm)+SDIM_dot(ds2[k],vnorm)); ddfn += co2*dAdv2[k][i]* (SDIM_dot(ddss21[kk][ii],vnorm) +SDIM_dot(ddss22[kk][ii],vnorm)); ddfn += co2*dAdv2[kk][ii]* (SDIM_dot(ddss21[k][i],vnorm) +SDIM_dot(ddss22[k][i],vnorm)); dddsf = ((i==ii) ? 4*SDIM_dot(s1,vnorm) : 0. ) - 2*s1[i]*vnorm[ii] - 2*s1[ii]*vnorm[i]; ddfn -= co3*dddsf; } h = 1.5*hh0*dAdv2[k][i]*(dfndv2[kk][ii]/nn-fn/nn/nn*dnndv2[kk][ii]) + 1.5*hh0*dAdv2[kk][ii]*(dfndv2[k][i]/nn-fn/nn/nn*dnndv2[k][i]) + 1.5*area*(dfndv2[k][i]/nn-fn/nn/nn*dnndv2[k][i])* (dfndv2[kk][ii]/nn-fn/nn/nn*dnndv2[kk][ii]) + 1.5*area*hh0*(ddfn/nn - dfndv2[k][i]*dnndv2[kk][ii]/nn/nn - dfndv2[kk][ii]*dnndv2[k][i]/nn/nn +2*fn*dnndv2[k][i]*dnndv2[kk][ii]/nn/nn/nn - fn*ddnn/nn/nn); if ( k == kk ) h += 0.75*ddAdv2dv2[k][i][ii]*hh0*hh0; break; } if ( !is_finite(h) ) { sprintf(errmsg, "Star sq curvature hessian infinite at vertex %s\n", ELNAME(v_info->id)); kb_error(3620,errmsg,RECOVERABLE); } h22[i][ii] += h; h02[i][ii] -= h; h20[i][ii] -= h; h00[i][ii] += h; } /* end for i, ii */ } /* end for kk */ } /* end for k */ if ( get_vattr(v_info->id) & AXIAL_POINT ) { REAL fudge = 1./rotorder; if ( sym_flags & DOUBLE_AXIAL ) fudge *= 2; fudge *= rotorder/v_info->axial_order; for ( i = 0 ; i < v_info->vcount ; i++ ) for ( ii = 0 ; ii < v_info->vcount ; ii++ ) for ( k = 0 ; k < SDIM ; k++ ) for ( kk = 0 ; kk < SDIM ; kk++ ) v_info->hess[i][ii][k][kk] *= fudge; } all_exit: if ( v_info->vcount > MAXV ) { myfree((char*)a); free_matrix(ds1); if ( mode != METHOD_VALUE ) free_matrix3(ddss11); } return energy; } /* end star_sq_curve_method_all() */ /************************************************************************* * * function: star_sqcurve_method_value() * * purpose: calculate squared mean curvature of given vertex * from precalculated data. * */ REAL star_sqcurve_method_value(v_info) struct qinfo *v_info; { #define USE_ALL #ifdef USE_ALL return star_sqcurve_method_all(v_info,METHOD_VALUE); #else /* following is streamlined for just energy */ int variety; /* PLAIN_SQ, EFF_SQ, NORMAL_SQ */ int pairs; int j,k; REAL energy,ff,fn,nn,area; REAL dAdv[MAXCOORD], a, d, s1s1, s1s2, s2s2; REAL vnorm[MAXCOORD]; REAL ds1,ds2,coeff; REAL *s1,*s2; REAL **s = v_info->sides[0]; REAL temp[MAXCOORD]; MAT2D(proj,MAXCOORD,MAXCOORD); /* for constraint projection */ int concount; /* number of constraints vertex is on */ int final_edge; /* of pairs; wrap to first for complete star */ if ( METH_INSTANCE(v_info->method)->gen_method == star_normal_sq_mean_curvature_mi ) variety = NORMAL_SQ; else { if ( h0_flag ) kb_error(1617,"Can only use star_normal_sq_mean_curvature with h_zero.\n", RECOVERABLE); if ( METH_INSTANCE(v_info->method)->gen_method == star_eff_area_sq_mean_curvature_mi ) variety = EFF_SQ; else variety = PLAIN_SQ; } pairs = (v_info->vcount - 1); if ( pairs <= 0 ) return 0.0; if ( v_info->flags & INCOMPLETE_STAR ) { pairs--; final_edge = pairs; } else final_edge = 0; /* constraint projection */ concount = constr_proj_matrix(v_info->id,proj); /* basic dot products */ area = 0.0; for ( j = 0 ; j < SDIM ; j++ ) dAdv[j] = vnorm[j] = 0.0; for ( k = 0 ; k < pairs ; k++ ) { s1 = s[k]; s2 = s[(k+1==pairs)?final_edge:k+1]; s1s1 = SDIM_dot(s1,s1); s1s2 = SDIM_dot(s1,s2); s2s2 = SDIM_dot(s2,s2); d = s1s1*s2s2 - s1s2*s1s2; a = sqrt(d); area += a; coeff = 0.5/a; for ( j = 0 ; j < SDIM ; j++ ) { ds1 = (s2s2*s1[j] - s1s2*s2[j]); ds2 = (s1s1*s2[j] - s1s2*s1[j]); dAdv[j] -= coeff*(ds1 + ds2); } if ( variety != PLAIN_SQ ) { cross_prod(s1,s2,temp); for ( j = 0 ; j < SDIM ; j++ ) vnorm[j] += 0.5*temp[j]; } } area *= 0.5; if ( concount ) { /* project force and normal to constraints. This is enough to take care of energy and gradient, since force and energy occur only in dot products. */ int i; for ( i = 0 ; i < SDIM ; i++ ) temp[i] = dAdv[i]; matvec_mul(proj,temp,dAdv,SDIM,SDIM); for ( i = 0 ; i < SDIM ; i++ ) temp[i] = vnorm[i]; matvec_mul(proj,temp,vnorm,SDIM,SDIM); } /* energy */ ff = SDIM_dot(dAdv,dAdv); switch ( variety ) { case PLAIN_SQ: energy = 0.75/area*ff; break; case EFF_SQ: nn = SDIM_dot(vnorm,vnorm); energy = 0.75*area*ff/nn; break; case NORMAL_SQ: { REAL h0_val; switch ( h0_flag ) { case H0_IN_GLOBAL: h0_val = h0_value; break; case H0_IN_ATTR: h0_val = *VREAL(v_info->id,h0_attr); break; default: h0_val = 0; } fn = SDIM_dot(dAdv,vnorm); if ( fn == 0.0 ) energy = 0.0; else energy = 0.75*area*(ff/fn-2*h0_val/3)*(ff/fn-2*h0_val/3); break; } } if ( get_vattr(v_info->id) & AXIAL_POINT ) { energy /= rotorder; if ( sym_flags & DOUBLE_AXIAL ) energy *= 2; energy *= rotorder/v_info->axial_order; } return energy; #endif } /************************************************************************* * * function: star_sqcurve_method_grad() * * purpose: Convert square curvature data into forces for a vertex. * */ REAL star_sqcurve_method_grad(v_info) struct qinfo *v_info; { #ifdef USE_ALL return star_sqcurve_method_all(v_info,METHOD_GRADIENT); #else /* version optimized for gradient */ int variety; /* PLAIN_SQ, EFF_SQ, NORMAL_SQ */ int pairs; int i,j,k; REAL energy,ff,fn,nn,area,g; REAL dAdv[MAXCOORD], *a=NULL, *d, *s1s1, *s1s2, *s2s2; REAL vnorm[MAXCOORD]; REAL **dAdv1,**dAdv2,dvnorm1[MAXCOORD][MAXCOORD] ,dvnorm2[MAXCOORD][MAXCOORD],**ds2; REAL *s1,*s2; REAL **s = v_info->sides[0]; REAL temp[MAXCOORD],ea,a15,ef1,ef2,ns1,ns2; REAL aa[MAXV*5]; MAT2D(ds1,12*MAXV,MAXCOORD); REAL h0adj; switch ( h0_flag ) { case H0_IN_GLOBAL: h0adj = 2./3*h0_value; break; case H0_IN_ATTR: h0adj = 2./3*(*VREAL(v_info->id,h0_attr)); break; default: h0adj = 0.0; break; } for ( i = 0 ; i < SDIM ; i++ ) dvnorm1[i][i] = dvnorm2[i][i] = 0.0; if ( METH_INSTANCE(v_info->method)->gen_method == star_normal_sq_mean_curvature_mi ) variety = NORMAL_SQ; else if ( METH_INSTANCE(v_info->method)->gen_method == star_eff_area_sq_mean_curvature_mi ) variety = EFF_SQ; else variety = PLAIN_SQ; pairs = (v_info->vcount - 1); if ( pairs <= 0 ) return 0.0; if ( v_info->vcount > MAXV ) { a = (REAL*)mycalloc(5*pairs,sizeof(REAL)); ds1 = dmatrix(0,12*v_info->vcount,0,SDIM-1); } else { memset((char*)aa,0,sizeof(REAL)*5*pairs); a = aa; memset((char*)ds1[0],0,sizeof(REAL)*12*v_info->vcount*MAXCOORD); } d = a+pairs; s1s1 = d + pairs; s1s2 = s1s1 + pairs; s2s2 = s1s2 + pairs; ds2 = ds1 + pairs; dAdv1 = ds2 + pairs; dAdv2 = dAdv1 + pairs; /* basic dot products */ area = 0.0; for ( j = 0 ; j < SDIM ; j++ ) dAdv[j] = vnorm[j] = 0.0; for ( k = 0 ; k < pairs ; k++ ) { s1 = s[k]; s2 = s[(k+1==pairs)?0:k+1]; s1s1[k] = SDIM_dot(s1,s1); s1s2[k] = SDIM_dot(s1,s2); s2s2[k] = SDIM_dot(s2,s2); d[k] = s1s1[k]*s2s2[k] - s1s2[k]*s1s2[k]; a[k] = 0.5*sqrt(d[k]); area += a[k]; for ( j = 0 ; j < SDIM ; j++ ) { ds1[k][j] = 2*(s2s2[k]*s1[j] - s1s2[k]*s2[j]); ds2[k][j] = 2*(s1s1[k]*s2[j] - s1s2[k]*s1[j]); dAdv1[k][j] = 0.125/a[k]*ds1[k][j]; dAdv2[k][j] = 0.125/a[k]*ds2[k][j]; dAdv[j] -= dAdv1[k][j] + dAdv2[k][j]; } if ( variety != PLAIN_SQ ) { cross_prod(s1,s2,temp); for ( j = 0 ; j < SDIM ; j++ ) vnorm[j] += 0.5*temp[j]; } } /* energy */ ff = SDIM_dot(dAdv,dAdv); switch ( variety ) { case PLAIN_SQ: energy = 0.75/area*ff; a15 = 1.5/area; /* coefficient for later */ break; case EFF_SQ: nn = SDIM_dot(vnorm,vnorm); energy = 0.75*area*ff/nn; ef1 = 1.5*area/nn; /* coefficients for later */ ef2 = 1.5*area/nn/nn*ff; break; case NORMAL_SQ: fn = SDIM_dot(dAdv,vnorm); if ( fn == 0.0 ) energy = 0.0; else { energy = 0.75*area*(ff/fn-h0adj)*(ff/fn-h0adj); ns1 = 3*area/fn*(ff/fn-h0adj); /* coefficients for later */ ns2 = 1.5*area*(ff/fn-h0adj)*ff/fn/fn; } break; } /* gradient */ ea = energy/area; for ( k = 0 ; k < pairs ; k++ ) { REAL co = 0.25/a[k]; REAL coco = 0.5*co/a[k]; REAL *grad2 = (k+1==pairs)?v_info->grad[1]:v_info->grad[k+2]; s1 = s[k]; s2 = s[(k+1==pairs)?0:k+1]; if ( variety != PLAIN_SQ ) { dvnorm1[0][1] = -0.5*s2[2]; dvnorm1[0][2] = 0.5*s2[1]; dvnorm1[1][0] = 0.5*s2[2]; dvnorm1[1][2] = -0.5*s2[0]; dvnorm1[2][0] = -0.5*s2[1]; dvnorm1[2][1] = 0.5*s2[0]; dvnorm2[0][1] = 0.5*s1[2]; dvnorm2[0][2] = -0.5*s1[1]; dvnorm2[1][0] = -0.5*s1[2]; dvnorm2[1][2] = 0.5*s1[0]; dvnorm2[2][0] = 0.5*s1[1]; dvnorm2[2][1] = -0.5*s1[0]; } for ( i = 0 ; i < SDIM ; i++ ) { REAL coeff1 = coco*dAdv1[k][i]; REAL coeff2 = coco*dAdv2[k][i]; REAL dAdn1,dAdn2; REAL ddAdv1dv,ddAdv2dv; dAdn1 = dAdn2 = ddAdv1dv = ddAdv2dv = 0.0; for ( j = 0 ; j < SDIM ; j++ ) { REAL ddss11,ddss12,ddss21,ddss22; REAL t1,t2; ddss11 = -s2[i]*s2[j]; ddss12 = 2*s1[i]*s2[j] - s2[i]*s1[j]; ddss21 = 2*s2[i]*s1[j] - s1[i]*s2[j]; ddss22 = -s1[i]*s1[j]; if ( i == j ) { ddss11 += s2s2[k]; ddss12 -= s1s2[k]; ddss21 -= s1s2[k]; ddss22 += s1s1[k]; } t1 = (coeff1*(ds1[k][j]+ds2[k][j]) - co*(ddss11 + ddss12)); ddAdv1dv += dAdv[j]*t1; t2 = (coeff2*(ds1[k][j]+ds2[k][j]) - co*(ddss21 + ddss22)); ddAdv2dv += dAdv[j]*t2; if ( variety == NORMAL_SQ ) { dAdn1 += vnorm[j]*t1; dAdn2 += vnorm[j]*t2; } } switch ( variety ) { case PLAIN_SQ: g = a15*ddAdv1dv - ea*dAdv1[k][i] ; v_info->grad[k+1][i] += g; v_info->grad[0][i] -= g; g = a15*ddAdv2dv - ea*dAdv2[k][i]; grad2[i] += g; v_info->grad[0][i] -= g; break; case EFF_SQ: g = ea*dAdv1[k][i] + ef1*ddAdv1dv - ef2*SDIM_dot(dvnorm1[i],vnorm); v_info->grad[k+1][i] += g; v_info->grad[0][i] -= g; g = ea*dAdv2[k][i] + ef1*ddAdv2dv - ef2*SDIM_dot(dvnorm2[i],vnorm); grad2[i] += g; v_info->grad[0][i] -= g; break; case NORMAL_SQ: if ( fn != 0.0 ) { g = ea*dAdv1[k][i] + ns1*ddAdv1dv - ns2*(dAdn1 + SDIM_dot(dvnorm1[i],dAdv)); v_info->grad[k+1][i] += g; v_info->grad[0][i] -= g; g = ea*dAdv2[k][i] + ns1*ddAdv2dv - ns2*(dAdn2 + SDIM_dot(dvnorm2[i],dAdv)); grad2[i] += g; v_info->grad[0][i] -= g; } break; } } } if ( get_vattr(v_info->id) & AXIAL_POINT ) { REAL fudge = 1./rotorder; if ( sym_flags & DOUBLE_AXIAL ) fudge *= 2; fudge *= rotorder/v_info->axial_order; energy *= fudge; for ( i = 0 ; i < v_info->vcount ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) v_info->grad[i][j] *= fudge; } if ( v_info->vcount > MAXV ) { myfree((char*)a); free_matrix(ds1); } return energy; #endif } /************************************************************************* * * function: star_sqcurve_method_hess() * * */ REAL star_sqcurve_method_hess(v_info) struct qinfo *v_info; { return star_sqcurve_method_all(v_info,METHOD_HESSIAN); } /************************************************************************* ************************************************************************** Method circle_willmore Alexander Bobenko's circle-based discrete Willmore energy. Is conformally invariant. At each vertex, energy is the sum of the angles between facet circumcircles - 2*pi. More simply done as edge quantity, since angles at each end are the same. For edge e, if adjacent facet edge loops are a e d and b c -e, then circle angle beta for edge has cos(beta) = (--)/|a|/|b|/|c|/|d| For now, assumes all vertices are faceted, and fully starred. Severe numerical difficulties: Not smooth when angle beta is zero, which is all too common. Set of zero angles should be codimension 2, which means generally avoided, but still crops up. **************************************************************************/ void circle_willmore_init(mode,mi) int mode; /* METHOD_VALUE or METHOD_GRADIENT */ struct method_instance *mi; { REAL vertex_adjust; if ( web.modeltype != LINEAR ) kb_error(4007,"circle_willmore method requires the LINEAR model.\n", RECOVERABLE); if ( web.representation != SOAPFILM ) kb_error(3222,"circle_willmore method requires the SOAPFILM model.\n", RECOVERABLE); vertex_adjust = -M_PI*web.skel[VERTEX].count; binary_tree_add(mi->value_addends,vertex_adjust); } REAL circle_willmore_all(e_info,mode) struct qinfo *e_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ { REAL *q = e_info->sides[0][0]; REAL *p = e_info->sides[0][1]; REAL *r = e_info->sides[0][2]; REAL pp = dot(p,p,SDIM); REAL qq = dot(q,q,SDIM); REAL rr = dot(r,r,SDIM); REAL pq = dot(p,q,SDIM); REAL pr = dot(p,r,SDIM); REAL qr = dot(q,r,SDIM); REAL value; REAL cosbeta; REAL dcdp[MAXCOORD],dcdq[MAXCOORD],dcdr[MAXCOORD]; REAL coeff; int i; cosbeta = (rr*pq - qq*pr - pp*rr + pp*qr)/sqrt(pp)/sqrt(rr)/ sqrt(qq-2*pq+pp)/sqrt(rr-2*qr+qq); if ( fabs(cosbeta) > 1.0 ) { sprintf(errmsg,"edge %s cosine of angle out of range in circular_willmore", ELNAME(e_info->id)); kb_error(3850,errmsg,WARNING); } value = acos(cosbeta); if ( mode == METHOD_VALUE ) return value; /* Gradient */ if ( cosbeta*cosbeta >= 1.0 ) coeff = 0.0; coeff = -1/sqrt(1 - cosbeta*cosbeta); for ( i = 0 ; i < SDIM ; i++ ) { dcdp[i] = (rr*q[i] - qq*r[i] - 2*rr*p[i] + 2*qr*p[i])/sqrt(pp)/sqrt(rr)/ sqrt(qq-2*pq+pp)/sqrt(rr-2*qr+qq) - cosbeta/pp*p[i] - cosbeta/(qq-2*pq+pp)*(-q[i] + p[i]); dcdq[i] = (rr*p[i] - 2*pr*q[i] + pp*r[i])/sqrt(pp)/sqrt(rr)/ sqrt(qq-2*pq+pp)/sqrt(rr-2*qr+qq) - cosbeta/(qq-2*pq+pp)*(q[i] - p[i]) - cosbeta/(rr-2*qr+qq)*(-r[i] + q[i]); dcdr[i] = (2*r[i]*pq - qq*p[i] - 2*pp*r[i]+ pp*q[i])/sqrt(pp)/sqrt(rr)/ sqrt(qq-2*pq+pp)/sqrt(rr-2*qr+qq) - cosbeta/rr*r[i] - cosbeta/(rr-2*qr+qq)*(r[i] - q[i]); e_info->grad[1][i] = coeff*dcdq[i]; e_info->grad[0][i] -= coeff*dcdq[i]; e_info->grad[2][i] = coeff*dcdp[i]; e_info->grad[0][i] -= coeff*dcdp[i]; e_info->grad[3][i] = coeff*dcdr[i]; e_info->grad[0][i] -= coeff*dcdr[i]; } if ( mode == METHOD_GRADIENT ) return value; /* Hessian */ return value; } REAL circle_willmore_value(e_info) struct qinfo *e_info; { return circle_willmore_all(e_info,METHOD_VALUE); } REAL circle_willmore_grad(e_info) struct qinfo *e_info; { return circle_willmore_all(e_info,METHOD_GRADIENT); } REAL circle_willmore_hess(e_info) struct qinfo *e_info; { return circle_willmore_all(e_info,METHOD_HESSIAN); } evolver-2.30c.dfsg/src/hessian2.c0000644000175300017530000013660311410765113017106 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************* * * FILE: hessian2.c * * Contents: more hessian routines */ #include "include.h" REAL difference_lagrangian ARGS((void)); int sturm ARGS((int,REAL*,REAL*,REAL)); REAL ev_bisect ARGS((REAL,REAL,int,int,int,REAL*,REAL*)); /*************************************************************************** * * function: difference_lagrangian() * * purpose: calculate lagrangian E - \lambda Q for difference_hessian, * where E is energy and Q is constraint with lagrange multiplier * \lambda */ REAL difference_lagrangian() { REAL sum; body_id b_id; struct gen_quant *q; int k; sum = web.total_energy; FOR_ALL_BODIES(b_id) if ( get_battr(b_id) & FIXEDVOL ) sum -= get_body_pressure(b_id)*get_body_volume(b_id); for ( k = 0 ; k < gen_quant_count ; k++ ) { q = GEN_QUANT(k); if ( q->flags & Q_FIXED ) sum -= q->pressure*q->value; } return sum; } /************************************************************************** * * function: difference_hessian() * * purpose: calculation of hessian by differences in energies. * meant only for checking other hessian calculations. * */ void difference_hessian(S,verhead,rhs) struct linsys *S; struct hess_verlist *verhead; /* main vertex list */ REAL *rhs; { REAL dx = 0.00001; /* wiggle size */ REAL first[MAXCOORD]; MAT2D(second,MAXCOORD,MAXCOORD); REAL e1,e2,e3,e4; vertex_id v_id,vv_id; int i,j; REAL energyuu,energyud,energydu,energydd; /* really Lagrangians */ /* right hand side and self second derivatives */ /* move vertices 1 by 1 */ FOR_ALL_VERTICES(v_id) { int ord = loc_ordinal(v_id); struct hess_verlist *v = verhead+ord; REAL *x = get_coord(v_id); if ( v->freedom == 0 ) continue; for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j <= i ; j++ ) { x[i] += dx; x[j] += dx; global_timestamp++; calc_energy(); calc_content(Q_FIXED); energyuu = difference_lagrangian(); x[j] -= 2*dx; global_timestamp++; calc_energy(); calc_content(Q_FIXED); energyud = difference_lagrangian(); x[i] -= 2*dx; global_timestamp++; calc_energy(); calc_content(Q_FIXED); energydd = difference_lagrangian(); x[j] += 2*dx; global_timestamp++; calc_energy(); calc_content(Q_FIXED); energydu = difference_lagrangian(); x[i] += dx; x[j] -= dx; /* back to normal */ first[i] = (energyuu - energydu + energyud - energydd)/4/dx; second[i][j] = second[j][i] = (energyuu - energyud - energydu + energydd)/4/dx/dx; } fill_grad(S,v,first,rhs); if ( hess_flag ) fill_self_entry(S,v_id,second); } /* second derivatives, go through all pairs of vertices */ if ( hess_flag ) FOR_ALL_VERTICES(v_id) { int ord = loc_ordinal(v_id); struct hess_verlist *v = verhead+ord; REAL *x = get_coord(v_id); if ( v->freedom == 0 ) continue; FOR_ALL_VERTICES(vv_id) { int ordd = loc_ordinal(vv_id); struct hess_verlist *vv = verhead+ordd; REAL *xx = get_coord(vv_id); if ( vv->freedom == 0 ) continue; if ( ord <= ordd ) continue; /* hessian is lower triangular */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { x[i] += dx; xx[j] += dx; global_timestamp++; calc_energy(); calc_content(Q_FIXED); e1 = difference_lagrangian(); x[i] -= 2*dx; global_timestamp++; calc_energy(); calc_content(Q_FIXED); e2 = difference_lagrangian(); xx[j] -= 2*dx; global_timestamp++; calc_energy(); calc_content(Q_FIXED); e3 = difference_lagrangian(); x[i] += 2*dx; global_timestamp++; calc_energy(); calc_content(Q_FIXED); e4 = difference_lagrangian(); second[i][j] = (e1 - e2 + e3 - e4)/4/dx/dx; x[i] -= dx; xx[j] += dx; } fill_mixed_entry(S,v_id,vv_id,second); } } calc_energy(); calc_content(Q_FIXED); /* cleanup */ } /* end difference_hessian */ /****************************************************************** * * function: dirichlet() * * Purpose: Minimization via Dirichlet integral, * as done by Pinkall and Polthier. * * Argument: 0 for stepsize 1 (to minimum); 1 for seek minimum * * Return: stepsize */ /* Notes on method: Idea is to minimize Dirichlet energy of image of current surface. Dirichlet energy of image of triangle is 0.25*\sum_i cot(\alpha_i)*a_i^2 where \alpha_i is angle of vertex in facet in domain and a_i is length of opposite edge in image. Hence energy is pure quadratic and susceptible to exact solution. When equilibrium is reached, identity map is conformal so area is minimized. Numerical solution uses mechanisms of hessian minimization. */ REAL dirichlet(seekmode) int seekmode; /* 1 if want to search in direction of min */ { REAL side[FACET_EDGES][MAXCOORD]; REAL *x,*y; int r,s; REAL area; REAL cota; int i,j; facet_id f_id; edge_id e_id; vertex_id v[3]; facetedge_id fe; REAL ss[FACET_VERTS],sd[FACET_VERTS]; int ii,iii; REAL **rs; /* one right side for each coordinate */ REAL **xx; /* one solution vector for each coordinate */ struct hess_verlist *vc; /* current vertex */ struct hess_verlist *rv,*sv; REAL scale; REAL best_scale; struct linsys S; vertex_id v_id; if ( fixed_constraint_flag ) kb_error(2081,"Cannot do dirichlet with constraints.\n",RECOVERABLE); hmode = SINGLE_DEGREE; /* same hessian for each coordinate */ hessian_init(&S,NULL); rs = dmatrix(0,SDIM-1,0,S.A_rows); /* right side for each coord */ xx = dmatrix(0,SDIM-1,0,S.A_rows); /* solution vector for each coord */ /* differences from current */ /* fill in */ FOR_ALL_FACETS(f_id) { fe = get_facet_fe(f_id); for ( i = 0 ; i < FACET_VERTS ; i++ ) { e_id = get_fe_edge(fe); get_edge_side(e_id,side[i]); v[i] = get_edge_tailv(e_id); fe = get_next_edge(fe); } for ( i = 0 ; i < FACET_VERTS ; i++ ) { ss[i] = SDIM_dot(side[i],side[i]); sd[i] = SDIM_dot(side[i],side[(i+2)%FACET_VERTS]); } area = sqrt(ss[0]*ss[2] - sd[0]*sd[0]); for ( i = 0 ; i < FACET_VERTS ; i++ ) { ii = (i+1)%FACET_VERTS; iii = (i+2)%FACET_VERTS; x = get_coord(v[ii]); y = get_coord(v[iii]); rv = get_vertex_vhead(v[ii]); sv = get_vertex_vhead(v[iii]); r = rv->rownum; s = sv->rownum; cota = -sd[i]/area; if ( get_vattr(v[ii]) & FIXED ) if ( get_vattr(v[iii]) & FIXED ) continue; else /* rhs contribution */ { sp_hash_search(&S,sv->rownum,sv->rownum,0.5*cota); for ( j = 0 ; j < SDIM ; j++ ) rs[j][s] += 0.5*cota*(y[j] - x[j]); } else if ( get_vattr(v[iii]) & FIXED ) { /* rhs contribution */ sp_hash_search(&S,rv->rownum,rv->rownum,0.5*cota); for ( j = 0 ; j < SDIM ; j++ ) rs[j][r] += 0.5*cota*(x[j] - y[j]); } else /* main hessian contribution */ { sp_hash_search(&S,rv->rownum,rv->rownum,0.5*cota); sp_hash_search(&S,sv->rownum,sv->rownum,0.5*cota); if ( sv->rownum < rv->rownum ) sp_hash_search(&S,sv->rownum,rv->rownum,-0.5*cota); else sp_hash_search(&S,rv->rownum,sv->rownum,-0.5*cota); for ( j = 0 ; j < SDIM ; j++ ) { rs[j][r] += cota/2*(x[j] - y[j]); rs[j][s] += cota/2*(y[j] - x[j]); } } } } /* extract stuff from hash table */ sp_hash_end(&S,S.A_rows,S.total_rows,A_OFF); /* solve system */ memset((char*)&S,0,sizeof(struct linsys)); (*sp_AIJ_setup_func)(S.A_rows,&S); (*sp_constraint_setup_func) (web.skel[BODY].max_ord+1 + gen_quant_count,&S); if ( sp_ordering_func ) (*sp_ordering_func)(&S); sp_factor(&S); (*sp_hess_project_setup_func)(&S); for ( i = 0 ; i < SDIM ; i++ ) sp_hessian_solve(&S,rs[i],xx[i],NO_SET_PRESSURE); /* move vertices */ if ( seekmode == 0 ) { best_scale = 1.0; } else { REAL energies[3]; REAL denom; int k; vertex_id v_id; save_coords(&saved,SAVE_IN_ATTR); /* in case this doesn't work */ energies[0] = web.total_energy; for ( scale = 1.0, k = 1 ; k < 3 ; scale += 1.0, k++ ) { FOR_ALL_VERTICES(v_id) { REAL *coord; vc = get_vertex_vhead(v_id); if ( vc->freedom == 0 ) continue; coord = get_coord(v_id); for ( j = 0 ; j < SDIM ; j++ ) coord[j] = coord[j] - scale*xx[j][vc->rownum]; } global_timestamp++; calc_energy(); /* energy after motion */ if ( hess_debug || itdebug ) #ifdef LONGDOUBLE printf("scale %f energy %*.*Lf\n",(DOUBLE)scale,DWIDTH,DPREC,web.total_energy); #else printf("scale %f energy %20.15f\n",(DOUBLE)scale,web.total_energy); #endif energies[k] = web.total_energy; restore_coords(&saved,SAVE_IN_ATTR); } denom = energies[0] - 2*energies[1] + energies[2]; if ( denom <= 0.0 ) best_scale = 1.0; /* energy not convex */ else best_scale = 0.5*(3*energies[0]-4*energies[1]+energies[2])/denom; } scale = best_scale; FOR_ALL_VERTICES(v_id) { REAL *coord; vc = get_vertex_vhead(v_id); if ( vc->freedom == 0 ) continue; coord = get_coord(vc->v_id); for ( j = 0 ; j < SDIM ; j++ ) coord[j] = coord[j] - scale*xx[j][vc->rownum]; } free_matrix(rs); free_matrix(xx); free_system(&S); hessian_exit(NULL); return best_scale; } /* end dirichlet() */ /****************************************************************** * * function: sobolev() * * Purpose: Minimization via Sobolev inner product, * as done by Renka and Neuberger. * * Argument: seekmode - 0 for jump to quadratic min, 1 to seek * * Return: stepsize */ /* Notes on method: R & N's method of using a Sobolev inner product to calculate the gradient is equivalent to using a positive definite quadratic form approximation for the area, and minimizing the quadratic form. Numerical solution uses mechanisms of hessian minimization. */ REAL sobolev(seekmode) int seekmode; /* 1 if seek for best stepsize */ { REAL side[FACET_EDGES][MAXCOORD]; int r; REAL area; int i,j,k,n; facet_id f_id; edge_id e_id; vertex_id v[3]; vertex_id v_id; facetedge_id fe; REAL ss[FACET_VERTS],sd[FACET_VERTS]; int ii,iii; REAL *rs,*rstmp; /* one right side for each coordinate */ REAL *rs_save; struct hess_verlist *vc; /* current vertex */ struct hess_verlist *rv,*sv; REAL scale; REAL best_scale; struct linsys S; if ( fixed_constraint_flag ) kb_error(2082,"Cannot do sobolev with constraints.\n",RECOVERABLE); kb_error(2083,"Sobolev not working properly.\n",WARNING); hmode = UNRESTRICTED; /* full hessian needed */ hessian_init(&S,NULL); rs = (REAL*)temp_calloc(S.A_rows,sizeof(REAL)); /* right side for each coord */ rs_save = (REAL*)temp_calloc(S.A_rows,sizeof(REAL)); /* right side for each coord */ rstmp = (REAL*)temp_calloc(S.A_rows,sizeof(REAL)); /* right side for each coord */ /* fill in */ FOR_ALL_FACETS(f_id) { fe = get_facet_fe(f_id); for ( i = 0 ; i < FACET_VERTS ; i++ ) { e_id = get_fe_edge(fe); get_edge_side(e_id,side[i]); v[i] = get_edge_tailv(e_id); fe = get_next_edge(fe); } for ( i = 0 ; i < FACET_VERTS ; i++ ) { ss[i] = SDIM_dot(side[i],side[i]); sd[i] = SDIM_dot(side[i],side[(i+2)%FACET_VERTS]); } area = sqrt(ss[0]*ss[2] - sd[0]*sd[0]); for ( i = 0 ; i < FACET_VERTS ; i++ ) { ii = (i+1)%FACET_VERTS; iii = (i+2)%FACET_VERTS; rv = get_vertex_vhead(v[ii]); sv = get_vertex_vhead(v[iii]); r = rv->rownum; /* vertex self-term */ if ( !(get_vattr(v[iii]) & FIXED) ) { for ( j = 0 ; j < SDIM ; j++ ) { sp_hash_search(&S,sv->rownum+j,sv->rownum+j,ss[i]/area); for ( k = j+1 ; k < SDIM ; k++ ) sp_hash_search(&S,sv->rownum+j,sv->rownum+k, -side[i][j]*side[i][k]/area); } } /* edge cross-term */ if ( !(get_vattr(v[iii]) & FIXED) && !(get_vattr(v[ii]) & FIXED) ) for ( j = 0 ; j < SDIM ; j++ ) { if ( rv->rownum > sv->rownum ) { sp_hash_search(&S,sv->rownum+j,rv->rownum+j,sd[i]/area); for ( k = 0 ; k < SDIM ; k++ ) sp_hash_search(&S,sv->rownum+k,rv->rownum+j, -side[i][j]*side[iii][k]/area); } else { for ( k = 0 ; k < SDIM ; k++ ) sp_hash_search(&S,rv->rownum+k,sv->rownum+j, -side[i][j]*side[iii][k]/area); } } /* right side */ if ( !(get_vattr(v[ii]) & FIXED) ) for ( j = 0 ; j < SDIM ; j++ ) rs[r+j] -= (sd[i]*side[iii][j] - ss[iii]*side[i][j])/area; } } if ( hess_debug ) { printf("Gradient: \n"); FOR_ALL_VERTICES(v_id) { vc = get_vertex_vhead(v_id); if ( vc->freedom == 0 ) continue; printf("n: %s ",ELNAME(v_id)); for ( j = 0 ; j < SDIM ; j++ ) printf ("%f ",(DOUBLE)rs[vc->rownum+j]); printf("\n"); } } /* extract stuff from hash table */ sp_hash_end(&S,S.A_rows,S.total_rows,A_OFF); /* solve system */ memcpy((char*)rs_save,(char*)rs,sizeof(REAL)*S.A_rows); (*sp_AIJ_setup_func)(S.A_rows,&S); (*sp_constraint_setup_func) (web.skel[BODY].max_ord+1 + gen_quant_count,&S); if ( sp_ordering_func ) (*sp_ordering_func)(&S); sp_factor(&S); (*sp_hess_project_setup_func)(&S); sp_hessian_solve(&S,rs,rstmp,NO_SET_PRESSURE); for ( j = 0 ; j < S.N ; j++ ) rs[j] = rstmp[j]; if ( hess_debug ) { printf("Motion: \n"); for ( n = 0, vc = vhead ; n < vhead_count ; n++, vc++ ) { if ( vc->freedom == 0 ) continue; printf("n: %d ",n); for ( j = 0 ; j < SDIM ; j++ ) printf ("%f ",(DOUBLE)rs[vc->rownum+j]); printf("\n"); } printf("Dot with gradient: %f\n",(DOUBLE)dot(rs,rs_save,S.A_rows)); } /* move vertices */ if ( seekmode == 0 ) { best_scale = 1.0; } else { REAL energies[3]; REAL denom; int k; save_coords(&saved,SAVE_IN_ATTR); /* in case this doesn't work */ energies[0] = web.total_energy; for ( scale = 1.0, k = 1 ; k < 3 ; scale += 1.0, k++ ) { FOR_ALL_VERTICES(v_id) { REAL *coord; vc = get_vertex_vhead(v_id); if ( vc->freedom == 0 ) continue; coord = get_coord(v_id); for ( j = 0 ; j < SDIM ; j++ ) coord[j] = coord[j] - scale*rs[vc->rownum+j]; } global_timestamp++; calc_energy(); /* energy after motion */ if ( hess_debug || itdebug ) #ifdef LONGDOUBLE printf("scale %f energy %*.*Lf\n",(DOUBLE)scale,DWIDTH,DPREC,web.total_energy); #else printf("scale %f energy %20.15f\n",(DOUBLE)scale,web.total_energy); #endif energies[k] = web.total_energy; restore_coords(&saved,SAVE_IN_ATTR); } denom = energies[0] - 2*energies[1] + energies[2]; if ( denom <= 0.0 ) best_scale = 1.0; /* energy not convex */ else best_scale = 0.5*(3*energies[0]-4*energies[1]+energies[2])/denom; } scale = best_scale; FOR_ALL_VERTICES(v_id) { REAL *coord; vc = get_vertex_vhead(v_id); if ( vc->freedom == 0 ) continue; coord = get_coord(v_id); for ( j = 0 ; j < SDIM ; j++ ) coord[j] = coord[j] - scale*rs[vc->rownum+j]; } temp_free((char*)rs); temp_free((char*)rs_save); temp_free((char*)rstmp); free_system(&S); hessian_exit(NULL); return best_scale; } /*************************************************************************** Lanczos routines, for solving hessian for motion or eigenvectors without factoring. ***************************************************************************/ /******************************************************************** * * function: householder() * * purpose: perform Householder reduction. * returns b so (I-bvv^T)x = 0 mostly */ REAL householder ARGS((REAL*,REAL*,int)); REAL householder(x,v,n) REAL *x; /* vector to be zeroed, except first element */ REAL *v; /* vector used to generate transformation */ int n; /* size of x,v */ { REAL maxx; REAL a = 0.0,b; int i; for ( maxx = 0.0, i = 0 ; i < n ; i++ ) if ( fabs(x[i]) > maxx ) maxx = fabs(x[i]); for ( i = 0 ; i < n ; i++ ) { v[i] = x[i]/maxx; a += v[i]*v[i]; } a = sqrt(a); b = 1/a/(a+fabs(v[0])); v[0] += v[0]<0.0 ? -a : a; return b; } /****************************************************************** * * function: sturm() * * purpose: Sturm sequence for number of eigenvalues below x * for tridiagonal matrix */ int sturm(n,alpha,beta,x) int n; /* size of matrix */ REAL *alpha; /* main diagonal, size n */ REAL *beta; /* subdiagonal, size n-1 */ REAL x; /* test eigenvalue */ { int i; int changes; REAL p,pp,t; /* Sturm sequence for number of eigenvalues below x */ pp = 1.0; p = alpha[0] - x; changes = p < 0.0 ? 1 : 0; for ( i = 1 ; i < n ; i++ ) { t = (alpha[i] - x)*p - beta[i-1]*beta[i-1]*pp; if ( t*p < 0.0 ) changes++; pp = p; p = t; } return changes; } /***************************************************************** * * function: ev_bisect() * * purpose: returns smallest eigenvalue in interval * */ REAL ev_bisect(left,right,cleft,cright,n,alpha,beta) REAL left; /* left endpoint of interval */ REAL right; /* right endpoint of interval */ int cleft; /* sturm changes at left endpoint of interval */ int cright; /* sturm changes at right endpoint of interval */ int n; /* size of matrix */ REAL *alpha; /* main diagonal, size n */ REAL *beta; /* subdiagonal, size n-1 */ { int cmid; REAL mid; REAL lowev = 0.0; mid = (left+right)/2; if ( right-left < 3*machine_eps ) { #ifdef LONGDOUBLE sprintf(msg,"%d at %2.*Lg\n",DPREC,cright-cleft,mid); #else sprintf(msg,"%d at %2.15g\n",cright-cleft,mid); #endif outstring(msg); return mid; } cmid = sturm(n,alpha,beta,mid); if ( cmid < cright ) lowev = ev_bisect(mid,right,cmid,cright,n,alpha,beta); if ( cleft < cmid ) lowev = ev_bisect(left,mid,cleft,cmid,n,alpha,beta); return lowev; } /************************************************************************** * * function: chebychev_ev() * * purpose: use Chebychev iteration to solve for eigenvalue and * eigenvector. * */ void chebychev_ev(S) struct linsys *S; { REAL *v,*w; /* working vectors */ REAL *r; REAL t; /* temp */ int i,k; REAL maxev; /* eigenvalue upper bound */ REAL minev; /* eigenvalue lower bound */ REAL nextev; REAL coeffA,coeffc; REAL rayleigh=1.0; /* Rayleigh quotient */ int maxk; char response[20]; REAL *X; v = (REAL *)temp_calloc(S->N+S->concount+1,sizeof(REAL)); w = (REAL *)temp_calloc(S->N+S->concount+1,sizeof(REAL)); r = (REAL *)temp_calloc(S->N+S->concount+1,sizeof(REAL)); X = (REAL *)temp_calloc(S->N+S->concount+1,sizeof(REAL)); /* first, need to find upper bound on eigenvalues */ maxev = 1.0; nextev = 0.0; minev = 0.0; do { prompt("Max iterations: ",response,sizeof(response)); maxk = atoi(response); do { coeffA = 2/(maxev-nextev); coeffc = -2*nextev/(maxev-nextev) - 1; if ( minev >= 0.0 ) for ( i = 0 ; i < S->N ; i++ ) w[i] = drand48() - .5; (*sp_mul_func)(S,w,v); for ( i = 0 ; i < S->N ; i++ ) r[i] = coeffA*v[i] + coeffc*w[i]; coeffA *= 2; coeffc *= 2; for ( k = 0 ; k < maxk ; k++ ) { REAL *tmp; REAL norm; tmp = r; r = w; w = tmp; (*sp_mul_func)(S,w,v); for ( i = 0 ; i < S->N ; i++ ) r[i] = coeffA*v[i] + coeffc*w[i] - r[i]; /* periodically check Rayleigh quotient to see if bound needs raising */ norm = dot(w,w,S->N); rayleigh = dot(w,v,S->N)/norm; if ( rayleigh > maxev ) { maxev = 1.2*rayleigh + .5; break; } /* just some checking */ if ( (k % 50)==0 ) printf("%4d. rayleigh %2.15g maxev %g\n",k,(DOUBLE)rayleigh,(DOUBLE)maxev); /* renormalize occasionally */ if ( (norm > 25.0) || (norm < .04) ) { t = 1/sqrt(norm); for ( i = 0 ; i < S->N ; i++ ) { w[i] *= t; r[i] *= t; } } } if ( rayleigh < minev-.00000001 ) { minev = rayleigh; nextev = 0.99*rayleigh; } } while ( k < maxk ); } while ( k > 0 ); printf("%4d. rayleigh %2.15g\n",k,(DOUBLE)rayleigh); last_eigenvalue = rayleigh; /* else try again with increased maxev */ /* now have got eigenvector for neg eigenvalue */ t = 1/sqrt(dot(r,r,S->N)); for ( i = 0 ; i < S->N ; i++ ) X[i] = t*r[i]; /* normalized */ temp_free((char*)w); temp_free((char*)v); temp_free((char*)r); temp_free((char*)X); } /* end chebychev_ev() */ /************************************************************************** * * function: chebychev_hess() * * purpose: use Chebychev iteration to solve for Hessian minimum. * Requires semidefinite hessian. * */ void chebychev_hess(S) struct linsys *S; { REAL *v,*w; /* working vectors */ REAL *r; REAL t; /* temp */ int i,k; REAL maxev; /* eigenvalue upper bound */ REAL minev; /* eigenvalue lower bound */ REAL nextev; REAL coeffA,coeffc; REAL rayleigh=0.0; /* Rayleigh quotient */ REAL *X; v = (REAL *)temp_calloc(S->N+1,sizeof(REAL)); w = (REAL *)temp_calloc(S->N+1,sizeof(REAL)); r = (REAL *)temp_calloc(S->N+1,sizeof(REAL)); X = (REAL *)temp_calloc(S->N+1,sizeof(REAL)); /* first, need to find upper bound on eigenvalues */ maxev = 1.0; nextev = 0.0; minev = 0.0; do { coeffA = 2/(maxev-nextev); coeffc = -2*nextev/(maxev-nextev) - 1; if ( minev >= 0.0 ) for ( i = 0 ; i < S->N ; i++ ) w[i] = drand48() - .5; (*sp_mul_func)(S,w,v); for ( i = 0 ; i < S->N ; i++ ) r[i] = coeffA*v[i] + coeffc*w[i]; coeffA *= 2; coeffc *= 2; for ( k = 0 ; k < 200 ; k++ ) { REAL *tmp; REAL norm; tmp = r; r = w; w = tmp; (*sp_mul_func)(S,w,v); for ( i = 0 ; i < S->N ; i++ ) r[i] = coeffA*v[i] + coeffc*w[i] - r[i]; /* periodically check Rayleigh quotient to see if bound needs raising */ norm = dot(w,w,S->N); rayleigh = dot(w,v,S->N)/norm; if ( rayleigh > maxev ) { maxev = rayleigh + .5; break; } /* just some checking */ if ( (k % 50)==0 ) printf("%4d. rayleigh %2.15g maxev %g\n",k,(DOUBLE)rayleigh,(DOUBLE)maxev); /* renormalize occasionally */ if ( (k&0xFFF0) == 0 ) { t = 1/sqrt(norm); for ( i = 0 ; i < S->N ; i++ ) { w[i] *= t; r[i] *= t; } } } if ( rayleigh < minev-.00000001 ) { minev = rayleigh; nextev = 0.9*rayleigh; k = 0; continue; } } while ( k < 200 ); printf("%4d. rayleigh %2.15g\n",k,(DOUBLE)rayleigh); /* else try again with increased maxev */ /* now have got eigenvector for neg eigenvalue */ t = 1/sqrt(dot(r,r,S->N)); for ( i = 0 ; i < S->N ; i++ ) X[i] = t*r[i]; /* normalized */ temp_free((char*)w); temp_free((char*)v); temp_free((char*)r); temp_free((char*)X); } /* end chebychev_hess() */ /**************************************************************************** * * function: eigenprobe_command() * * purpose: handle "eigenprobe lambda" command from command line * */ void eigenprobe_command(lambda,iters) REAL lambda; int iters; /* iteration bound */ { struct linsys S; int i,k; REAL t,oldt; REAL *W = NULL; /* for use when metric is needed */ REAL *X = NULL; /* for eigenvector */ hmode = hessian_normal_flag; hessian_init(&S,NULL); hess_flag = 1; rhs_flag = 0; hessian_fill(&S,NULL); S.lambda = lambda; (*sp_AIJ_setup_func)(S.A_rows,&S); (*sp_constraint_setup_func) (web.skel[BODY].max_ord+1 + gen_quant_count,&S); if ( sp_ordering_func ) (*sp_ordering_func)(&S); if ( hessian_linear_metric_flag ) { linear_metric_setup(&S,&Met); } else if ( web.area_norm_flag ) { star_metric_setup(&S,&Met); } sp_factor(&S); (*sp_hess_project_setup_func)(&S); sprintf(msg,"Eigencounts: %d <, %d ==, %d > \n",S.neg,S.zero,S.pos); outstring(msg); if ( iters <= 0 ) goto ep_exit; if ( web.area_norm_flag || hessian_linear_metric_flag ) W = (REAL*)temp_calloc(S.N,sizeof(REAL)); X = (REAL*)temp_calloc(S.N,sizeof(REAL)); /* inverse iteration */ /* random starting vector */ for ( i = 0 ; i < S.N ; i++ ) X[i] = drand48(); t = 1/sqrt(dot(X,X,S.N)); for ( i = 0 ; i < S.N ; i++ ) X[i] *= t; oldt = 1e30; /* for convergence test */ for ( k = 0 ; k < iters ; k++ ) { REAL oldv0=1.0; /* for sign */ int oldvi=0; /* index of nonzero component */ REAL eps = 1/sqrt((REAL)(S.N))/2; for ( i = 0, oldv0 = 0.0 ; i < S.N ; i++ ) if ( fabs(X[i]) > eps ) { oldvi = i; oldv0 = X[i]; break; } if ( web.area_norm_flag || hessian_linear_metric_flag ) (*sp_mul_func)(&Met,X,W); else W = X; sp_hessian_solve(&S,W,X,NO_SET_PRESSURE); if ( web.area_norm_flag || hessian_linear_metric_flag ) t = 1/sqrt(sparse_metric_dot(X,X,&Met)); else t = 1/sqrt(dot(X,X,S.N)); if ( X[oldvi]*oldv0 < 0. ) t = -t; /* get sign right */ #ifdef LONGDOUBLE if ( k % 10 == 0 ) printf("%d ev = %*.*Lf\n",k,DWIDTH,DPREC,S.lambda+t); #else if ( k % 10 == 0 ) printf("%d ev = %20.15f\n",k,S.lambda+t); #endif for ( i = 0 ; i < S.N ; i++ ) X[i] *= t; if ( fabs(t-oldt) <= 100*machine_eps*fabs(t) ) { break; } oldt = t; } #ifdef LONGDOUBLE printf("%d ev = %*.*Lf\n",k,DWIDTH,DPREC,S.lambda+t); #else printf("%d ev = %20.15f\n",k,S.lambda+t); #endif if ( S.zero != 0 ) last_eigenvalue = S.lambda; else last_eigenvalue = S.lambda+t; if ( web.area_norm_flag || hessian_linear_metric_flag ) temp_free((char*)W); ep_exit: free_system(&S); if ( hessian_linear_metric_flag || web.area_norm_flag ) free_system(&Met); hessian_exit(X); temp_free((char*)X); } /* end eigenprobe_command() */ /**************************************************************************** * * function: lanczos_command() * * purpose: handle "lanczos lambda" command from command line * */ void lanczos_command(lambda,krydim) REAL lambda; int krydim; /* dimension of Krylov subspace */ { struct linsys S; REAL evalues[NPRINT]; int i,nprint; hmode = hessian_normal_flag; hessian_init(&S,NULL); hess_flag = 1; rhs_flag = 0; hessian_fill(&S,NULL); S.lambda = lambda; (*sp_AIJ_setup_func)(S.A_rows,&S); (*sp_constraint_setup_func) (web.skel[BODY].max_ord+1 + gen_quant_count,&S); if ( sp_ordering_func ) (*sp_ordering_func)(&S); if ( hessian_linear_metric_flag ) { linear_metric_setup(&S,&Met); } else if ( web.area_norm_flag ) { star_metric_setup(&S,&Met); } sp_factor(&S); (*sp_hess_project_setup_func)(&S); sprintf(msg,"Eigencounts: %d <, %d ==, %d > \n",S.neg,S.zero,S.pos); outstring(msg); nprint = lanczos(&S,krydim,evalues,NPRINT); /* list, ones near probe value */ for ( i = 0 ; i < nprint ; i++ ) { #ifdef LONGDOUBLE sprintf(msg,"%d %*.*Lf\n",i+1,DWIDTH,DPREC,evalues[i]); #else sprintf(msg,"%d %20.15f\n",i+1,evalues[i]); #endif outstring(msg); } free_system(&S); if ( hessian_linear_metric_flag || web.area_norm_flag ) free_system(&Met); hessian_exit(NULL); } /* end lanczos_command() */ /***************************************************************************** * * function: lanczos() * * purpose: find eigenvalues near probe value * returns number distinct ones found * */ int realabs_comp ARGS((REAL*,REAL*)); int realabs_comp(a,b) REAL *a,*b; {if(fabs(*a)fabs(*b))return 1;return 0;} int lanczos(S,krydim,evalues,nprint) struct linsys *S; int krydim; /* dimension of krylov space to use */ REAL *evalues; /* for return of eigenvalues */ int nprint; /* number to return */ { REAL *diag; /* main diagonal of tridiagonal */ REAL *subdiag; /* subdiagonal */ REAL *v,*w,*mw; /* working vectors; w is Lanczos vector */ REAL *r; REAL t; /* temp */ int i,j; if ( krydim > S->N-S->CN ) krydim = S->N-S->CN; if ( nprint > krydim ) nprint = krydim; diag = (REAL *)temp_calloc(krydim+1,sizeof(REAL)); subdiag = (REAL *)temp_calloc(krydim+1,sizeof(REAL)); v = (REAL *)temp_calloc(S->N+S->concount+1,sizeof(REAL)); w = (REAL *)temp_calloc(S->N+S->concount+1,sizeof(REAL)); r = (REAL *)temp_calloc(S->N+S->concount+1,sizeof(REAL)); if ( web.area_norm_flag || hessian_linear_metric_flag ) mw = (REAL *)temp_calloc(S->N+S->concount+1,sizeof(REAL)); else mw = NULL; /* Lanczos iteration to tridiagonal, starting at index 1 in diag and subdiag */ /* not doing any re-orthogonalization, so multiple eigenvalues can let large eigenvalues creep in spuriously multiple */ for ( i = 0 ; i < S->N ; i++ ) w[i] = drand48() - .5; if ( web.area_norm_flag || hessian_linear_metric_flag ) t = 1/sqrt(sparse_metric_dot(w,w,&Met)); else t = 1/sqrt(dot(w,w,S->N)); for ( i = 0 ; i < S->N ; i++ ) w[i] *= t; subdiag[0] = 1.0; for ( j = 0 ; j < krydim ; ) { if ( subdiag[j] == 0.0 ) { outstring("premature subdiag = 0\n"); break; } if ( j > 0 ) { for ( i = 0 ; i < S->N ; i++ ) { t = w[i]; w[i] = v[i]/subdiag[j]; v[i] = -subdiag[j]*t; } } /* v = Aw + v */ if ( web.area_norm_flag || hessian_linear_metric_flag ) { bk_mul(&Met,w,mw); sp_hessian_solve(S,mw,r,NO_SET_PRESSURE); } else sp_hessian_solve(S,w,r,NO_SET_PRESSURE); for ( i = 0 ; i < S->N ; i++ ) v[i] += r[i]; j = j + 1; if ( web.area_norm_flag || hessian_linear_metric_flag ) diag[j] = sparse_metric_dot(w,v,&Met); else diag[j] = dot(w,v,S->N); for ( i = 0 ; i < S->N ; i++ ) v[i] -= diag[j]*w[i]; if ( web.area_norm_flag || hessian_linear_metric_flag ) subdiag[j] = sqrt(sparse_metric_dot(v,v,&Met)); else subdiag[j] = sqrt(dot(v,v,S->N)); } /* solve tridiagonal */ tridiag_QL(diag+1,subdiag+1,krydim); /* sort eigenvalues */ qsort((char*)(diag+1),krydim,sizeof(REAL),FCAST realabs_comp); /* unshift */ for ( i = 1 ; i <= krydim ; i++ ) if ( diag[i] == 0.0 ) diag[i] = S->lambda; else diag[i] = S->lambda + 1/diag[i]; /* uniqify */ for ( i = 2, j = 1; i <= krydim ; i++ ) { if ( fabs(diag[j] - diag[i]) < 100*machine_eps ) continue; diag[++j] = diag[i]; } krydim = j; if ( nprint > krydim ) nprint = krydim; /* list, ones near probe value */ for ( i = 0 ; i < nprint ; i++ ) evalues[i] = diag[krydim-i]; temp_free((char*)diag); temp_free((char*)subdiag); temp_free((char*)v); temp_free((char*)w); temp_free((char*)r); if ( mw ) temp_free((char*)mw); return nprint; } /* end lanczos() */ /***************************************************************************** * * function: selective_lanczos() * * purpose: find eigenvalues near probe value using selective * reorthogonalization, i.e. saves basis vectors for which Av * grows significantly. Does not do uniqifying of eigenvalues. * returns number found * */ int selective_lanczos(S,krydim,evalues,nprint) struct linsys *S; int krydim; /* dimension of krylov space to use */ REAL *evalues; /* for return of eigenvalues */ int nprint; /* number to return */ { REAL *diag; /* main diagonal of tridiagonal */ REAL *subdiag; /* subdiagonal */ REAL *v,*w,*mw; /* working vectors; w is Lanczos vector */ REAL *r; REAL t; /* temp */ int i,j,k; REAL **basis; /* for saving basis vectors for reortho */ REAL *rayleigh; /* rayleigh quotients associated to basis vectors */ int basis_count = 0; /* how many basis vectors saved */ REAL growth; if ( krydim > S->N ) krydim = S->N; if ( nprint > krydim ) nprint = krydim; diag = (REAL *)temp_calloc(krydim+1,sizeof(REAL)); subdiag = (REAL *)temp_calloc(krydim+1,sizeof(REAL)); v = (REAL *)temp_calloc(S->N+S->concount+1,sizeof(REAL)); w = (REAL *)temp_calloc(S->N+S->concount+1,sizeof(REAL)); r = (REAL *)temp_calloc(S->N+S->concount+1,sizeof(REAL)); if ( web.area_norm_flag || hessian_linear_metric_flag ) mw = (REAL *)temp_calloc(S->N+S->concount+1,sizeof(REAL)); else mw = NULL; basis = dmatrix(0,nprint-1,0,S->N-1); rayleigh = (REAL*)temp_calloc(nprint,sizeof(REAL)); /* Lanczos iteration to tridiagonal, starting at index 1 in diag and subdiag */ for ( i = 0 ; i < S->N ; i++ ) w[i] = drand48() - .5; if ( web.area_norm_flag || hessian_linear_metric_flag ) t = 1/sqrt(sparse_metric_dot(w,w,&Met)); else t = 1/sqrt(dot(w,w,S->N)); for ( i = 0 ; i < S->N ; i++ ) w[i] *= t; subdiag[0] = 1.0; for ( j = 0 ; j < krydim ; ) { if ( subdiag[j] == 0.0 ) { outstring("premature subdiag = 0\n"); break; } if ( j > 0 ) { for ( i = 0 ; i < S->N ; i++ ) { t = w[i]; w[i] = v[i]/subdiag[j]; v[i] = -subdiag[j]*t; } } /* v = Aw + v */ if ( web.area_norm_flag || hessian_linear_metric_flag ) { bk_mul(&Met,w,mw); sp_hessian_solve(S,mw,r,NO_SET_PRESSURE); } else sp_hessian_solve(S,w,r,NO_SET_PRESSURE); for ( i = 0 ; i < S->N ; i++ ) v[i] += r[i]; j = j + 1; if ( web.area_norm_flag || hessian_linear_metric_flag ) diag[j] = sparse_metric_dot(w,v,&Met); else diag[j] = dot(w,v,S->N); for ( i = 0 ; i < S->N ; i++ ) v[i] -= diag[j]*w[i]; /* reorthogonalize */ for ( k = 0 ; k < basis_count ; k++ ) { REAL d; REAL *p = basis[k]; if ( web.area_norm_flag || hessian_linear_metric_flag ) d = sparse_metric_dot(basis[k],v,&Met); else d = dot(basis[k],v,S->N); for ( i = 0 ; i < S->N ; i++,p++ ) v[i] -= d*(*p); } if ( web.area_norm_flag || hessian_linear_metric_flag ) subdiag[j] = sqrt(sparse_metric_dot(v,v,&Met)); else subdiag[j] = sqrt(dot(v,v,S->N)); /* maybe save this basis vector for orthogonalization */ growth = sqrt(subdiag[j]*subdiag[j]+diag[j]*diag[j]+subdiag[j-1]*subdiag[j-1]); if ( growth > 1.0 ) { /* save */ int which = -1; /* which to replace, with non-replace initial value */ if ( basis_count < nprint ) which = basis_count++; else /* maybe replace smaller growth vector */ { REAL diff = 0.0; for ( i = 0 ; i < basis_count ; i++ ) if ( growth - rayleigh[i] > diff ) { diff = growth - rayleigh[i]; which = i; } } if ( which >= 0 ) { rayleigh[which] = growth; memcpy((char*)(basis[which]),(char*)w,S->N*sizeof(REAL)); } } } /* solve tridiagonal */ tridiag_QL(diag+1,subdiag+1,krydim); /* sort eigenvalues */ qsort((char*)(diag+1),krydim,sizeof(REAL),FCAST realabs_comp); /* unshift */ for ( i = 1 ; i <= krydim ; i++ ) diag[i] = S->lambda + 1/diag[i]; /* list, ones near probe value */ for ( i = 0 ; i < nprint ; i++ ) evalues[i] = diag[krydim-i]; temp_free((char*)diag); temp_free((char*)subdiag); temp_free((char*)v); temp_free((char*)w); if ( mw ) temp_free((char*)mw); temp_free((char*)r); temp_free((char*)rayleigh); free_matrix(basis); return nprint; } /* end selective_lanczos() */ /*************************************************************************** * * function: tridiag_QL() * * purpose: QL algorithm with implicit shifts to determine eigenvalues * of a tridiagonal matrix. From Numerical Recipes in C. */ #define SIGN(a,b) ((b)<0.0 ? -fabs(a) : fabs(a)) void tridiag_QL(d,e,n) REAL *d; /* n diagonal elements, d[0] in a[0][0] */ REAL *e; /* n-1 subdiagonal, e[0] in a[1][0], plus an extra! */ int n; /* size */ { int m,l,iter,i; REAL s,r,p,g,f,dd,c,b; d--;e--; /* to agree with Numerical Recipes indexing */ e[n] = 0.0; for ( l = 1 ;l <= n ; l++ ) { iter = 0; do { for ( m=l ; m <= n-1 ; m++ ) /* test matrix splitting */ { dd = fabs(d[m]) + fabs(d[m+1]); if ( fabs(e[m]) + dd == dd ) break; } if ( m != l ) { if ( iter++ >= 30 ) kb_error(1654,"Over 30 iterations in QL algorithm solving for eigenvalues.\n",RECOVERABLE); g = (d[l+1]-d[l])/(2*e[l]); /* form shift */ r = sqrt(g*g+1.0); g = d[m] - d[l] + e[l]/(g + SIGN(r,g)); s = c = 1.0; p = 0.0; for ( i = m-1 ; i >= l ; i-- ) { f = s*e[i]; b = c*e[i]; if ( fabs(f) >= fabs(g) ) { c = g/f; r = sqrt(c*c + 1.0); e[i+1] = f*r; c *= (s = 1/r); } else { s = f/g; r = sqrt(s*s + 1.0); e[i+1] = g*r; s *= (c = 1/r); } g = d[i+1] - p; r = (d[i] - g)*s + 2*c*b; p = s*r; d[i+1] = g + p; g = c*r - b; } d[l] -= p; e[l] = g; e[m] = 0.0; } } while ( m != l ); } } /* end tridiagonal_QL () */ /**************************************************************************** * * function: LQ_decomp() * * purpose: orthonormalize rows of a rectangular matrix A by Gram-Schmidt and * return factorizion into A = LQ where L is lower * triangular and Q is orthonormal, Q M Q^T = I. */ void LQ_decomp(A,rows,cols,Q,L,M) REAL **A; /* input */ int rows; /* number of rows in A */ int cols; /* number of columns in A */ REAL **Q; /* orthonormal rows, size rows * cols */ REAL **L; /* factor, size rows * rows */ struct linsys *M; /* metric to use; NULL if Euclidean */ { int i,j,k; REAL t; for ( i = 0 ; i < rows ; i++ ) { if ( Q != A )/* copy over */ for ( k = 0 ; k < cols ; k++ ) Q[i][k] = A[i][k]; for ( j = 0 ; j < i ; j++ ) { t = M ? sparse_metric_dot(Q[j],Q[i],M) : dot(Q[j],Q[i],cols); L[i][j] = t; for ( k = 0 ; k < cols ; k++ ) Q[i][k] -= t*Q[j][k]; } t = M ? sparse_metric_dot(Q[i],Q[i],M) : dot(Q[i],Q[i],cols); if ( t != 0.0 ) { L[i][i] = t = sqrt(t); t = 1/t; for ( k = 0 ; k < cols ; k++ ) Q[i][k] *= t; } } } /* end LQ_decomp() */ /**************************************************************************** * * function: QR_full() * * purpose: QR algorithm for eigenvalues of dense symmetric matrix * Not too good, since doesn't shift. Has problem with * multiple eigenvalues. * */ void QR_full ARGS((REAL **,REAL *,int, struct linsys *)); void QR_full(A,evalues,n,M) REAL **A; /* input, destroyed */ REAL *evalues; /* for reporting eigenvalues */ int n; /* size of system */ struct linsys *M; /* metric, NULL for Euclidean */ { REAL **Q,**L; int i,j; REAL resid; int count = 0; Q = dmatrix(0,n-1,0,n-1); L = dmatrix(0,n-1,0,n-1); do { LQ_decomp(A,n,n,Q,L,M); mat_mult(Q,L,A,n,n,n); for ( i = 0, resid = 0.0 ; i < n ; i++ ) for ( j = 0 ; j < i ; j++ ) resid += fabs(A[i][j]); #ifdef LONGDOUBLE sprintf(msg,"QR residual %*.*Lf\n",DWIDTH,DPREC,resid); #else sprintf(msg,"QR residual %20.15f\n",resid); #endif outstring (msg); } while ( (resid > 1000*machine_eps) && (count++ < 20) ); for ( i = 0 ; i < n ; i++ ) evalues[i] = A[i][i]; } /* end QR_full() */ /**************************************************************************** * * function: ritz_command() * * purpose: handle "ritz(lambda,eigencount)" command from command line * */ void ritz_command(lambda,ritzdim) REAL lambda; /* probe value */ int ritzdim; /* dimension of Rayleigh-Ritz subspace */ { struct linsys S; REAL *rhs; memset((char*)&S,0,sizeof(struct linsys)); hmode = hessian_normal_flag; hessian_init(&S,&rhs); hess_flag = 1; rhs_flag = 0; hessian_fill(&S,&rhs); if ( sparse_constraints_flag ) (*sp_AIJ_setup_func)(S.total_rows,&S); else (*sp_AIJ_setup_func)(S.A_rows,&S); (*sp_constraint_setup_func) (web.skel[BODY].max_ord+1 + gen_quant_count,&S); if ( sp_ordering_func ) (*sp_ordering_func)(&S); if ( hessian_linear_metric_flag ) { linear_metric_setup(&S,&Met); } else if ( web.area_norm_flag ) { star_metric_setup(&S,&Met); } do_ritz(&S,lambda,ritzdim,NULL); free_system(&S); if ( hessian_linear_metric_flag || web.area_norm_flag ) free_system(&Met); temp_free((char*)rhs); hessian_exit(NULL); } /* end ritz_command() */ /************************************************************************** * * function: do_ritz() * * purpose: Do Ritz subspace iteration * Hessian already set up. * Input: If ritzdim over 1000, ritzdim/1000 is the number of extra * vectors to include in the Ritz subspace, in hopes that * including a few extra will find an eigenvalue gap big enough * to speed convergence. */ REAL *evalues; void do_ritz(S,lambda,ritzdim,ritzvec) struct linsys *S; /* Hessian */ REAL lambda; /* shift value */ int ritzdim; /* dimension of subspace */ REAL **ritzvec; /* non-null if want eigenvectors returned */ { int i,j; REAL **Lmat,**rbasis,**Arbasis; REAL resid; REAL **A; REAL *oldevalues; REAL **evectors; int converged; /* number of eigenvectors converged */ int iter; REAL diff = 1e30,olddiff=1e30; REAL **mw; struct linsys *M; /* metric to use */ REAL *work; int ritzwanted; int maxritz; int old_breakflag; /* see if user wants extra ritz vectors */ ritzwanted = ritzdim % 1000; ritzdim = (ritzdim/1000) + ritzwanted; if ( ritzdim < 0 ) { outstring("Ritz dimension cannot be negative.\n"); return; } S->lambda = lambda; sp_factor(S); (*sp_hess_project_setup_func)(S); sprintf(msg,"Eigencounts: %d <, %d ==, %d > \n",S->neg,S->zero,S->pos); outstring(msg); if ( ritzdim == 0 ) return; maxritz = S->N - (augmented_hessian_mode ? 2 : 1)* S->CN; if ( ritzdim > maxritz ) ritzdim = maxritz; if ( ritzwanted > ritzdim ) ritzwanted = ritzdim; /* now find near-invariant subspace by power iteration and orthog */ Lmat = dmatrix(0,ritzdim-1,0,ritzdim-1); /* for QR decomp */ if ( ritzvec ) rbasis = ritzvec; else rbasis = dmatrix(0,ritzdim-1,0,S->N-1+S->concount); Arbasis = dmatrix(0,ritzdim-1,0,S->N-1+S->concount); /* multiplied basis */ A = dmatrix(0,ritzdim-1,0,ritzdim-1); mw = dmatrix(0,ritzdim-1,0,S->N+S->concount); evalues = (REAL*)realloc(evalues,ritzdim*sizeof(REAL)); oldevalues = (REAL*)temp_calloc(ritzdim,sizeof(REAL)); work = (REAL*)temp_calloc(2*S->N,sizeof(REAL)); for ( i = 0 ; i < ritzdim ; i++ ) oldevalues[i] = -1e18; /* unlikely value */ evectors = dmatrix(0,ritzdim-1,0,ritzdim-1); /* for augmented hessian, want augmented part of rhs 0 always */ for ( i = 0 ; i < ritzdim ; i++ ) for ( j = 0 ; j < S->A_rows ; j++ ) rbasis[i][j] = drand48() - .5; iter = 0; converged = 0; iterate_flag = 1; /* for politer interrupt message */ if ( web.area_norm_flag || hessian_linear_metric_flag ) M = &Met; else M = NULL; old_breakflag = breakflag; do { if ( web.area_norm_flag || hessian_linear_metric_flag ) { for ( i = converged ; i < ritzdim ; i++ ) bk_mul(&Met,rbasis[i],mw[i]); sp_hessian_solve_multi(S,mw+converged,Arbasis+converged, ritzdim-converged); } else sp_hessian_solve_multi(S,rbasis+converged,Arbasis+converged, ritzdim-converged); if ( augmented_hessian_mode ) { /* set augmented part to 0 */ for ( i = converged ; i < ritzdim ; i++ ) for ( j = S->A_rows ; j < S->N ; j++ ) Arbasis[i][j] = 0.0; } LQ_decomp(Arbasis,ritzdim,S->A_rows,rbasis,Lmat,M); iter ++; /* add up off-diagonal residuals */ for ( i = 0, resid = 0.0 ; i < ritzdim ; i++ ) for ( j = 0 ; j < i ; j++ ) resid += fabs(Lmat[i][j]); /* get matrix in Rayleigh-Ritz subspace */ for ( i = converged ; i < ritzdim ; i++ ) { if ( web.area_norm_flag || hessian_linear_metric_flag ) { bk_mul(&Met,rbasis[i],mw[i]); memcpy((char*)(rbasis[i]),(char*)(mw[i]),Met.N*sizeof(REAL)); } } sp_hessian_solve_multi(S,rbasis+converged,Arbasis+converged, ritzdim-converged); if ( augmented_hessian_flag ) { /* set augmented part to 0 */ for ( i = converged ; i < ritzdim ; i++ ) for ( j = S->A_rows ; j < S->N ; j++ ) Arbasis[i][j] = 0.0; } mat_mul_tr(rbasis+converged,Arbasis+converged,A,ritzdim-converged, S->A_rows,ritzdim-converged); jacobi_eigenpairs(A,ritzdim-converged,evalues+converged,evectors+converged,work); msg[0] = 0 ; /* get eigenvector basis for subspace */ tr_mat_mul(evectors+converged,Arbasis+converged,rbasis+converged, ritzdim-converged,ritzdim-converged,S->A_rows); for ( i = converged ; i < ritzdim ; i++ ) if ( evalues[i] == oldevalues[i] ) { converged++; #ifdef LONGDOUBLE sprintf(msg,"%3d. %*.*Lf\n",converged,DWIDTH,DPREC,1/evalues[i]+lambda); #else sprintf(msg,"%3d. %20.15f\n",converged,1/evalues[i]+lambda); #endif outstring(msg); } else break; olddiff = diff; for ( diff = 0.0 ; i < ritzwanted ; i++ ) { diff += fabs(evalues[i] - oldevalues[i]); oldevalues[i] = evalues[i]; } } while ( (iter<1000) && ((diff < olddiff)||(diff > 10*S->N*machine_eps)) && (diff > 100*machine_eps) && !breakflag ); breakflag = old_breakflag; /* normalize eigenvectors */ for ( i = 0 ; i < ritzdim ; i++ ) { REAL mag; if ( M ) mag = 1/sqrt(sparse_metric_dot(rbasis[i],rbasis[i],M)); else mag = 1/sqrt(dot(rbasis[i],rbasis[i],S->A_rows)); for ( j = 0 ; j < S->A_rows ; j++ ) rbasis[i][j] *= mag; } /* list, ones near probe value */ /* convert and insertion sort */ for ( i = 0 ; i < ritzwanted ; i++ ) { REAL eig; REAL *rtmp; if ( evalues[i] == 0.0 ) eig = 1e30; else eig = 1/evalues[i] + lambda; rtmp = rbasis[i]; for ( j = i ; j > converged ; j-- ) { if ( evalues[j-1] > eig ) { evalues[j] = evalues[j-1]; rbasis[j] = rbasis[j-1]; } else break; } evalues[j] = eig; rbasis[j] = rtmp; } for ( i = converged ; i < ritzwanted ; i++ ) { if ( evalues[i] > 1e29 ) sprintf(msg,"%3d. Singular??\n",i+1); #ifdef LONGDOUBLE else sprintf(msg,"%3d. %*.*Lf\n",i+1,DWIDTH,DPREC,evalues[i]); #else else sprintf(msg,"%3d. %18.13f\n",i+1,evalues[i]); #endif outstring(msg); } sprintf(msg, "Iterations: %d. Total eigenvalue changes in last iteration: %10.8g\n", iter,(DOUBLE)diff); outstring(msg); if ( S->zero != 0 ) last_eigenvalue = S->lambda; else last_eigenvalue = evalues[0]; set_eigenvalue_list_global(evalues,ritzdim); free_matrix(Lmat); free_matrix(A); if ( ritzvec == NULL ) free_matrix(rbasis); free_matrix(Arbasis); free_matrix(evectors); temp_free((char*)oldevalues); temp_free((char*)work); free_matrix(mw); } /* end do_ritz() */ evolver-2.30c.dfsg/src/stringq.c0000644000175300017530000002514111410765113017053 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************************ * * File: stringq.c * * Purpose: * Functions to calculate area, length, and energy according to * the QUADRATIC STRING model. */ #include "include.h" /* combining coefficients for coordinates of interpolation pts */ /* initialized in scoeff_init() */ REAL gcombo[EDGE_CTRL][EDGE_INTERP]; /* antisymmetric coefficients for area integral */ /* with diagonal elements to give area over x-axis for torus ease */ REAL scoeff[EDGE_CTRL][EDGE_CTRL] = { { 0.5, -2.0/3, 1.0/6 },{2.0/3, 0.0, -2.0/3},{-1.0/6, 2.0/3, -0.5} } ; /* length gradient coefficients for string forces */ /* sdip[control][eval] - eval is integration index control is control point index for grad */ REAL sdip[EDGE_CTRL][EDGE_INTERP]; /* integration coefficients for string forces */ /* ssimp[control][eval] */ REAL ssimp[EDGE_CTRL][EDGE_INTERP]; /* three-point gaussian integration on [0,2] */ REAL gauss2pt[EDGE_INTERP] = { 0.225403330758517, 1.0, 1.774596669241483 }; REAL gauss2wt[EDGE_INTERP] = { 5.0/9, 8.0/9, 5.0/9 }; REAL interpoly(k,u) int k; REAL u; { switch ( k ) { case 0: return (1 - u)*(2 - u)/2; case 1: return u*(2 - u); case 2: return u*(u - 1)/2; } return 0.0; /* error return */ } REAL interpolyderiv(k,u) int k; REAL u; { switch ( k ) { case 0: return u - 1.5; case 1: return 2 - 2*u; case 2: return u - 0.5; } return 0.0; /* error return */ } void scoeff_init() { int i,j; for ( i = 0 ; i < EDGE_CTRL ; i++ ) for ( j = 0 ; j < EDGE_INTERP ; j++ ) { gcombo[i][j] = interpoly(i,gauss2pt[j]); sdip[i][j] = interpolyderiv(i,gauss2pt[j]); ssimp[i][j] = gauss2wt[j]*sdip[i][j]; } } /************************************************************************ * * Calculates all forces on control points due to edge and * accumulates them at each control point. * Quadratic version. */ void edge_force_q(e_id) edge_id e_id; { REAL *x[EDGE_CTRL],*force[EDGE_CTRL],etang[EDGE_INTERP][MAXCOORD]; REAL norm[MAXCOORD]; REAL len; REAL density = get_edge_density(e_id); vertex_id hv = get_edge_headv(e_id); vertex_id mv = get_edge_midv(e_id); vertex_id tv = get_edge_tailv(e_id); REAL torusx[MAXCOORD]; /* for unwrapping head in torus */ int i,j,k; x[0] = get_coord(tv); x[1] = get_coord(mv); x[2] = get_coord(hv); force[0] = get_force(tv); force[1] = get_force(mv); force[2] = get_force(hv); if ( web.torus_flag ) { /* unwrap head */ (*sym_wrap)(x[2],torusx,get_edge_wrap(e_id)); x[2] = torusx; } for ( i = 0 ; i < EDGE_INTERP ; i++ ) { for ( j = 0 ; j < SDIM ; j ++ ) { etang[i][j] = 0.0; for ( k = 0 ; k < EDGE_CTRL ; k++ ) etang[i][j] += sdip[k][i]*x[k][j]; } norm[i] = sqrt(SDIM_dot(etang[i],etang[i])); if ( norm[i] > 0.0 ) for ( j = 0 ; j < SDIM ; j ++ ) etang[i][j] /= norm[i]; } for ( i = 0 ; i < EDGE_CTRL ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( k = 0 ; k < EDGE_INTERP ; k++ ) force[i][j] -= density*ssimp[i][k]*etang[k][j]; len = 0.0; for ( i = 0 ; i < EDGE_INTERP ; i++ ) len += gauss2wt[i]*norm[i]; set_edge_length(e_id,len); if ( web.area_norm_flag ) { add_vertex_star(hv,len); add_vertex_star(mv,len); add_vertex_star(tv,len); } /* calculate gravitational forces */ if ( web.gravflag && !( get_eattr(e_id) & NONCONTENT) ) { REAL z; REAL gdensity = edge_grav_density(e_id); if ( gdensity != 0.0 ) { for ( i = 0 ; i < EDGE_INTERP ; i++ ) { z = 0.0; for ( j = 0 ; j < EDGE_CTRL ; j++ ) z += gcombo[j][i]*x[j][1]; for ( j = 0 ; j < EDGE_CTRL ; j++ ) { force[j][1] -= -gdensity*z*etang[i][0]*gcombo[j][i]; force[j][0] -= -0.5*gdensity*z*z*sdip[j][i]; } } } } } /************************************************************************ * * Returns energy due to one edge. * * Quadratic version. */ void edge_energy_q(e_id) edge_id e_id; { REAL *x[EDGE_CTRL]; REAL etang[MAXCOORD]; vertex_id v[EDGE_CTRL]; REAL len; int i,j,k; REAL z; REAL density = edge_grav_density(e_id); REAL torusx[MAXCOORD]; /* for unwrapping head in torus */ v[0] = get_edge_tailv(e_id); v[1] = get_edge_midv(e_id); v[2] = get_edge_headv(e_id); for ( i = 0 ; i < EDGE_CTRL ; i++ ) x[i] = get_coord(v[i]); if ( web.torus_flag ) { /* unwrap head */ (*sym_wrap)(x[2],torusx,get_edge_wrap(e_id)); x[2] = torusx; } /* calculate tangents at integration points and accumulate */ len = 0.0; for ( i = 0 ; i < EDGE_INTERP ; i++ ) { for ( j = 0 ; j < SDIM ; j ++ ) { etang[j] = 0.0; for ( k = 0 ; k < EDGE_CTRL ; k++ ) etang[j] += sdip[k][i]*x[k][j]; } len += gauss2wt[i]*sqrt(SDIM_dot(etang,etang)); /* calculate gravitational energy */ if ( web.gravflag && (density != 0.0) && !(get_eattr(e_id) & NONCONTENT) ) { for ( i = 0 ; i < EDGE_INTERP ; i++ ) { z = 0.0; for ( j = 0 ; j < EDGE_CTRL ; j++ ) z += gcombo[j][i]*x[j][1]; binary_tree_add(web.total_energy_addends,-0.5*density*z*z*etang[0]); } } } set_edge_length(e_id,len); /* following three lines for area normalization option */ add_vertex_star(v[0],len); add_vertex_star(v[1],len); add_vertex_star(v[2],len); binary_tree_add(web.total_energy_addends,len*get_edge_density(e_id)); if ( web.representation == STRING ) /* don't count triple junction as area */ web.total_area += len; } /********************************************************************** * * Adds contribution of edge to areas of neighboring facets, via * Green's theorem. x-y plane only. * */ void edge_area_q(fe_id) facetedge_id fe_id; { body_id b_id1,b_id2; facet_id f_id1,f_id2; REAL *x[EDGE_CTRL]; REAL area = 0.0; vertex_id v[EDGE_CTRL]; int i,j; edge_id e_id = get_fe_edge(fe_id); if ( get_eattr(e_id) & NONCONTENT ) return; /* be sure have original orientation of edge so torus midpoints have same wrap as tail */ if ( inverted(e_id) ) { invert(e_id); invert(fe_id); } f_id1 = get_fe_facet(fe_id); f_id2 = facet_inverse(get_fe_facet(fe_id)); if ( !valid_id(f_id1) && !valid_id(f_id2) ) return; /* no facets */ b_id1 = get_facet_body(f_id1); b_id2 = get_facet_body(f_id2); v[0] = get_edge_tailv(e_id); v[1] = get_edge_midv(e_id); v[2] = get_edge_headv(e_id); for ( i = 0 ; i < EDGE_CTRL ; i++ ) x[i] = get_coord(v[i]); if ( web.torus_flag ) { MAT2D(u,EDGE_CTRL,MAXCOORD); int wy; /* wraps */ WRAPTYPE wrap = get_edge_wrap(e_id); mat_mul_tr(x,web.inverse_periods,u,EDGE_CTRL,SDIM,SDIM); for ( i = 0 ; i < EDGE_CTRL ; i++ ) for ( j = 0 ; j < EDGE_CTRL ; j ++ ) area += scoeff[i][j]*u[i][1]*u[j][0]; wy = WRAPNUM((wrap>>TWRAPBITS) & WRAPMASK); area += wy*u[2][0]; area *= web.torusv; } else for ( i = 0 ; i < EDGE_CTRL ; i++ ) for ( j = 0 ; j < EDGE_CTRL ; j ++ ) area += scoeff[i][j]*x[i][1]*x[j][0]; /* add to body areas */ if ( valid_id(f_id1 ) ) add_facet_area(f_id1, area); if ( valid_id(b_id1 ) ) add_body_volume(b_id1, area); if ( valid_id(b_id2 ) ) add_body_volume(b_id2, -area); } /***************************************************************** * * Function: string_grad_q() * * Purpose: Calculate area gradients at vertices. */ void string_grad_q() { body_id bi_id; /* identifier for body i */ body_id bj_id; /* identifier for body j */ facetedge_id fe_id; REAL *x[EDGE_CTRL]; int i,j,k; vertex_id v[EDGE_CTRL]; volgrad *vgptr; MAT2D(u,EDGE_CTRL,MAXCOORD); /* affine coordinates of vertices */ MAT2D(g,EDGE_CTRL,MAXCOORD); MAT2D(grad,EDGE_CTRL,MAXCOORD); int wrap,wrapnum; FOR_ALL_FACETEDGES(fe_id) { edge_id e_id = get_fe_edge(fe_id); if ( get_eattr(e_id) & NONCONTENT ) continue; /* be sure have original orientation of edge so torus midpoints have same wrap as tail */ if ( inverted(e_id) ) { invert(e_id); invert(fe_id); } bi_id = get_facet_body(get_fe_facet(fe_id)); bj_id = get_facet_body(get_fe_facet(get_next_facet(fe_inverse(fe_id)))); if ( !(valid_id(bi_id) && (get_battr(bi_id) & (FIXEDVOL|PRESSURE))) && !(valid_id(bj_id) && (get_battr(bj_id) & (FIXEDVOL|PRESSURE))) ) continue; v[0] = get_fe_tailv(fe_id); v[1] = get_fe_midv(fe_id); v[2] = get_fe_headv(fe_id); for ( i = 0 ; i < EDGE_CTRL ; i++ ) x[i] = get_coord(v[i]); if ( web.torus_flag ) { /* get affine coordinates of vertices */ mat_mul_tr(x,web.inverse_periods,u,edge_ctrl,SDIM,SDIM); /* main integral over edge */ for ( i = 0 ; i < EDGE_CTRL ; i++ ) for ( j = 0 ; j < EDGE_CTRL ; j++ ) { REAL v = scoeff[j][i]; if ( v == 0.0 ) continue; g[i][0] += v*u[j][1]; g[j][1] += v*u[i][0]; } /* wrap correction */ wrap = (get_edge_wrap(e_id)>>TWRAPBITS) & WRAPMASK; wrapnum = WRAPNUM(wrap); g[2][0] += wrapnum; for ( k = 0 ; k < EDGE_CTRL ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) g[k][j] *= web.torusv; mat_mult(g,web.inverse_periods,grad,EDGE_CTRL,SDIM,SDIM); } else { for ( i = 0 ; i < EDGE_CTRL ; i++ ) { grad[i][0] = grad[i][1] = 0.0; for ( k = 0 ; k < EDGE_CTRL ; k++ ) { grad[i][0] += scoeff[k][i]*x[k][1]; grad[i][1] += scoeff[i][k]*x[k][0]; } } } if ( valid_id(bi_id) && (get_battr(bi_id) & (FIXEDVOL|PRESSURE)) ) { for ( i = 0 ; i < EDGE_CTRL ; i++ ) { vgptr = get_bv_new_vgrad(get_body_fixnum(bi_id),v[i]); vgptr->bb_id = bi_id; vgptr->grad[0] += grad[i][0]; vgptr->grad[1] += grad[i][1]; } } if ( valid_id(bj_id) && !equal_id(bj_id,bi_id) && (get_battr(bj_id) & (FIXEDVOL|PRESSURE)) ) { for ( i = 0 ; i < EDGE_CTRL ; i++ ) { vgptr = get_bv_new_vgrad(get_body_fixnum(bj_id),v[i]); vgptr->bb_id = bj_id; vgptr->grad[0] -= grad[i][0]; vgptr->grad[1] -= grad[i][1]; } } } } evolver-2.30c.dfsg/src/lexinit2.c0000644000175300017530000031762011410765113017130 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************* * * file: lexinit2.c * * Purpose: Routines used by initialize() in lexinit.c * Split because lexinit.c got too big for Mac * */ #include "include.h" #include "lex.h" #include "ytab.h" #define yylex() kb_yylex(NULL) /********************************************************************* * * function: torus_period_init() * * purpose: set up torus period arrays. */ void torus_period_init() { int i; if ( web.torus_period ) return; web.torus_period = dmatrix(0,SDIM-1,0,SDIM-1); web.inverse_periods = dmatrix(0,SDIM-1,0,SDIM-1); set_torus_periods_global(); set_inverse_periods_global(); for ( i = 0 ; i < SDIM ; i++ ) web.torus_period[i][i] = web.inverse_periods[i][i] = 1.0; web.torusv = 1.0; } /********************************************************************* * * function: torus_display_period_init() * * purpose: set up torus period arrays. */ void torus_display_period_init() { int i; if ( web.torus_display_period ) return; web.torus_display_period = dmatrix(0,SDIM-1,0,SDIM-1); web.inverse_display_periods = dmatrix(0,SDIM-1,0,SDIM-1); for ( i = 0 ; i < SDIM ; i++ ) web.torus_display_period[i][i] = web.inverse_display_periods[i][i] = 1.0; } /**************************************************************** * * Function: read_periods() * * Purpose: Reads torus periods. */ void read_periods() { int i,j; torus_period_init(); /* read in torus periods */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { int esize; esize = exparse(0,&torus_period_expr[i][j],USERCOPY); if ( esize <= 0 ) { sprintf(errmsg, "Bad torus_period[%d][%d] definition. Check space dimension\n", i+1,j+1); kb_error(3903,errmsg,DATAFILE_ERROR); return; } sprintf(torus_period_expr[i][j].name,"torus period [%d][%d]",i,j); } calc_periods(ADJUST_VOLUMES); } /**************************************************************** * * Function: read_display_periods() * * Purpose: Reads torus periods for display. */ void read_display_periods() { int i,j; torus_display_period_init(); /* read in display periods */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { int esize; esize = exparse(0,&torus_display_period_expr[i][j],USERCOPY); if ( esize <= 0 ) { sprintf(errmsg, "Bad display_period[%d][%d] definition. Check space dimension\n", i+1,j+1); kb_error(1670,errmsg,DATAFILE_ERROR); return; } sprintf(torus_display_period_expr[i][j].name,"display period [%d][%d]",i,j); } calc_periods(ADJUST_VOLUMES); } /**************************************************************** * * Function: read_parameter() * * Purpose: Reads one adjustable parameter. */ void read_parameter() { int n=0; struct global *p; int oldtok = tok; /* whether variable */ tok = yylex(); /* dispose of PARAMETER */ if ( tok == IDENT_ ) { p = globals(yylval.i); if ( !(p->flags & LEFTOVER) && !addload_flag ) { sprintf(errmsg,"Redefinition of identifier '%s'.\n",yytext); kb_error(1671,errmsg,DATAFILE_ERROR); } p->flags &= ~LEFTOVER; } else { if ( tok != NEWIDENT_ ) { kb_error(1672,"Need PARAMETER identifier.\n",WARNING); return; } if ( strlen(yytext) < 2 ) kb_error(1480,"Identifiers must be at least two characters long.\n", DATAFILE_ERROR); n = add_global(yytext); if ( n < 0 ) kb_error(1350,"Duplicate parameter name.\n",DATAFILE_ERROR); p = globals(n); } p->value.real = 0.0; /* default */ p->type = REAL_TYPE; p->flags |= ORDINARY_PARAM; p->attr.varstuff.delta = OPTPARAM_DELTA; p->attr.varstuff.pscale = 1.0; if ( oldtok == OPTIMIZING_PARAMETER_ ) { p->flags |= OPTIMIZING_PARAMETER; if ( optparamcount >= MAXOPTPARAM-1 ) kb_error(1674,"Too many optimizing parameters. Change MAXOPTPARAM in extern.h if you really need more.\n",DATAFILE_ERROR); else optparam[optparamcount++].pnum = n; } tok = yylex(); /* dispose of IDENT_ */ if ( (tok == '=') || (tok == ASSIGN_) ) /* have initial value */ { if ( read_const(&p->value.real) < 0 ) kb_error(1351,"Need constant expression for initial value.\n",DATAFILE_ERROR); else tok = yylex(); /* get back token exparse pushed back */ if ( tok == ';' ){ verb_flag = 0; tok = yylex(); /* permit ; */ } } else if ( tok == PARAMETER_FILE_ ) { tok = yylex(); if ( tok == QUOTATION_ ) { FILE *pfd = path_open(yytext,NOTDATAFILENAME); int k; REAL val; if ( pfd == NULL ) { sprintf(errmsg, "Cannot open parameter file %s.\n",yytext); kb_error(1356,errmsg, DATAFILE_ERROR); return; } p->value.file.value_file = mycalloc(strlen(yytext)+1,1); strcpy(p->value.file.value_file,yytext); p->value.file.values = (REAL *)mycalloc(1000,sizeof(REAL)); while ( fgets(msg,msgmax,pfd) ) { #ifdef LONGDOUBLE sscanf(msg,"%d %Lf",&k,&val); #else sscanf(msg,"%d %lf",&k,&val); #endif if ( k >= 1000 ) { kb_error(1677,"Too high element number in parameter file.\n",WARNING); break; } p->value.file.values[k] = val; } fclose(pfd); p->flags |= FILE_VALUES; tok = yylex(); } else kb_error(1678,"Parameter file name missing.\n",DATAFILE_ERROR); } else { unput_tok(); if ( read_const(&p->value.real) >= 0 ) kb_error(2672,"Missing '=' after parameter name.\n",WARNING); tok = yylex(); /* get back token exparse pushed back */ if ( tok == ';' ){ verb_flag = 0; tok = yylex(); /* permit ; */ } } for (;;) { if ( (tok == DELTA_) || ( stricmp(yytext,"delta") == 0) ) { tok = yylex(); /* dispose of IDENT_ */ if ( (tok == '=') || (tok == ASSIGN_) ) /* have initial value */ { if ( read_const(&p->attr.varstuff.delta) < 0 ) kb_error(1675,"Need constant expression for delta.\n",DATAFILE_ERROR); else tok = yylex(); /* get back token exparse pushed back */ } } else if ( tok == SCALE_ ) { tok = yylex(); /* dispose of IDENT_ */ if ( (tok == '=') || (tok == ASSIGN_) ) /* have initial value */ { if ( read_const(&p->attr.varstuff.pscale) < 0 ) kb_error(1676,"Need constant expression for scale.\n",DATAFILE_ERROR); else tok = yylex(); /* get back token exparse pushed back */ } } else break; } recovery_flag = 0; } /************************************************************************* * * Reads and parses information for one free boundary specification. * Current line starts with BOUNDARY keyword. * Returns boundary number, or -1 in case of early error. */ int read_boundary() { int bnum=0; /* boundary number */ int pcount; /* number of parameters */ int i,k; int esize; struct boundary *bdry; if ( V_BOUNDARY_ATTR == 0 ) { int one = 1; V_BOUNDARY_ATTR = add_attribute(VERTEX,"__v_boundary",INTEGER_TYPE,0,&one,0,NULL); E_BOUNDARY_ATTR = add_attribute(EDGE,"__e_boundary",INTEGER_TYPE,0,&one,0,NULL); F_BOUNDARY_ATTR = add_attribute(FACET,"__f_boundary",INTEGER_TYPE,0,&one,0,NULL); } tok = yylex(); /* eat BOUNDARY token */ if ( (tok != INTEGER_) && (tok != NEWIDENT_) && (tok != BOUNDARY_NAME_) ) { kb_error(1679,"Need boundary number or name.\n",DATAFILE_ERROR); return -1; } if ( tok == INTEGER_ ) { bnum = yylval.i; /* boundary number */ if ( bnum < 0 ) { sprintf(errmsg,"Bad boundary number: %d.\n",bnum); kb_error(1680,errmsg,DATAFILE_ERROR); tok = yylex(); return -1; } if ( bnum >= web.bdrymax ) { web.boundaries = (struct boundary *)kb_realloc((char*)web.boundaries, (bnum+10)*sizeof(struct boundary)); web.bdrymax = bnum+10; } if ( bnum > web.highbdry ) web.highbdry = bnum; if ( web.boundaries[bnum].attr & IN_USE ) { int moved_flag = 0; if ( web.boundaries[bnum].attr & NAMED_THING ) /* move named boundary */ do { for ( i = 0 ; i < web.bdrymax ; i++ ) if ( !(web.boundaries[i].attr & IN_USE) ) { web.boundaries[i] = web.boundaries[bnum]; web.boundaries[i].num = i; k = lookup_global(web.boundaries[i].name); globals(k)->value.bnum = i; if ( i > web.highbdry ) web.highbdry = i; moved_flag = 1; break; } if ( i >= web.bdrymax ) { web.boundaries = (struct boundary *)kb_realloc((char*)web.boundaries, (web.bdrymax+10)*sizeof(struct boundary)); web.bdrymax = web.bdrymax+10; } } while ( !moved_flag ); else if ( !(web.boundaries[bnum].attr & BDRY_FORWARD_DEF) && !addload_flag) { sprintf(errmsg,"Boundary number %d already defined.\n",bnum); kb_error(2120,errmsg,DATAFILE_ERROR); tok = yylex(); return -1; } } } else /* name */ { for ( i = 0 ; i < web.bdrymax ; i++ ) { if ( !(web.boundaries[i].attr & IN_USE) ) { bnum = i; break; } if ( stricmp(yytext,web.boundaries[i].name) == 0 ) { if ( (datafile_flag == IN_DATAFILE) && !addload_flag && !(web.boundaries[i].attr & BDRY_FORWARD_DEF)) { sprintf(errmsg,"Boundary name %s already used.\n",yytext); kb_error(2121,errmsg,DATAFILE_ERROR); tok = yylex(); return -1; } else { bnum = i; break; } } } if ( i > web.highbdry ) web.highbdry = i; if ( i >= web.bdrymax ) { web.boundaries = (struct boundary *)kb_realloc((char*)web.boundaries, (web.bdrymax+10)*sizeof(struct boundary)); web.bdrymax = web.bdrymax+10; bnum = i; } if ( tok == NEWIDENT_ ) { k = add_global(yytext); globals(k)->flags |= BOUNDARY_NAME; globals(k)->value.bnum = bnum; } } bdry = web.boundaries + bnum; memset((char*)bdry,0,sizeof(struct boundary)); bdry->num = bnum; bdry->attr |= IN_USE; if ( tok == INTEGER_ ) sprintf(web.boundaries[bnum].name,"%d",bnum); else { strncpy(bdry->name,yytext,BDRYNAMESIZE-1); bdry->attr |= NAMED_THING; } tok = yylex(); if ( tok == ';' ) { bdry->attr |= BDRY_FORWARD_DEF; verb_flag = 0; /* set by ';' */ tok = yylex(); return bnum; } if ( tok != PARAMETERS_ ) { sprintf(errmsg,"Expecting PARAMETERS keyword for boundary %s.\n",bdry->name); kb_error(1683,errmsg,DATAFILE_ERROR); pcount = bdry->pcount = 1; /* try to continue */ } else { tok = gettok(INTEGER_); bdry->pcount = pcount = yylval.i; } if ( pcount > web.maxparam ) web.maxparam = pcount; if ( (pcount < 0) || (tok != INTEGER_) ) { sprintf(errmsg,"Bad parameter count %d for boundary %s. Assuming 1.\n", pcount,bdry->name); kb_error(1684,errmsg,DATAFILE_ERROR); pcount = bdry->pcount = 1; /* try to continue */ } if ( pcount > MAXPARAM ) { sprintf(errmsg, "Parameter count for boundary %s exceeds %d. Assuming %d.\n", bdry->name,MAXPARAM,MAXPARAM); kb_error(1685,errmsg,DATAFILE_ERROR); bdry->pcount = pcount = MAXPARAM; /* try to continue */ } tok = yylex(); for (;;) switch (tok ) { case CONVEX_: web.convex_flag = 1; bdry->attr |= B_CONVEX; tok = yylex(); break; case CONTENT_RANK_: { REAL rank; if ( read_const(&rank) < 0 ) kb_error(3467,"Need integer value for content_rank.\n", DATAFILE_ERROR); bdry->content_rank = (int)rank; tok = yylex(); /* lookahead */ break; } case NONWALL_: bdry->attr |= NONWALL; tok = yylex(); break; case PARTNER_HITTING_: bdry->attr |= PARTNER_HITTING; tok = yylex(); break; default: goto read_coord_funcs; } read_coord_funcs: /* read and parse coordinate functions */ bdry->coordf[0] = (struct expnode *)mycalloc(SDIM+2*MAXCOORD, sizeof(struct expnode)); for ( i = 1 ; i < SDIM ; i++ ) bdry->coordf[i] = bdry->coordf[0] + i; for ( i = 0 ; i < SDIM ; i++ ) { if ( (tok != COORD_ ) || ( yylval.i != 1 + i )) { sprintf(errmsg, "Bad coordinate %d definition for boundary %s.\n",i+1,bdry->name); goto berr; } boundary_expr_flag = 1; esize = exparse(pcount,bdry->coordf[i],USERCOPY); boundary_expr_flag = 0; tok = yylex(); if ( esize <= 0 ) { sprintf(errmsg, "Bad coordinate %d definition for boundary %s.\n",i+1,bdry->name); goto berr; } sprintf(msg,"boundary %s component %d",bdry->name,i+1); strncpy(bdry->coordf[i]->name,msg,EXPNAMESIZE-1); } /* various integrands */ for ( i = 0 ; i < MAXCOORD ; i++ ) { bdry->envect[i] = bdry->coordf[0] + SDIM + i; bdry->convect[i] = bdry->coordf[0] + SDIM + MAXCOORD + i; } for (;;) switch ( tok ) { case ENERGY_: /* read and parse energy function */ tok = yylex(); for ( i = 0 ; i < MAXCOORD ; i++ ) { if ( (tolower(yytext[0]) != 'e') || (yytext[1] != '1' + i) ) break; esize = exparse(SDIM,bdry->envect[i],USERCOPY); tok = yylex(); if ( esize <= 0 ) { sprintf(errmsg, "Bad energy component %d definition for boundary %s.\n", i+1,bdry->name); kb_error(1367,errmsg,DATAFILE_ERROR); return bnum; } sprintf(msg,"boundary %s energy component %d",bdry->name,i+1); strncpy(bdry->envect[i]->name,msg,EXPNAMESIZE-1); } if ( i == 0 ) { sprintf(errmsg,"Missing energy components for boundary %s\n", bdry->name); kb_error(5932,errmsg,DATAFILE_ERROR); } bdry->compcount = i; bdry->attr |= CON_ENERGY; break; case CONTENT_: /* read and parse content vector potential */ tok = yylex(); bdry->attr |= CON_CONTENT; for ( i = 0 ; i < SDIM ; i++ ) { if ( (tolower(yytext[0]) != 'c') || (yytext[1] != '1' + i) ) break; esize = exparse(SDIM,bdry->convect[i],USERCOPY); tok = yylex(); if ( esize <= 0 ) { sprintf(errmsg, "Bad content component %d definition for boundary %s.\n", i+1,bdry->name); kb_error(1378,errmsg,DATAFILE_ERROR); return bnum; } sprintf(msg, "boundary %s content component %d", bdry->name,i+1); strncpy(bdry->convect[i]->name,msg,EXPNAMESIZE-1); } /* check consistency of number of components */ if ( bdry->compcount ) { if ( bdry->compcount != i ) kb_error(1381, "Inconsistent number of components in content integrand.\n", WARNING); } else { if ( (i != 1) && (i != SDIM) ) kb_error(1449,"Illegal number of components in content integrand.\n", DATAFILE_ERROR); bdry->compcount = i; } bdry->attr |= CON_CONTENT; break; default: return bnum; } /* end switch for integrands */ berr: kb_error(1686,errmsg,DATAFILE_ERROR); return bnum; } /* end read_boundary() */ /************************************************************************* * * Reads and parses information for one free constraint specification. * Current line starts with constraint keyword. * Returns constraint number. */ int read_constraint() { int cnum=0; /* constraint number */ int i,k; int esize; int more_attr; struct constraint *con; int done_flag; tok = yylex(); /* eat CONSTRAINT */ if ( (tok != INTEGER_) && (tok != NEWIDENT_) && ( tok != CONSTRAINT_NAME_) ) { kb_error(1687,"Need constraint number or name.\n",DATAFILE_ERROR); return -1; } if ( tok == INTEGER_ ) { cnum = yylval.i; /* constraint number */ if ( cnum < 0 ) { kb_error(1688,"Constraint number must be positive.\n",DATAFILE_ERROR); tok = yylex(); return -1; } while ( cnum >= web.maxcon ) { int newcons = web.maxcon ? 2*web.maxcon : 16; web.constraints = (struct constraint *) kb_realloc((char*)web.constraints, newcons*sizeof(struct constraint)); web.maxcon = newcons; } if ( cnum > web.highcon ) web.highcon = cnum; con = get_constraint(cnum); if ( con->attr & IN_USE ) { int moved_flag = 0; if ( con->attr & NAMED_THING ) do { /* Oops. Move the named constraint */ for ( i = 0 ; i < web.maxcon ; i++ ) { struct constraint *c = get_constraint(i); if ( !(c->attr & IN_USE) ) { struct global *g; *c = *con; k = lookup_global(c->name); g = globals(k); g->value.cnum = i; moved_flag = 1; if ( i > web.highcon ) web.highcon = i; break; } } if ( i >= web.maxcon ) { int newcons = 2*web.maxcon; web.constraints = (struct constraint *) kb_realloc((char*)web.constraints, newcons*sizeof(struct constraint)); web.maxcon = newcons; con = get_constraint(cnum); } } while ( !moved_flag ); else if ( datafile_flag & !(con->attr & CON_FORWARD_DEF) & !addload_flag ) { sprintf(errmsg,"Constraint number %d already defined.\n",cnum); kb_error(1690,errmsg,DATAFILE_ERROR); tok = yylex(); return -1; } else con->attr &= ~CON_FORWARD_DEF; } } else /* named constraint */ { for ( i = 0 ; i < web.maxcon ; i++ ) { con = get_constraint(i); if ( !(con->attr & IN_USE) ) { cnum = i; break; } if ( stricmp(yytext,get_constraint(i)->name) == 0 ) { if ( datafile_flag == IN_DATAFILE && !(con->attr & CON_FORWARD_DEF) && !addload_flag ) { sprintf(errmsg,"Constraint name %s already used.\n",yytext); kb_error(2122,errmsg,DATAFILE_ERROR); tok = yylex(); return -1; } else { cnum = i; con->attr &= ~CON_FORWARD_DEF; break; } } } if ( i >= web.maxcon ) { int newcons = web.maxcon ? 2*web.maxcon : 16; web.constraints = (struct constraint *) kb_realloc((char*)web.constraints, newcons*sizeof(struct constraint)); cnum = web.maxcon; web.maxcon = newcons; } if ( i > web.highcon ) web.highcon = i; if ( tok == NEWIDENT_ ) { k = add_global(yytext); globals(k)->flags |= CONSTRAINT_NAME; globals(k)->value.cnum = cnum; } } con = GETCONSTR(cnum); memset((char*)con,0,sizeof(struct constraint)); if ( tok == INTEGER_ ) sprintf(con->name,"%d",cnum); else { strncpy(con->name,yytext,CONNAMESIZE-1); con->attr |= NAMED_THING; } con->attr |= IN_USE; tok = yylex(); if ( tok == ';' ) { con->attr |= CON_FORWARD_DEF; verb_flag = 0; /* ';' sets verb_flag */ tok = yylex(); return cnum; } for ( more_attr = 1 ; more_attr ; ) switch ( tok ) { case CONTENT_RANK_: { REAL rank; if ( read_const(&rank) < 0 ) kb_error(3467,"Need integer value for content_rank.\n", DATAFILE_ERROR); con->content_rank = (int)rank; tok = yylex(); /* lookahead */ break; } case CONVEX_: web.convex_flag = 1; con->attr |= B_CONVEX; tok = yylex(); break; case NONWALL_: con->attr |= NONWALL; tok = yylex(); break; case NONNEGATIVE_: web.constr_flag = 1; con->attr |= NONNEGATIVE; one_sided_present = 1; tok = yylex(); break; case NONPOSITIVE_: web.constr_flag = 1; con->attr |= NONPOSITIVE; one_sided_present = 1; tok = yylex(); break; case GLOBAL_: web.constr_flag = 1; con->attr |= GLOBAL; web.con_global_map[web.con_global_count++] = (conmap_t)cnum; tok = yylex(); break; default: more_attr = 0; } if ( one_sided_present && (raw_velocity_attr < 0) ) { int dim = SDIM; raw_velocity_attr = add_attribute(VERTEX,RAW_VELOCITY_ATTR_NAME, REAL_TYPE,1,&dim,0,NULL); } /* read and parse defining function */ constraint_init(con); if ( tok != FUNCTION_ ) { sprintf(errmsg, "Expected function definition for constraint %s.\n",con->name); kb_error(1691,errmsg,DATAFILE_ERROR); return cnum; } esize = exparse(SDIM,con->formula,USERCOPY); tok = yylex(); if ( esize <= 0 ) { sprintf(errmsg,"Bad function definition for constraint %s.\n",con->name); kb_error(1692,errmsg,DATAFILE_ERROR); return cnum; } sprintf(msg,"constraint %s formula",con->name); strncpy(con->formula->name,msg,EXPNAMESIZE-1); /* various integrands */ done_flag = 0; while ( !done_flag ) switch ( tok ) { case ENERGY_: /* read and parse energy function */ tok = yylex(); for ( i = 0 ; i < MAXCONCOMP ; i++ ) { if ( (tolower(yytext[0]) != 'e') || (yytext[1] != '1' + i) ) break; esize = exparse(SDIM,con->envect[i],USERCOPY); tok = yylex(); if ( esize <= 0 ) { sprintf(errmsg, "Bad energy component %d definition for constraint %s.\n", i+1,con->name); kb_error(1693,errmsg,DATAFILE_ERROR); return cnum; } sprintf(msg,"constraint %s energy component %d",con->name,i+1); strncpy(con->envect[i]->name,msg,EXPNAMESIZE-1); } if ( i == 0 ) { sprintf(errmsg,"Missing energy components for constraint %s\n", con->name); kb_error(3228,errmsg,DATAFILE_ERROR); } con->compcount = i; con->attr |= CON_ENERGY; break; case CONTENT_: /* read and parse content vector potential */ tok = yylex(); con->attr |= CON_CONTENT; for ( i = 0 ; i < SDIM ; i++ ) { if ( (tolower(yytext[0]) != 'c') || (yytext[1] != '1' + i) ) break; esize = exparse(SDIM,con->convect[i],USERCOPY); tok = yylex(); if ( esize <= 0 ) { sprintf(errmsg, "Bad content component %d definition for constraint %s.\n", i+1,con->name); kb_error(1694,errmsg,DATAFILE_ERROR); return cnum; } sprintf(msg,"constraint %s content component %d",con->name,i+1); strncpy(con->convect[i]->name,msg,EXPNAMESIZE-1); } /* check consistency of number of components */ if ( con->compcount ) { if ( con->compcount != i ) kb_error(1695, "Inconsistent number of components in content integrand.\n", WARNING); } else { if ( (i != 1) && (i != SDIM) ) kb_error(1696,"Illegal number of components in content integrand.\n", DATAFILE_ERROR); con->compcount = i; } con->attr |= CON_CONTENT; break; default: done_flag = 1; break; } /* end switch for integrands */ if ( everything_quantities_flag ) convert_constraint_to_quantities(cnum); return cnum; } /************************************************************************* * * Reads and parses information for one surface energy specification. * Current line starts with SURFACE ENERGY keyword. * Optional GLOBAL keyword. OBSOLETE and now an error as of version 2.17. */ void read_surface_energy() { kb_error(1702, "Numbered surface energies are obsolete. Please use named quantity.\n", DATAFILE_ERROR); tok = yylex(); } /************************************************************************* * * function: read_method_instance() * * purpose: reads information for definition of one method instance. * return: instance number */ int read_method_instance() { char mname[40]; int mi = -1; tok = yylex(); /* name */ if ( tok != NEWIDENT_ && tok != METHOD_NAME_ ) { kb_error(1708,"Need instance name.\n",DATAFILE_ERROR); return -1; } if ( tok == METHOD_NAME_ ) { mi = yylval.i; /* redefinition or was forward */ } strncpy(mname,yytext,sizeof(mname)); tok = yylex(); if ( tok == ';' ) /* forward definition */ { verb_flag = 0; tok = yylex(); mi = new_method_instance(NULL,mname); METH_INSTANCE(mi)->flags |= Q_FORWARD_DEF; return mi; } if ( tok != METHOD_ ) { kb_error(1709,"Missing METHOD keyword.\n",DATAFILE_ERROR); return -1; } tok = yylex(); if ( tok != NEWIDENT_ && tok != MEAN_CURV_INT_ /* kludge */) { kb_error(1710,"Need method name.\n",DATAFILE_ERROR); return -1; } mi = new_method_instance(yytext,mname); tok = yylex(); if ( mi >= 0 ) read_instance_attr(mi); METH_INSTANCE(mi)->flags &= ~Q_FORWARD_DEF; return mi; } /************************************************************************* * * function: read_instance_attr() * * purpose: Read attributes of method instance, whether in method_instance * definition or part of quantity definition. */ void read_instance_attr(mnum) int mnum; /* number of method instance */ { int bflag; int spec_flag = 0; int esize,i; struct gen_quant_method *gm; int comps,etype; REAL val; int expr_count = 0; gm = basic_gen_methods + METH_INSTANCE(mnum)->gen_method; /* read further attributes of instance */ for (bflag=0;bflag==0;) switch ( tok ) { case GLOBAL_: if ( !(METH_INSTANCE(mnum)->flags & GLOBAL_INST) ) apply_method_num(NULLID,mnum); tok = yylex(); break; case MODULUS_: if ( read_const(&(METH_INSTANCE(mnum)->modulus)) <= 0 ) kb_error(1711,"Missing modulus.\n",WARNING); else tok = yylex(); break; case ELEMENT_MODULUS_: tok = yylex(); if ( tok != EXTRA_ATTRIBUTE_ ) kb_error(2123,"Need extra attribute name after ELEMENT_MODULUS.\n", DATAFILE_ERROR); METH_INSTANCE(mnum)->elmodulus = find_extra(idname,&etype); if ( etype != METH_INSTANCE(mnum)->type ) kb_error(2124,"Extra attribute is for wrong type of element.\n",RECOVERABLE); if ( EXTRAS(etype)[METH_INSTANCE(mnum)->elmodulus].type != REAL_TYPE ) kb_error(2125,"Element modulus attribute type must be REAL.\n", DATAFILE_ERROR); METH_INSTANCE(mnum)->flags |= ELEMENT_MODULUS_FLAG; tok = yylex(); break; case IGNORE_FIXED_: METH_INSTANCE(mnum)->flags |= IGNORE_FIXED; tok = yylex(); break; case IGNORE_CONSTRAINTS_: METH_INSTANCE(mnum)->flags |= IGNORE_CONSTR; sqcurve_ignore_constr = 1; /* kludge */ tok = yylex(); break; case K_VEC_ORDER_: if ( read_const(&val) <= 0 ) { kb_error(1712,"Missing k_vector_order value.\n",DATAFILE_ERROR); val = 1.; } else tok = yylex(); METH_INSTANCE(mnum)->vec_order = (int)val; break; case PARAMETER_1_: if ( read_const(&(METH_INSTANCE(mnum)->parameter_1)) <= 0 ) kb_error(1713,"Missing parameter_1 value.\n",WARNING); else tok = yylex(); METH_INSTANCE(mnum)->flags |= METH_PARAMETER_1; break; case SCALAR_INTEGRAND_: /* read and parse integrand function */ if ( !(gm->spec_flags & SPEC_SCALAR) ) { kb_error(1714,"No scalar integrand for this method.\n", DATAFILE_ERROR); /* read in anyway */ } spec_flag |= SPEC_SCALAR; expr_count = 1; METH_INSTANCE(mnum)->expr[0] = (struct expnode *)mycalloc(expr_count,sizeof(struct expnode)); if ( gm->spec_flags & SPEC_EXTRADIM ) { esize = exparse(2*SDIM,METH_INSTANCE(mnum)->expr[0],USERCOPY); spec_flag |= SPEC_EXTRADIM; } else esize = exparse(SDIM,METH_INSTANCE(mnum)->expr[0],USERCOPY); tok = yylex(); if ( esize <= 0 ) { sprintf(errmsg, "Bad integrand definition.\n"); kb_error(1715,errmsg,DATAFILE_ERROR); } sprintf(msg, "%s scalar integrand", METH_INSTANCE(mnum)->name); strncpy(METH_INSTANCE(mnum)->expr[0]->name,msg,EXPNAMESIZE-1); break; case VECTOR_INTEGRAND_: /* read and parse integrand vector */ if ( !(gm->spec_flags & (SPEC_VECTOR|SPEC_KVECTOR)) ) { kb_error(1716,"No vector integrand for this method.\n", DATAFILE_ERROR); comps = SDIM; /* read vector integrand anyway to get past it */ } if ( gm->spec_flags & SPEC_VECTOR ) { spec_flag |= SPEC_VECTOR; comps = SDIM; } else /* SPEC_KVECTOR */ { spec_flag |= SPEC_KVECTOR; comps = (SDIM-METH_INSTANCE(mnum)->vec_order)*SDIM; if ( comps <= 0 ) kb_error(1717,"Need k_vector_order.\n",DATAFILE_ERROR); } if ( comps > MAXMEXPR ) kb_error(1718,"Total components exceeds MAXMEXPR.\n",DATAFILE_ERROR); expr_count = comps; METH_INSTANCE(mnum)->expr[0]=(struct expnode *)mycalloc(comps, sizeof(struct expnode)); tok = yylex(); for ( i = 0 ; i < comps ; i++ ) { METH_INSTANCE(mnum)->expr[i] = METH_INSTANCE(mnum)->expr[0] + i; if ( (tolower(yytext[0]) != 'q') || (atoi(yytext+1) != 1 + i )) { sprintf(errmsg,"Expected component %d definition.\n",i+1); kb_error(1719,errmsg,DATAFILE_ERROR); break; } esize = exparse(SDIM,METH_INSTANCE(mnum)->expr[i],USERCOPY); tok = yylex(); if ( esize <= 0 ) { sprintf(errmsg, "Bad component %d definition.\n",i+1); kb_error(1720,errmsg,DATAFILE_ERROR); } sprintf(msg,"%s vector integrand component %d", METH_INSTANCE(mnum)->name,i+1); strncpy(METH_INSTANCE(mnum)->expr[i]->name,msg,EXPNAMESIZE-1); } if ( (tolower(yytext[0]) == 'q') && isdigit(yytext[1]) ) { sprintf(errmsg,"k_vector method needs only %d (complement of order) vectors.\n",(SDIM-METH_INSTANCE(mnum)->vec_order)); kb_error(2863,errmsg,RECOVERABLE); } break; case FORM_INTEGRAND_: /* 2-form */ /* read and parse integrand 2-form */ if ( !(gm->spec_flags & SPEC_2FORM) ) { kb_error(1721,"No 2-form integrand for this method.\n", DATAFILE_ERROR); /* read in anyway to get past it */ } spec_flag |= SPEC_2FORM; comps = (SDIM*(SDIM-1))/2; if ( comps > MAXMEXPR ) kb_error(1722,"Total components exceeds MAXMEXPR.\n", DATAFILE_ERROR); expr_count = comps; METH_INSTANCE(mnum)->expr[0]=(struct expnode *)mycalloc(comps, sizeof(struct expnode)); tok = yylex(); for ( i = 0 ; i < comps ; i++ ) { METH_INSTANCE(mnum)->expr[i] = METH_INSTANCE(mnum)->expr[0] + i; if ( (tolower(yytext[0]) != 'q') || (atoi(yytext+1) != 1 + i ) ) { sprintf(errmsg,"Expected component %d definition.\n",i+1); kb_error(1723,errmsg,DATAFILE_ERROR); } esize = exparse(SDIM,METH_INSTANCE(mnum)->expr[i],USERCOPY); tok = yylex(); if ( esize <= 0 ) { sprintf(errmsg, "Bad component %d definition.\n",i+1); kb_error(1724,errmsg,DATAFILE_ERROR); } sprintf(msg,"%s form integrand component %d", METH_INSTANCE(mnum)->name,i+1); strncpy(METH_INSTANCE(mnum)->expr[i]->name,msg,EXPNAMESIZE-1); } break; default: bflag = 1; break; } if ( (gm->spec_flags & SPEC_SCALAR) & ~spec_flag ) kb_error(1725,"Need scalar integrand for this method.\n",DATAFILE_ERROR); if ( (gm->spec_flags & SPEC_VECTOR) & ~spec_flag ) kb_error(1726,"Need vector integrand for this method.\n",DATAFILE_ERROR); if ( (gm->spec_flags & SPEC_KVECTOR) & ~spec_flag ) kb_error(2862, "Need k_vector_order and vector_integrand for this method.\n", DATAFILE_ERROR); /* Check use of boundary parameters */ for ( i = 0 ; i < expr_count ; i++ ) { struct method_instance *mi = METH_INSTANCE(mnum); if ( mi->expr[i] && (mi->expr[i]->flag & USING_PARAM_FLAG) ) { sprintf(errmsg, "%s: Cannot use boundary parameters in gradient or hessian.\n", METH_INSTANCE(mnum)->name); kb_error(3895,errmsg,WARNING); } } METH_INSTANCE(mnum)->flags &= ~Q_FORWARD_DEF; } /************************************************************************* * * Reads and parses information for one quantity specification, * in particular, facet integrands and fixed value. * Current line starts with QUANTITY keyword. * Optional GLOBAL keyword. */ int read_quantity() { tok = gettok(INTEGER_); if ( tok != INTEGER_ ) { return read_named_quantity(); } kb_error(1727, "Numbered quantities are obsolete. Please use named quantity.\n", DATAFILE_ERROR); tok = yylex(); return 0; } /*********************************************************************** * * function: read_named_quantity() * * purpose: parse named quantity definition. * return: quantity number. */ int read_named_quantity() { int n; int gnum; /* quantity number */ char qname[400]; int meth; struct method_instance *mi; char inst_name[400]; int namecount; /* number of uninstantiated methods */ int globality; int esize; int lagmulflag = 0; if ( (tok != NEWIDENT_) && (tok != IDENT_) && (tok != QUANTITY_NAME_) ) { kb_error(1733,"Need quantity name.\n",DATAFILE_ERROR); return -1; } strncpy(qname,yytext,sizeof(qname)); /* check not already defined and add to list */ tok = yylex(); if ( tok == ';' ) { gnum = new_quantity(qname,Q_INFO); GEN_QUANT(gnum)->flags |= Q_FORWARD_DEF; verb_flag = 0; /* set by ';' */ tok = yylex(); return gnum; } if ( tok == FIXED_ ) { gnum = new_quantity(qname,Q_FIXED); tok = yylex(); /* eat '=' */ if ( tok != '=' ) kb_error(1734,"Missing '='\n",DATAFILE_ERROR); if (read_const(&(GEN_QUANT(gnum)->target)) <= 0) kb_error(1735,"Missing quantity target value. \n",DATAFILE_ERROR); tok = yylex(); } else if ( tok == ENERGY_ ) { gnum = new_quantity(qname,Q_ENERGY); tok = yylex(); } else if ( tok == INFO_ONLY_ ) { gnum = new_quantity(qname,Q_INFO); tok = yylex(); } else if ( tok == CONSERVED_ ) { gnum = new_quantity(qname,Q_CONSERVED); tok = yylex(); } else { kb_error(1736, "Need type of quantity: energy, fixed, conserved, or info_only.\n", DATAFILE_ERROR); gnum = new_quantity(qname,Q_INFO); } namecount = 0; for (;;) /* further attributes */ switch ( tok ) { case GLOBAL_METHOD_ : case METHOD_ : if ( GEN_QUANT(gnum)->flags & Q_COMPOUND ) kb_error(1737, "Can't list separate methods with function of methods.\n", DATAFILE_ERROR); globality = tok; tok = yylex(); /* see if instance or method */ for ( n=LOW_INST ; n < meth_inst_count ; n++ ) { mi = METH_INSTANCE(n); if ( stricmp(mi->name,yytext) == 0 ) break; } if ( n >= meth_inst_count ) { /* need to instantiate method */ strncpy(inst_name,qname,sizeof(qname)); sprintf(inst_name+strlen(inst_name),"%d_",++namecount); strncat(inst_name,yytext,sizeof(qname)-strlen(inst_name)); meth = new_method_instance(yytext,inst_name); if ( meth >= 0 ) { METH_INSTANCE(meth)->flags |= IMPLICIT_INSTANCE; attach_method_num(gnum,meth); if ( globality == GLOBAL_METHOD_) apply_method(NULLID,inst_name); tok = yylex(); read_instance_attr(meth); } } else /* predefined instance */ { int mnum = attach_method(gnum,yytext); if ( (globality == GLOBAL_METHOD_) && (mnum>=0) && !(METH_INSTANCE(mnum)->flags & GLOBAL_INST) ) apply_method(NULLID,yytext); tok = yylex(); } break; case LAGRANGE_MULTIPLIER_: if ( read_const(&(GEN_QUANT(gnum)->pressure)) <= 0 ) kb_error(2126,"Missing lagrange_multiplier value.\n",DATAFILE_ERROR); else { GEN_QUANT(gnum)->flags |= Q_PRESSURE_SET; lagmulflag = 1; tok = yylex(); } break; case VOLCONST_: if (read_const(&(GEN_QUANT(gnum)->volconst)) <= 0) kb_error(1738,"Missing quantity volconst value. \n",DATAFILE_ERROR); else tok = yylex(); break; case TOLERANCE_: if ( read_const(&GEN_QUANT(gnum)->tolerance) <= 0 ) kb_error(2127,"Missing tolerance value.\n",DATAFILE_ERROR); else tok=yylex(); if ( GEN_QUANT(gnum)->tolerance <= 0.0 ) kb_error(2128,"Tolerance must be positive.\n",DATAFILE_ERROR); break; case MODULUS_: if (read_const(&(GEN_QUANT(gnum)->modulus)) <= 0) kb_error(1739,"Missing quantity modulus value. \n",DATAFILE_ERROR); else tok = yylex(); break; case FUNCTION_: /* compound function of instances */ cur_quant = gnum; if ( GEN_QUANT(cur_quant)->method_count > 0 && !addload_flag) kb_error(1740, "Can't list separate methods with function of methods.\n", DATAFILE_ERROR); reading_comp_quant_flag = 1; esize = exparse(0,&(GEN_QUANT(gnum)->expr),USERCOPY); reading_comp_quant_flag = 0; cur_quant = -1 ; tok = yylex(); if ( esize <= 0 ) { sprintf(errmsg, "Bad function definition.\n"); kb_error(1741,errmsg,DATAFILE_ERROR); } sprintf(msg,"%s formula",GEN_QUANT(gnum)->name); strncpy(GEN_QUANT(gnum)->expr.name,msg,EXPNAMESIZE-1); GEN_QUANT(gnum)->flags |= Q_COMPOUND; if ( gnum > compound_quant_list_head ) // avoid loop with addload { GEN_QUANT(gnum)->next_compound = compound_quant_list_head; compound_quant_list_head = gnum; } break; default: goto named_exit; /* done with quantity */ } named_exit: if ( !lagmulflag ) pressure_set_flag = 0; GEN_QUANT(gnum)->flags &= ~Q_FORWARD_DEF; return gnum; } /************************************************************* * * Function: add_outside() * * Purpose: Adds body outside all other bodies. Used in * dynamic pressure calculations. */ void add_outside() { return; /* temporary turn off */ #ifdef OUTSIDEBODY body_id b_id; /* both models */ b_id = new_body(); web.outside_body = b_id; set_attr(b_id,PRESSURE); /* since has ambient pressure */ if ( web.representation == STRING ) { edge_id e_id; facet_id f_id; f_id = new_facet(); /* outside facet */ set_facet_body(f_id,b_id); /* add to outside of every edge bordering just ONE cell */ FOR_ALL_EDGES(e_id) { facetedge_id new_fe; facetedge_id fe_id; fe_id = get_edge_fe(e_id); if ( !valid_id(fe_id) ) continue; if ( !equal_id(fe_id,get_next_facet(fe_id)) ) continue; new_fe = new_facetedge(inverse_id(f_id),e_id); set_next_facet(fe_id,new_fe); set_prev_facet(fe_id,new_fe); set_next_facet(new_fe,fe_id); set_prev_facet(new_fe,fe_id); set_facet_fe(f_id,new_fe); } } else /* SOAPFILM */ { facet_id f_id; /* add to outside of every facet bordering just ONE cell */ FOR_ALL_FACETS(f_id) { body_id b1_id,b2_id; b1_id = get_facet_body(f_id); b2_id = get_facet_body(inverse_id(f_id)); if ( valid_id(b1_id) == valid_id(b2_id) ) continue; if ( valid_id(b1_id) ) set_facet_body(inverse_id(f_id),b_id); if ( valid_id(b2_id) ) set_facet_body(f_id,b_id); } } #endif } /********************************************************************* * * Function: fix_volconst() * * Purpose: For torus, figure out constant volume adjustments, * figuring that given volumes are within 1/2 of a * torus volume of their true value */ void fix_volconst() { REAL adjust; body_id b_id; if ( web.pressure_flag ) return; /* get volume of piece of unit cell */ if ( SDIM == 2 ) { adjust = web.torusv /* /2 */; } else /* web.representation == SOAPFILM */ { adjust = web.torusv /* /6 */; } /* adjust volconsts */ FOR_ALL_BODIES(b_id) if ( get_battr(b_id) & FIXEDVOL ) { REAL vol = get_body_volume(b_id); REAL fix = get_body_fixvol(b_id); REAL vc = get_body_volconst(b_id); REAL calcvol = vol-vc; REAL newvc = fix - calcvol; newvc = adjust*floor(0.5+newvc/adjust); set_body_volconst(b_id,newvc); set_body_volume(b_id,calcvol+newvc,SETSTAMP); } } /******************************************************************* * * Function: fe_reorder() * * Purpose: Order facets properly around edge. * NOTE: Does have body agreement override geometry, so be careful * about body setup before entry. */ struct fsort { facetedge_id fe; REAL angle; }; static int fcompare(a,b) struct fsort *a,*b; { if ( a->angle < b->angle ) return -1; if ( a->angle > b->angle ) return 1; return 0; } void fe_reorder(e_id) edge_id e_id; { int fcount = 0; facetedge_id fe; #define FSORTMAX 100 struct fsort fe_list[FSORTMAX]; REAL side[MAXCOORD],norm_a[MAXCOORD],side_a[MAXCOORD]; REAL side_b[MAXCOORD],norm_aa[MAXCOORD],a_norm,aa_norm; REAL c,s,angle; int i,j,k; facetedge_id first_fe; int bodies_ok; int body_order = 0; /* set if order triple line by bodies */ /* see if we have 3 or more facets */ fe = first_fe = get_edge_fe(e_id); if ( valid_id(fe) ) do { fcount++; fe = get_next_facet(fe); } while ( valid_id(fe) && !equal_id(fe,first_fe) ); if ( fcount <= 2 ) return; if ( fcount > FSORTMAX ) { kb_error(1742,"More than 100 facets on an edge; not sorted.\n",WARNING); return; } /* common case of three facets, check for body determination */ if ( fcount == 3 ) { /* see if there is a unique order of facets */ facetedge_id fa = get_next_facet(fe); facetedge_id fb = get_prev_facet(fe); body_id ef = get_facet_body(get_fe_facet(fe)); body_id eb = get_facet_body(inverse_id(get_fe_facet(fe))); body_id af = get_facet_body(get_fe_facet(fa)); body_id ab = get_facet_body(inverse_id(get_fe_facet(fa))); body_id bf = get_facet_body(get_fe_facet(fb)); body_id bb = get_facet_body(inverse_id(get_fe_facet(fb))); int asis = (eb==af) && (ab==bf) && (bb==ef); int other = (eb==bf) && (bb==af) && (ab==ef); if ( asis && !other ) body_order = 1; else if ( other && !asis ) { /* swap */ set_next_facet(fe,fb); set_next_facet(fb,fa); set_next_facet(fa,fe); set_prev_facet(fe,fa); set_prev_facet(fa,fb); set_prev_facet(fb,fe); body_order = 1; } /* else fall through to geometric test */ } /* use first facet as reference facet */ /* to get basis for calculating angles */ get_edge_side(e_id,side); fe = get_edge_fe(e_id); fe_list[0].fe = fe; get_fe_side(get_next_edge(fe),side_a); cross_prod(side,side_a,norm_a); cross_prod(norm_a,side,norm_aa); a_norm = sqrt(SDIM_dot(norm_a,norm_a)); aa_norm = sqrt(SDIM_dot(norm_aa,norm_aa)); /* now get angles to rest of facets */ fe_list[0].angle = 0.0; for ( i = 1 ; i < fcount ; i++ ) { fe = get_next_facet(fe); fe_list[i].fe = fe; get_fe_side(get_next_edge(fe),side_b); s = SDIM_dot(side_b,norm_a)*aa_norm; c = SDIM_dot(side_b,norm_aa)*a_norm; angle = atan2(s,c); fe_list[i].angle = angle; } if ( body_order ) { /* warn if not agreeing with geometric order */ if ( ((fe_list[1].angle > 0) && (fe_list[2].angle > fe_list[0].angle) && (fe_list[2].angle < fe_list[1].angle)) || (( (fe_list[1].angle < 0) && ((fe_list[2].angle > fe_list[0].angle) || (fe_list[2].angle < fe_list[1].angle))) ) ) { sprintf(errmsg, "Edge %s facets body order disagrees with geometric order.\n", ELNAME(e_id)); kb_error(3857,errmsg,WARNING); } return; } /* sort by angle */ /* Bombed in IRIX 6 long double mode qsort((char *)&fe_list[1],fcount-1,sizeof(struct fsort),FCAST fcompare); */ for ( j = 0 ; j < fcount-1 ; j++ ) for ( k = j+1 ; k < fcount ; k++ ) if ( fe_list[k].angle < fe_list[j].angle ) { struct fsort ftemp; ftemp = fe_list[k]; fe_list[k] = fe_list[j]; fe_list[j] = ftemp; } /* check consistency for facets on bodies */ bodies_ok = 1; for ( i = 0 ; i < fcount ; i++ ) { facet_id f1,f2; int ii = (i == fcount-1) ? 0 : i+1; f1 = facet_inverse(get_fe_facet(fe_list[i].fe)); f2 = get_fe_facet(fe_list[ii].fe); if ( !equal_id(get_facet_body(f1),get_facet_body(f2)) ) { bodies_ok = 0; break; } } if ( !bodies_ok ) { REAL best_violation; int k,kk,bestk; /* try swaps of adjacent facets, and pick workable one with least violation of angle ordering */ best_violation = 1e30; bestk = -1; for ( k = 0 ; k < fcount ; k++ ) { struct fsort ftemp; kk = (k==fcount-1) ? 0 : k+1; ftemp = fe_list[k]; fe_list[k] = fe_list[kk]; fe_list[kk] = ftemp; bodies_ok = 1; for ( i = 0 ; i < fcount-1 ; i++ ) { facet_id f1,f2; f1 = facet_inverse(get_fe_facet(fe_list[i].fe)); f2 = get_fe_facet(fe_list[i+1].fe); if ( !equal_id(get_facet_body(f1),get_facet_body(f2)) ) { bodies_ok = 0; break; } } if ( bodies_ok ) { REAL violation = fabs(fe_list[k].angle-fe_list[kk].angle); if ( violation > M_PI ) violation = 2*M_PI - violation; /* shortest way */ if ( violation < best_violation ) { bestk = k; best_violation = violation; } } ftemp = fe_list[kk]; fe_list[kk] = fe_list[k]; fe_list[k] = ftemp; } if ( bestk >= 0 ) { struct fsort ftemp; kk = (bestk==fcount-1) ? 0 : bestk+1; ftemp = fe_list[bestk]; fe_list[bestk] = fe_list[kk]; fe_list[kk] = ftemp; } } /* relink in proper order */ for ( i = 0 ; i < fcount ; i++ ) { set_next_facet(fe_list[i].fe,fe_list[(i+1)%fcount].fe); set_prev_facet(fe_list[i].fe,fe_list[(i+fcount-1)%fcount].fe); } } /************************************************************** * * Function: gettok() * * Purpose: get next integer or real value, possibly * with '-' preceding. */ int gettok(kind) int kind; { int sign = 1; tok = yylex(); if ( tok == ',' ) tok = yylex(); /* skip separating comma */ if ( tok == '-' ) { sign = -1; tok = yylex(); } if ( tok == UMINUS_ ) { sign = -1; tok = yylex(); } if ( tok != kind ) { if ( !((tok == INTEGER_) && (kind == REAL_) ) && !((tok == INTEGER_AT_) && (kind == INTEGER_)) ) { if ( sign == -1 ) kb_error(2129,"Unexpected minus sign.\n",DATAFILE_ERROR); return tok; } /* caller should check for error, and if error leave tok as lookahead */ } yylval.i *= sign; yylval.r *= sign; tok = kind; return tok; } /******************************************************************* * * function: read_const() * * purpose: read constant expression from datafile * and return value. * * input: REAL *value - address for value * * return: < 0 error * 0 no expression * > 0 valid expression */ int read_const(value) REAL *value; { int retval; struct expnode node; /* for getting constant expression */ memset(&node,0,sizeof(struct expnode)); const_expr_flag = 1; if ( (retval = exparse(0,&node,NOUSERCOPY)) <= 0 ) { const_expr_flag = 0; return retval; } /* sprintf(node.name,"constant expression at line %d",line_no); */ *value = eval(&node,NULL,NULLID,NULL); if ( node.locals ) { if ( node.locals->list ) myfree((char*)node.locals->list ); myfree((char*)(node.locals)); } const_expr_flag = 0; return 1; } /********************************************************************* * * function: const_expr() * * purpose: get numerical value of string expresssion * * input: char *str - string with expression * REAL *value - address for value * * return: < 0 error * 0 no expression * > 0 valid expression */ int const_expr(str,value) char *str; REAL *value; { char *old_cmdptr; /* in case in middle of another parse */ int ret; /* return value */ if ( str == NULL ) return 0; old_cmdptr = cmdptr; cmdptr = str; yylex_init(); ret = read_const(value); cmdptr = old_cmdptr; return ret; } /********************************************************************* * * function: string_fixup() * * purpose: put minimal facet-edges on string net, so can * do edge changes. all facet-edges given the null facet. * */ void string_fixup() { edge_id e_id; facetedge_id fe; /* do all bare edges */ FOR_ALL_EDGES(e_id) { fe = get_edge_fe(e_id); if ( valid_id(fe) ) continue; fe = new_facetedge(NULLFACET,e_id); set_next_facet(fe,fe); set_prev_facet(fe,fe); set_next_edge(fe,inverse_id(fe)); set_prev_edge(fe,inverse_id(fe)); set_edge_fe(e_id,fe); } } /******************************************************* * * phase_initialize() * * Purpose: Read in phase boundary energies from file * * Input: Name of file with phase boundary energies * First line has number of phases * Succeeding lines have pair of phase numbers * and boundary energy. * */ void phase_initialize(phasename) char *phasename; { FILE *pfd; int i,j; /* which phases */ REAL value; char line[200]; int one = 1; phase_flag = 1; if ( web.representation == STRING ) F_PHASE_ATTR = add_attribute(FACET,"phase",INTEGER_TYPE,0,&one,0,NULL); else B_PHASE_ATTR = add_attribute(BODY,"phase",INTEGER_TYPE,0,&one,0,NULL); /* save name */ strncpy(phase_file_name,phasename,sizeof(phase_file_name)); pfd = path_open(phasename,NOTDATAFILENAME); if ( pfd == NULL ) { sprintf(errmsg, "Cannot open phase boundary energy file %s.\n",phasename); kb_error(1746,errmsg, DATAFILE_ERROR); return; } fscanf(pfd,"%d",&phasemax); phase_data = dmatrix(0,phasemax,0,phasemax); for ( i = 0 ; i <= phasemax ; i++ ) for ( j = 0 ; j <= phasemax ; j++ ) phase_data[i][j] = 1.0; while ( fgets(line,sizeof(line),pfd) ) { #ifdef LONGDOUBLE if ( sscanf(line,"%d %d %Lf",&i,&j,&value) == 3 ) #else if ( sscanf(line,"%d %d %lf",&i,&j,&value) == 3 ) #endif { if ( (i < 0) || (i > phasemax) || (j < 0) || (j > phasemax) ) { sprintf(errmsg,"Bad phase numbers: %d %d\n",i,j); kb_error(1747,errmsg,DATAFILE_ERROR); } phase_data[i][j] = phase_data[j][i] = value; } } fclose(pfd); } /**************************************************************** * * Function: read_transforms() * * Purpose: Reads additional view transforms. * Works both from datafile and commandline */ void read_transforms(count) int count; /* number, if known from command */ { int i,j,n; REAL value; MAT2D(temp_mat,MAXCOORD+1,MAXCOORD+1); lists_flag = 1; if ( count > 0 ) transform_count = count+1; else { /* find how many transforms */ if ( (tok = gettok(INTEGER_)) != INTEGER_ ) { kb_error(1748,"Missing number of transforms.\n",DATAFILE_ERROR); transform_count = 1; } else transform_count = yylval.i+1; /* including identity */ } if ( view_transforms ) { free_matrix3(view_transforms); view_transforms = NULL;} if ( transform_count <= 0 ) return; view_transforms = dmatrix3(transform_count,SDIM+1,SDIM+1); set_view_transforms_global(); allocate_transform_colors(transform_count); view_transform_det = (int*)mycalloc(transform_count,sizeof(int)); matcopy(view_transforms[0],identmat,SDIM+1,SDIM+1); transform_colors[0] = SAME_COLOR; view_transform_det[0] = 1; /* read in transform matrices, in homogeneous coords */ for ( n = 1 ; n < transform_count ; n++ ) { tok = yylex(); if ( tok == COLOR_ ) { if ( (tok = gettok(INTEGER_)) != INTEGER_ ) kb_error(1749,"Missing transform color.\n",DATAFILE_ERROR); transform_colors[n] = yylval.i; transform_colors_flag = 1; } else if ( tok == SWAP_COLORS_ ) { transform_colors[n] = SWAP_COLORS; transform_colors_flag = 1; } else { transform_colors[n] = SAME_COLOR; unput_tok(); } for ( i = 0 ; i <= SDIM ; i++ ) { for ( j = 0 ; j <= SDIM ; j++ ) { if ( read_const(&value) <= 0 ) { kb_error(1750,"Not enough values for transform matrix.\n", DATAFILE_ERROR); return; } view_transforms[n][i][j] = value; } matcopy(temp_mat,view_transforms[n],SDIM+1,SDIM+1); if ( determinant(temp_mat,SDIM+1) > 0.0 ) view_transform_det[n] = 1; else view_transform_det[n] = -1; } } transforms_flag = 1; /* default is to show */ lists_flag = 0; if ( n == transform_count ) tok = yylex(); /* lookahead */ } /**************************************************************** * * Function: convert_constraint_to_quantities() * * Purpose: Create method for constraint energy. */ void convert_constraint_to_quantities(i) int i; /* number of constraint */ { int j; char qname[100]; char inst_name[100]; int gq; int meth; edge_id e_id; struct constraint *con = get_constraint(i); if ( !(con->attr & CON_ENERGY) ) return; if ( con->attr & NAMED_THING ) { sprintf(qname,"constraint_%s_energy",con->name); sprintf(inst_name,"constraint_%s_energy_inst",con->name); } else { sprintf(qname,"constraint_%d_energy",i); sprintf(inst_name,"constraint_%d_energy_inst",i); } gq = new_quantity(qname,Q_ENERGY); GEN_QUANT(gq)->flags |= DEFAULT_QUANTITY; if ( web.representation == STRING ) meth = new_method_instance("vertex_scalar_integral",inst_name); else meth = new_method_instance("edge_vector_integral",inst_name); METH_INSTANCE(meth)->flags |= IMPLICIT_INSTANCE; attach_method_num(gq,meth); if ( con->attr & GLOBAL ) apply_method_num(NULLID,meth); else { vertex_id v_id; if ( web.representation == STRING ) { FOR_ALL_VERTICES(v_id) if ( v_on_constraint(v_id,i) ) apply_quantity(v_id,gq); } else { FOR_ALL_EDGES(e_id) if ( e_on_constraint(e_id,i) ) apply_quantity(e_id,gq); } } for ( j = 0 ; j < SDIM ; j++ ) METH_INSTANCE(meth)->expr[j] = con->envect[j]; con->attr |= USURPED_BY_QUANTITY; /* prevent dual expr deallocation */ con->energy_method = meth; } /**************************************************************** * * Function: read_transform_generators() * * Purpose: Reads view transform generators. * Works both from datafile and commandline */ void read_transform_generators(count) int count; /* number, if known from command */ { int i,j,n; lists_flag = 1; if ( count > 0 ) transform_count = count; else { /* find how many transforms */ if ( (tok = gettok(INTEGER_)) != INTEGER_ ) { kb_error(1751,"Missing number of view_transform_generators.\n",DATAFILE_ERROR); transform_gen_count = 1; } else transform_gen_count = yylval.i; } if ( transform_gen_count < 0 ) { kb_error(3900,"Missing number of view_transform_generators.\n",DATAFILE_ERROR); transform_gen_count = 0; } if ( transform_gen_swap ) myfree((char*)transform_gen_swap); transform_gen_swap = (int*)mycalloc(transform_gen_count+SDIM,sizeof(int)); view_transform_gens = dmatrix3(web.torus_flag?(transform_gen_count+SDIM): transform_gen_count,SDIM+1,SDIM+1); view_transform_gens_expr = (expnodearray *)mycalloc((MAXCOORD+1)*(MAXCOORD+1)*transform_gen_count, sizeof(struct expnode)); /* read in transform matrices, in homogeneous coords */ for ( n = 0 ; n < transform_gen_count ; n++ ) { tok = yylex(); if ( tok == SWAP_COLORS_ ) { transform_gen_swap[n] = 1; } else unput_tok(); for ( i = 0 ; i <= SDIM ; i++ ) { for ( j = 0 ; j <= SDIM ; j++ ) { int esize = exparse(0,&view_transform_gens_expr[n][i][j],USERCOPY); if ( esize < 0 ) { kb_error(1752,"Not enough entries for transform generator matrix.\n", DATAFILE_ERROR); return; } } } } if ( n == transform_gen_count ) tok = yylex(); /* lookahead */ if ( web.torus_flag ) transform_gen_count += SDIM; transforms_flag = 1; /* default is to show */ lists_flag = 0; } /*************************************************************************** * * function: convert_to_quantities() * * purpose: convert numbered quantities, surface energies, and * constraint integrals to named quantities. * Used in everything_quantities mode. */ void convert_to_quantities() { int gq; int meth; char inst_name[40]; char qname[100]; int i,j; body_id b_id; edge_id e_id; int q; char formula[1000]; char *gformula; if ( everything_quantities_flag ) return; if ( web.wulff_flag && (web.dimension != 2) ) kb_error(1754,"Can't do wulff energy method for strings yet.\n",RECOVERABLE); #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) { mpi_convert_to_quantities(); outstring("Converting to all named quantities..."); } #else outstring("Converting to all named quantities..."); #endif /* set up default length or area quantities */ if ( web.representation == STRING ) { q = new_quantity("default_length",Q_ENERGY); default_area_quant_num = q; } else q = new_quantity("default_length",Q_INFO); GEN_QUANT(q)->flags |= DEFAULT_QUANTITY; if ( length_method_name[0] ) { meth = find_method_instance(length_method_name); /* user instance? */ if ( meth < 0 ) /* try pre-defined method */ meth = new_method_instance(length_method_name,"default_length_inst"); } else if ( web.dimension >= 3 ) { /* can't do edges in simplex model */ meth = -1; } else if ( klein_metric_flag ) { meth = new_method_instance("klein_length","default_length_inst"); } else if ( web.conformal_flag ) { int oldflag = datafile_flag; meth = new_method_instance("edge_scalar_integral","default_length_inst"); strcpy(formula,"density*sqrt("); gformula = print_express(&web.metric[0][0],'X'); if ( strlen(gformula) > sizeof(formula)-20 ) kb_error(1755,"Conformal metric formula too long.\n",RECOVERABLE); strcat(formula,gformula); strcat(formula,")"); METH_INSTANCE(meth)->expr[0] = (struct expnode *)mycalloc(1,sizeof(struct expnode)); cmdptr = formula; datafile_flag = 1; exparse(SDIM,METH_INSTANCE(meth)->expr[0],USERCOPY); sprintf(METH_INSTANCE(meth)->expr[0]->name,"conformal metric"); cmdptr = NULL; datafile_flag = oldflag; } else if ( web.metric_flag ) { int oldflag = datafile_flag; meth = new_method_instance("edge_general_integral","default_length_inst"); strcpy(formula,"density*sqrt("); for ( i = 1 ; i <= SDIM ; i++ ) for ( j = 1 ; j <= i ; j++ ) { if ( i > 1 ) strcat(formula,"+"); if ( i==j ) sprintf(formula+strlen(formula),"X%d^2*(",i+SDIM); else sprintf(formula+strlen(formula),"2*X%d*X%d*(",i+SDIM,j+SDIM); gformula = print_express(&web.metric[i-1][j-1],'X'); if ( strlen(gformula) > sizeof(formula)-20 ) kb_error(1756,"Metric formula too long.\n",RECOVERABLE); strcat(formula,gformula); strcat(formula,")"); } strcat(formula,")"); METH_INSTANCE(meth)->expr[0] = (struct expnode *)mycalloc(1,sizeof(struct expnode)); cmdptr = formula; datafile_flag = 1; exparse(2*SDIM,METH_INSTANCE(meth)->expr[0],USERCOPY); sprintf(METH_INSTANCE(meth)->expr[0]->name,"conformal metric"); cmdptr = NULL; datafile_flag = oldflag; } else if ( web.representation == STRING ) meth = new_method_instance("density_edge_length","default_length_inst"); else meth = new_method_instance("edge_length","default_length_inst"); length_method_number = meth; if ( meth >= 0 ) { METH_INSTANCE(meth)->flags |= IMPLICIT_INSTANCE; METH_INSTANCE(meth)->flags |= DEFAULT_INSTANCE; if ( web.representation == STRING ) METH_INSTANCE(meth)->flags |= USE_DENSITY; attach_method_num(q,meth); apply_method_num(NULLID,meth); /* global method */ } if ( web.representation != STRING ) /* soapfilm or simplex */ { q = new_quantity("default_area",Q_ENERGY); default_area_quant_num = q; GEN_QUANT(q)->flags |= DEFAULT_QUANTITY; if ( area_method_name[0] ) { meth = find_method_instance(area_method_name); /* user instance? */ if ( meth < 0 ) /* try pre-defined method */ meth = new_method_instance(area_method_name,"default_area_inst"); } else if ( web.wulff_flag ) { meth = new_method_instance("wulff_energy","default_area_inst"); } else if ( klein_metric_flag ) { meth = new_method_instance("klein_area","default_area_inst"); } else if ( web.conformal_flag ) { int oldflag = datafile_flag; meth = new_method_instance("facet_scalar_integral","default_area_inst"); strcpy(formula,"density*("); gformula = print_express(&web.metric[0][0],'X'); if ( strlen(gformula) > sizeof(formula)-20 ) kb_error(1757,"Conformal metric formula too long.\n",RECOVERABLE); strcat(formula,gformula); strcat(formula,")"); METH_INSTANCE(meth)->expr[0] = (struct expnode *)mycalloc(1,sizeof(struct expnode)); cmdptr = formula; datafile_flag = 1; exparse(SDIM,METH_INSTANCE(meth)->expr[0],USERCOPY); sprintf(METH_INSTANCE(meth)->expr[0]->name,"conformal metric"); cmdptr = NULL; datafile_flag = oldflag; } else if ( web.metric_flag ) { meth = new_method_instance("metric_facet_area","default_area_inst"); } else meth = new_method_instance("density_facet_area","default_area_inst"); METH_INSTANCE(meth)->flags |= IMPLICIT_INSTANCE | DEFAULT_INSTANCE; METH_INSTANCE(meth)->flags |= USE_DENSITY; attach_method_num(q,meth); apply_method_num(NULLID,meth); /* global method */ } /* constraint energy integrals */ for ( i = 0 ; i < web.maxcon ; i++ ) convert_constraint_to_quantities(i); /* boundary energy integrals */ for ( i = 0 ; i < web.bdrymax ; i++ ) { struct boundary *bdry = web.boundaries + i; if ( !(bdry->attr & CON_ENERGY) ) continue; if ( bdry->attr & NAMED_THING ) { sprintf(qname,"boundary_%s_energy",bdry->name); sprintf(inst_name,"boundary_%s_energy_inst",bdry->name); } else { sprintf(qname,"boundary_%d_energy",i); sprintf(inst_name,"boundary_%d_energy_inst",i); } gq = new_quantity(qname,Q_ENERGY); GEN_QUANT(gq)->flags |= DEFAULT_QUANTITY; if ( web.representation == STRING ) meth = new_method_instance("vertex_scalar_integral",inst_name); else meth = new_method_instance("edge_vector_integral",inst_name); METH_INSTANCE(meth)->flags |= IMPLICIT_INSTANCE; attach_method_num(gq,meth); { vertex_id v_id; if ( web.representation == STRING ) { FOR_ALL_VERTICES(v_id) if ( bdry == get_boundary(v_id) ) apply_quantity(v_id,gq); } else { FOR_ALL_EDGES(e_id) if ( bdry == get_edge_boundary(e_id) ) apply_quantity(e_id,gq); } } for ( j = 0 ; j < SDIM ; j++ ) METH_INSTANCE(meth)->expr[j] = bdry->envect[j]; bdry->attr |= USURPED_BY_QUANTITY; /* prevent dual expr deallocation */ bdry->energy_method = meth; } /* bodies */ FOR_ALL_BODIES(b_id) convert_new_body_to_quantity(b_id); /* set up individual stuff */ convert_bodies_to_quantities(); /* add quantities to edges and facets */ /* gravity */ /* if ( web.gravflag ) do it always anyway! */ { sprintf(qname,"gravity_quant"); sprintf(inst_name,"gravity_inst"); gq = new_quantity(qname,Q_ENERGY); GEN_QUANT(gq)->flags |= DEFAULT_QUANTITY; GEN_QUANT(gq)->modulus = web.grav_const; gravity_quantity_num = gq; if ( web.representation == STRING ) meth = new_method_instance("string_gravity",inst_name); else meth = new_method_instance("gravity_method",inst_name); METH_INSTANCE(meth)->flags |= IMPLICIT_INSTANCE; attach_method_num(gq,meth); apply_method_num(NULLID,meth); } if ( mean_curv_int_flag ) mean_curv_int_quantity_num = add_standard_quantity("mean_curvature_integral",1.0); if ( sqgauss_flag ) add_standard_quantity("sq_gauss_curvature",1.0); /* square mean curvature */ if ( square_curvature_flag ) { sprintf(qname,"sq_mean_curvature_quant"); sprintf(inst_name,"sq_mean_curvature_inst"); gq = new_quantity(qname,Q_ENERGY); GEN_QUANT(gq)->flags |= DEFAULT_QUANTITY; sq_mean_curv_quantity_num = gq; if ( web.representation == STRING ) meth = new_method_instance("sqcurve_string",inst_name); else meth = new_method_instance("sq_mean_curvature",inst_name); METH_INSTANCE(meth)->flags |= IMPLICIT_INSTANCE; attach_method_num(gq,meth); apply_method_num(NULLID,meth); GEN_QUANT(gq)->modulus = globals(square_curvature_param)->value.real; } if ( web.convex_flag ) { sprintf(qname,"gap_quant"); sprintf(inst_name,"gap_energy"); gq = new_quantity(qname,Q_ENERGY); GEN_QUANT(gq)->flags |= DEFAULT_QUANTITY; gap_quantity_num = gq; meth = new_method_instance("gap_energy",inst_name); METH_INSTANCE(meth)->flags |= IMPLICIT_INSTANCE; attach_method_num(gq,meth); apply_method_num(NULLID,meth); } quantities_only_flag = everything_quantities_flag = 1; outstring("Done.\n"); } /**************************************************************************** * * function: convert_body_to_quantity() * * purpose: Convert a body volume to a named quantity. Done for * individual bodies so can be called when new body created, * i.e. rebody(). */ void convert_body_to_quantity(b_id) body_id b_id; { int gq; int meth1; char inst_name1[40]; char qname[40]; int i,j,k; edge_id e_id; facet_id f_id; facetedge_id fe; char formula[100]; struct gen_quant *g = NULL; i = ordinal(b_id) + 1; sprintf(qname,"body_%d_vol",i); sprintf(inst_name1,"body_%d_vol_meth",i); if ( get_battr(b_id) & FIXEDVOL ) { gq = find_quantity(qname); if ( gq < 0 ) gq = new_quantity(qname,Q_FIXED); g = GEN_QUANT(gq); g->target = get_body_fixvol(b_id); } else if ( get_battr(b_id) & PRESSURE ) { REAL p; gq = find_quantity(qname); if ( gq < 0 ) gq = new_quantity(qname,Q_ENERGY); g = GEN_QUANT(gq); p = get_body_pressure(b_id); g->modulus = -p; if ( p == 0.0 ) { g->flags &= ~Q_ENERGY; g->flags |= Q_INFO; /* to force volume calc even for zero pressure */ g->modulus = 1.0; } } else { gq = find_quantity(qname); if ( gq < 0 ) gq = new_quantity(qname,Q_INFO); g = GEN_QUANT(gq); } g->flags |= DEFAULT_QUANTITY; g->b_id = b_id; set_body_volquant(b_id,gq); g->volconst = get_body_volconst(b_id); g->pressure = get_body_pressure(b_id); g->value = get_body_volume(b_id); g->oldvalue = get_body_volume(b_id); if ( web.representation == STRING ) { meth1 = find_method_instance(inst_name1); if ( meth1 < 0 ) { /* have to create */ if ( area_method_name[0] ) { meth1 = find_method_instance(area_method_name); /* user instance? */ if ( meth1 < 0 ) /* try pre-defined method */ meth1 = new_method_instance(area_method_name,inst_name1); } else if ( web.symmetric_content ) { meth1 = new_method_instance("edge_vector_integral",inst_name1); METH_INSTANCE(meth1)->expr[0] = (struct expnode *)mycalloc(SDIM,sizeof(struct expnode)); for ( j = 0 ; j < 2 ; j++ ) { if ( j==0 )sprintf(formula,"-y/2"); if ( j==1 )sprintf(formula,"x/2"); cmdptr = formula; METH_INSTANCE(meth1)->expr[j] = METH_INSTANCE(meth1)->expr[0] + j; datafile_flag = 1; exparse(SDIM,METH_INSTANCE(meth1)->expr[j],USERCOPY); sprintf(METH_INSTANCE(meth1)->expr[j]->name, "symmetric content component %d",j+1); datafile_flag = 0; cmdptr = NULL; } } else { meth1 = new_method_instance("edge_area",inst_name1); } METH_INSTANCE(meth1)->flags |= DEFAULT_INSTANCE | BODY_INSTANCE; METH_INSTANCE(meth1)->modulus = 1.0; attach_method_num(gq,meth1); set_body_volmeth(b_id,meth1); } FOR_ALL_EDGES(e_id) { fe = get_edge_fe(e_id); if ( !valid_id(fe) ) continue; if ( get_eattr(e_id) & NONCONTENT ) continue; f_id = get_fe_facet(fe); if ( valid_id(f_id)) { if ( equal_id(b_id,get_facet_body(f_id)) ) apply_method_num(e_id,meth1); else if ( equal_id(b_id,get_facet_body(inverse_id(f_id))) ) apply_method_num(inverse_id(e_id),meth1); } fe = get_next_facet(fe); f_id = get_fe_facet(fe); if ( valid_id(f_id)) { /* check both in case of torus wrapping */ if ( equal_id(b_id,get_facet_body(f_id)) ) apply_method_num(e_id,meth1); if ( equal_id(b_id,get_facet_body(inverse_id(f_id))) ) apply_method_num(inverse_id(e_id),meth1); } } } else /* SOAPFILM */ { meth1 = find_method_instance(inst_name1); if ( meth1 < 0 ) { /* have to create */ if ( volume_method_name[0] ) { meth1 = find_method_instance(volume_method_name); /* user instance? */ if ( meth1 < 0 ) /* try pre-defined method */ meth1 = new_method_instance(volume_method_name,inst_name1); } else if ( web.symmetric_content ) { meth1 = new_method_instance("facet_vector_integral",inst_name1); METH_INSTANCE(meth1)->expr[0] = (struct expnode *)mycalloc(SDIM,sizeof(struct expnode)); for ( j = 0 ; j < SDIM ; j++ ) { sprintf(formula,"x%d/%d",j+1,SDIM); cmdptr = formula; METH_INSTANCE(meth1)->expr[j] = METH_INSTANCE(meth1)->expr[0] + j; datafile_flag = 1; exparse(SDIM,METH_INSTANCE(meth1)->expr[j],USERCOPY); sprintf(METH_INSTANCE(meth1)->expr[j]->name, "symmetric content component %d",j+1); datafile_flag = 0; cmdptr = NULL; } } else { meth1 = new_method_instance("facet_volume",inst_name1); } METH_INSTANCE(meth1)->flags |= DEFAULT_INSTANCE | BODY_INSTANCE; METH_INSTANCE(meth1)->modulus = 1.0; attach_method_num(gq,meth1); set_body_volmeth(b_id,meth1); } FOR_ALL_FACETS(f_id) { /* check both in case of torus wrapping */ if ( get_fattr(f_id) & NONCONTENT ) continue; if ( equal_id(b_id,get_facet_body(f_id)) ) apply_method_num(f_id,meth1); if ( equal_id(b_id,get_facet_body(inverse_id(f_id))) ) apply_method_num(inverse_id(f_id),meth1); } } /* constraint content integrals */ for ( j = 0 ; j < web.maxcon ; j++ ) { struct constraint *con = get_constraint(j); vertex_id v_id; if ( !(con->attr & CON_CONTENT) ) continue; if ( con->attr & NAMED_THING ) sprintf(inst_name1,"body_%d_%s_meth",i,con->name); else sprintf(inst_name1,"body_%d_con_%d_meth",i,j); meth1 = find_method_instance(inst_name1); if ( meth1 < 0 ) { /* have to create */ if ( web.representation == STRING ) { meth1 = new_method_instance("vertex_scalar_integral",inst_name1); } else { meth1 = new_method_instance("edge_vector_integral",inst_name1); } METH_INSTANCE(meth1)->flags |= DEFAULT_INSTANCE | BODY_INSTANCE; METH_INSTANCE(meth1)->connum = j; attach_method_num(gq,meth1); con = get_constraint(j); /* may have moved */ if ( web.representation == STRING ) METH_INSTANCE(meth1)->expr[0] = con->convect[0]; else for ( k = 0 ; k < SDIM ; k++ ) METH_INSTANCE(meth1)->expr[k] = con->convect[k]; } if ( web.representation == STRING ) { FOR_ALL_VERTICES(v_id) if ( v_on_constraint(v_id,j) ) { edge_id first_e = e_id = get_vertex_edge(v_id); int max_rank,min_rank,jj; conmap_t *map = get_v_constraint_map(v_id); min_rank = MAXINT; max_rank = 0; for ( jj = 1 ; jj <= (int)map[0] ; jj++ ) { struct constraint *c; if ( !(map[j] & CON_HIT_BIT) ) continue; c = get_constraint(map[jj]); if ( c->content_rank < min_rank ) min_rank = c->content_rank; if ( c->content_rank > max_rank ) max_rank = c->content_rank; } if ( valid_id(e_id) ) do { facetedge_id first_fe = fe = get_edge_fe(e_id); if ( valid_id(fe) && !(get_eattr(e_id) & NONCONTENT) ) do { f_id = get_fe_facet(fe); if ( equal_id(b_id,get_facet_body(f_id)) && ( (!inverted(f_id) && con->content_rank >= max_rank) || (inverted(f_id) && con->content_rank <= min_rank)) ) apply_method_num(inverse_id(v_id),meth1); if ( equal_id(b_id,get_facet_body(inverse_id(f_id))) && ( (!inverted(f_id) && con->content_rank >= max_rank) || (inverted(f_id) && con->content_rank <= min_rank)) ) apply_method(v_id,inst_name1); fe = get_next_facet(fe); } while ( !equal_id(fe,first_fe) ); e_id = get_next_tail_edge(e_id); } while ( !equal_id(first_e,e_id)); } } else /* SOAPFILM */ { FOR_ALL_EDGES(e_id) if ( e_on_constraint(e_id,j) ) { facetedge_id first_fe = fe = get_edge_fe(e_id); if ( valid_id ( fe ) ) { conmap_t *conmap = get_e_constraint_map(e_id); int min_rank = MAXINT; int max_rank = 0; int jj; for ( jj = 1 ; jj <= (int)conmap[0] ; jj++ ) { struct constraint *c = get_constraint(conmap[jj]); if ( c->content_rank < min_rank ) min_rank = c->content_rank; if ( c->content_rank > max_rank ) max_rank = c->content_rank; } do { f_id = get_fe_facet(fe); if ( !(get_fattr(f_id) & NONCONTENT) ) { if ( equal_id(b_id,get_facet_body(f_id)) && (con->content_rank >= max_rank) ) apply_method_num(e_id,meth1); if ( equal_id(b_id,get_facet_body(inverse_id(f_id))) && (con->content_rank <= min_rank) ) apply_method_num(inverse_id(e_id),meth1); } fe = get_next_facet(fe); } while ( !equal_id(fe,first_fe) ); } } } } /* boundary content integrals */ for ( j = 0 ; j < web.bdrymax ; j++ ) { struct boundary *bdry = web.boundaries + j; vertex_id v_id; if ( !(bdry->attr & CON_CONTENT) ) continue; sprintf(inst_name1,"body_%d_bdry_%s_meth",i+1,bdry->name); meth1 = find_method_instance(inst_name1); if ( meth1 < 0 ) { /* have to create */ if ( web.representation == STRING ) { meth1 = new_method_instance("vertex_scalar_integral",inst_name1); } else { meth1 = new_method_instance("edge_vector_integral",inst_name1); } METH_INSTANCE(meth1)->flags |= DEFAULT_INSTANCE | BODY_INSTANCE; METH_INSTANCE(meth1)->connum = j; attach_method_num(gq,meth1); if ( web.representation == STRING ) METH_INSTANCE(meth1)->expr[0] = bdry->convect[0]; else for ( k = 0 ; k < SDIM ; k++ ) METH_INSTANCE(meth1)->expr[k] = bdry->convect[k]; } if ( web.representation == STRING ) { FOR_ALL_VERTICES(v_id) if ( bdry == get_boundary(v_id) ) { edge_id first_e = e_id = get_vertex_edge(v_id); if ( valid_id(e_id) ) do { facetedge_id first_fe = fe = get_edge_fe(e_id); if ( valid_id(fe) && !(get_eattr(e_id) & NONCONTENT) ) do { f_id = get_fe_facet(fe); if ( equal_id(b_id,get_facet_body(f_id)) ) apply_method_num(v_id,meth1); if ( equal_id(b_id,get_facet_body(inverse_id(f_id))) ) apply_method_num(inverse_id(v_id),meth1); fe = get_next_facet(fe); } while ( !equal_id(fe,first_fe) ); e_id = get_next_tail_edge(e_id); } while ( !equal_id(first_e,e_id)); } } else /* SOAPFILM */ { FOR_ALL_EDGES(e_id) if ( bdry == get_edge_boundary(e_id) ) { facetedge_id first_fe = fe = get_edge_fe(e_id); if ( valid_id(fe) ) do { f_id = get_fe_facet(fe); if ( !(get_fattr(f_id) & NONCONTENT) ) { if ( equal_id(b_id,get_facet_body(f_id)) ) apply_method_num(e_id,meth1); if ( equal_id(b_id,get_facet_body(inverse_id(f_id))) ) apply_method_num(inverse_id(e_id),meth1); } fe = get_next_facet(fe); } while ( !equal_id(fe,first_fe) ); } } } create_pressure_quant(b_id); } /* end convert_body_to_quantity() */ /**************************************************************************** * * function: convert_new_body_to_quantity() * * purpose: Convert a new body volume to a named quantity. Does not * do anything about putting quantities on edges or faces. * To can be called when new body created. * Call convert_bodies_to_quantity() to apply quantities. */ void convert_new_body_to_quantity(b_id) body_id b_id; { int gq; int meth1; char inst_name1[40]; char qname[40]; int i,j; char formula[100]; struct gen_quant *g=NULL; i = ordinal(b_id) + 1; sprintf(qname,"body_%d_vol",i); sprintf(inst_name1,"body_%d_vol_meth",i); if ( get_battr(b_id) & FIXEDVOL ) { gq = find_quantity(qname); if ( gq < 0 ) gq = new_quantity(qname,Q_FIXED); g = GEN_QUANT(gq); g->target = get_body_fixvol(b_id); } else if ( get_battr(b_id) & PRESSURE ) { REAL p; gq = find_quantity(qname); if ( gq < 0 ) gq = new_quantity(qname,Q_ENERGY); g = GEN_QUANT(gq); p = get_body_pressure(b_id); g->modulus = -p; if ( p == 0.0 ) { g->flags &= ~Q_ENERGY; g->flags |= Q_INFO; /* to force volume calc even for zero pressure */ g->modulus = 1.0; } } else { gq = find_quantity(qname); if ( gq < 0 ) gq = new_quantity(qname,Q_INFO); g = GEN_QUANT(gq); } g->flags |= DEFAULT_QUANTITY; g->b_id = b_id; set_body_volquant(b_id,gq); g->volconst = get_body_volconst(b_id); g->pressure = get_body_pressure(b_id); g->value = get_body_volume(b_id); g->oldvalue = get_body_volume(b_id); if ( web.representation == STRING ) { meth1 = find_method_instance(inst_name1); if ( meth1 < 0 ) { /* have to create */ if ( area_method_name[0] ) { meth1 = find_method_instance(area_method_name); /* user instance? */ if ( meth1 < 0 ) /* try pre-defined method */ meth1 = new_method_instance(area_method_name,inst_name1); } else if ( web.symmetric_content ) { meth1 = new_method_instance("edge_vector_integral",inst_name1); METH_INSTANCE(meth1)->expr[0] = (struct expnode *)mycalloc(SDIM,sizeof(struct expnode)); for ( j = 0 ; j < SDIM ; j++ ) { int old_datafile_flag; char *old_cmdptr = cmdptr; int old_tok = tok; if ( j==0 )sprintf(formula,"-y/2"); else if ( j==1 )sprintf(formula,"x/2"); else sprintf(formula,"0"); cmdptr = formula; METH_INSTANCE(meth1)->expr[j] = METH_INSTANCE(meth1)->expr[0] + j; old_datafile_flag = datafile_flag; datafile_flag = 1; exparse(SDIM,METH_INSTANCE(meth1)->expr[j],USERCOPY); sprintf(METH_INSTANCE(meth1)->expr[j]->name, "symmetric content component %d",j+1); datafile_flag = old_datafile_flag; cmdptr = old_cmdptr; tok = old_tok; } } else { meth1 = new_method_instance("edge_area",inst_name1); } METH_INSTANCE(meth1)->flags |= DEFAULT_INSTANCE | BODY_INSTANCE; METH_INSTANCE(meth1)->modulus = 1.0; attach_method_num(gq,meth1); set_body_volmeth(b_id,meth1); } } else /* SOAPFILM */ { meth1 = find_method_instance(inst_name1); if ( meth1 < 0 ) { /* have to create */ if ( volume_method_name[0] ) { meth1 = find_method_instance(volume_method_name); /* user instance? */ if ( meth1 < 0 ) /* try pre-defined method */ meth1 = new_method_instance(volume_method_name,inst_name1); } else if ( web.symmetric_content ) { meth1 = new_method_instance("facet_vector_integral",inst_name1); METH_INSTANCE(meth1)->expr[0] = (struct expnode *)mycalloc(SDIM,sizeof(struct expnode)); for ( j = 0 ; j < SDIM ; j++ ) { int old_datafile_flag; char *old_cmdptr = cmdptr; int old_tok = tok; sprintf(formula,"x%d/%d",j+1,SDIM); cmdptr = formula; METH_INSTANCE(meth1)->expr[j] = METH_INSTANCE(meth1)->expr[0] + j; old_datafile_flag = datafile_flag; datafile_flag = 1; exparse(SDIM,METH_INSTANCE(meth1)->expr[j],USERCOPY); sprintf(METH_INSTANCE(meth1)->expr[j]->name,"symmetric content component %d",j+1); datafile_flag = old_datafile_flag; cmdptr = old_cmdptr; tok = old_tok; } } else { meth1 = new_method_instance("facet_volume",inst_name1); } METH_INSTANCE(meth1)->flags |= DEFAULT_INSTANCE | BODY_INSTANCE; METH_INSTANCE(meth1)->modulus = 1.0; attach_method_num(gq,meth1); set_body_volmeth(b_id,meth1); } } create_pressure_quant(b_id); } /* end convert_new_body_to_quantity() */ /**************************************************************************** * * function: create_body_constraint_content_method() * * purpose: Create a new method instance for constraint content. * * return: method number */ int create_body_constraint_content_method(b_id,connum) body_id b_id; int connum; { struct constraint *con = get_constraint(connum); int k; int meth1; char inst_name1[100]; if ( con->attr & NAMED_THING ) sprintf(inst_name1,"body_%d_%s_meth",ordinal(b_id)+1,con->name); else sprintf(inst_name1,"body_%d_con_%d_meth",ordinal(b_id)+1,connum); meth1 = find_method_instance(inst_name1); if ( meth1 < 0 ) { /* have to create */ if ( web.representation == STRING ) { meth1 = new_method_instance("vertex_scalar_integral",inst_name1); } else { meth1 = new_method_instance("edge_vector_integral",inst_name1); } METH_INSTANCE(meth1)->flags |= DEFAULT_INSTANCE | BODY_INSTANCE; con = get_constraint(connum); /* may have moved */ if ( web.representation == STRING ) METH_INSTANCE(meth1)->expr[0] = con->convect[0]; else for ( k = 0 ; k < SDIM ; k++ ) METH_INSTANCE(meth1)->expr[k] = con->convect[k]; attach_method_num(get_body_volquant(b_id),meth1); } return meth1; } /**************************************************************************** * * function: create_body_boundary_content_method() * * purpose: Create a new method instance for boundary content. * * return: method number */ int create_body_boundary_content_method(b_id,bdrynum) body_id b_id; int bdrynum; { struct boundary *bdry = web.boundaries + bdrynum; int k; int meth1; char inst_name1[100]; sprintf(inst_name1,"body_%d_bdry_%s_meth",ordinal(b_id)+1,bdry->name); meth1 = find_method_instance(inst_name1); if ( meth1 < 0 ) { /* have to create */ if ( web.representation == STRING ) { meth1 = new_method_instance("vertex_scalar_integral",inst_name1); } else { meth1 = new_method_instance("edge_vector_integral",inst_name1); } METH_INSTANCE(meth1)->flags |= DEFAULT_INSTANCE | BODY_INSTANCE; if ( web.representation == STRING ) METH_INSTANCE(meth1)->expr[0] = bdry->convect[0]; else for ( k = 0 ; k < SDIM ; k++ ) METH_INSTANCE(meth1)->expr[k] = bdry->convect[k]; attach_method_num(get_body_volquant(b_id),meth1); } return meth1; } /**************************************************************************** * * function: convert_bodies_to_quantities() * * purpose: Apply all body quantity method instances to elements. */ void convert_bodies_to_quantities() { body_id b_id; int meth1; char inst_name1[100]; int i,j,k; edge_id e_id; facet_id f_id; facetedge_id fe; /* The main surface integral */ if ( web.representation == STRING ) { FOR_ALL_EDGES(e_id) { if ( get_eattr(e_id) & NONCONTENT ) continue; fe = get_edge_fe(e_id); if ( !valid_id(fe) ) continue; f_id = get_fe_facet(fe); for ( i = 0 ; i < 2 ; i++ ) /* at most two facets per edge */ { if ( valid_id(f_id)) { b_id = get_facet_body(f_id); if ( valid_id(b_id) ) apply_method_num(e_id,get_body_volmeth(b_id)); b_id = get_facet_body(inverse_id(f_id)); if ( valid_id(b_id) ) apply_method_num(inverse_id(e_id),get_body_volmeth(b_id)); } fe = get_next_facet(fe); f_id = get_fe_facet(fe); } } } else /* SOAPFILM */ { FOR_ALL_FACETS(f_id) { /* check both in case of torus wrapping */ if ( get_fattr(f_id) & NONCONTENT ) continue; b_id = get_facet_body(f_id); if ( valid_id(b_id) ) apply_method_num(f_id,get_body_volmeth(b_id)); b_id = get_facet_body(inverse_id(f_id)); if ( valid_id(b_id) ) apply_method_num(inverse_id(f_id),get_body_volmeth(b_id)); } } /* constraint content integrals */ /* create instance for each possible body/constraint content pair */ for ( j = 0 ; j < web.maxcon ; j++ ) { struct constraint *con = get_constraint(j); if ( !(con->attr & CON_CONTENT) ) continue; FOR_ALL_BODIES(b_id) { if ( con->attr & NAMED_THING ) sprintf(inst_name1,"body_%d_%s_meth",ordinal(b_id)+1,con->name); else sprintf(inst_name1,"body_%d_con_%d_meth",ordinal(b_id)+1,j); meth1 = find_method_instance(inst_name1); if ( meth1 < 0 ) { /* have to create */ if ( web.representation == STRING ) { meth1 = new_method_instance("vertex_scalar_integral",inst_name1); } else { meth1 = new_method_instance("edge_vector_integral",inst_name1); } METH_INSTANCE(meth1)->flags |= DEFAULT_INSTANCE | BODY_INSTANCE; con = get_constraint(j); /* may have moved */ if ( web.representation == STRING ) METH_INSTANCE(meth1)->expr[0] = con->convect[0]; else for ( k = 0 ; k < SDIM ; k++ ) METH_INSTANCE(meth1)->expr[k] = con->convect[k]; } } } if ( web.representation == STRING ) { vertex_id v_id; FOR_ALL_VERTICES(v_id) { conmap_t *con = get_v_constraint_map(v_id); int concount = (int)con[0]; edge_id first_e; int max_rank,min_rank; min_rank = MAXINT; max_rank = 0; for ( j = 1 ; j <= (int)con[0] ; j++ ) { struct constraint *c; if ( !(con[j] & CON_HIT_BIT) ) continue; c = get_constraint(con[j]); if ( c->content_rank < min_rank ) min_rank = c->content_rank; if ( c->content_rank > max_rank ) max_rank = c->content_rank; } for ( i = 1 ; i <= concount ; i++ ) { conmap_t *con = get_v_constraint_map(v_id); /* might have moved */ int connum = con[i] & CONMASK; struct constraint *c = get_constraint(connum); if ( !(c->attr & CON_CONTENT)) continue; first_e = e_id = get_vertex_edge(v_id); if ( valid_id(e_id) ) do { char name[100]; facetedge_id first_fe = fe = get_edge_fe(e_id); if ( valid_id(fe) && !(get_eattr(e_id) & NONCONTENT) ) do { f_id = get_fe_facet(fe); fe = get_next_facet(fe); if ( !valid_id(f_id) ) continue; b_id = get_facet_body(f_id); if ( valid_id(b_id) && ( (!inverted(f_id) && c->content_rank >= max_rank) || (inverted(f_id) && c->content_rank <= min_rank))) { if ( c->attr & NAMED_THING ) sprintf(name,"body_%d_%s_meth",ordinal(b_id)+1,c->name); else sprintf(name,"body_%d_con_%d_meth",ordinal(b_id)+1,connum); attach_method(get_body_volquant(b_id),name); apply_method(inverse_id(v_id),name); } b_id = get_facet_body(inverse_id(f_id)); if ( valid_id(b_id) && ( (!inverted(f_id) && c->content_rank >= max_rank) || (inverted(f_id) && c->content_rank <= min_rank))) { if ( c->attr & NAMED_THING ) sprintf(name,"body_%d_%s_meth",ordinal(b_id)+1,c->name); else sprintf(name,"body_%d_con_%d_meth",ordinal(b_id)+1,connum); attach_method(get_body_volquant(b_id),name); apply_method(v_id,name); } } while ( !equal_id(fe,first_fe) ); e_id = get_next_tail_edge(e_id); } while ( !equal_id(first_e,e_id)); } } } else /* SOAPFILM */ { FOR_ALL_EDGES(e_id) { conmap_t *con = get_e_constraint_map(e_id); int concount = (int)con[0]; facetedge_id first_fe = fe = get_edge_fe(e_id); if ( valid_id ( fe ) ) { int min_rank, max_rank; min_rank = MAXINT; max_rank = 0; for ( j = 1 ; j <= concount ; j++ ) { struct constraint *c = get_constraint(con[j]); if ( c->content_rank < min_rank ) min_rank = c->content_rank; if ( c->content_rank > max_rank ) max_rank = c->content_rank; } for ( i = 1 ; i <= concount ; i++ ) { conmap_t *con = get_e_constraint_map(e_id); /* might have moved */ int connum = con[i]&CONMASK; struct constraint *c = get_constraint(connum); if ( !(c->attr & CON_CONTENT) ) continue; fe = first_fe; do { char name[100]; f_id = get_fe_facet(fe); fe = get_next_facet(fe); if ( !valid_id(f_id) ) continue; if ( get_fattr(f_id) & NONCONTENT ) continue; b_id = get_facet_body(f_id); if ( valid_id(b_id) && (c->content_rank >= max_rank)) { if ( c->attr & NAMED_THING ) sprintf(name,"body_%d_%s_meth",ordinal(b_id)+1,c->name); else sprintf(name,"body_%d_con_%d_meth",ordinal(b_id)+1,connum); attach_method(get_body_volquant(b_id),name); apply_method(e_id,name); } b_id = get_facet_body(inverse_id(f_id)); if ( valid_id(b_id) && (c->content_rank <= min_rank) ) { if ( c->attr & NAMED_THING ) sprintf(name,"body_%d_%s_meth",ordinal(b_id)+1,c->name); else sprintf(name,"body_%d_con_%d_meth",ordinal(b_id)+1,connum&CONMASK); attach_method(get_body_volquant(b_id),name); apply_method(inverse_id(e_id),name); } } while ( !equal_id(fe,first_fe) ); } } } } /* boundary content integrals */ for ( j = 0 ; j < web.bdrymax ; j++ ) { struct boundary *bdry = web.boundaries + j; if ( !(bdry->attr & CON_CONTENT) ) continue; FOR_ALL_BODIES(b_id) { sprintf(inst_name1,"body_%d_bdry_%s_meth",ordinal(b_id)+1,bdry->name); meth1 = find_method_instance(inst_name1); if ( meth1 < 0 ) { /* have to create */ if ( web.representation == STRING ) { meth1 = new_method_instance("vertex_scalar_integral",inst_name1); } else { meth1 = new_method_instance("edge_vector_integral",inst_name1); } METH_INSTANCE(meth1)->flags |= DEFAULT_INSTANCE | BODY_INSTANCE; if ( web.representation == STRING ) METH_INSTANCE(meth1)->expr[0] = bdry->convect[0]; else for ( k = 0 ; k < SDIM ; k++ ) METH_INSTANCE(meth1)->expr[k] = bdry->convect[k]; attach_method_num(get_body_volquant(b_id),meth1); } } } if ( web.representation == STRING ) { vertex_id v_id; FOR_ALL_VERTICES(v_id) { struct boundary *bdry = get_boundary(v_id); edge_id first_e; if ( !bdry || !(bdry->attr & CON_CONTENT) ) continue; first_e = e_id = get_vertex_edge(v_id); if ( valid_id(e_id) ) do { facetedge_id first_fe = fe = get_edge_fe(e_id); if ( valid_id(fe) && !(get_eattr(e_id) & NONCONTENT) ) do { f_id = get_fe_facet(fe); b_id = get_facet_body(f_id); if ( valid_id(b_id) ) { sprintf(inst_name1,"body_%d_bdry_%s_meth",ordinal(b_id)+1,bdry->name); meth1 = find_method_instance(inst_name1); apply_method_num(v_id,meth1); } b_id = get_facet_body(inverse_id(f_id)); if ( valid_id(b_id) ) { sprintf(inst_name1,"body_%d_bdry_%s_meth",ordinal(b_id)+1,bdry->name); meth1 = find_method_instance(inst_name1); apply_method_num(inverse_id(v_id),meth1); } fe = get_next_facet(fe); } while ( !equal_id(fe,first_fe) ); e_id = get_next_tail_edge(e_id); } while ( !equal_id(first_e,e_id)); } } else /* SOAPFILM */ { FOR_ALL_EDGES(e_id) { struct boundary *bdry = get_edge_boundary(e_id); facetedge_id first_fe; if ( !bdry || !(bdry->attr & CON_CONTENT) ) continue; first_fe = fe = get_edge_fe(e_id); if ( valid_id(fe) ) do { f_id = get_fe_facet(fe); fe = get_next_facet(fe); if ( !valid_id(f_id) ) continue; if ( get_fattr(f_id) & NONCONTENT ) continue; b_id = get_facet_body(f_id); if ( valid_id(b_id) ) { sprintf(inst_name1,"body_%d_bdry_%s_meth",ordinal(b_id)+1,bdry->name); meth1 = find_method_instance(inst_name1); apply_method_num(inverse_id(e_id),meth1); } b_id = get_facet_body(inverse_id(f_id)); if ( valid_id(b_id) ) { sprintf(inst_name1,"body_%d_bdry_%s_meth",ordinal(b_id)+1,bdry->name); meth1 = find_method_instance(inst_name1); apply_method_num(e_id,meth1); } } while ( !equal_id(fe,first_fe) ); } } /* done in convert_new_body... if ( web.pressure_flag ) FOR_ALL_BODIES(b_id) create_pressure_quant(b_id); */ } /*************************************************************************** * * function: create_pressure_quant() * * purpose: create a named quantity for the ambient pressure energy. */ void create_pressure_quant(b_id) body_id b_id; { if ( web.pressure_flag ) { /* create formula and do compound quantity */ int gq = get_body_volquant(b_id); int mc = GEN_QUANT(gq)->method_count; char *volsum = temp_calloc(40,mc); char *formula = temp_calloc(2,40*mc + 100); char qname[1000]; int q,i,j; sprintf(qname,"body_%d_ambient_energy",ordinal(b_id)+1); q = find_quantity(qname); if ( q < 0 ) q = new_quantity(qname,Q_INFO); set_body_ambquant(b_id,q); GEN_QUANT(q)->flags |= DEFAULT_QUANTITY; sprintf(volsum,"body[%d].volconst",ordinal(b_id)+1); for ( j = 0 ; j < mc ; j++ ) { int m = GEN_QUANT(gq)->meth_inst[j]; struct method_instance *mi = METH_INSTANCE(m); strcat(volsum,((mi->modulus > 0.0) ? "+" : "+")); strcat(volsum,mi->name); strcat(volsum,".value"); mi->flags |= Q_COMPOUND; } GEN_QUANT(q)->method_count = 0; i = ordinal(b_id)+1; sprintf(formula,"-ambient_pressure_value*(body[%d].target*(log(%s)-log(body[%d].target))-(%s-body[%d].target))", i,volsum,i,volsum,i); cmdptr = formula; exparse(0,&(GEN_QUANT(q)->expr),USERCOPY); sprintf(msg,"%s formula",qname); strncpy(GEN_QUANT(q)->expr.name,msg,EXPNAMESIZE-1); cmdptr = NULL; GEN_QUANT(q)->flags |= Q_COMPOUND; GEN_QUANT(q)->next_compound = compound_quant_list_head; compound_quant_list_head = q; if ( get_battr(b_id) & FIXEDVOL ) GEN_QUANT(q)->flags |= Q_ENERGY; else GEN_QUANT(q)->flags |= Q_INFO; GEN_QUANT(gq)->flags &= ~Q_FIXED; GEN_QUANT(gq)->flags |= Q_INFO; temp_free(volsum); temp_free(formula); } } /***************************************************************************** * * Function: read_array_initializer() * * Purpose: Read bracketed array initial data. Next unread token should * be the first '{'. */ void read_array_initializer(struct array *a) { char *spot; char *spots[MAXARRAYDIMS]; int depth; int blocksize; int items[MAXARRAYDIMS]; tok = yylex(); /* should be '{' */ if ( tok != '{' ) kb_error(3041,"Missing '{'; expected array initializer.\n",RECOVERABLE); depth = 0; spot = (char *)a + a->datastart; spots[depth] = spot; blocksize = a->datacount; for (;;) { if ( depth == a->dim ) { int k; for ( k = 0 ; ; k++ ) { if ( read_single_value(a->datatype,spot) ) { if ( k < a->sizes[depth-1] ) spot += a->itemsize; else kb_error(2132,"Too many initializers.\n",DATAFILE_ERROR); } else { tok = yylex(); /* get back token exparse pushed back */ if ( tok != ',' ) break; } } } if ( tok == '{' ) { if ( blocksize ) blocksize /= a->sizes[depth]; spots[depth] = spot; items[depth]++; if ( depth > 0 && items[depth] > a->sizes[depth-1] ) kb_error(2539,"Too many initializers.\n",DATAFILE_ERROR); depth++; items[depth] = 0; if ( depth != a->dim ) tok = yylex(); } else if ( tok == '}' ) { tok = yylex(); depth--; if ( depth == 0 ) return; blocksize *= a->sizes[depth]; spots[depth] += blocksize*a->itemsize; spot = spots[depth]; } else if ( tok == ',' ) { tok = yylex(); spots[depth] += blocksize*a->itemsize; } else { sprintf(errmsg,"Illegal token \"%s\" in array initialization.\n",yytext); kb_error(2540,errmsg,DATAFILE_ERROR); } } } /* end read_array_initializer() */ /**************************************************************************** * * function: define_array() * * purpose: read an array parameter in the top of the datafile, * with possible initialization. * * incoming assumption: array name in yytext. */ void define_array(void) { int n; struct global *g; struct array *a; int dim; int size; int sizes[MAXARRAYDIMS]; int itemsize=0; int datatype=0; size_t rem; if ( strlen(yytext) < 2 ) kb_error(1867,"Identifiers must be at least two characters long.\n", DATAFILE_ERROR); n = lookup_global(yytext); if ( n >= 0 ) { g = globals(n); if ( !(g->flags & ARRAY_PARAM) ) kb_error(3213,"Array name already in use for something else.\n", DATAFILE_ERROR); } else { n = add_global(yytext); if ( n < 0 ) kb_error(1673,"Duplicate parameter name.\n",DATAFILE_ERROR); g = globals(n); g->flags |= ARRAY_PARAM; } tok = yylex(); /* dispose of IDENT_ */ if ( tok == DATATYPE_ ) { datatype = yylval.datatype; itemsize = datatype_size[datatype]; } else { kb_error(2130,"Need an array data type.\n",DATAFILE_ERROR); datatype = REAL_TYPE; itemsize = datatype_size[datatype]; } g->type = datatype; tok = yylex(); /* dispose of type */ g->attr.arrayptr = (struct array*)mycalloc(1,sizeof(struct array)); g->attr.arrayptr->dim = 0; while ( tok == '[' ) { REAL val; if ( read_const(&val) < 0 ) kb_error(1057,"Need dimension number.\n",DATAFILE_ERROR); sizes[g->attr.arrayptr->dim] = (int)val; if ( sizes[g->attr.arrayptr->dim] < 0 ) kb_error(1059,"Array dimension cannot be negative.\n",DATAFILE_ERROR); tok = yylex(); /* get ] */ if ( tok != ']' ) kb_error(2131,"Expecting ].\n",DATAFILE_ERROR); tok = yylex(); /* eat ] */ g->attr.arrayptr->dim++; } if ( g->attr.arrayptr->dim == 0 ) sizes[g->attr.arrayptr->dim++] = 1; for ( size = 1, dim=0 ; dim < g->attr.arrayptr->dim ; dim++ ) { size *= sizes[dim]; } a = g->attr.arrayptr = (struct array*)kb_realloc((char*)(g->attr.arrayptr), sizeof(struct array)+dim*sizeof(int) + (size+1)*itemsize); /* extra for alignment */ a->dim = g->attr.arrayptr->dim; a->itemsize = itemsize; a->datatype = datatype; a->datacount = size; for ( dim=0 ; dim < g->attr.arrayptr->dim ; dim++ ) a->sizes[dim] = sizes[dim]; a->datastart = sizeof(struct array) + a->dim*sizeof(int); /* align datastart to size of item */ rem = a->datastart % a->itemsize; if ( rem ) a->datastart += a->itemsize - rem; /* Initialization */ if ( (tok == '=') || (tok == ASSIGN_) ) /* have initial values */ read_array_initializer(a); } /**************************************************************************** * * Function: check_forwards() * * Purpose: Make sure all forward declarations have been implemented. */ void check_forwards() { int i; /* constraints */ for ( i = 0 ; i < web.maxcon ; i++ ) { struct constraint *con = get_constraint(i); if ( con->attr & CON_FORWARD_DEF ) { sprintf(errmsg,"Forward declared constraint %s not instantiated.\n", con->name); kb_error(5911,errmsg,RECOVERABLE); } } /* boundaries */ for ( i = 0 ; i < web.bdrymax ; i++ ) { struct boundary *bdry = web.boundaries + i; if ( bdry->attr & BDRY_FORWARD_DEF ) { sprintf(errmsg,"Forward declared boundary %s not instantiated.\n", bdry->name); kb_error(3227,errmsg,RECOVERABLE); } } /* quantities */ for ( i = 0 ; i < gen_quant_count ; i++ ) { if ( GEN_QUANT(i)->flags & Q_FORWARD_DEF ) { sprintf(errmsg,"Forward declared quantity %s not instantiated.\n", GEN_QUANT(i)->name); kb_error(3225,errmsg,RECOVERABLE); } } /* method instances */ for ( i = 1 ; i < meth_inst_count ; i++ ) { if ( METH_INSTANCE(i)->flags & Q_FORWARD_DEF ) { sprintf(errmsg,"Forward declared method_instance %s not instantiated.\n", METH_INSTANCE(i)->name); kb_error(3226,errmsg,RECOVERABLE); } } } evolver-2.30c.dfsg/src/diffuse.c0000644000175300017530000000547311410765113017017 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ #include "include.h" /******************************************************************** * * Function: diffuse() * * Purpose: Diffuse mass through permeable surfaces. * Assumes string lengths or film areas * already calculated; also assumes pressures * known. */ /* for per-element diffusion constant */ #define DIFFUSE_EATTR_NAME "edge_diffusion" #define DIFFUSE_FATTR_NAME "facet_diffusion" int edge_diffusion_attr; int facet_diffusion_attr; void diffuse() { edge_id e_id; facet_id f_id; facetedge_id fe_id; body_id b_id,bb_id; REAL pressure; REAL mass; REAL coeff; if ( web.representation == STRING ) { edge_diffusion_attr = find_attribute(EDGE,DIFFUSE_EATTR_NAME); FOR_ALL_EDGES(e_id) { fe_id = get_edge_fe(e_id); if ( !valid_id(fe_id) ) return; b_id = get_facet_body(get_fe_facet(fe_id)); if ( !valid_id(b_id) ) invert(fe_id); b_id = get_facet_body(get_fe_facet(fe_id)); if ( !valid_id(b_id) ) continue; pressure = get_body_pressure(b_id); if ( !equal_id(fe_id,get_next_facet(fe_id)) ) { fe_id = get_next_facet(fe_id); bb_id = get_facet_body(get_fe_facet(fe_id)); if ( !valid_id(bb_id) ) invert(fe_id); bb_id = get_facet_body(get_fe_facet(fe_id)); pressure -= get_body_pressure(bb_id); } else bb_id = NULLBODY; if ( edge_diffusion_attr >= 0 ) coeff = *(REAL*)get_extra(e_id,edge_diffusion_attr); else coeff = web.diffusion_const; mass = web.scale*coeff*pressure*get_edge_length(e_id); set_body_fixvol(b_id,get_body_fixvol(b_id)-mass); if ( valid_id(bb_id) ) set_body_fixvol(bb_id,get_body_fixvol(bb_id)+mass); } } /* end STRING */ else { facet_diffusion_attr = find_attribute(FACET,DIFFUSE_FATTR_NAME); FOR_ALL_FACETS(f_id) { pressure = 0.0; b_id = get_facet_body(f_id); if ( valid_id(b_id) ) pressure = get_body_pressure(b_id); else pressure = web.pressure; bb_id = get_facet_body(inverse_id(f_id)); if ( valid_id(bb_id) ) pressure -= get_body_pressure(bb_id); else pressure -= web.pressure; if ( facet_diffusion_attr >= 0 ) coeff = *(REAL*)get_extra(f_id,facet_diffusion_attr); else coeff = web.diffusion_const; mass = web.scale*coeff*pressure*get_facet_area(f_id); if ( valid_id(b_id) ) set_body_fixvol(b_id,get_body_fixvol(b_id)-mass); if ( valid_id(bb_id) ) set_body_fixvol(bb_id,get_body_fixvol(bb_id)+mass); } } } /* end diffuse() */ evolver-2.30c.dfsg/src/model.h0000644000175300017530000000401711410765113016470 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************** * * File: model.h * * Header file for evolver. Defines model parameters, i.e. * for torus model, lagrange model, space dimension. * */ #ifdef SDIM #define DEFAULT_SDIM SDIM #ifndef MAXCOORD #define MAXCOORD SDIM #endif #if SDIM==2 #define SDIM_dot(x,y) ((x)[0]*(y)[0] + (x)[1]*(y)[1]) #else #if SDIM==3 #define SDIM_dot(x,y) ((x)[0]*(y)[0] + (x)[1]*(y)[1] + (x)[2]*(y)[2]) #else #define SDIM_dot(x,y) dot(x,y,SDIM) #endif #endif #else #define SDIM web.sdim #define SDIM_dot(x,y) dot(x,y,SDIM) #define DEFAULT_SDIM 3 #endif /* maximum number of shared memory processors */ #ifdef SHARED_MEMORY #ifndef MAXPROCS #define MAXPROCS 4 #endif #else #define MAXPROCS 1 #endif /* maximum dimensionality */ #ifndef MAXCOORD #define MAXCOORD 4 #endif /* MAXPARAM is maximum number of boundary parameters. Must be at most MAXCOORD since is saved in same space by save_coord(). */ #define MAXPARAM MAXCOORD #define EDGE_VERTS 2 #define FACET_VERTS 3 #define FACET_EDGES 3 /* model types for web.modeltype */ #define LINEAR 1 #define QUADRATIC 2 #define LAGRANGE 3 /* Quadratic model point counts */ /* Control points per edge */ #define EDGE_CTRL 3 /* Control points per facet */ #define FACET_CTRL 6 /* Integration points per edge */ #define EDGE_INTERP 3 /* Integration points per facet */ #define FACET_INTERP 7 /* quadratic interpolation coefficients */ /* partials of interpolation polynomials at midpoints of patch edges */ /* control points are numbered 0 to 5 counterclockwise */ /* midpoints are numbered 0 to 2 counterclockwise after control pt 0 */ /* dip[midpoint][control point][partial] */ extern REAL dip[3][FACET_CTRL][2]; /* Histogram size */ #define HISTO_BINS 40 #define HISTO_BINSIZE (1/M_LN2) evolver-2.30c.dfsg/src/stringl.c0000644000175300017530000005333711410765113017056 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************************ * * File: stringl.c * * Contents: Functions to calculate area, energy and their gradients * according to the LINEAR STRING model. */ #include "include.h" /************************************************************************ * * Returns energy due to one edge. * Find edge's contribution to total length and facet areas. * Areas with respect to origin are calculated * and then oriented contributions added for each facet. * */ void edge_energy_l(e_id) edge_id e_id; { REAL energy; vertex_id v[2]; int i; REAL *x[2]; REAL xx[MAXCOORD]; REAL side[MAXCOORD]; v[0] = get_edge_tailv(e_id); v[1] = get_edge_headv(e_id); x[0] = get_coord(v[0]); x[1] = get_coord(v[1]); if ( web.symmetry_flag ) { (*sym_wrap)(x[1],xx,get_edge_wrap(e_id)); x[1] = xx; } for ( i = 0 ; i < SDIM ; i++ ) side[i] = x[1][i] - x[0][i]; /* energy due to linear tension */ if ( web.metric_flag ) { if ( klein_metric_flag ) energy = klein_length(x[0],x[1]); else energy = simplex_energy_metric(v,x); } else { /* calculate length energy */ energy = sqrt(SDIM_dot(side,side)); set_edge_length(e_id,energy); if ( web.wulff_flag ) { REAL wulff[MAXCOORD]; /* energy covector to side vector */ (*get_wulff)(side,wulff); energy = SDIM_dot(side,wulff); } } if ( web.representation == STRING ) web.total_area += energy; /* don't count triple junction as area */ energy *= get_edge_density(e_id); /* calculate gravitational energy */ if ( web.gravflag && !(get_eattr(e_id) & NONCONTENT) ) { REAL *t,*h; REAL grav_e; REAL density = edge_grav_density(e_id); if ( density != 0.0 ) { t = get_coord(v[0]); h = get_coord(v[1]); grav_e = (t[0]-h[0])*(t[1]*t[1] + t[1]*h[1] + h[1]*h[1])/6; energy += density*grav_e; } } web.total_energy = web.total_energy + energy; } /************************************************************************ * * Calculates all forces on control points due to edge and * accumulates them at each control point. */ void edge_force_l(e_id) edge_id e_id; { REAL len; REAL side[MAXCOORD]; int i,j; vertex_id hv,tv; REAL density = get_edge_density(e_id); REAL forces[2][MAXCOORD]; /* total forces from this facet */ REAL *forceptr[2]; /* pointers to forces */ REAL *force[2]; /* vertex forces */ WRAPTYPE wraps[2]; REAL xx[MAXCOORD]; REAL *x[2]; vertex_id v[2]; v[0] = tv = get_edge_tailv(e_id); v[1] = hv = get_edge_headv(e_id); x[0] = get_coord(v[0]); x[1] = get_coord(v[1]); for ( i = 0 ; i < 2 ; i++ ) forceptr[i] = forces[i]; if ( web.symmetry_flag ) { (*sym_wrap)(x[1],xx,get_edge_wrap(e_id)); x[1] = xx; } for ( i = 0 ; i < SDIM ; i++ ) side[i] = x[1][i] - x[0][i]; memset((char*)forces,0,sizeof(forces)); /* set to 0 */ add_vertex_valence(tv,1); add_vertex_valence(hv,1); /* force due to linear tension */ if ( web.metric_flag ) { if ( klein_metric_flag ) { len = klein_length(x[0],x[1]); klein_length_grad(x[0],x[1],forces[0],forces[1]); } else { len = simplex_energy_metric(v,x); simplex_force_metric(v,x,density,forceptr); } } else len = sqrt(SDIM_dot(side,side)); set_edge_length(e_id,len); /* add to endpoint stars */ add_vertex_star(tv,len); add_vertex_star(hv,len); if ( !web.metric_flag ) if ( len != 0.0 ) for ( i = 0 ; i < SDIM ; i++ ) { forces[0][i] += density*side[i]/len; forces[1][i] -= density*side[i]/len; } /* calculate gravitational forces */ if ( web.gravflag && !(get_eattr(e_id) & NONCONTENT) ) { REAL *t,*h; REAL gdensity = edge_grav_density(e_id); if ( gdensity != 0.0 ) { t = get_coord(tv); h = get_coord(hv); forces[0][0] += -gdensity*(t[1]*t[1] + t[1]*h[1] + h[1]*h[1])/6; forces[1][0] += gdensity*(t[1]*t[1] + t[1]*h[1] + h[1]*h[1])/6; forces[0][1] += -gdensity*(t[0]-h[0])*(2*t[1] + h[1])/6; forces[1][1] += -gdensity*(t[0]-h[0])*(t[1] + 2*h[1])/6; } } force[0] = get_force(tv); force[1] = get_force(hv); if ( web.symmetry_flag ) { REAL wforce[MAXCOORD]; /* unwrapped forces */ REAL *t = get_coord(tv); for ( j = 0 ; j < SDIM ; j++ ) force[0][j] += forces[0][j]; wraps[1] = get_edge_wrap(e_id); (*sym_form_pullback)(t,wforce,forces[1],wraps[1]); for ( j = 0 ; j < SDIM ; j++ ) force[1][j] += wforce[j]; } else for ( i = 0 ; i < 2 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) force[i][j] += forces[i][j]; } /****************************************************************** * * Function: edge_grav_density() * * Purpose: Finds gravitational energy density multiplier across * an edge in the string model. Includes grav const. */ REAL edge_grav_density(e_id) edge_id e_id; { facetedge_id fe,first_fe; body_id b_id; REAL density = 0.0; facet_id f_id; /* if body is on facet agreeing with edge orientation, then pressure is positive. */ fe = first_fe = get_edge_fe(e_id); while ( valid_id(fe) ) { f_id = get_fe_facet(fe); b_id = get_facet_body(f_id); if ( (valid_id(b_id)) && (get_battr(b_id) & DENSITY) ) density += get_body_density(b_id); b_id = get_facet_body(inverse_id(f_id)); if ( (valid_id(b_id)) && (get_battr(b_id) & DENSITY) ) density -= get_body_density(b_id); fe = get_next_facet(fe); if ( equal_id(fe,first_fe) ) break; } return web.grav_const*density; } /********************************************************************** * * Adds contribution of edge to areas of neighboring facets, via * Green's theorem. x-y plane only. * */ void edge_area_l(fe_id) facetedge_id fe_id; { REAL *xt,*xh; REAL area; body_id b_id1,b_id2; /* bodies to add area to */ facet_id f_id1,f_id2; /* facets to add area to */ edge_id e_id = get_fe_edge(fe_id); if ( get_eattr(e_id) & NONCONTENT ) return; if ( inverted(e_id) ) { fe_id = inverse_id(fe_id); e_id = inverse_id(e_id); } f_id1 = get_fe_facet(fe_id); f_id2 = facet_inverse(get_fe_facet(fe_id)); if ( !valid_id(f_id1) && !valid_id(f_id2) ) return; /* no facets */ b_id1 = get_facet_body(f_id1); b_id2 = get_facet_body(f_id2); if ( web.torus_flag ) { REAL v1,v2,v3,v4; int wx,wy; /* wraps */ WRAPTYPE wrap = get_edge_wrap(e_id); /* new way, accurate modulo unit cell area */ area = 0.0; xt = get_coord(get_edge_tailv(e_id)); xh = get_coord(get_edge_headv(e_id)); v1 = dot(web.inverse_periods[0],xt,2); v2 = dot(web.inverse_periods[0],xh,2); v3 = dot(web.inverse_periods[1],xt,2); v4 = dot(web.inverse_periods[1],xh,2); wx = WRAPNUM(wrap & WRAPMASK); wy = WRAPNUM((wrap>>TWRAPBITS) & WRAPMASK); area += wy*(v2+wx); area -= ((v2+wx - v1)*(v4+wy + v3)/2 ); area *= web.torusv; } /* end torus */ else { xt = get_coord(get_edge_tailv(e_id)); xh = get_coord(get_edge_headv(e_id)); /* calculate area above x1 axis */ area = (xt[0] - xh[0])*(xt[1] + xh[1])/2; } /* add to cell areas */ if ( valid_id(f_id1) ) add_facet_area(f_id1,area); if ( valid_id(b_id1) ) add_body_volume(b_id1,area); if ( valid_id(b_id2) ) add_body_volume(b_id2,-area); } /*********************************************************************** * * Function: string_grad_l() * * Purpose: construct vertex area gradients * Note: force is local, so toroidal wrappings don't matter, * as long as we get the edges right, which get_edge_side() does. * */ void string_grad_l() { body_id bi_id; /* identifier for body i */ facetedge_id fe_id; REAL side[MAXCOORD]; volgrad *hvgptr,*tvgptr; vertex_id headv,tailv; facet_id f_id; edge_id e_id; REAL *xt,*xh; int i; FOR_ALL_FACETEDGES(fe_id) { /* see which side has body, if any */ f_id = get_fe_facet(fe_id); bi_id = get_facet_body(f_id); if ( !valid_id(bi_id) ) { invert(fe_id); bi_id = get_facet_body(get_fe_facet(fe_id)); } if ( !valid_id(bi_id) ) continue; /* nothing here */ if ( !(get_battr(bi_id) & (FIXEDVOL|PRESSURE) ) ) continue; e_id = get_fe_edge(fe_id); if ( get_eattr(e_id) & NONCONTENT ) continue; get_edge_side(e_id,side); headv = get_fe_headv(fe_id); tailv = get_fe_tailv(fe_id); xt = get_coord(tailv); xh = get_coord(headv); /* gradient due to edge */ hvgptr = get_bv_new_vgrad(get_body_fixnum(bi_id),headv); hvgptr->bb_id = bi_id; tvgptr = get_bv_new_vgrad(get_body_fixnum(bi_id),tailv); tvgptr->bb_id = bi_id; if ( web.torus_flag ) { REAL v1,v2,v3,v4; int wx=0,wy=0; /* wraps */ WRAPTYPE wrap = get_edge_wrap(e_id); REAL hgrad[MAXCOORD]; REAL tgrad[MAXCOORD]; /* new way, accurate modulo unit cell area */ for ( i = 0 ; i < SDIM ; i++ ) hgrad[i] = tgrad[i] = 0.0; xt = get_coord(get_edge_tailv(e_id)); xh = get_coord(get_edge_headv(e_id)); v1 = dot(web.inverse_periods[0],xt,2); v2 = dot(web.inverse_periods[0],xh,2); v3 = dot(web.inverse_periods[1],xt,2); v4 = dot(web.inverse_periods[1],xh,2); wx = WRAPNUM(wrap & WRAPMASK); wy = WRAPNUM((wrap>>TWRAPBITS) & WRAPMASK); if ( wy ) for ( i = 0 ; i < SDIM ; i++ ) hgrad[i] += wy*web.inverse_periods[0][i]; for ( i = 0 ; i < SDIM ; i++ ) { hgrad[i] -= (v4+wy + v3)/2*web.inverse_periods[0][i]; tgrad[i] += (v4+wy + v3)/2*web.inverse_periods[0][i]; hgrad[i] -= (v2+wx - v1)/2*web.inverse_periods[1][i]; tgrad[i] -= (v2+wx - v1)/2*web.inverse_periods[1][i]; } for ( i = 0 ; i < SDIM ; i++ ) { hvgptr->grad[i] += web.torusv*hgrad[i]; tvgptr->grad[i] += web.torusv*tgrad[i]; } } /* end torus */ else { /* grads of area = (xt[0] - xh[0])*(xt[1] + xh[1])/2 */ hvgptr->grad[0] += -(xt[1] + xh[1])/2; hvgptr->grad[1] += (xt[0] - xh[0])/2; tvgptr->grad[0] += (xt[1] + xh[1])/2; tvgptr->grad[1] += (xt[0] - xh[0])/2; } } } /******************************************************************* * * Function: string_bdry_grad() * * Purpose: Add cell area gradients due to boundary integrals. */ void string_bdry_grad() { vertex_id v_id; REAL dummy,partial[MAXCOORD]; /* for eval_all */ int i; FOR_ALL_VERTICES(v_id) { volgrad *vgptri; struct boundary *bdry; ATTR attr = get_vattr(v_id); edge_id e_id, start_e, next_e; if ( attr & FIXED ) continue; if ( !(attr & BOUNDARY) ) continue; if ( !(attr & BDRY_CONTENT) ) continue; bdry = get_boundary(v_id); if ( bdry->pcount != 1 ) return ; start_e = get_vertex_edge(v_id); if ( !valid_id(start_e) ) continue; next_e = start_e; do { facetedge_id fe_id0,fe_id; facet_id f_id; body_id bf; int sign; e_id = next_e; fe_id0 = get_edge_fe(e_id); next_e = get_next_tail_edge(e_id); if ( !valid_id(fe_id0) ) continue; if ( get_eattr(e_id) & NONCONTENT ) continue; eval_all(bdry->convect[0],get_param(v_id),bdry->pcount,&dummy, partial,v_id); vgptri = get_vertex_vgrad(v_id); do { sign = 0; fe_id = fe_id0; do { f_id = get_fe_facet(fe_id); bf = get_facet_body(f_id); if ( equal_id(bf,vgptri->bb_id) ) { sign = 1; break; } else { facetedge_id fe_ida = inverse_id(fe_id); f_id = get_fe_facet(fe_ida); bf = get_facet_body(f_id); if ( equal_id(bf,vgptri->bb_id) ) { sign = -1; break; } } fe_id = get_next_facet(fe_id); } while ( fe_id != fe_id0 ); if ( sign ) for ( i = 0 ; i < bdry->pcount ; i++ ) vgptri->grad[i] += partial[i]; vgptri = vgptri->chain; } while ( vgptri ); } while ( !equal_id(next_e,start_e) ); } } /******************************************************************* * * Function: string_constr_grad() * * Purpose: Add cell area gradients due to constraint integrals. */ void string_constr_grad() { vertex_id v_id; edge_id e_id, next_e, start_e; FOR_ALL_VERTICES(v_id) { struct volgrad *vgptri; struct constraint *constr; facetedge_id fe_id,fe_id0; ATTR attr = get_vattr(v_id); if ( attr & FIXED ) continue; if ( !(attr & CONSTRAINT) ) continue; if ( !(attr & BDRY_CONTENT) ) continue; start_e = get_vertex_edge(v_id); if ( !valid_id(start_e) ) continue; e_id = next_e = start_e; do { e_id = next_e; fe_id0 = get_edge_fe(e_id); next_e = get_next_tail_edge(e_id); if ( !valid_id(fe_id0) ) continue; if ( get_eattr(e_id) & NONCONTENT ) continue; vgptri = get_vertex_vgrad(v_id); while ( vgptri ) { int sign=0,i,j; conmap_t * conmap = get_v_constraint_map(v_id); facet_id f_id; body_id bf; fe_id = fe_id0; do { f_id = get_fe_facet(fe_id); bf = get_facet_body(f_id); if ( equal_id(bf,vgptri->bb_id) ) { sign = 1; break; } else { facetedge_id fe_ida = inverse_id(fe_id); f_id = get_fe_facet(fe_ida); bf = get_facet_body(f_id); if ( equal_id(bf,vgptri->bb_id) ) { sign = -1; break; } } fe_id = get_next_facet(fe_id); } while ( fe_id != fe_id0 ); if ( sign ) for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { REAL val,derivs[MAXCOORD]; constr = get_constraint(conmap[j]); if ( !(constr->attr & CON_CONTENT) ) continue; eval_all(constr->convect[0],get_coord(v_id),SDIM,&val,derivs,v_id); for ( i = 0 ; i < SDIM ; i++ ) vgptri->grad[i] -= sign*derivs[i]; } vgptri = vgptri->chain; } } while ( !equal_id(next_e,start_e) ); } } /********************************************************************* edge_general method, linear and quadratic *********************************************************************/ /********************************************************************* * * function: edge_general_init() * * purpose: check dimension of edge * */ void edge_general_init(mode,mi) int mode; struct method_instance *mi; { if ( web.dimension > 2 ) kb_error(2189, "Can only do edge_general_integral if edges are 1 dimensional.\n", RECOVERABLE); } /********************************************************************* * * function: edge_general_value() * * purpose: method value * */ REAL edge_general_value(e_info) struct qinfo *e_info; { int m,k; REAL value = 0.0; REAL z[2*MAXCOORD+1]; /* pointers to coord and tangent */ REAL sign = (get_eattr(e_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; if ( web.modeltype == LAGRANGE ) return edge_general_value_lagrange(e_info); for ( m = 0 ; m < gauss1D_num ; m++ ) { REAL weight = gauss1Dwt[m]; for ( k = 0 ; k < SDIM ; k++ ) { z[k] = e_info->gauss_pt[m][k]; z[SDIM+k] = sign*e_info->sides[m][0][k]; } z[2*SDIM] = m; /* kludge for attr interp */ value += weight*eval(METH_INSTANCE(e_info->method)->expr[0],z,e_info->id,NULL); } return value; } /********************************************************************* * * function: edge_general_grad() * * purpose: method gradient * */ REAL edge_general_grad(e_info) struct qinfo *e_info; { int m,j,k; REAL value = 0.0; REAL val; REAL derivs[2*MAXCOORD]; REAL z[2*MAXCOORD+1]; /* pointers to coord and tangent */ REAL sign = (get_eattr(e_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; if ( web.modeltype == LAGRANGE ) return edge_general_grad_lagrange(e_info); for ( m = 0 ; m < gauss1D_num ; m++ ) { REAL weight = gauss1Dwt[m]; for ( k = 0 ; k < SDIM ; k++ ) { z[k] = e_info->gauss_pt[m][k]; z[SDIM+k] = sign*e_info->sides[m][0][k]; } z[2*SDIM] = m; /* kludge for attr interp */ eval_all(METH_INSTANCE(e_info->method)->expr[0],z,2*SDIM,&val,derivs,e_info->id); value += weight*val; for ( k = 0 ; k < edge_ctrl ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) e_info->grad[k][j] += weight*(gauss1poly[k][m]*derivs[j] + gauss1polyd[k][m]*derivs[j+SDIM]); } return value; } /********************************************************************* * * function: edge_general_hess() * * purpose: method hessian * */ REAL edge_general_hess(e_info) struct qinfo *e_info; { int m,j,jj,k,kk; REAL value = 0.0; REAL val; REAL derivs[2*MAXCOORD]; REAL z[2*MAXCOORD+1]; /* pointers to coord and tangent */ MAT2D(second,2*MAXCOORD,2*MAXCOORD); /* second derivatives */ REAL sign = (get_eattr(e_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; if ( web.modeltype == LAGRANGE ) return edge_general_hess_lagrange(e_info); for ( m = 0 ; m < gauss1D_num ; m++ ) { REAL weight = gauss1Dwt[m]; for ( k = 0 ; k < SDIM ; k++ ) { z[k] = e_info->gauss_pt[m][k]; z[SDIM+k] = sign*e_info->sides[m][0][k]; } z[2*SDIM] = m; /* kludge for attr interp */ eval_second(METH_INSTANCE(e_info->method)->expr[0],z,2*SDIM,&val,derivs,second,e_info->id); value += weight*val; for ( k = 0 ; k < edge_ctrl ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) e_info->grad[k][j] += weight*(gauss1poly[k][m]*derivs[j] + gauss1polyd[k][m]*derivs[j+SDIM]); for ( k = 0 ; k < edge_ctrl ; k++ ) for ( kk = 0 ; kk < edge_ctrl ; kk++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) e_info->hess[k][kk][j][jj] += weight* (gauss1poly[k][m]*gauss1poly[kk][m]*second[j][jj] + gauss1polyd[k][m]*gauss1poly[kk][m]*second[j+SDIM][jj] + gauss1poly[k][m]*gauss1polyd[kk][m]*second[j][jj+SDIM] + gauss1polyd[k][m]*gauss1polyd[kk][m]*second[j+SDIM][jj+SDIM]); } return value; } /********************************************************************* * * function: edge_general_value_lagrange() * * purpose: method value * */ REAL edge_general_value_lagrange(e_info) struct qinfo *e_info; { int m,k; REAL value = 0.0; REAL z[2*MAXCOORD+1]; /* pointers to coord and tangent */ REAL sign = (get_eattr(e_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; struct gauss_lag *gl = &gauss_lagrange[1][web.gauss1D_order]; for ( m = 0 ; m < gl->gnumpts ; m++ ) /* integration point number */ { for ( k = 0 ; k < SDIM ; k++ ) { z[k] = e_info->gauss_pt[m][k]; z[SDIM+k] = sign*e_info->sides[m][0][k]; } z[2*SDIM] = m; /* kludge for attr interp */ value += gl->gausswt[m]*eval(METH_INSTANCE(e_info->method)->expr[0],z,e_info->id,NULL); } return value; } /********************************************************************* * * function: edge_general_grad_lagrange() * * purpose: method gradient * */ REAL edge_general_grad_lagrange(e_info) struct qinfo *e_info; { int m,j,k; REAL value = 0.0; REAL val; REAL derivs[2*MAXCOORD]; REAL z[2*MAXCOORD+1]; /* pointers to coord and tangent */ REAL sign = (get_eattr(e_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; struct gauss_lag *gl = &gauss_lagrange[1][web.gauss1D_order]; for ( m = 0 ; m < gl->gnumpts ; m++ ) /* integration point number */ { REAL weight = gl->gausswt[m]; for ( k = 0 ; k < SDIM ; k++ ) { z[k] = e_info->gauss_pt[m][k]; z[SDIM+k] = sign*e_info->sides[m][0][k]; } z[2*SDIM] = m; /* kludge for attr interp */ eval_all(METH_INSTANCE(e_info->method)->expr[0],z,2*SDIM,&val,derivs,e_info->id); value += weight*val; for ( k = 0 ; k < gl->lagpts ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) e_info->grad[k][j] += weight*(gl->gpoly[m][k]*derivs[j] + gl->gpolypart[m][0][k]*derivs[j+SDIM]); } return value; } /********************************************************************* * * function: edge_general_hess_lagrange() * * purpose: method hessian * */ REAL edge_general_hess_lagrange(e_info) struct qinfo *e_info; { int m,j,jj,k,kk; REAL value = 0.0; REAL val; REAL derivs[2*MAXCOORD]; REAL z[2*MAXCOORD+1]; /* pointers to coord and tangent */ MAT2D(second,2*MAXCOORD,2*MAXCOORD); /* second derivatives */ REAL sign = (get_eattr(e_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; struct gauss_lag *gl = &gauss_lagrange[1][web.gauss1D_order]; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL weight = gl->gausswt[m]; for ( k = 0 ; k < SDIM ; k++ ) { z[k] = e_info->gauss_pt[m][k]; z[SDIM+k] = sign*e_info->sides[m][0][k]; } z[2*SDIM] = m; /* kludge for attr interp */ eval_second(METH_INSTANCE(e_info->method)->expr[0],z,2*SDIM,&val,derivs,second,e_info->id); value += weight*val; for ( k = 0 ; k < gl->lagpts ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) e_info->grad[k][j] += weight*(gl->gpoly[m][k]*derivs[j] + gl->gpolypart[m][0][k]*derivs[j+SDIM]); for ( k = 0 ; k < gl->lagpts ; k++ ) for ( kk = 0 ; kk < gl->lagpts ; kk++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) e_info->hess[k][kk][j][jj] += weight* (gl->gpoly[m][k]*gl->gpoly[m][kk]*second[j][jj] + gl->gpolypart[m][0][k]*gl->gpoly[m][kk]*second[j+SDIM][jj] + gl->gpoly[m][k]*gl->gpolypart[m][0][kk]*second[j][jj+SDIM] + gl->gpolypart[m][0][k]*gl->gpolypart[m][0][kk]*second[j+SDIM][jj+SDIM]); } return value; } evolver-2.30c.dfsg/src/matrix.c0000644000175300017530000027157211410765113016703 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /* matrix.c */ /* matrix routines, mostly from Numerical Recipes */ /* Note: matrix allocation routine assumes whole matrix can be allocated in one call to calloc. If not true (say for large matrices on an IBM PC), dmatrix() and free_matrix() will have to be modified */ /* Note: Matrices allocated as row-pointer structures. The -1 index is used to store a private copy of pointer to memory, so users may swap row pointers freely. */ #include "include.h" #ifdef BLAS /* prototypes */ void DPOTRF ARGS((char*,int*,double*,int*,int*)); void DPOTRS ARGS((char*,int*,int*,double*,int*,double*,int*,int*)); void DGEMM ARGS((char*,char*,int*,int*,int*,double*, double*,int*,double*,int*,double*,double*,int*)); void DSYMM ARGS((char*,char*,int*,int*,double*,double*,int*, double*,int*,double*,double*,int*)); void DGEMV ARGS((char*,int*,int*,double*,double*,int*,double*,int*,double*, double*,int*)); void DSYMV ARGS((char*,int*,double*,double*,int*,double*,int*,double*, double*,int*)); #endif /****************************************************************** * * Function: matcopy() * * Purpose: copies matrix b to matrix a * for zero-base indexing only */ void matcopy ARGS4((a,b,rows,cols), REAL **a, REAL **b, int rows, int cols) { int i; for ( i = 0 ; i < rows ; i++ ) memcpy((char *)a[i],(char *)b[i],cols*sizeof(REAL)); } /****************************************************************** * * Function: kb_dmatrix() * * Purpose: Allocates zeroed 2D matrix in pointer-pointer form, * a REAL matrix with range [rlo..rhi][clo..chi] */ #ifdef MEMSTRINGS REAL **kb_dmatrix ARGS6((rlo,rhi,clo,chi,file,line), int rlo, int rhi, int clo, int chi, char *file, int line) #else REAL **kb_dmatrix ARGS4((rlo,rhi,clo,chi), int rlo, int rhi, int clo, int chi) #endif { int i; REAL **m; #ifdef MEMSTRINGS if ( memdebug) { sprintf(msg,"dmatrix from %s %d.\n",file,line); outstring(msg); } #endif if ( rhi-rlo+1 == 0 ) return NULL; m = (REAL **)mycalloc((unsigned)(rhi-rlo+1+1),sizeof(REAL *)); m -= rlo; m++; /* room for private pointer */ m[rlo-1] = (REAL *) mycalloc((unsigned)(chi-clo+1),(rhi-rlo+1)*sizeof(REAL)); for ( i = rlo ; i <= rhi ; i++ ) m[i] = m[rlo-1] + (i - rlo)*(chi - clo + 1) - clo; return m; } /****************************************************************** * * Function: kb_dmatrix3() * * Purpose: Allocates a zeroed 3D matrix in pointer-pointer-pointer form, * a REAL matrix with range [0..n1-1][0..n2-1][0..n3-1] * */ #ifdef MEMSTRINGS REAL ***kb_dmatrix3 ARGS5((n1,n2,n3,file,line), int n1, int n2,int n3, char * file, int line) #else REAL ***kb_dmatrix3 ARGS3((n1,n2,n3), int n1,int n2,int n3) #endif { int i,j; REAL ***m; #ifdef MEMSTRINGS if ( memdebug ) { sprintf(msg,"dmatrix3 from %s %d.\n",file,line); outstring(msg); } #endif if ( n1 <= 0 ) n1 = 1; if ( n2 <= 0 ) n2 = 1; /* assumes all pointers same machine size and alignment */ m = (REAL ***)mycalloc((n2+1)*n1+1,sizeof(REAL **)); m++; /* room for private pointer to doubles */ for ( i = 0 ; i < n1 ; i++ ) m[i] = (REAL **)(m + n1 + i*n2); m[0][0] = (REAL *) mycalloc(n1*n2*n3,sizeof(REAL)); m[-1] = (REAL **)(m[0][0]); for ( i = 0 ; i < n1 ; i++ ) for ( j = 0 ; j < n2 ; j++ ) m[i][j] = m[0][0] + i*n2*n3 + j*n3; return m; } /********************************************************************* * * function: matrix3_reorder() * * purpose: Reorder data of a 3D matrix into canonical order. * Creates new matrix, and frees old one. * * return: */ REAL *** matrix3_reorder ARGS4((a,maxi,maxj,maxk), REAL ***a, int maxi, int maxj, int maxk) /* dimensions */ { int i,j,k; REAL ***newa; newa = dmatrix3(maxi,maxj,maxk); for ( i = 0 ; i < maxi; i++ ) for ( j = 0 ; j < maxj ; j++ ) for ( k = 0 ; k < maxk ; k++ ) newa[i][j][k] = a[i][j][k]; free_matrix3(a); return newa; } /****************************************************************** * * Function: kb_dmatrix4() * * Purpose: Allocates a zeroed 4D matrix in pointer form, * a REAL matrix with range [0..n1-1][0..n2-1][0..n3-1][0..n4-1] */ #ifdef MEMSTRINGS REAL ****kb_dmatrix4 ARGS6((n1,n2,n3,n4,file,line), int n1, int n2, int n3, int n4, char * file, int line) #else REAL ****kb_dmatrix4 ARGS4((n1,n2,n3,n4), int n1, int n2, int n3, int n4) #endif { int i,j,k; REAL ****m; #ifdef MEMSTRINGS if ( memdebug ) { sprintf(msg,"dmatrix4 from %s %d.\n",file,line); outstring(msg); } #endif if ( n1 <= 0 ) n1 = 1; if ( n2 <= 0 ) n2 = 1; if ( n3 <= 0 ) n3 = 1; /* assumes all pointers same machine size and alignment */ m = (REAL ****)mycalloc(1+n1+n1*n2+n1*n2*n3,sizeof(REAL ***)); m++; /* room for private pointer */ for ( i = 0 ; i < n1 ; i++ ) { m[i] = (REAL ***)(m + n1 + i*n2); for ( j = 0 ; j < n2 ; j++ ) m[i][j] = (REAL **)(m + n1 + n1*n2 + i*n2*n3 + j*n3); } m[0][0][0] = (REAL *) mycalloc(n1*n2*n3*n4,sizeof(REAL)); m[-1] = (REAL***)(m[0][0][0]); for ( i = 0 ; i < n1 ; i++ ) for ( j = 0 ; j < n2 ; j++ ) for ( k = 0 ; k < n3 ; k++ ) m[i][j][k] = m[0][0][0] + i*n2*n3*n4 + j*n3*n4 + k*n4; return m; } /****************************************************************** * * Function: kb_temp_dmatrix() * * Purpose: Allocates zeroed 2D matrix in pointer-pointer form, * a REAL matrix with range [rlo..rhi][clo..chi] * Temporary memory. */ #ifdef MEMSTRINGS REAL **kb_temp_dmatrix ARGS6((rlo,rhi,clo,chi,file,line), int rlo, int rhi, int clo, int chi, char *file, int line) #else REAL **kb_temp_dmatrix ARGS4((rlo,rhi,clo,chi), int rlo, int rhi, int clo, int chi) #endif { int i; REAL **m; #ifdef MEMSTRINGS if ( memdebug) { sprintf(msg,"temp_dmatrix from %s %d.\n",file,line); outstring(msg); } #endif if ( rhi-rlo+1 == 0 ) return NULL; m = (REAL **)temp_calloc((unsigned)(rhi-rlo+1+1),sizeof(REAL *)); m -= rlo; m++; /* room for private pointer */ m[rlo-1] = (REAL *) temp_calloc((unsigned)(chi-clo+1),(rhi-rlo+1)*sizeof(REAL)); for ( i = rlo ; i <= rhi ; i++ ) m[i] = m[rlo-1] + (i - rlo)*(chi - clo + 1) - clo; return m; } /****************************************************************** * * Function: kb_temp_dmatrix3() * * Purpose: Allocates a zeroed 3D matrix in pointer-pointer-pointer form, * a REAL matrix with range [0..n1-1][0..n2-1][0..n3-1] * Temporary memory. * */ #ifdef MEMSTRINGS REAL ***kb_temp_dmatrix3 ARGS5((n1,n2,n3,file,line), int n1, int n2,int n3, char * file, int line) #else REAL ***kb_temp_dmatrix3 ARGS3((n1,n2,n3), int n1,int n2,int n3) #endif { int i,j; REAL ***m; #ifdef MEMSTRINGS if ( memdebug ) { sprintf(msg,"temp_dmatrix3 from %s %d.\n",file,line); outstring(msg); } #endif if ( n1 <= 0 ) n1 = 1; if ( n2 <= 0 ) n2 = 1; /* assumes all pointers same machine size and alignment */ m = (REAL ***)temp_calloc((n2+1)*n1+1,sizeof(REAL **)); m++; /* room for private pointer to doubles */ for ( i = 0 ; i < n1 ; i++ ) m[i] = (REAL **)(m + n1 + i*n2); m[0][0] = (REAL *) temp_calloc(n1*n2*n3,sizeof(REAL)); m[-1] = (REAL **)(m[0][0]); for ( i = 0 ; i < n1 ; i++ ) for ( j = 0 ; j < n2 ; j++ ) m[i][j] = m[0][0] + i*n2*n3 + j*n3; return m; } /****************************************************************** * * Function: kb_temp_dmatrix4() * * Purpose: Allocates a zeroed 4D matrix in pointer form, * a REAL matrix with range [0..n1-1][0..n2-1][0..n3-1][0..n4-1] * Temporary memory. */ #ifdef MEMSTRINGS REAL ****kb_temp_dmatrix4 ARGS6((n1,n2,n3,n4,file,line), int n1, int n2, int n3, int n4, char * file, int line) #else REAL ****kb_temp_dmatrix4 ARGS4((n1,n2,n3,n4), int n1, int n2, int n3, int n4) #endif { int i,j,k; REAL ****m; #ifdef MEMSTRINGS if ( memdebug ) { sprintf(msg,"temp_dmatrix4 from %s %d.\n",file,line); outstring(msg); } #endif if ( n1 <= 0 ) n1 = 1; if ( n2 <= 0 ) n2 = 1; if ( n3 <= 0 ) n3 = 1; /* assumes all pointers same machine size and alignment */ m = (REAL ****)temp_calloc(1+n1+n1*n2+n1*n2*n3,sizeof(REAL ***)); m++; /* room for private pointer */ for ( i = 0 ; i < n1 ; i++ ) { m[i] = (REAL ***)(m + n1 + i*n2); for ( j = 0 ; j < n2 ; j++ ) m[i][j] = (REAL **)(m + n1 + n1*n2 + i*n2*n3 + j*n3); } m[0][0][0] = (REAL *) temp_calloc(n1*n2*n3*n4,sizeof(REAL)); m[-1] = (REAL***)(m[0][0][0]); for ( i = 0 ; i < n1 ; i++ ) for ( j = 0 ; j < n2 ; j++ ) for ( k = 0 ; k < n3 ; k++ ) m[i][j][k] = m[0][0][0] + i*n2*n3*n4 + j*n3*n4 + k*n4; return m; } /********************************************************************** * * Function: perm_matrix2() * * purpose: Allocate pointer-style 2D matrix in permanent memory. */ REAL **perm_matrix2(rows,cols) int rows,cols; { REAL **m; int i; m = (double**)calloc(rows,sizeof(double*)); m[0] = (double*)calloc(rows*cols,sizeof(double)); for ( i = 1 ; i < rows ; i++ ) m[i] = m[i-1] + cols; return m; } /****************************************************************** * * Function: matNd_setup() * * Purpose: routines for initializing matrices declared as local variables * with MAT2D etc macros. Note it does not zero the entries! */ REAL ** mat2d_setup ARGS4((name,spacename,rows,cols), REAL **name, REAL *spacename, int rows, int cols) { REAL **spot = name; for ( ; rows > 0 ; rows--,spacename += cols,spot++ ) *spot = spacename; return name; } REAL *** mat3d_setup ARGS5((name,spacename,rows,cols,levels), REAL ***name, REAL *spacename, int rows, int cols, int levels) { int i; REAL ***spot; REAL **row = (REAL **)(name + rows); for ( spot = name ; rows > 0 ; rows--,spot++ ) { *spot = row; for ( i = 0 ; i < cols ; i++,spacename += levels, row++ ) *row = spacename; } return name; } REAL **** mat4d_setup ARGS6((name,spacename,rows,cols,levels,tiers), REAL ****name, REAL *spacename, int rows, int cols, int levels, int tiers) { int i,j; REAL ***row = (REAL ***)(name + rows); REAL **col = (REAL **)(name + rows + rows*cols); REAL ****spot; for (spot=name ; rows > 0 ; rows--,spot++ ) { *spot = row; for ( i = 0 ; i < cols ; i++, row++ ) { *row = col; for ( j = 0 ; j < levels ; j++,spacename += tiers, col++ ) *col = spacename; } } return name; } /* end local declaration routines */ /****************************************************************** * * Function: ivector() * * Purpose: allocate integer or real vector with given index range. */ int *ivector ARGS2((lo,hi), int lo, int hi) /* allocates a int vector with range [lo..hi] */ { int *v; v = (int *)mycalloc((unsigned)(hi-lo+1),sizeof(int)); return v-lo; } void free_ivector ARGS3((v,lo,hi), int *v, int lo,int hi) { myfree((char *)(v+lo)); } /****************************************************************** * * Function: free_matrixN() * * Purpose: Deallocate storage allocated by kb_dmatrixN(). */ void free_matrix ARGS1((m), REAL **m) { if ( !m ) return; myfree((char *)m[-1]); /* using private pointer */ myfree((char *)(m-1)); } void free_matrix3 ARGS1((m), REAL ***m) { if ( !m ) return; myfree((char *)m[-1]); myfree((char *)(m-1)); } void free_matrix4 ARGS1((m), REAL ****m) { if ( !m ) return; myfree((char *)m[-1]); myfree((char *)(m-1)); } void free_temp_matrix ARGS1((m), REAL **m) { if ( !m ) return; temp_free((char *)m[-1]); /* using private pointer */ temp_free((char *)(m-1)); } void free_temp_matrix3 ARGS1((m), REAL ***m) { if ( !m ) return; temp_free((char *)m[-1]); temp_free((char *)(m-1)); } void free_temp_matrix4 ARGS1((m), REAL ****m) { if ( !m ) return; temp_free((char *)m[-1]); temp_free((char *)(m-1)); } /****************************************************************** * * Function: vector_add() * * Purpose: add vector b to vector a */ void vector_add ARGS3((a,b,n), REAL *a, REAL *b, int n) { for(;n!=0;n--) *(a++) += *(b++); } /****************************************************************** * * Function: vector_add_smul() * * Purpose: add scalar multiple of vector b to vector a */ void vector_add_smul ARGS4((a,b,c,n), REAL *a, REAL *b, REAL c, int n) { for(;n!=0;n--) *(a++) += c*(*(b++)); } /****************************************************************** * * Function: vector_sub() * * Purpose: subtract vector b from vector a */ void vector_sub ARGS3((a,b,n), REAL *a, REAL *b, int n) { for(;n!=0;n--) *(a++) -= *(b++); } /****************************************************************** * * Function: vnormal() * * Purpose: given 3 points, find cross product of sides */ void vnormal ARGS4((a,b,c,n), REAL *a, REAL *b, REAL *c, REAL *n) { REAL aa[MAXCOORD],bb[MAXCOORD]; int i; for ( i = 0 ; i < SDIM ; i++ ) { aa[i] = a[i] - c[i]; bb[i] = b[i] - c[i]; } cross_prod(aa,bb,n); } /****************************************************************** * * Function: cross_product() * * Purpose; Find 3D cross product of a and b, return in c */ void cross_prod ARGS3((a,b,c), REAL *a, REAL *b, REAL *c) { c[0] = a[1]*b[2] - a[2]*b[1]; c[1] = a[2]*b[0] - a[0]*b[2]; c[2] = a[0]*b[1] - a[1]*b[0]; } /****************************************************************** * * Function: triple_prod() * * Purpose: Find scalar triple product in 3D. */ REAL triple_prod ARGS3((a,b,c), REAL *a, REAL *b, REAL *c) { return a[0]*(b[1]*c[2] - b[2]*c[1]) - a[1]*(b[0]*c[2] - b[2]*c[0]) + a[2]*(b[0]*c[1] - b[1]*c[0]); } /****************************************************************** * * Function: dot(), dotdf(), dotf() * * Purpose: dot products of various REALs and floats. * */ /* dot product of REALS */ REAL dot ARGS3((a,b,n), REAL *a, REAL *b, int n) /* number of items */ { REAL x = 0.0; for ( ; --n >= 0 ; ) x += (*(a++))*(*(b++)); return x; } /* dot product for doubles and floats */ REAL dotdf ARGS3((a,b,n), REAL *a, float *b, int n) /* number of items */ { REAL x = 0.0; for ( ; --n >= 0 ; ) x += (*(a++))*(*(b++)); return x; } /* dot product for floats */ REAL dotf ARGS3((a,b,n), float *a, float *b, int n) /* number of items */ { REAL x = 0.0; for ( ; --n >= 0 ; ) x += (*(a++))*(*(b++)); return x; } /****************************************************************** * * Function: matvec_mul() * * Purpose: matrix times vector multiplication, c = a * b */ void matvec_mul ARGS5((a,b,c,rows,cols), REAL **a, REAL *b, REAL *c, int rows, int cols) { int i,j; for ( i = 0 ; i < rows ; i++ ) { c[i] = 0.0; for ( j = 0 ; j < cols ; j++ ) c[i] += a[i][j]*b[j]; } } /****************************************************************** * * Function: vec_mat_mul() * * Purpose: vector times matrix multiplication, c = a * b */ void vec_mat_mul ARGS5((a,b,c,rows,cols), REAL *a, REAL **b, REAL *c, int rows, int cols) { int i,j; for ( i = 0 ; i < cols ; i++ ) { c[i] = 0.0; for ( j = 0 ; j < rows ; j++ ) c[i] += a[j]*b[j][i]; } } /****************************************************************** * * Function: mat_mult() * * Purpose: matrix by matrix multiplication, c = a * b * a is imax x jmax, b is jmax x kmax, c is imax x kmax * a, b, and c need not be distinct. * Tests for zero entries in first matrix for larger matrices, * so if one matrix is sparse, put it first. */ void mat_mult ARGS6((a,b,c,imax,jmax,kmax), REAL **a, REAL **b, REAL **c, /* not assumed distinct */ int imax, int jmax,int kmax) { int i,j,k; if ( (a == c) || (b == c) ) { if ( (imax<=MAXCOORD)&&(kmax<=MAXCOORD)) { MAT2D(temp,MAXCOORD,MAXCOORD); /* local temp space */ for ( i = 0 ; i < imax ; i++ ) for ( k = 0 ; k < kmax ; k++ ) for ( j = 0, temp[i][k] = 0.0 ; j < jmax ; j++ ) temp[i][k] += a[i][j]*b[j][k]; matcopy(c,temp,imax,kmax); } else /* have to go to the effort to get temp work space */ { REAL **temp = dmatrix(0,imax-1,0,kmax-1); /* temporary storage */ for ( i = 0 ; i < imax ; i++ ) for ( j = 0 ; j < jmax ; j++ ) { REAL aa = a[i][j]; if ( aa==0.0 ) continue; for ( k = 0 ; k < kmax ; k++ ) temp[i][k] += aa*b[j][k]; } matcopy(c,temp,imax,kmax); free_matrix(temp); } } else { for ( i = 0 ; i < imax ; i++ ) { for ( k = 0 ; k < kmax ; k++ ) c[i][k] = 0.0; for ( j = 0 ; j < jmax ; j++ ) { REAL aa = a[i][j]; if ( aa == 0.0 ) continue; for ( k = 0 ; k < kmax ; k++ ) c[i][k] += aa*b[j][k]; } } } } /****************************************************************** * * Function: tr_mat_mul() * * Purpose: matrix transpose by matrix multiplication * output: c = aT*b * a is imax x jmax, b is imax x kmax, c is jmax x kmax * a, b, and c need not be distinct. */ void tr_mat_mul ARGS6((a,b,c,imax,jmax,kmax), REAL **a, REAL **b, REAL **c, /* not assumed distinct */ int imax,int jmax,int kmax) { REAL **temp; /* temporary storage, if needed */ int i,j,k; if ( (a == c) || (b == c) ) { temp = dmatrix(0,jmax-1,0,kmax-1); /* temporary storage */ for ( j = 0 ; j < jmax ; j++ ) for ( k = 0 ; k < kmax ; k++ ) for ( i = 0 ; i < imax ; i++ ) temp[j][k] += a[i][j]*b[i][k]; matcopy(c,temp,jmax,kmax); free_matrix(temp); } else { REAL *s; for ( j = 0 ; j < jmax ; j++ ) for ( k = 0, s = c[j] ; k < kmax ; k++,s++ ) { *s = 0.0; for ( i = 0 ; i < imax ; i++ ) *s += a[i][j]*b[i][k]; } } } /****************************************************************** * * Function: mat_mul_tr() * * Purpose: matrix by matrix transpose multiplication, c = a * bT * a is imax x jmax, b is kmax x jmax, c is imax x kmax * a, b, and c need not be distinct. */ void mat_mul_tr ARGS6((a,b,c,imax,jmax,kmax), REAL **a, REAL **b, REAL **c, /* not assumed distinct */ int imax, int jmax, int kmax) { REAL **temp; /* temporary storage, if needed */ int i,j,k; if ( (a == c) || (b == c) ) { temp = dmatrix(0,imax-1,0,kmax-1); /* temporary storage */ for ( i = 0 ; i < imax ; i++ ) for ( j = 0 ; j < jmax ; j++ ) for ( k = 0 ; k < kmax ; k++ ) temp[i][k] += a[i][j]*b[k][j]; matcopy(c,temp,imax,kmax); free_matrix(temp); } else { for ( k = 0 ; k < kmax ; k++ ) { for ( i = 0 ; i < imax ; i++ ) c[i][k] = 0.0; for ( j = 0 ; j < jmax ; j++ ) { REAL bb = b[k][j]; if ( bb == 0.0 ) continue; for ( i = 0 ; i < imax ; i++ ) c[i][k] += a[i][j]*bb; } } } } /****************************************************************** * * Function: mat_tsquare() * * Purpose: matrix times own transpose, b = a*aT * a and b must be different. */ void mat_tsquare ARGS4((a,b,n,m), REAL **a, /* original */ REAL **b, /* square b = a*aT */ int n, int m) /* a is nxm, b is nxn */ { int i,j; if ( a == b ) kb_error(2141,"mat_tsquare: a and b same (internal error).\n",RECOVERABLE); for ( i = 0 ; i < n ; i++ ) for ( j = 0 ; j <= i ; j++ ) b[i][j] = b[j][i] = dot(a[i],a[j],m); } /****************************************************************** * * Function: quadratic_form() * * Purpose: quadratic form evaluation, a*b*c; only uses lower triangle */ REAL quadratic_form ARGS4((a,b,c,n), REAL *a, REAL **b, REAL *c, int n) /* size */ { int i,j; REAL sum = 0.0; REAL temp; for ( i = 0 ; i < n ; i++ ) { temp = b[i][0]*c[0]; for ( j = 1 ; j <= i ; j++ ) temp += b[i][j]*c[j]; for ( ; j < n ; j++ ) temp += b[j][i]*c[j]; sum += a[i]*temp; } return sum; } /****************************************************************** * * Function: mat_inv() * * Purpose: in-place matrix inverse by gauss-jordan * returns -1 for singular matrix, * >= 0 for nonsingular, but value is not index, since * pivoting is not symmetric. * */ #define SWAP(a,b) {REAL temp = (a); (a) = (b); (b) = temp; } #define SMALL 10 int mat_inv ARGS2((a,n), REAL **a, /* matrix to invert in place */ int n) /* size of matrix */ { int *indxc,*indxr,*ipiv; int i,icol=0,irow=0,j,k,l,ll; REAL big,dum,pivinv; int retval = 1; /* default return value is success */ int temp1[SMALL],temp2[SMALL],temp3[SMALL]; /* avoid alloc for small sizes */ if ( n <= SMALL ) { indxc = temp1; indxr = temp2; ipiv = temp3; } else { /* large size */ indxc = ivector(0,n-1); indxr = ivector(0,n-1); ipiv = ivector(0,n-1); } for ( j = 0 ; j < n ; j++ ) ipiv[j] = -1; for ( i = 0 ; i < n ; i++ ) { /* find pivot */ big = 0.0; for ( j = 0 ; j < n ; j++ ) { if ( ipiv[j] != 0 ) for ( k = 0 ; k < n ; k++ ) { if ( ipiv[k] == -1 ) { if ( fabs(a[j][k]) >= big ) { big = fabs(a[j][k]); irow = j; icol = k; } } else if ( ipiv[k] > 0 ) { retval = -1; goto mat_inv_exit; } } } ++(ipiv[icol]); if ( irow != icol ) for ( l = 0 ; l < n ; l++ ) SWAP(a[irow][l],a[icol][l]) indxr[i] = irow; indxc[i] = icol; if ( a[icol][icol] == 0.0 ) { retval = -1; goto mat_inv_exit; } pivinv = 1/a[icol][icol]; a[icol][icol] = 1.0; for ( l = 0 ; l < n ; l++ ) a[icol][l] *= pivinv; for ( ll = 0 ; ll < n ; ll++ ) if ( ll != icol ) { dum = a[ll][icol]; a[ll][icol] = 0.0; for ( l = 0 ; l < n ; l++ ) a[ll][l] -= a[icol][l]*dum; } } for ( l = n-1 ; l >= 0 ; l-- ) { if ( indxr[l] != indxc[l] ) for ( k = 0 ; k < n ; k++ ) SWAP(a[k][indxr[l]],a[k][indxc[l]]) } mat_inv_exit: if ( n > SMALL ) { free_ivector(ipiv,0,n-1); free_ivector(indxr,0,n-1); free_ivector(indxc,0,n-1); } return retval; } /* end mat_inv() */ /*********************************************************************** * * Function: mat_approx_solve() * * Purpose: Solve dense linear system, possibly singular, with * criterion for omitting singular rows. Meant for * solving for redundant constraints. * * Return value: effective rank of matrix; solution in b */ REAL mat_approx_solve_epsilon = 1e-6; int mat_approx_solve ARGS3((a,n,b), REAL **a, /* matrix to invert in place */ int n, /* size of matrix */ REAL *b) /* right hand side */ { int i,j,l,ll,irow=0; REAL big,dum,pivinv; int rank = n; /* return value is effective rank of matrix */ /* normalize row magnitudes */ for ( i = 0 ; i < n ; i++ ) { REAL mag = 0.0; for ( j = 0 ; j < n ; j++ ) mag += a[i][j]*a[i][j]; if ( mag > 0.0 ) mag = 1/sqrt(mag); for ( j = 0 ; j < n ; j++ ) a[i][j] *= mag; b[i] *= mag; } /* now gaussian elimination */ for ( i = 0 ; i < n ; i++ ) { /* find pivot */ big = 0.0; for ( j = i ; j < n ; j++ ) { if ( fabs(a[j][i]) >= big ) { big = fabs(a[j][i]); irow = j; } } if ( irow != i ) { for ( l = 0 ; l < n ; l++ ) SWAP(a[irow][l],a[i][l]) SWAP(b[irow],b[i]); } if ( a[i][i] < mat_approx_solve_epsilon ) { rank--; continue; } pivinv = 1/a[i][i]; a[i][i] = 1.0; for ( l = i+1 ; l < n ; l++ ) a[i][l] *= pivinv; b[i] *= pivinv; for ( ll = 0 ; ll < n ; ll++ ) { if ( ll == i ) continue; { dum = a[ll][i]; a[ll][i] = 0.0; for ( l = 0 ; l < n ; l++ ) a[ll][l] -= a[i][l]*dum; b[ll] -= b[i]*dum; } } } return rank; } /* end mat_approx_solve() */ /****************************************************************** * * Function: mat_inv_sym() * * Purpose: in-place symmetric matrix inverse with Bunch-Kauffman pivoting * returns -1 for singular matrix, * >= 0 for nonsingular, value is index * */ #define PIV1 1 #define PIV2 2 #define PIV3 3 /* second of double pivot */ int mat_inv_sym ARGS2((a,n), REAL **a, /* matrix to invert in place */ int n) /* size of matrix */ { int *spiv; /* 1 or 2, size of pivot */ int *ipiv; /* permutation */ int i,irow=0,j,k; REAL piv; int temp1[SMALL],temp3[SMALL]; /* avoid alloc for small sizes */ int negs = 0; if ( n <= SMALL ) { spiv = temp1; ipiv = temp3; } else { /* large size */ spiv = ivector(0,n-1); ipiv = ivector(0,n-1); } for ( j = 0 ; j < n ; j++ ) ipiv[j] = j; /* First, do LDL with Bunch-Kaufman pivoting */ for ( i = 0 ; i < n ; i++ ) { /* find pivot */ /* max in pivot column */ int s = 1; /* pivot size */ double lambda = 0.0; for ( j = i+1 ; j < n ; j++ ) { if ( fabs(a[i][j]) > lambda ) { lambda = fabs(a[i][j]); irow = j; } } if ( lambda > 0.0 ) { if ( fabs(a[i][i]) > BKalpha*lambda ) s = 1; else { double sigma = 0.0; for ( j = i ; j < n ; j++ ) if ( fabs(a[j][irow]) > sigma ) sigma = fabs(a[j][irow]); if ( sigma*fabs(a[i][i]) >= BKalpha*lambda*lambda ) s = PIV1; else if ( fabs(a[irow][irow]) >= BKalpha*sigma ) { /* swap irow and i */ int itmp = ipiv[i]; ipiv[i] = ipiv[irow]; ipiv[irow] = itmp; s = PIV1; for ( j = 0 ; j < i ; j++ ) SWAP(a[i][j],a[irow][j]); SWAP(a[i][i],a[irow][irow]); for ( j = i+1 ; j < irow ; j++ ) SWAP(a[j][i],a[irow][j]); for ( j = irow+1 ; j < n ; j++ ) SWAP(a[j][i],a[j][irow]); } else { /* swap irow and i+1 */ int itmp = ipiv[i+1]; ipiv[i+1] = ipiv[irow]; ipiv[irow] = itmp; s = PIV2; if ( irow != i+1 ) { for ( j = 0 ; j <= i ; j++ ) SWAP(a[i+1][j],a[irow][j]); SWAP(a[i+1][i+1],a[irow][irow]); for ( j = i+2 ; j < irow ; j++ ) SWAP(a[j][i+1],a[irow][j]); for ( j = irow+1 ; j < n ; j++ ) SWAP(a[j][i+1],a[j][irow]); } } } } /* now pivot step */ if ( s == PIV1 ) { if ( a[i][i] == 0.0 ) { negs = -1; goto mat_inv_sym_exit; } piv = 1/a[i][i]; a[i][i] = piv; for ( j = i+1 ; j < n ; j++ ) { for ( k = i+1 ; k < j ; k++ ) a[j][k] -= a[j][i]*a[k][i]; a[j][j] -= a[j][i]*a[j][i]*piv; a[j][i] *= piv; } spiv[i] = PIV1; if ( piv < 0.0 ) negs++; } else /* s == 2, so 2 x 2 pivot */ { double t; double det = a[i][i]*a[i+1][i+1] - a[i+1][i]*a[i+1][i]; if ( det == 0.0 ) { negs = -1; goto mat_inv_sym_exit; } t = a[i][i]; a[i][i] = a[i+1][i+1]/det; a[i+1][i+1] = t/det; a[i][i+1] = a[i+1][i] /= -det; for ( j = i+1 ; j < n ; j++ ) { for ( k = i+1 ; k < j ; k++ ) a[j][k] -= a[j][i]*a[k][i] - a[j][i+1]*a[k][i+1]; t = a[i][i]*a[j][i] + a[i+1][i]*a[j][i+1]; a[j][i+1] = a[i+1][i]*a[j][i] + a[i+1][i+1]*a[j][i+1]; a[j][i] = t; } spiv[i] = PIV2; spiv[i+1] = PIV3; if ( det < 0.0 ) negs++; else if ( a[i][i] < 0.0 ) negs += 2; i++; /* extra increment */ } } /* convert from LDL to inverse */ /* invert L to upper triangle U */ for ( i = 1 ; i < n ; i++ ) { for ( j = 0 ; j < i-1 ; j++ ) { a[j][i] = -a[i][j]; for ( k = i+1 ; k < n ; k++ ) a[j][k] += a[k][i]*a[j][i]; } if ( spiv[i] == PIV3 ) for ( k = i+1 ; k < n ; k++ ) a[i-1][k] = 0.0; else { a[i-1][i] = -a[i][i-1]; for ( k = i+1 ; k < n ; k++ ) a[i-1][k] = a[k][i]*a[i-1][i]; } } /* multiply by already inverted diagonal into lower */ for ( j = 0 ; j < n ; j++ ) { if ( spiv[j] == PIV1 ) { for ( i = 0 ; i < j ; i++ ) a[j][i] = a[i][j]*a[j][j]; } else /* PIV2 */ { for ( i = 0 ; i < j-1 ; i++ ) { a[j][i] = a[i][j]*a[j][j] + a[i][j+1]*a[j+1][j]; a[j+1][i] = a[i][j+1]*a[j][j+1] + a[i][j+1]*a[j+1][j+1]; } j++; /* extra increment */ } } /* multiply lower triangle and upper to get full inverse in lower */ for ( i = 0 ; i < n ; i++ ) { for ( j = i ; j < n ; j++ ) { double sum; int first; if ( j == i ) { sum = a[i][i]; if ( spiv[j] == PIV2 ) first = i+2; else first = i+1; } else if ( (j == i-1) && (spiv[j]==PIV2) ) { sum = 0; first = i+1; } else { sum = a[i][j]*a[i][i]; first = i+1; } for ( k = first ; k < n ; k++ ) sum += a[i][k]*a[k][j]; a[i][j] = sum; } } /* unpermute lower to upper */ /* first, off diagonal */ for ( i = 0 ; i < n ; i++ ) { for ( j = 0 ; j < i ; j++ ) a[ipiv[j]][ipiv[i]] = a[i][j]; } /* and then the diagonal */ for ( i = 0 ; i < n ; i++ ) { j = i; while ( ipiv[j] != j ) { int ti = ipiv[j]; double t = a[ti][ti]; a[ti][ti] = a[j][j]; a[j][j] = t; ipiv[j] = ipiv[ti]; ipiv[ti] = ti; } } /* symmetrize from upper to lower */ for ( i = 0 ; i < n ; i++ ) for ( j = 0 ; j < i ; j++ ) a[i][j] = a[j][i]; mat_inv_sym_exit: if ( n > SMALL ) { free_ivector(ipiv,0,n-1); free_ivector(spiv,0,n-1); } return negs; } /************************************************************************* * * function: LD_factor() * * purpose: factor dense symmetric matrix into LDL^T form. Touches only * lower triangle. Does not pivot, so should be used only * on positive definite matrices. */ int LD_factor ARGS2(( H, N ), REAL **H, /* lower triangular, row by row */ int N) /* size */ { int i,j,k; int negs = 0; #ifdef BLAS if ( blas_flag ) { char uplo = 'U'; int stride = H[1] - H[0]; int info=0; DPOTRF(&uplo,&N,&H[0][0],&stride,&info); } else #endif for ( i = 0 ; i < N ; i++ ) /* pivot (i,i) */ { REAL pivot = H[i][i]; if ( pivot == 0.0 ) { kb_error(2553,"Trying to factor singular matrix; using 1 as pivot.\n", WARNING); pivot = 1.0; } if ( pivot < 0 ) negs++; pivot = 1/pivot; for ( j = i+1 ; j < N ; j++ ) /* row j */ { REAL x = H[j][i]; for ( k = i+1 ; k < j ; k++ ) /* col k */ H[j][k] -= x*H[k][i]; H[j][i] = x*pivot; H[j][j] -= x*x*pivot; } } return negs; } /*********************************************************************** * * function: LD_solve * * purpose: solve for a single rhs using matrix factored by LD_factor. */ void LD_solve ARGS4((LD,B,X,N), REAL **LD, /* from LD_factor() */ REAL *B, /* rhs */ REAL *X, /* solution */ int N) { int i,j; #ifdef BLAS if ( blas_flag ) { char uplo = 'U'; int stride = LD[1] - LD[0]; int info=0; int nrhs = 1; /* number of rhs */ for ( i = 0 ; i < N ; i++ ) X[i] = B[i]; /* since DPOTRS overwrites */ DPOTRS(&uplo,&N,&nrhs,&LD[0][0],&stride,&X[0],&N,&info); } else #endif { for ( i = 0 ; i < N ; i++ ) { X[i] = B[i]; for ( j = 0 ; j < i ; j++ ) X[i] -= LD[i][j]*X[j]; } for ( i = 0 ; i < N ; i++ ) X[i] /= LD[i][i]; for ( i = N-1 ; i >= 0 ; i-- ) { for ( j = i+1 ; j < N ; j++ ) X[i] -= LD[j][i]*X[j]; } } } /****************************************************************** * * Function: det_adjoint() * * Purpose: calculates determinant in place and leaves adjoint transpose */ REAL det_adjoint ARGS2((a,n), REAL **a, /* matrix to change in place */ int n) /* size of matrix */ { int *indxc,*indxr,*ipiv; int i,icol=0,irow=0,j,k,l,ll; REAL big,dum,pivinv,piv; int temp1[SMALL],temp2[SMALL],temp3[SMALL]; /* avoid alloc for small sizes */ REAL det = 1.0; /* will multiply by pivots */ if ( n <= 0 ) kb_error(1205,"Internal error: Matrix size not positive.",RECOVERABLE); if ( n == 1 ) { det = a[0][0]; a[0][0] = 1.0; return det; } if ( n == 2 ) { REAL temp; det = a[0][0]*a[1][1] - a[0][1]*a[1][0]; temp = a[0][0]; a[0][0] = a[1][1]; a[1][1] = temp; a[0][1] = -a[0][1]; a[1][0] = -a[1][0]; return det; } if ( n <= SMALL ) { indxc = temp1; indxr = temp2; ipiv = temp3; } else { /* large size */ indxc = ivector(0,n-1); indxr = ivector(0,n-1); ipiv = ivector(0,n-1); } for ( j = 0 ; j < n ; j++ ) ipiv[j] = -1; for ( i = 0 ; i < n-1 ; i++ ) { big = 0.0; for ( j = 0 ; j < n ; j++ ) if ( ipiv[j] != 0 ) for ( k = 0 ; k < n ; k++ ) { if ( ipiv[k] == -1 ) { if ( fabs(a[j][k]) >= big ) { big = fabs(a[j][k]); irow = j; icol = k; } } else if ( ipiv[k] > 0 ) { kb_error(1206,"Internal: ipiv > 0.\n",WARNING); det = 0.0; goto det_exit; } } ++(ipiv[icol]); if ( irow != icol ) { for ( l = 0 ; l < n ; l++ ) SWAP(a[irow][l],a[icol][l]) det = -det; } indxr[i] = irow; indxc[i] = icol; det *= a[icol][icol]; /* build determinant */ if ( a[icol][icol] == 0.0 ) { goto det_lowrank; } pivinv = 1/a[icol][icol]; a[icol][icol] = 1.0; for ( l = 0 ; l < n ; l++ ) a[icol][l] *= pivinv; for ( ll = 0 ; ll < n ; ll++ ) if ( ll != icol ) { dum = a[ll][icol]; a[ll][icol] = 0.0; for ( l = 0 ; l < n ; l++ ) a[ll][l] -= a[icol][l]*dum; } } /* special treatment for last pivot; works even if zero */ for ( j = 0 ; j < n ; j++ ) if ( ipiv[j] != 0 ) { irow = icol = j; break; } indxr[n-1] = irow; indxc[n-1] = icol; piv = a[icol][icol]; a[icol][icol] = 1.0; for ( l = 0 ; l < n ; l++ ) a[icol][l] *= det; for ( ll = 0 ; ll < n ; ll++ ) if ( ll != icol ) { dum = a[ll][icol]; a[ll][icol] = 0.0; for ( l = 0 ; l < n ; l++ ) a[ll][l] = a[ll][l]*piv*det - a[icol][l]*dum; } det *= piv; for ( l = n-1 ; l >= 0 ; l-- ) { if ( indxr[l] != indxc[l] ) for ( k = 0 ; k < n ; k++ ) SWAP(a[k][indxr[l]],a[k][indxc[l]]) } det_exit: if ( n > SMALL ) { free_ivector(ipiv,0,n-1); free_ivector(indxr,0,n-1); free_ivector(indxc,0,n-1); } return det; det_lowrank: /* rank less than n-1, so adjoint = 0 */ for ( i = 0 ; i < n ; i++ ) for ( j = 0 ; j < n ; j++ ) a[i][j] = 0.0; det = 0.0; goto det_exit; } /****************************************************************** * * Function: determinant() * * Purpose: calculates determinant; no change in matrix for 3x3 or smaller * otherwise calls det_adjoint() */ REAL determinant ARGS2((a,n), REAL **a, /* matrix to change in place */ int n) /* size of matrix */ { if ( n == 1 ) { return a[0][0]; } if ( n == 2 ) { return a[0][0]*a[1][1] - a[0][1]*a[1][0]; } if ( n == 3 ) { return a[0][0]*(a[1][1]*a[2][2] - a[1][2]*a[2][1]) - a[0][1]*(a[1][0]*a[2][2] - a[1][2]*a[2][0]) + a[0][2]*(a[1][0]*a[2][1] - a[1][1]*a[2][0]); } return det_adjoint(a,n); /* other cases */ } /****************************************************************** * * Function: print_matrix() * */ void print_matrix ARGS3((a,rows,cols), REAL **a, int rows, int cols) { int i,j; for ( i = 0 ; i < rows ; i++ ) { msg[0] = 0; for ( j = 0 ; j < cols ; j++ ) sprintf(msg+strlen(msg),"%10.6f ",(DOUBLE)a[i][j]); strcat(msg,"\n"); outstring(msg); } } /****************************************************************** * * Function: exterior_product() * * Purpose: conversion of k vectors to a k-vector * components in index lexicographic order */ void exterior_product ARGS4((v,w,k,n), REAL **v, /* list of k vectors */ REAL *w, /* returned k-vector */ int k, /* number of vectors */ int n) /* space dimension */ { /* anticipate only small k, so just brute force */ int i1,i2,i3; switch ( k ) { case 1: for ( i1 = 0 ; i1 < n ; i1++ ) *(w++) = v[0][i1]; break; case 2: for ( i1 = 0 ; i1 < n ; i1++ ) for ( i2 = i1+1 ; i2 < n ; i2++ ) *(w++) = v[0][i1]*v[1][i2] - v[0][i2]*v[1][i1]; break; case 3: for ( i1 = 0 ; i1 < n ; i1++ ) for ( i2 = i1+1 ; i2 < n ; i2++ ) for ( i3 = i2+1 ; i3 < n ; i3++ ) *(w++) = v[0][i1]*v[1][i2]*v[2][i3] + v[0][i2]*v[1][i3]*v[2][i1] + v[0][i3]*v[1][i1]*v[2][i2] - v[0][i1]*v[1][i3]*v[2][i2] - v[0][i3]*v[1][i2]*v[2][i1] - v[0][i2]*v[1][i1]*v[2][i3] ; break; default: sprintf(errmsg,"Exterior product of %d vectors.\n",k); kb_error(1207,errmsg,RECOVERABLE); break; } } /********************************************************************** * * function: kernel_basis() * * purpose: Find basis for kernel of matrix (nullspace of rows) */ int kernel_basis ARGS4((a,ker,imax,jmax), REAL **a, /* the matrix, will be altered */ REAL **ker, /* for basis vectors in columns */ int imax, int jmax) /* rows and columns of a */ { int i,j,k; int pivrow[20]; /* pivot row in column */ int n; /* nullity */ for ( j = 0 ; j < jmax ; j++ ) pivrow[j] = -1; /* mark as no pivot in col */ /* get row echelon form, pivot largest in each row */ for ( i = 0 ; i < imax ; i++ ) { int piv = -1; REAL b,big,p; /* find largest element in row */ big = 0.0; for ( j = 0 ; j < jmax ; j++ ) if ( fabs(a[i][j]) > big ) { big = fabs(a[i][j]); piv = j; } if ( piv == -1 ) continue; /* row of zeros */ pivrow[piv] = i; /* pivot step */ p = a[i][piv]; for ( j = 0 ; j < jmax ; j++ ) a[i][j] /= p; for ( k = 0 ; k < imax ; k++ ) { if ( k == i ) continue; b = a[k][piv]; for ( j = 0 ; j < jmax ; j++ ) a[k][j] -= b*a[i][j]; } } /* now find kernel basis */ for ( j = 0, n = 0 ; j < jmax ; j++ ) { if ( pivrow[j] >= 0 ) continue; /* column has leading 1 */ /* column j is parameter column */ for ( k = 0 ; k < jmax ; k++ ) { if ( pivrow[k] >= 0 ) ker[k][n] = -a[pivrow[k]][j]; else if ( k == j ) ker[k][n] = 1.0; else ker[k][n] = 0.0; } n++; } return n; /* nullity */ } /********************************************************************** * * function: kernel_basis_rows() * * purpose: Find basis for kernel of matrix (nullspace of rows) * Returns basis rowwise. * basis vectors normalized, but not orthohormal. */ int kernel_basis_rows ARGS4((a,ker,imax,jmax), REAL **a, /* the matrix, will be altered */ REAL **ker, /* for basis vectors in rows */ int imax, int jmax) /* rows and columns of a */ { int i,j,k; int pivrow[20]; /* pivot row in column */ int pivcol[20]; /* pivot column in row */ int n; /* nullity */ int detsign=1; /* to try to keep orientation of normal positive */ for ( j = 0 ; j < jmax ; j++ ) pivrow[j] = -1; /* mark as no pivot in col */ /* get row echelon form, pivot largest in each row */ for ( i = 0 ; i < imax ; i++ ) { int piv = -1; REAL b,big,p; /* find largest element in row */ big = 0.0; for ( j = 0 ; j < jmax ; j++ ) if ( fabs(a[i][j]) > big ) { big = fabs(a[i][j]); piv = j; } if ( piv == -1 ) continue; /* row of zeros */ pivrow[piv] = i; pivcol[i] = piv; /* pivot step */ p = a[i][piv]; if ( p < 0 ) detsign = -detsign; for ( j = 0 ; j < jmax ; j++ ) a[i][j] /= p; for ( k = 0 ; k < imax ; k++ ) { if ( k == i ) continue; b = a[k][piv]; for ( j = 0 ; j < jmax ; j++ ) a[k][j] -= b*a[i][j]; } } /* now find kernel basis */ for ( j = 0, n = 0 ; j < jmax ; j++ ) { int sign; if ( pivrow[j] >= 0 ) continue; /* column has leading 1 */ /* column j is parameter column */ pivcol[imax+n] = j; /* get sign for pos det */ for (sign = detsign, k=0 ; k <= imax+n ; k++ ) for ( i = k+1 ; i <= imax+n ; i++ ) if ( pivcol[k] > pivcol[i] ) sign = -sign; for ( k = 0 ; k < jmax ; k++ ) { if ( pivrow[k] >= 0 ) ker[n][k] = -sign*a[pivrow[k]][j]; else if ( k == j ) ker[n][k] = sign; else ker[n][k] = 0.0; } n++; } /* normalize */ for ( i = 0 ; i < n ; i ++ ) { REAL mag; mag = sqrt(SDIM_dot(ker[i],ker[i])); for ( j = 0 ; j < SDIM ; j++ ) ker[i][j] /= mag; } return n; /* nullity */ } /********************************************************************* * * function: matrix_index() * * purpose: return number of negative eigenvalues of matrix * Does not destroy original. For symmetric matrices. * */ int matrix_index ARGS2((M,n), REAL **M, /* square matrix */ int n) /* size */ { REAL **a = dmatrix(0,n-1,0,n-1); REAL *tempptr; int row,col,prow=0; REAL maxp; int i,j; int indx = 0; REAL temp; REAL *firstrow; /* for proper freeing after swapping rows */ firstrow = a[0]; matcopy(a,M,n,n); /* basically, gauss elimination to lower triangular form with partial pivoting. */ for ( col = 0 ; col < n ; col++ ) { /* find max pivot in diagonal */ maxp = 0.0; for ( row = col ; row < n ; row++ ) if ( fabs(a[row][row]) > maxp ) { maxp = fabs(a[row][row]); prow = row; } if ( maxp == 0.0 ) continue; if ( prow != col ) { /* swap rows and columns to keep symmetric */ tempptr = a[prow]; a[prow] = a[col]; a[col] = tempptr; for ( j = col; j < n ; j++ ) { temp = a[j][col]; a[j][col] = a[j][prow]; a[j][prow] = temp; } } if ( a[col][col] < 0.0 ) indx++; for ( row = col+1 ; row < n ; row++ ) for ( i = col+1 ; i < n ; i++ ) { a[row][i] -= a[row][col]/a[col][col]*a[col][i]; } } a[0] = firstrow; free_matrix(a); return indx; } /**************************************************************************** * * function: jacobi_eigenpairs() * * purpose: find eigenpairs of small dense symmetric matrix by jacobi rotations * From Numerical Recipes 11.1. * * output: eigenvalues in d[], sorted in descending order, and * corresponding eigenvectors in columns of v[][]. */ void jacobi_eigenpairs ARGS5((a,n,d,v,work), REAL **a, /* input matrix, destroyed */ int n, /* size */ REAL *d, /* for return of eigenvalues */ REAL **v, /* for return of eigenvectors */ REAL *work) /* space for 2*n values */ { REAL sm,h,tresh,dum,g,t,theta,tau,s,c; int i,iq,ip,nrot,j; REAL *z,*b; z = work; b = work+n; /* initialize v to identity */ for ( ip = 0 ; ip < n ; ip++ ) { for ( iq = 0 ; iq < n ; iq++ ) v[ip][iq] = 0.0; v[ip][ip] = 1.0; } for ( ip = 0 ; ip < n ; ip++ ) { b[ip] = a[ip][ip]; d[ip] = b[ip]; z[ip] = 0.0; } nrot = 0; for ( i = 1 ; i < 50 ; i++ ) { sm = 0.0; for ( ip = 0 ; ip < n-1; ip++ ) for ( iq = ip + 1 ; iq < n ; iq++ ) sm += fabs(a[ip][iq]); if ( sm == 0.0 ) goto jacobi_exit; /* normal exit */ if ( i < 4 ) tresh = .2*sm/n/n; else tresh = 0.0; for ( ip = 0 ; ip < n-1 ; ip++ ) for ( iq = ip+1 ; iq < n ; iq++ ) { g = 100*fabs(a[ip][iq]); dum = fabs(d[ip]); if ( (i > 4) && (dum+g == dum) && (fabs(d[iq])+g == fabs(d[iq])) ) a[ip][iq] = 0.0; else if ( fabs(a[ip][iq]) > tresh ) { h = d[iq] - d[ip]; if ( fabs(h) + g == fabs(h) ) t = a[ip][iq]/h; else { theta = .5*h/a[ip][iq]; t = 1.0/(fabs(theta) + sqrt(1 + theta*theta)); if ( theta < 0.0 ) t = -t; } c = 1.0/sqrt(1 + t*t); s = t*c; tau = s/(1+c); h = t*a[ip][iq]; z[ip] -= h; z[iq] += h; d[ip] -= h; d[iq] += h; a[ip][iq] = 0.0; for ( j = 0 ; j <= ip-1 ; j++ ) { g = a[j][ip]; h = a[j][iq]; a[j][ip] = g - s*(h+g*tau); a[j][iq] = h + s*(g-h*tau); } for ( j = ip+1 ; j <= iq-1 ; j++ ) { g = a[ip][j]; h = a[j][iq]; a[ip][j] = g - s*(h+g*tau); a[j][iq] = h + s*(g-h*tau); } for ( j = iq+1 ; j < n ; j++ ) { g = a[ip][j]; h = a[iq][j]; a[ip][j] = g - s*(h+g*tau); a[iq][j] = h + s*(g-h*tau); } for ( j = 0 ; j < n ; j++ ) { g = v[j][ip]; h = v[j][iq]; v[j][ip] = g - s*(h+g*tau); v[j][iq] = h + s*(g-h*tau); } nrot++; } /* end if */ } /* end iq */ /* end ip */ for ( ip = 0 ; ip < n ; ip++ ) { b[ip] += z[ip]; d[ip] = b[ip]; z[ip] = 0.0; } } /* end i */ kb_error(2548,"50 Jacobi iterations should never happen.\n",WARNING); return; jacobi_exit: /* sort eigenpairs in descending order, insertion sort */ for ( i = 0 ; i < n-1 ; i++ ) { REAL p; int k; k = i; p = d[i]; for ( j = i + 1 ; j < n ; j++ ) { if ( d[j] >= p ) { k = j; p = d[j]; } } if ( k != i ) { d[k] = d[i]; d[i] = p; for ( j = 0 ; j < n ; j++ ) { p = v[j][i]; v[j][i] = v[j][k]; v[j][k] = p; } } } return; } /********************************************************************** * * function: det_hess(a,h,n) * * Purpose: find hessian of determinant as function of entries * * Returns h[i1][j1][i2][j2] as d^2 det(a)/da[i1][j1]/da[i2][j2] */ void det_hess ARGS3((a,h,n), REAL **a, REAL ****h, int n) /* size */ { int i1,i2,jj1,j2,k; /* copy original matrix into h lots of times */ for ( i1 = 0 ; i1 < n ; i1++ ) for ( jj1 = 0 ; jj1 < n ; jj1++ ) for ( i2 = 0 ; i2 < n ; i2++ ) for ( j2 = 0 ; j2 < n ; j2++ ) h[i1][jj1][i2][j2] = a[i2][j2]; /* replace element row and column with identity stuff */ for ( i1 = 0 ; i1 < n ; i1++ ) for ( jj1 = 0 ; jj1 < n ; jj1++ ) { for ( k = 0 ; k < n ; k++ ) { h[i1][jj1][i1][k] = 0.0; h[i1][jj1][k][jj1] = 0.0; } h[i1][jj1][i1][jj1] = 1.0; } /* find adjoints */ for ( i1 = 0 ; i1 < n ; i1++ ) for ( jj1 = 0 ; jj1 < n ; jj1++ ) { det_adjoint(h[i1][jj1],n); h[i1][jj1][jj1][i1] = 0.0; /* need fixup */ } /* transpose to get back to hessian */ for ( i1 = 0 ; i1 < n ; i1++ ) for ( jj1 = 0 ; jj1 < n ; jj1++ ) for ( i2 = 1 ; i2 < n ; i2++ ) for ( j2 = 0 ; j2 < i2 ; j2++ ) { REAL tmp = h[i1][jj1][i2][j2]; h[i1][jj1][i2][j2] = h[i1][jj1][j2][i2]; h[i1][jj1][j2][i2] = tmp; } } /********************************************************************** * * function: gram_schmidt() * * purpose: orthonormalize rows of a matrix * * return: number of independent rows */ int gram_schmidt ARGS3((mat,rows,cols), REAL **mat, int rows, int cols) { int i,j,k; REAL d; for ( i = 0 ; i < rows ; i++ ) { for ( j = 0 ; j < i ; j++ ) { REAL c = dot(mat[i],mat[j],cols); for ( k = 0 ; k < cols ; k++ ) mat[i][k] -= c*mat[j][k]; } d = dot(mat[i],mat[i],cols); if ( d == 0.0 ) { rows--; for ( k = 0 ; k < cols ; k++ ) mat[i][k] = mat[rows][k]; i--; } else { d = 1/sqrt(d); for ( k = 0 ; k < cols ; k++ ) mat[i][k] *= d; } } return rows; } /************************************************************************** * * function: mat_inv_sparse() * * purpose: invert large sparse symmetric matrix in place, with input in * dense format. * Should be just temporary until callers get fully integrated * with sparse matrix stuff. */ int mat_inv_sparse ARGS2((a,n), REAL **a, /* matrix to invert in place */ int n) /* size of matrix */ { struct linsys S; int i,j,k; REAL *BB,*Y; memset(&S,0,sizeof(S)); S.N = n; /* count entries */ S.IA = (int *)temp_calloc(n+1,sizeof(int)); for ( i = 0, k = 0 ; i < n ; i++ ) { S.IA[i] = k + A_OFF; for ( j = i ; j < n ; j++ ) if ( a[i][j] != 0.0 ) k++; } S.IA[i] = k + A_OFF; /* allocate main space and fill */ S.JA = (int *)temp_calloc(k,sizeof(int)); S.A = (REAL *)temp_calloc(k,sizeof(REAL)); S.P = (int *)temp_calloc(n,sizeof(int)); S.IP = (int *)temp_calloc(n,sizeof(int)); for ( i = 0, k = 0 ; i < n ; i++ ) { for ( j = i ; j < n ; j++ ) if ( a[i][j] != 0.0 ) { S.A[k] = a[i][j]; S.JA[k] = j + A_OFF; k++; } } #ifdef USEMINDEG xmd_factor(&S); /* use mindeg, since internals known */ #else ysmp_factor(&S); #endif /* solve back for inverse matrix */ BB = (REAL*)temp_calloc(S.N+10,sizeof(REAL)); /* intermediate solutions */ Y = (REAL*)temp_calloc(S.N+10,sizeof(REAL)); /* intermediate solutions */ for ( i = 0 ; i < n ; i++ ) { memset(BB,0,n*sizeof(REAL)); BB[i] = 1.0; #ifdef USEMINDEG /* mindeg is too specific, using vertex structure to optimize for hessian */ /* solve U^T Y = B */ int *jp;REAL *e; for ( j = 0 ; j < S.N ; j++ ) { int start,end; Y[j] = BB[S.LJA[S.LIJA[j]]]; /* for BK inner permutation */ if ( S.psize[j] == FIRSTOFPAIR ) start = 2; else start = 1; end = S.LIA[j+1]; for ( i=S.LIA[j]+start, e=S.LA+i , jp=S.LJA+S.LIJA[j]+start ; i < end ; i++,e++,jp++ ) BB[*jp] -= (*e)*Y[j]; } /* solve D V = Y (will use Y to store V) */ for ( j = 0 ; j < S.N ; j++ ) { if ( S.psize[j] == ONEBYONE ) Y[j] /= S.LA[S.LIA[j]]; else if ( S.psize[j] == ZEROPIVOT ) Y[j] = 0.0; else { REAL piv[2][2]; REAL pinv[2][2]; REAL det,yy; piv[0][0] = S.LA[S.LIA[j]]; piv[0][1] = piv[1][0] = S.LA[S.LIA[j]+1]; piv[1][1] = S.LA[S.LIA[j+1]]; det = piv[0][0]*piv[1][1] - piv[0][1]*piv[1][0]; pinv[0][0] = piv[1][1]/det; pinv[1][0] = pinv[0][1] = -piv[0][1]/det; pinv[1][1] = piv[0][0]/det; yy = Y[j]*pinv[0][0] + Y[j+1]*pinv[1][0]; Y[j+1] = Y[j]*pinv[0][1] + Y[j+1]*pinv[1][1]; Y[j] = yy; j++; } } /* solve U X = V */ for ( j = S.N-1 ; j >= 0 ; j-- ) { int start,end; if ( S.psize[j] == FIRSTOFPAIR ) start = 2; else start = 1; end = S.LIA[j+1]; for ( k=S.LIA[j]+start, e=S.LA+k, jp=S.LJA+S.LIJA[j]+start ; k < end ; k++,e++,jp++ ) Y[j] -= (*e)*BB[*jp]; BB[S.LJA[S.LIJA[j]]] = Y[j]; } /* unpermute */ for ( j = 0 ; j < S.N ; j++ ) a[i][S.P[j]] = BB[j]; /* end of mindeg version */ #else ysmp_solve(&S,BB,BB); for ( j = 0 ; j < S.N ; j++ ) a[i][j] = BB[j]; #endif } temp_free((char*)Y); temp_free((char*)BB); free_system(&S); return 1; } /************************************************************************* General sparse matrix construction scheme. Does not assume symmetry. Usage: call sp_hash_init() to initialize, sp_hash() to add value, sp_hash_end() at end to sort and gather into linear system. Some benchmarks for using sparse_constraints vs dense constraints on tgrid4: Seconds for 'g' Bodies Sparse Dense 16 64 0.0141 256 0.9281 1024 0.0953 58.36 memory 13MB (dense) 4096 0.687 16384 3.737 65536 27.3 262144 182.5 10^9 memory 555MB (sparse) 64GB(dense) So there is a five million fold speed-up for the largest sparse system my machine can handle. No jiggling, so only one pass through volume projection. With jiggling and zero scale, single iteration shows volumes projected back to targets with maximum error 5e-7! Benchmarking on twointor, likewise no jiggle, m 0, g: Bodies Sparse Dense 2 0.0048 0.0025 4 0.0066 0.0040 16 0.0181 0.0147 64 0.0722 0.0713 256 0.285 1.118 1024 1.73 59.1 memory 45MB(dense) 4096 16.3 memory 116MB(sparse) 8192 145.5 memory 250MB(sparse) Note twointor.fe has more elements per body (13 v, 36 f) than tgrid4.fe (1 v, 1 f) and has a denser constraint matrix, being three dimensional instead of two dimensional. **************************************************************************/ #define SP_PRIME 99991 #define sp_hash(row,col) (abs((row)*97+(col)*SP_PRIME)) /******************************************************************** * * function: sp_hash_init() * * purpose: Initialize hash table. */ void sp_hash_init ARGS2((S,size_estimate), struct linsys *S, int size_estimate) /* from sp_hash_end of previous invocation */ { int i; int estimate = SDIM*(web.skel[VERTEX].max_ord+10); S->table_size = size_estimate > 0 ? ( size_estimate > estimate ? estimate : size_estimate) : estimate; S->max_fill = 4*S->table_size/5; /* leave enough empty for decent hash */ if ( !hessian_quiet_flag ) { sprintf(msg,"Sparse init alloc: %d\n",S->table_size); outstring(msg); } S->hashcount = 0; if ( S->hashtable ) temp_free((char*)S->hashtable); S->hashtable = (struct sp_entry *)temp_calloc(S->table_size,sizeof(struct sp_entry)); for ( i = 0 ; i < S->table_size ; i++ ) S->hashtable[i].row = HASHEMPTY; S->hash_extraprobes = 0; } /******************************************************************** * * function: sp_hash_expand() * * purpose: Expands hash table */ void sp_hash_expand ARGS1((S), struct linsys *S) { struct sp_entry *newtable,*oldtable; int i; struct sp_entry *e; int newsize; int oldsize = S->table_size; if ( !S->hashtable ) sp_hash_init(S,0); newsize = S->table_size*2; oldtable = S->hashtable; newtable = (struct sp_entry *)temp_calloc(newsize,sizeof(struct sp_entry)); for ( i = 0 ; i < newsize ; i++ ) newtable[i].row = HASHEMPTY; S->table_size = newsize; S->max_fill = 4*S->table_size/5; S->hashtable = newtable; /* reinsert */ for ( i = 0, e = oldtable ; i < oldsize ; i++,e++ ) if ( e->row != HASHEMPTY ) { int spot = sp_hash(e->row,e->col) % S->table_size; struct sp_entry *ee; for ( ee = S->hashtable + spot; ee->row != HASHEMPTY ; spot++ ) { if ( spot == S->table_size ) spot = 0; ee = S->hashtable + spot; } *ee = *e; } temp_free((char*)oldtable); if ( !hessian_quiet_flag ) { sprintf(msg,"Expanded hashtable size: %d.\n",S->table_size); outstring(msg); } } /******************************************************************** * * function: sp_hash_search() * * purpose: Finds existing entry or allocates entry. * Installs key values, and adds hessian value. */ void sp_hash_search ARGS4((S,row,col,value), struct linsys *S, int row, int col, /* Note these are in reverse order as from hessian version */ REAL value) /* value to add */ { struct sp_entry *e; int spot; if ( value == 0.0 ) return; if ( S->hashcount >= S->max_fill ) sp_hash_expand(S); /* search hash table */ spot = sp_hash(row,col) % S->table_size; e = S->hashtable + spot; while ( e->row != HASHEMPTY ) { if ( (e->row == row) && (e->col == col) ) { e->value += value; return; } spot++; if ( spot >= S->table_size ) spot -= S->table_size; e = S->hashtable + spot; S->hash_extraprobes++; } /* if here, then have empty slot and need to insert */ e->col = col; e->row = row; S->hashcount++; e->value = value; } /******************************************************************** * * function: sp_hash_end() * * purpose: Puts entries in sparse packed format; allocates own lists. * Create sorted list of sparse entries, row major order * Does 2-digit radix sort on row,col with count sort on each. * Deallocate hessian hash table. * * return: Estimated size of hash table to use next time. */ int sp_hash_end ARGS4((S,rows,cols,index_start), struct linsys *S, int rows, /* number of rows */ int cols, /* number of columns */ int index_start) /* for 0 or 1 based indexing */ { int i; struct sp_entry *e; int *counts; int *starts; int *spots; int sum,oldsum; S->N = rows; S->maxN = rows > cols ? rows : cols; S->IA = (int *)temp_calloc(S->maxN+1,sizeof(int)); counts = (int *)temp_calloc(3*cols+1,sizeof(int)); starts = counts + cols; spots = starts + cols + 1; /* first stage: column binning */ /* count entries in columns */ for ( i = 0, e = S->hashtable ; i < S->table_size ; i++, e++ ) if ( e->row != HASHEMPTY ) counts[e->col]++; /* get starting points of each column */ for ( i = 0, sum = 0 ; i < cols ; i++ ) { spots[i] = starts[i] = sum; sum += counts[i]; } starts[cols] = sum; S->maxA = sum + S->maxN; /* extra room for later expansion */ S->JA = (int *)temp_calloc(S->maxA,sizeof(int)); S->A = (REAL *)temp_calloc(S->maxA,sizeof(REAL)); /* sort into column bins */ for ( i = 0, e = S->hashtable ; i < S->table_size ; i++,e++ ) { struct sp_entry eorig; struct sp_entry etmp; if ( e->row == HASHEMPTY ) continue; if ( i < spots[e->col] ) continue; /* already in place */ eorig = *e; e->row = HASHEMPTY; do { /* follow chain of replacements */ int spot; spot = spots[eorig.col]++; etmp = S->hashtable[spot]; S->hashtable[spot] = eorig; eorig = etmp; } while ( eorig.row != HASHEMPTY ); /* stop when get back to empty */ } /* Second stage: row binning */ /* count entries in row */ for ( i = 0, e = S->hashtable ; i < sum ; i++, e++ ) S->IA[e->row]++; /* get starting points of each row */ for ( i = 0, sum = 0 ; i < rows ; i++ ) { oldsum = sum; sum += S->IA[i]; S->IA[i] = oldsum; } S->IA[rows] = sum; /* sort into row bins */ for ( i = 0, e = S->hashtable ; i < sum ; i++,e++ ) { S->JA[S->IA[e->row]] = e->col; S->A[S->IA[e->row]] = e->value; S->IA[e->row]++; } /* put back IA */ for ( i = rows ; i > 0 ; i-- ) S->IA[i] = S->IA[i-1]; S->IA[0] = 0; if ( index_start ) { for ( i = 0 ; i < S->IA[rows] ; i++ ) S->JA[i] += index_start; for ( i = 0 ; i <= rows ; i++ ) S->IA[i] += index_start; } temp_free((char*)counts); if ( !hessian_quiet_flag ) { sprintf(msg,"Sparse entries: %d Final hashtable size: %d\n", S->hashcount, S->table_size); outstring(msg); sprintf(msg,"Hash extra probes: %d\n",S->hash_extraprobes); outstring(msg); } temp_free((char*)S->hashtable); S->hashtable = NULL; return S->IA[S->N] + S->IA[S->N]/3; /* estimate for next time */ } /******************************************************************** End sparse hash routines. ********************************************************************/ /******************************************************************* Using BLAS and LAPACK to factor large dense symmetric matrix for Hessian with lots of constraints. To enable, -DBLAS in Makefile, and link with libblas and liblapack (ATLAS or whatever; Intel Math Kernel Library mkl_c.lib for Intel; needs mkl_sys.dll also). Command line toggle blas_enable. Usage: Incoming matrix should be block lower triangular, with blocksize BLAS_BLOCKSIZE. Full blocks on diagonal, although only lower triangle has to filled on entry to LD_block_factor(). Last odd rows also have to be full multiple of BLAS_BLOCKSIZE. Call LD_block_factor(H,N) first, then LD_block_solve(H,B,X,N) to solve for one right hand side. ********************************************************************/ /* Timings on block factoring, 1200 MHz dual Athlon MP. Entries are Mflops Matrix size block 512 1024 2048 4096 8192 size 16 233 225 204 192 184 32 340 360 366 371 373 64 315 362 410 443 459 128 211 283 340 377 399 256 131 184 240 288 321 512 99 122 164 213 254 For comparison, my LDL in plain C runs at 32 Mflops, and LAPACK DPPTRF at 66 Mflops. Seems to use both CPUs for the DGEMM matrix multiplies, but only one for factoring. */ #ifdef BLAS /* CPU clock timing */ __int32 LD_block_factor_elapsed_time[2]; int blocksize = BLAS_BLOCKSIZE; static REAL workspace[BLAS_BLOCKSIZE][BLAS_BLOCKSIZE]; static REAL *work[BLAS_BLOCKSIZE]; static REAL workv[BLAS_BLOCKSIZE]; static int ipiv[BLAS_BLOCKSIZE]; /* for Bunch-Kaufman pivot info */ /************************************************************************* * * function: LD_block_factor() * * Purpose: BLAS and LAPACK used to block factor symmetric dense matrix. * Usage: Incoming matrix should be block lower triangular, with * blocksize BLAS_BLOCKSIZE. Full blocks on diagonal, although * only lower triangle has to filled on entry to LD_block_factor(). * Last odd rows also have to be full multiple of BLAS_BLOCKSIZE. * H will be overwritten. * * Return value: >= 0 index of matrix * < 0 error */ /********************************************************************** * * function: kb_DSYTRF() * * purpose: My replacement for buggy MKL code for DSYTRF * Does in-place Bunch-Kaufman factoring of lowet * triangular dense matrix (although full storage room). * * returns: index of matrix. */ REAL BK_alpha = 0.6404; void L_swap_columns(H,N,stride,i,r) REAL *H; int N; int stride; int i,r; /* the columns to swap */ { int j; REAL tmp; REAL *ispot; REAL *rspot; if ( i == r ) return; if ( i > r ) { j = i; i = r; r = j; } for ( j=0, ispot=H+i*stride, rspot=H+r*stride ; j < i ; j++,ispot++,rspot++ ) { tmp = *ispot; *ispot = *rspot; *rspot = tmp; } tmp = *ispot; *ispot = H[r*stride+r]; H[r*stride+r] = tmp; for ( j = i+1, ispot += stride, rspot++ ; j < r ; j++,ispot+=stride,rspot++ ) { tmp = *ispot; *ispot = *rspot; *rspot = tmp; } for ( j = r+1, ispot+=stride,rspot+=stride; j < N ; j++,ispot+=stride,rspot+= stride) { tmp = *ispot; *ispot = *rspot; *rspot = tmp; } } /************************************************************************ * * function: kb_DSYTRF() * * purpose: Bunch-Kaufman factor dense matrix. * Uses lower triangle of full matrix. * * return: Index of matrix (number of negative entries on diagonal) */ int kb_DSYTRF(N,H,stride,ipiv,info) int N; /* matrix size */ REAL *H; /* start of storage */ int stride; /* entries between row starts */ int *ipiv; /* returned pivot info; my own scheme: 1-based indexing abs value is what original column wound up here; pos for 1x1, neg for first(!) of 2x2 pivots. */ int *info; /* 0 for success, -1 error, 1 singular */ { int negs = 0; int i,j,k; *info = 0; /* default is success */ for ( i = 0 ; i < N ; i++ ) ipiv[i] = i+1; /* identity perm */ for ( i = 0 ; i < N ; i++ ) { REAL colmax,rcolmax; int r; /* row of max value */ int pivotsize = 1; REAL *ispot,*jspot,*kspot; /* first step is pivot selection */ colmax = 0.0; r = -1; for ( j = i+1 ; j < N ; j++ ) { REAL v = fabs(H[j*stride + i]); if ( v > colmax ) { colmax = v; r = j; } } if ( r == -1 ) /* zero column */ { if ( H[i*stride+i] < 0.0 ) negs++; else if ( H[i*stride+i] == 0.0 ) *info = 1; goto skippivot; } if ( fabs(H[i*stride + i]) >= BK_alpha*colmax ) goto dopivot; /* now need max in column r */ rcolmax = colmax; for ( j = i+i ; j <= r ; j++ ) { REAL v = fabs(H[r*stride+j]); if ( v > rcolmax ) rcolmax = v; } for ( j = r+1 ; j < N ; j++ ) { REAL v = fabs(H[j*stride+r]); if ( v > rcolmax ) rcolmax = v; } if ( fabs(H[i*stride+i])*rcolmax >= BK_alpha*colmax*colmax ) goto dopivot; if ( fabs(H[r*stride+r]) >= BK_alpha*rcolmax ) { L_swap_columns(H,N,stride,i,r); j = ipiv[i]; ipiv[i] = ipiv[r]; ipiv[r] = j; } else { pivotsize = 2; L_swap_columns(H,N,stride,i+1,r); j = ipiv[i+1]; ipiv[i+1] = ipiv[r]; ipiv[r] = j; } dopivot: if ( pivotsize == 1 ) { REAL pivot; ispot = H+i*stride+i; pivot = *ispot; if ( pivot == 0.0 ) {*info = 1; goto skippivot;} if ( pivot < 0.0 ) negs++; pivot = 1/pivot; for ( j = i+1 ; j < N ; j++ ) { REAL x; jspot = H+j*stride+i; x = *jspot; *jspot *= pivot; kspot = ispot+stride; for ( k = i+1, jspot++ ; k <= j ; k++, jspot++, kspot+=stride ) *jspot -= *kspot*x; } } else /* pivotsize 2 */ { REAL det,p11,p21,p22,tmp; ispot = H+i*stride+i; p11 = *ispot; ispot += stride; p21 = *ispot; p22 = ispot[1]; det = p11*p22 - p21*p21; if ( (det > 0.0) && (p11+p22 < 0.0) ) negs += 2; else if ( det < 0.0 ) negs++; tmp = p11; p11 = p22/det; p22 = tmp/det; p21 = -p21/det; for ( j = i+2; j < N ; j++ ) { REAL x1,x2; jspot = H+j*stride+i; x1 = jspot[0]; x2 = jspot[1]; jspot[0] = p11*x1 + p21*x2; jspot[1] = p21*x1 + p22*x2; kspot = ispot+stride; for ( k = i+2, jspot+=2 ; k <= j ; k++, jspot++, kspot+=stride ) *jspot -= kspot[0]*x1 + kspot[1]*x2; } ipiv[i] = -ipiv[i]; i++; } skippivot: ; } return negs; } /********************************************************************** * * function: kb_DSYTRS() * * purpose: solve for single rhs using factored matrix. */ void kb_DSYTRS(N,LD,stride,ipiv,B,work,info) int N; REAL *LD; /* from kb_DSYTRF() */ int stride; int *ipiv; /* ipiv[i] is what column wound up in column i, 1-based indexing */ REAL *B; /* rhs,solution */ REAL *work; /* must be size N */ int *info; { int i,j; REAL *X = work; REAL *ispot,*jspot; /* permute */ for ( i = 0 ; i < N ; i++ ) work[i] = B[abs(ipiv[i])-1]; /* solve left factor */ for ( i = 0, ispot = LD ; i < N ; i++, ispot += stride ) { if ( ipiv[i] > 0 ) /* 1x1 pivot */ { for ( j = 0, jspot = ispot ; j < i ; j++,jspot++ ) X[i] -= (*jspot)*X[j]; } else /* 2x2 pivot */ { for ( j = 0, jspot = ispot ; j < i ; j++,jspot++ ) { X[i] -= (*jspot)*X[j]; X[i+1] -= jspot[stride]*X[j]; } i++; ispot += stride; } } for ( i = 0, ispot = LD ; i < N ; i++, ispot+=stride+1 ) { if ( ipiv[i] > 0 ) /* 1x1 pivot */ X[i] /= *ispot; else /* 2x2 pivot */ { REAL p11 = *ispot; REAL p21 = ispot[stride]; REAL p22 = ispot[stride+1]; REAL det = p11*p22 - p21*p21; REAL x1 = X[i], x2 = X[i+1]; X[i] = (p22*x1 - p21*x2)/det; X[i+1] = (-p21*x1 + p11*x2)/det; i++; ispot += stride+1; } } for ( i = N-1 ; i >= 0 ; i-- ) { if ( ipiv[i] > 0 ) /* 1x1 pivot */ for ( j = i+1, jspot = ispot ; j < N ; j++ ) X[i] -= LD[j*stride+i]*X[j]; else /* 2x2 pivot */ { for ( j = i+2 ; j < N ; j++ ) { X[i] -= LD[j*stride+i]*X[j]; } } } /* permute */ for ( i = 0 ; i < N ; i++ ) B[abs(ipiv[i])-1] = work[i]; } /************************************************************************ * * function: kb_DSYTRI() * * purpose: convert factored matrix to inverse. * */ void kb_DSYTRI(N,H,stride,ipiv,info) int N; REAL *H; /* from kb_DSYTRF() */ int stride; int *ipiv; /* ipiv[i] is what column wound up in column i, 1-based indexing */ int *info; { int i,j,k; REAL *ispot,sum,tmp; /* convert L to inverse */ for ( i = 0 ; i < N ; i++ ) { int jstart = (ipiv[i] < 0) ? i+2 : i+1; for ( j = jstart ; j < N ; j++ ) { int endk = ((i>0) && (ipiv[i-1] < 0)) ? i-1 : i; for ( k = 0 ; k < endk ; k++ ) H[j*stride+k] -= H[i*stride+k]*H[j*stride+i]; H[j*stride+i] = -H[j*stride+i]; } } /* convert D to inverse */ for ( i = 0 ; i < N ; i++ ) { ispot = H + i*stride + i; if ( ipiv[i] > 0 ) *ispot = 1/(*ispot); else /* 2x2 pivot */ { REAL p11 = *ispot; REAL p21 = ispot[stride]; REAL p22 = ispot[stride+1]; REAL det = p11*p22 - p21*p21; H[i*stride+i] = p22/det; H[(i+1)*stride+i] = -p21/det; H[(i+1)*stride+i+1] = p11/det; i++; } } /* multiply out L^TD into upper triangle */ for ( i = 0 ; i < N ; i++ ) { if ( ipiv[i] > 0 ) { REAL p = H[i*stride+i]; for ( k = 0 ; k < i ; k++ ) H[k*stride+i] = p*H[i*stride+k]; } else { REAL p11 = H[i*stride+i]; REAL p21 = H[(i+1)*stride+i]; REAL p22 = H[(i+1)*stride+i+1]; for ( k = 0 ; k < i ; k++ ) { H[k*stride+i] = p11*H[i*stride+k] + p21*H[(i+1)*stride+k]; H[k*stride+i+1] = p21*H[i*stride+k] + p22*H[(i+1)*stride+k]; } H[i*stride+i+1] = H[(i+1)*stride+i]; i++; } } /* multiply the two triangles L^TD*L into the lower */ for ( i = 0 ; i < N ; i++ ) { if ( ipiv[i] > 0 ) /* 1x1 pivot */ { for ( j = 0 ; j < i ; j++ ) { for ( k = i, sum = 0.0 ; k < N ; k++ ) sum += H[i*stride+k]*H[k*stride+j]; H[i*stride+j] = sum; } for ( j = i+1, sum = 0.0 ; j < N ; j++ ) sum += H[i*stride+j]*H[j*stride+i]; H[i*stride+i] += sum; } else /* 2x2 pivot */ { for ( j = 0 ; j < i ; j++ ) { REAL tmp; for ( k = i, sum = 0.0 ; k < N ; k++ ) sum += H[i*stride+k]*H[k*stride+j]; tmp = sum; for ( k = i, sum = 0.0 ; k < N ; k++ ) sum += H[(i+1)*stride+k]*H[k*stride+j]; H[(i+1)*stride+j] = sum; H[i*stride+j] = tmp; } /* diagonal */ for ( j = i+2, sum = 0.0 ; j < N ; j++ ) sum += H[(i+1)*stride+j]*H[j*stride+i]; tmp = sum; for ( j = i+2, sum = 0.0 ; j < N ; j++ ) sum += H[i*stride+j]*H[j*stride+i]; H[i*stride+i] += sum; for ( j = i+2, sum = 0.0 ; j < N ; j++ ) sum += H[(i+1)*stride+j]*H[j*stride+(i+1)]; H[(i+1)*stride+(i+1)] += sum; H[(i+1)*stride+i] += tmp; i++; } } /* permute, using upper triangle as work space */ /* diagonal, top row as work space */ for ( i = 0 ; i < N ; i++ ) H[0*stride+abs(ipiv[i])-1] = H[i*stride+i]; for ( i = 1 ; i < N ; i++ ) H[i*stride+i] = H[0*stride+i]; /* now the main part */ for ( i = 1 ; i < N ; i++ ) { int fromi = abs(ipiv[i])-1; for ( j = 0 ; j < i ; j++ ) { int fromj = abs(ipiv[j])-1; if ( fromi > fromj ) H[fromj*stride+fromi] = H[i*stride+j]; else H[fromi*stride+fromj] = H[i*stride+j]; } } /* copy upper to lower */ for ( i = 1 ; i < N ; i++ ) for ( j = 0 ; j < i ; j++ ) H[i*stride+j] = H[j*stride+i]; } /************************************************************************* * * function: LD_block_factor() * * purpose: Do LDL factoring in block form to take advantage of DGEMM. * * return: Index. */ /* returns index */ int LD_block_factor( H, N ) /* actually, D^-1 on diagonal */ REAL **H; /* lower block triangular, row by row */ /* i.e. each consecutive BLOCKSIZE rows same length, */ /* in multiples of BLOCKSIZE */ int N; /* size */ { int i,j,k; int info; int rowcount; /* rows in block; less than blocksize for last block */ int negs = 0; for ( i = 0 ; i < N ; i += blocksize ) /* pivot (i,i) */ { /* get pivot inverse in place */ int stride = i+blocksize; rowcount = (N - i > blocksize) ? blocksize : N - i; /* Inverse of diagonal block */ /* Bunch-Kaufman factor */ negs += kb_DSYTRF(rowcount,&H[i][i],stride,ipiv,&info); /* now the inverse */ kb_DSYTRI(rowcount,&H[i][i],stride,ipiv,&info); /* update row */ for ( j = i+blocksize ; j < N ; j += blocksize ) /* row j */ { REAL x = H[j][i]; char transa = 'T'; char transb = 'N'; REAL alpha = -1.0; REAL beta = 1.0; int stridea; int strideb; int stridec; char side = 'L'; int ii,jj; char uplo; rowcount = (N - j > blocksize) ? blocksize : N - j; for ( k = i+blocksize ; k < j ; k += blocksize ) /* col k */ { stridea = k+blocksize; strideb = j+blocksize; stridec = j+blocksize; DGEMM(&transa,&transb,&blocksize,&rowcount,&blocksize,&alpha, &H[k][i],&stridea,&H[j][i],&strideb,&beta,&H[j][k],&stridec); } /* H[j][i] = x*pivot; temp store result in work */ alpha = 1.0; beta = 0.0; stridea = i+blocksize; strideb = j+blocksize; stridec = blocksize; alpha = 1.0; beta = 0.0; side = 'L'; /* symmetric matrix on the left */ uplo = 'U'; /* transposed since Fortran */ DSYMM(&side,&uplo,&blocksize,&rowcount,&alpha,&H[i][i],&stridea, &H[j][i],&strideb,&beta,&workspace[0][0],&stridec); /* H[j][j] -= x*pivot*x; */ transa = 'T'; transb = 'N'; alpha = -1.0; beta = 1.0; stridea = j+blocksize; strideb = blocksize; stridec = j+blocksize; DGEMM(&transa,&transb,&rowcount,&rowcount,&blocksize,&alpha, &H[j][i],&stridea,&workspace[0][0],&strideb,&beta,&H[j][j],&stridec); /* copy workspace back to H[j][i] */ for ( jj = 0 ; jj < rowcount ; jj++ ) for ( ii = 0 ; ii < blocksize ; ii++ ) H[j+jj][i+ii] = workspace[jj][ii]; } } return negs; } /************************************************************************** * * function: LD_block_solve() * * purpose: Solve for single right hand side using factoring produced * by LD_block_factor(). */ void LD_block_solve(LD,B,X,N) REAL **LD; /* from LD_block_factor(), inverse blocks on diagonal */ REAL *B; /* rhs */ REAL *X; /* solution */ int N; /* size of system */ { int i,j; REAL alpha = 0.0; REAL beta = 0.0; int stridea = 1; int incx=1,incy=1; char trans = 'N'; int rowcount; /* rows in block; less than blocksize at end */ for ( i = 0 ; i < N ; i++ ) X[i] = B[i]; for ( i = 0 ; i < N ; i += blocksize ) { rowcount = (N - i > blocksize) ? blocksize : N - i; for ( j = 0 ; j < i ; j += blocksize ) { /* X[i] -= LD[i][j]*X[j]; */ alpha = -1.0; beta = 1.0; stridea = i+blocksize; incx = 1; incy = 1; trans = 'T'; DGEMV(&trans,&blocksize,&rowcount,&alpha,&LD[i][j],&stridea, &X[j],&incx,&beta,&X[i],&incy); } } for ( i = 0 ; i < N ; i += blocksize ) { /* X[i] /= LD[i][i]; */ char uplo = 'U'; rowcount = (N - i > blocksize) ? blocksize : N - i; alpha = 1.0; beta = 0.0; stridea = i+blocksize; incx = 1; incy = 1; DSYMV(&uplo,&rowcount,&alpha,&LD[i][i],&stridea,&X[i],&incx, &beta,&workv[0],&incy); /* need separate destination */ for ( j = 0 ; j < rowcount ; j++ ) X[i+j] = workv[j]; } for ( i = N-rowcount ; i >= 0 ; i -= blocksize ) { for ( j = i+blocksize ; j < N ; j += blocksize ) { /* X[i] -= LD[j][i]*X[j]; */ rowcount = (N - j > blocksize) ? blocksize : N - j; alpha = -1.0; beta = 1.0; stridea = j+blocksize; incx = 1; incy = 1; trans = 'N'; DGEMV(&trans,&blocksize,&rowcount,&alpha,&LD[j][i],&stridea, &X[j],&incx,&beta,&X[i],&incy); } } } #endif /**********************************************************************/ /* Now some Bezier and Lagrange stuff for refining and graphics subdivision */ /********************************************************************** * * function: bezier_eval() * * purpose: Evaluation of multi-dimensional simplicial Bezier basis * function at given point. */ REAL bezier_eval ARGS4((order,dim,inx,x), int order, /* Lagrange order */ int dim, /* simplicial dimension */ int *inx, /* which basis function, barycentric, sums to order */ REAL *x) /* barycentric coords of target, sums to 1 */ { REAL prod, prodpart[MAXCOORD]; int i,j,k,mm; for ( k = 0 ; k <= dim ; k++ ) prodpart[k] = 0.0; for ( i = 0, prod = 1.0, mm = 1 ; i <= dim ; i++ ) for ( j = 0 ; j < inx[i] ; j++, mm++ ) { for ( k = 0 ; k <= dim ; k++ ) { prodpart[k] *= mm*x[i]/(j+1); if ( i == k ) prodpart[k] += prod*mm/(j+1.); } prod *= mm*x[i]/(j+1.); } return prod; } /*************************************************************************** * * function: bezier_eval_1d() * * purpose: Get point on curve at given parameter. */ void bezier_eval_1d ARGS5((order,sdim,p,ctrl,dest), int order, int sdim, /* space dimension*/ REAL p, /* parameter, 0 to 1 */ REAL **ctrl, /* control point coordinates */ REAL *dest) /* output point */ { int inx[2]; REAL t[2]; int i,k; t[0] = p; t[1] = 1 - p; for ( k = 0 ; k < sdim ; k++ ) dest[k] = 0.0; for ( i = 0 ; i <= order ; i++ ) { REAL bezval; inx[0] = order-i; inx[1] = i; bezval = bezier_eval(order,1,inx,t); for ( k = 0 ; k < sdim ; k++ ) dest[k] += bezval*ctrl[i][k]; } } /*************************************************************************** * * function: bezier_convert_1D_init() * * purpose: initialize matrix for converting from points on curve to * control points. * */ void bezier_convert_1D_init ARGS1((order), int order) { int i,j; REAL x[2]; /* barycentric coords of target point */ int inx[2]; /* barycentric index of basis function */ REAL **a = dmatrix(0,order,0,order); REAL **b = dmatrix(0,order,0,order); if ( bezier1invert[order] ) return; /* fill in forward matrix */ for ( i = 0 ; i <= order ; i++ ) /* point on curve */ { x[0] = (order-i)/(REAL)order; x[1] = i/(REAL)order; for ( j = 0 ; j <= order ; j++ ) /* control point */ { inx[0] = order-j; inx[1] = j; a[i][j] = b[i][j] = bezier_eval(order,1,inx,x); } } mat_inv(a,order+1); bezier1invert[order] = a; bezier1revert[order] = b; } /*************************************************************************** * * function: bezier_convert_2D_init() * * purpose: initialize matrix for converting from points on surface to * control points. * */ void bezier_convert_2D_init ARGS1((order), int order) { int i,j,k,m,kk,mm; REAL x[3]; /* barycentric coords of target point */ int inx[3]; /* barycentric index of basis function */ int numpts = ((order+1)*(order+2))/2; REAL **a = dmatrix(0,numpts-1,0,numpts-1); REAL **b = dmatrix(0,numpts-1,0,numpts-1); if ( bezier2invert[order] ) return; /* fill in forward matrix */ for ( i = 0, kk=0 ; i <= order ; i++ ) /* point on curve */ for ( k = 0 ; i+k <= order ; k++,kk++ ) /* point on curve */ { x[0] = (order-i-k)/(REAL)order; x[1] = i/(REAL)order; x[2] = k/(REAL)order; for ( j = 0, mm=0 ; j <= order ; j++ ) /* control point */ for ( m = 0 ; m+j <= order ; m++,mm++ ) /* control point */ { inx[0] = order-j-m; inx[1] = j; inx[2] = m; a[kk][mm] = b[kk][mm] = bezier_eval(order,2,inx,x); } } mat_inv(a,numpts); bezier2invert[order] = a; bezier2revert[order] = b; } /************************************************************************* * * function: bezier_trans_1d() * * purpose: Create transition matrix from old order bezier control points * to new. Goes through intermediary of curve points. */ void bezier_trans_1d ARGS3((old_order,new_order,trans), int old_order, int new_order, REAL **trans) /* trans[new][old] */ { int i,k; int inx[2]; REAL t[2]; MAT2D(x,MAXLAGRANGE+1,MAXLAGRANGE+1); /* old control to curve points */ if ( !bezier1invert[new_order] ) bezier_convert_1D_init(new_order); /* old control to curve point matrix */ for ( i = 0 ; i <= new_order ; i++ ) { t[0] = (new_order - i)/(REAL)new_order; t[1] = i/(REAL)new_order; for ( k = 0 ; k <= old_order ; k++ ) { inx[0] = old_order-k; inx[1] = k; x[i][k] = bezier_eval(old_order,1,inx,t); } } /* now get final transition matrix */ mat_mult(bezier1invert[new_order],x,trans,new_order+1,new_order+1,old_order+1); } /************************************************************************* * * function: bezier_trans_2d() * * purpose: Create transition matrix from old order bezier control points * to new. Goes through intermediary of surface points. */ void bezier_trans_2d ARGS3((old_order,new_order,trans), int old_order, int new_order, REAL **trans) /* trans[new][old] */ { int i,j,k,m,jj,kk; int inx[3]; REAL t[3]; int old_numpts = ((old_order+1)*(old_order+2))/2; int new_numpts = ((new_order+1)*(new_order+2))/2; REAL **x; /* old control to surface points */ x = dmatrix(0,new_numpts-1,0,old_numpts-1); if ( !bezier2invert[new_order] ) bezier_convert_2D_init(new_order); /* old control to surface point matrix */ for ( j = 0, jj=0 ; j <= new_order ; j++ ) for ( i = 0 ; i+j <= new_order ; i++,jj++ ) { t[0] = (new_order - i - j)/(REAL)new_order; t[1] = j/(REAL)new_order; t[2] = i/(REAL)new_order; for ( k = 0, kk = 0 ; k <= old_order ; k++ ) for ( m = 0 ; m+k <= old_order ; m++, kk++ ) { inx[0] = old_order-k-m; inx[1] = k; inx[2] = m; x[jj][kk] = bezier_eval(old_order,2,inx,t); } } /* now get final transition matrix */ mat_mult(bezier2invert[new_order],x,trans,new_numpts,new_numpts,old_numpts); } /*************************************************************************** * * function: bezier_refine_1d_init() * * purpose: set up matrices for calculating new control points for * refined edge. */ void bezier_refine_1d_init ARGS1((order), int order) { int i,k; int inx[2]; REAL t[2]; MAT2D(x,2*MAXLAGRANGE+1,MAXLAGRANGE+1); /* old control to curve points */ if ( bezier_refine_1d[order] ) return; bezier_refine_1d[order] = dmatrix(0,2*order,0,order); if ( !bezier1invert[order] ) bezier_convert_1D_init(order); /* old control to curve point matrix */ for ( i = 0 ; i <= 2*order ; i++ ) { t[0] = (2*order - i)/(REAL)(2*order); t[1] = i/(REAL)(2*order); for ( k = 0 ; k <= order ; k++ ) { inx[0] = order-k; inx[1] = k; x[i][k] = bezier_eval(order,1,inx,t); } } /* now get final transition matrix */ mat_mult(bezier1invert[order],x,bezier_refine_1d[order], order+1,order+1,order+1); mat_mult(bezier1invert[order],x+order,bezier_refine_1d[order]+order, order+1,order+1,order+1); } /*************************************************************************** * * function: bezier_refine_2d_init() * * purpose: set up matrices for calculating new control points for * refined facet. */ void bezier_refine_2d_init ARGS1((order), int order) { int i,j,k,m,kk,mm,jj; int inx[3]; REAL t[3]; int old_numpts = ((order+1)*(order+2))/2; int new_numpts = (2*order+1)*(order+1); REAL ***x = dmatrix3(2*order+1,2*order+1,old_numpts); /* old control to curve points */ REAL **z,**w; /* for pieces of whole matrix */ if ( bezier_refine_2d[order] ) return; bezier_refine_2d[order] = dmatrix(0,new_numpts,0,old_numpts); if ( !bezier2invert[order] ) bezier_convert_2D_init(order); /* old control to curve point matrix */ for ( j = 0 ; j <= 2*order ; j++ ) for ( i = 0 ; i+j <= 2*order ; i++ ) { t[0] = (2*order - i - j)/(REAL)(2*order); t[1] = j/(REAL)(2*order); t[2] = i/(REAL)(2*order); for ( k = 0, kk = 0 ; k <= order ; k++ ) for ( m = 0 ; m+k <= order ; m++,kk++ ) { inx[0] = order-k-m; inx[1] = k; inx[2] = m; x[i][j][kk] = bezier_eval(order,2,inx,t); } } /* now get final transition matrix in four sections */ z = dmatrix(0,old_numpts-1,0,old_numpts-1); w = dmatrix(0,old_numpts-1,0,old_numpts-1); /* lower left */ for ( j = 0, jj = 0 ; j <= order ; j++ ) for ( i = 0 ; i+j <= order ; i++, jj++ ) for ( kk = 0 ; kk < old_numpts ; kk++ ) z[jj][kk] = x[i][j][kk]; mat_mult(bezier2invert[order],z,w,old_numpts,old_numpts,old_numpts); for ( j = 0, jj = 0, mm = 0 ; j <= order ; j++, mm += order ) for ( i = 0 ; i+j <= order ; i++, jj++, mm++ ) for ( kk = 0 ; kk < old_numpts ; kk++ ) bezier_refine_2d[order][mm][kk] = w[jj][kk]; /* lower right */ for ( j = 0, jj = 0 ; j <= order ; j++ ) for ( i = 0 ; i+j <= order ; i++, jj++ ) for ( kk = 0 ; kk < old_numpts ; kk++ ) z[jj][kk] = x[i+order][j][kk]; mat_mult(bezier2invert[order],z,w,old_numpts,old_numpts,old_numpts); for ( j = 0, jj = 0, mm = order ; j <= order ; j++, mm += order ) for ( i = 0 ; i+j <= order ; i++, jj++, mm++ ) for ( kk = 0 ; kk < old_numpts ; kk++ ) bezier_refine_2d[order][mm][kk] = w[jj][kk]; /* upper left */ for ( j = 0, jj = 0 ; j <= order ; j++ ) for ( i = 0 ; i+j <= order ; i++, jj++ ) for ( kk = 0 ; kk < old_numpts ; kk++ ) z[jj][kk] = x[i][j+order][kk]; mat_mult(bezier2invert[order],z,w,old_numpts,old_numpts,old_numpts); for ( j = 0, jj = 0, mm = 3*((order+1)*order)/2 ; j <= order ; j++ ) for ( i = 0 ; i+j <= order ; i++, jj++, mm++ ) for ( kk = 0 ; kk < old_numpts ; kk++ ) bezier_refine_2d[order][mm][kk] = w[jj][kk]; /* middle */ for ( j = order, jj = 0 ; j >= 0 ; j-- ) for ( i = order ; i+j >= order ; i--, jj++ ) for ( kk = 0 ; kk < old_numpts ; kk++ ) z[jj][kk] = x[i][j][kk]; mat_mult(bezier2invert[order],z,w,old_numpts,old_numpts,old_numpts); for ( j=order, jj=0, mm = ((3*order+2)*(order+1))/2-1 ; j >= 0 ; j--,mm -= 2*(order-j)-1 ) for ( i = order ; i+j >= order ; i--, jj++, mm-- ) for ( kk = 0 ; kk < old_numpts ; kk++ ) bezier_refine_2d[order][mm][kk] = w[jj][kk]; free_matrix3(x); free_matrix(z); free_matrix(w); } /*********************************************************************** * * function: lagrange_eval_1d() * * purpose: Evaluate one curve point using Lagrange interpolation. */ void lagrange_eval_1d ARGS5((order,sdim,t,ctrl,dest), int order, int sdim, /* space dimension*/ REAL t, /* parameter, 0 to 1 */ REAL **ctrl, /* control point coordinates */ REAL *dest) /* output point */ { int i,k,m; for ( k = 0 ; k < sdim ; k++ ) dest[k] = 0.0; for ( i = 0 ; i <= order ; i++ ) { REAL p=1.0; for ( m = 0 ; m <= order ; m++ ) { if ( m == i ) continue; p *= (t*order - m)/(i - m); } for ( k = 0 ; k < sdim ; k++ ) dest[k] += p*ctrl[i][k]; } } /*********************************************************************** * * function: lagrange_eval_2d() * * purpose: Evaluate one surface point using Lagrange 2D interpolation. */ void lagrange_eval_2d ARGS5((order,sdim,t,ctrl,dest), int order, int sdim, /* space dimension*/ REAL *t, /* parameters, 0 to 1 */ REAL **ctrl, /* control point coordinates, in linear order */ REAL *dest) /* output point */ { int i,j,k,m,jj; for ( m = 0 ; m < sdim ; m++ ) dest[m] = 0.0; for ( j = 0, jj = 0 ; j <= order ; j++ ) { for ( i = 0 ; i+j <= order ; i++,jj++ ) { REAL p=1.0; k = order-i-j; for ( m = 0 ; m < i ; m++ ) p *= (t[0]*order - m)/(i - m); for ( m = 0 ; m < j ; m++ ) p *= (t[1]*order - m)/(j - m); for ( m = 0 ; m < k ; m++ ) p *= ((1-t[0]-t[1])*order - m)/(k - m); for ( m = 0 ; m < sdim ; m++ ) dest[m] += p*ctrl[jj][m]; } } } /*************************************************************************** * * function: bezier_eval_2d() * * purpose: Get point on surface at given parameters. */ void bezier_eval_2d ARGS5((order,sdim,p,ctrl,dest), int order, int sdim, /* space dimension*/ REAL *p, /* two parameters, 0 to 1 barycentric */ REAL **ctrl, /* control point coordinates, in linear order */ REAL *dest) /* output point */ { int inx[3]; REAL t[3]; int i,j,k,jj; t[0] = p[0]; t[1] = p[1]; t[2] = 1.0 - t[0] - t[1]; for ( k = 0 ; k < sdim ; k++ ) dest[k] = 0.0; for ( j = 0, jj = 0 ; j <= order ; j++ ) for ( i = 0 ; i+j <= order ; i++,jj++ ) { REAL bezval; inx[0] = order-i-j; inx[1] = j; inx[2] = i; bezval = bezier_eval(order,2,inx,t); for ( k = 0 ; k < sdim ; k++ ) dest[k] += bezval*ctrl[jj][k]; } } /************************************************************************** * * function: tetra_vol() * * purpose: calculate volume of a tetrahedron in 3D from its four vertices. */ REAL tetra_vol ARGS4((a,b,c,d), REAL *a, REAL *b, REAL *c, REAL *d) { REAL bb[3],cc[3],dd[3]; REAL vol; int i; for ( i = 0 ; i < 3 ; i++ ) { bb[i] = b[i] - a[i]; cc[i] = c[i] - a[i]; dd[i] = d[i] - a[i]; } vol = (bb[0]*(cc[1]*dd[2] - cc[2]*dd[1]) + bb[1]*(cc[2]*dd[0] - cc[0]*dd[2]) + bb[2]*(cc[0]*dd[1] - cc[1]*dd[0]))/6; return vol; } /**************************************************************************** * * function: matrix_multiply_command() * * purpose: implements the matrix_multiply user command. */ void matrix_multiply_command ARGS6((a,b,c,adata,bdata,cdata), struct array *a, struct array *b,struct array *c, /* array infofor the matrices */ REAL *adata, REAL *bdata, REAL *cdata ) /* data addresses */ { int i,j,k; if ( (a->dim == 2) && (b->dim == 2) ) { if ( c->dim != 2 ) kb_error(3798,"matrix_multiply third array is not two-dimensional.\n", RECOVERABLE); if ( a->sizes[1] != b->sizes[0] ) kb_error(4012, "matrix_multiply: sizes of first and second matrices disagree.\n", RECOVERABLE); if ( a->sizes[0] != c->sizes[0] ) kb_error(3217, "matrix_multiply: sizes of first and third matrices disagree.\n", RECOVERABLE); if ( b->sizes[1] != c->sizes[1] ) kb_error(3862, "matrix_multiply: sizes of second and third matrices disagree.\n", RECOVERABLE); /* finally ready to roll */ for ( i = 0 ; i < a->sizes[0] ; i++ ) for ( j = 0 ; j < b->sizes[1] ; j++ ) { REAL sum = 0.0; for ( k = 0 ; k < a->sizes[1] ; k++ ) sum += adata[i*a->sizes[1]+k]*bdata[k*b->sizes[1]+j]; cdata[i*c->sizes[1]+j] = sum; } return; } /* end 2D by 2D */ if ( (a->dim == 1) && (b->dim == 2) ) { if ( c->dim != 1 ) kb_error(4013,"matrix_multiply third array is not one-dimensional.\n", RECOVERABLE); if ( a->sizes[0] != b->sizes[0] ) kb_error(3860, "matrix_multiply: sizes of first and second matrices disagree.\n", RECOVERABLE); if ( b->sizes[1] != c->sizes[0] ) kb_error(4010, "matrix_multiply: sizes of second and third matrices disagree.\n", RECOVERABLE); /* finally ready to roll */ for ( j = 0 ; j < b->sizes[1] ; j++ ) { REAL sum = 0.0; for ( k = 0 ; k < a->sizes[0] ; k++ ) sum += adata[k]*bdata[k*b->sizes[1]+j]; cdata[j] = sum; } return; } /* end 1D by 2D */ if ( (a->dim == 2) && (b->dim == 1) ) { if ( c->dim != 1 ) kb_error(4014,"matrix_multiply third array is not one-dimensional.\n", RECOVERABLE); if ( a->sizes[1] != b->sizes[0] ) kb_error(4011, "matrix_multiply: sizes of first and second matrices disagree.\n", RECOVERABLE); /* finally ready to roll */ for ( i = 0 ; i < a->sizes[0] ; i++ ) { REAL sum = 0.0; for ( k = 0 ; k < a->sizes[1] ; k++ ) sum += adata[i*a->sizes[1]+k]*bdata[k]; cdata[i] = sum; } return; } /* end 2D by 1D */ kb_error(3869,"matrix_multiply: illegal matrix dimensions\n",RECOVERABLE); } /**************************************************************************** * * function: matrix_inverse_command() * * purpose: implements the matrix_inverse user command. */ int matrix_inverse_command ARGS4((a,b,adata,bdata), struct array *a,struct array *b,/* variable id numbers for the matrices */ REAL *adata,REAL *bdata /* source and destination matrices */ ) { int i; int retval = 1; /* 1 for success, 0 for singular */ REAL **bptrs; if ( a->sizes[0] != a->sizes[1] ) kb_error(3218, "matrix_inverse: first matrix is not square.\n", RECOVERABLE); if ( b->sizes[0] != b->sizes[1] ) kb_error(3872, "matrix_inverse: second matrix is not square.\n", RECOVERABLE); if ( a->sizes[0] != b->sizes[0] ) kb_error(3873, "matrix_inverse: sizes of first and second matrices disagree.\n", RECOVERABLE); if ( bdata != adata ) for ( i = 0 ; i < a->datacount ; i++ ) bdata[i] = adata[i]; /* set up row pointers in allocated space after data */ bptrs = (REAL**)(bdata + b->datacount); for ( i = 0 ; i < b->sizes[0] ; i++ ) bptrs[i] = bdata + i*b->sizes[1]; /* finally ready to roll */ retval = mat_inv(bptrs,a->sizes[0]); if ( retval < 0 ) retval = 0; return retval; } /**************************************************************************** * * function: matrix_determinant_command() * * purpose: implements the matrix_determinant user command. */ REAL matrix_determinant_command ARGS2((a,adata), struct array *a, /* variable id numbers for the matrix */ REAL *adata ) /* data address */ { REAL **atmp; int i,j; REAL retval; if ( a->sizes[0] != a->sizes[1] ) kb_error(3220, "matrix_determinant: matrix is not square.\n", RECOVERABLE); if ( a->datatype != REAL_TYPE ) kb_error(3221,"matrix_determinant: matrix is not of type REAL.\n", RECOVERABLE); atmp = dmatrix(0,a->sizes[0],0,a->sizes[1]); for ( i = 0 ; i < a->sizes[0] ; i++ ) for ( j = 0 ; j < a->sizes[1] ; j++ ) atmp[i][j] = adata[i*a->sizes[0]+j]; /* finally ready to roll */ retval = determinant(atmp,a->sizes[0]); free_matrix(atmp); return retval; } evolver-2.30c.dfsg/src/sqcurve2.c0000644000175300017530000024634511410765113017151 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************** * * File: sqcurve2.c * * Purpose: Does calculations needed for including square curvature * in energy. Linear model only. * Named quantity methods. */ #include "include.h" static int h0_flag; /* set to use (H - H_0)^2 */ static REAL h0_value; /* value of H_0 */ static REAL sq_mean_mix; /* proportion for mix_sq_mean_curvature */ static REAL selfsim_coeff; /* self similarity */ long sqcurve_init_timestamp; /* when last initialized */ long sqcurve_grad_init_timestamp; /* when last initialized */ /* local prototypes */ void sqcurve_energy_precalc ARGS((vertex_id *,REAL (*)[MAXCOORD],WRAPTYPE*)); void sqcurve_grad_precalc ARGS((vertex_id *,edge_id *,REAL (*)[MAXCOORD], WRAPTYPE*)); /*********************************************************************** * * Function: sqcurve_method_init() * * Purpose: Initializes data structures for square curvature. */ void sqcurve_method_init(mode,mi) int mode; /* METHOD_VALUE or METHOD_GRADIENT */ struct method_instance *mi; { int k,n; facet_id f_id; struct gen_quant_method *gm; MAT2D(x,MAXCOORD,MAXCOORD); vertex_id vv_id; WRAPTYPE wraps[FACET_VERTS]; int eltype; if ( web.modeltype != LINEAR ) kb_error(1758,"sq_mean_curvature method method only for LINEAR model.\n", RECOVERABLE); if ( everything_quantities_flag && square_curvature_flag) GEN_QUANT(sq_mean_curv_quantity_num)->modulus = globals(square_curvature_param)->value.real; if ( mi ) { if ( sq_mean_curvature_mi < 0 ) { /* see what method indices correspond to what methods */ for ( n=0,gm = basic_gen_methods ; gm->name[0] != ' ' ; gm++,n++ ) { if ( stricmp(gm->name,"sq_mean_curvature") == 0 ) sq_mean_curvature_mi = n; if ( stricmp(gm->name,"eff_area_sq_mean_curvature") == 0 ) eff_area_sq_mean_curvature_mi = n; if ( stricmp(gm->name,"normal_sq_mean_curvature") == 0 ) normal_sq_mean_curvature_mi = n; if ( stricmp(gm->name,"mix_sq_mean_curvature") == 0 ) mix_sq_mean_curvature_mi = n; } } if ( mi->gen_method == eff_area_sq_mean_curvature_mi ) if ( SDIM != 3 ) kb_error(1759,"eff_area_sq_mean_curvature method only for 3D space.\n", RECOVERABLE); if ( mi->gen_method == normal_sq_mean_curvature_mi ) if ( SDIM != 3 ) kb_error(1760,"normal_sq_mean_curvature method only for 3D space.\n", RECOVERABLE); if ( mi->gen_method == normal_sq_mean_curvature_mi ) if ( SDIM != 3 ) kb_error(1450,"mix_sq_mean_curvature method only for 3D space.\n", RECOVERABLE); /* get mix coefficient */ if ( mi->gen_method == normal_sq_mean_curvature_mi ) { k = lookup_global("sq_mean_mix"); if ( k >= 0 ) sq_mean_mix = globals(k)->value.real; else sq_mean_mix = 0.0; } } /* end if ( mi ) */ /* see if using (H - H_0)^2 adjustment */ h0_flag = 0; h0_attr = find_extra("h_zero",&eltype); if ( h0_attr >= 0 ) h0_flag = H0_IN_ATTR; else { k = lookup_global("h_zero"); if ( k >= 0 ) { h0_flag = H0_IN_GLOBAL; h0_value = globals(k)->value.real; } } if ( self_similar_flag ) { int param = lookup_global(SELFSIM_NAME); if ( param < 0 ) /* missing, so add */ { param = add_global(SELFSIM_NAME); globals(param)->value.real = 1.0; /* default */ globals(param)->flags |= ORDINARY_PARAM | RECALC_PARAMETER | ALWAYS_RECALC; } selfsim_coeff = globals(param)->value.real/6; /* area and volume factor */ if ( h0_flag == 0 ) { h0_flag = H0_IN_GLOBAL; h0_value = 0.0; } } /* necessary precalculations, if anything changed */ if ( ( mode == METHOD_VALUE ) && ( sqcurve_init_timestamp < global_timestamp || !v_curve) ) { v_curve = (struct v_curve_t *)temp_calloc(web.skel[VERTEX].max_ord+1, sizeof(struct v_curve_t)); FOR_ALL_FACETS(f_id) { /* get side vectors */ int i,j; vertex_id v_id[FACET_VERTS]; facetedge_id fe_id; REAL side[FACET_EDGES][MAXCOORD]; fe_id = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { v_id[i] = get_fe_tailv(fe_id); fe_id = get_next_edge(fe_id); } get_facet_verts(f_id,x,wraps); /* in tail order */ for ( i = 0 ; i < FACET_EDGES ; i++ ) { int ii = (i+1)%FACET_EDGES; for ( j = 0 ; j < SDIM ; j++ ) side[i][j] = x[ii][j] - x[i][j]; } sqcurve_energy_precalc(v_id,side,wraps); } sqcurve_init_timestamp = global_timestamp; } if ( ( mode == METHOD_GRADIENT ) && ( sqcurve_grad_init_timestamp < global_timestamp || !v_curve ) ) { MAT2D(a,MAXCOORD,MAXCOORD); REAL af[MAXCOORD], gaf[MAXCOORD]; MAT2D(grad,MAXCOORD,MAXCOORD); MAT2D(adft,MAXCOORD,MAXCOORD); MAT3D(seconds,MAXCOORD,MAXCOORD,MAXCOORD); v_curve = (struct v_curve_t *)temp_calloc(web.skel[VERTEX].max_ord+1, sizeof(struct v_curve_t)); e_curve = (struct e_curve_t *)temp_calloc(web.skel[EDGE].max_ord+1, sizeof(struct e_curve_t)); FOR_ALL_FACETS(f_id) { /* get side vectors */ int i,j; vertex_id v_id[FACET_VERTS]; edge_id e_id[FACET_VERTS]; facetedge_id fe_id; REAL side[FACET_EDGES][MAXCOORD]; fe_id = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { v_id[i] = get_fe_tailv(fe_id); e_id[i] = get_fe_edge(fe_id); fe_id = get_next_edge(fe_id); } get_facet_verts(f_id,x,wraps); /* in tail order */ for ( i = 0 ; i < FACET_EDGES ; i++ ) { int ii = (i+1)%FACET_EDGES; for ( j = 0 ; j < SDIM ; j++ ) side[i][j] = x[ii][j] - x[i][j]; } sqcurve_grad_precalc(v_id,e_id,side,wraps); } FOR_ALL_VERTICES(vv_id) { struct v_curve_t *vc = v_curve + loc_ordinal(vv_id); if ( !boundary_curvature_flag ) vc->a = vc->area; if ( (get_vattr(vv_id) & CONSTRAINT) && !(sqcurve_ignore_constr) ) { conmap_t * conmap = get_v_constraint_map(vv_id); int i,j,oncount = 0; struct constraint *con[MAXCONPER]; REAL perp[MAXCOORD]; REAL dummy; for ( j = 1 ; j <= (int)conmap[0] ; j++ ) if ( conmap[j] & CON_HIT_BIT ) con[oncount++] = get_constraint(conmap[j]); /* stuff for gradient of projection operator */ for ( j = 0 ; j < oncount ; j++ ) eval_second(con[j]->formula,get_coord(vv_id),SDIM,&dummy, grad[j],seconds[j],vv_id); /* construct matrix A */ for ( i = 0 ; i < oncount ; i++ ) for ( j = 0 ; j < oncount ; j++ ) a[i][j] = SDIM_dot(grad[i],grad[j]); /* invert */ mat_inv(a,oncount); mat_mult(a,grad,adft,oncount,oncount,SDIM); matvec_mul(adft,vc->force,af,oncount,SDIM); vec_mat_mul(af,grad,gaf,oncount,SDIM); for ( k = 0 ; k < SDIM ; k++ ) { REAL sum = 0.0; for ( i = 0 ; i < oncount ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { sum += af[i]*seconds[i][k][j]*gaf[j]; sum += vc->force[j]*seconds[i][k][j]*af[i]; } vc->fpgradf[k] = -2*sum; } constr_proj(TANGPROJ,oncount,con,get_coord(vv_id), vc->force,perp,NULL,NO_DETECT,NULLID); for ( j = 0 ; j < SDIM ; j++ ) vc->force[j] -= perp[j]; constr_proj(TANGPROJ,oncount,con,get_coord(vv_id), vc->normal,perp,NULL,NO_DETECT,NULLID); for ( j = 0 ; j < SDIM ; j++ ) vc->normal[j] -= perp[j]; } if ( h0_flag ) { vc->norm = SDIM_dot(vc->normal,vc->normal); vc->f = SDIM_dot(vc->force,vc->normal); vc->h = vc->f/vc->norm*3; switch ( h0_flag ) { case H0_IN_GLOBAL: vc->term = vc->h - h0_value; break; case H0_IN_ATTR: vc->term = vc->h - *VREAL(vv_id,h0_attr); break; } } } sqcurve_grad_init_timestamp = global_timestamp; } } /******************************************************************** * * Function: sqcurve_energy_precalc() * * Purpose: Does square curvature energy calculation for a facet. * */ void sqcurve_energy_precalc(v_id,side,wraps) vertex_id *v_id; /* vertex list for facet */ REAL (*side)[MAXCOORD]; /* side vectors */ WRAPTYPE *wraps; /* in case of symmetry group */ { REAL t1t1,t1t2,t2t2; REAL det; struct v_curve_t *vc[FACET_VERTS]; int i,j; REAL area; t1t1 = SDIM_dot(side[0],side[0]); t1t2 = SDIM_dot(side[0],side[1]); t2t2 = SDIM_dot(side[1],side[1]); det = t1t1*t2t2 - t1t2*t1t2; area = sqrt(det)/2; for ( i = 0 ; i < FACET_VERTS ; i++ ) { vc[i] = v_curve + loc_ordinal(v_id[i]); vc[i]->area += area; } if ( boundary_curvature_flag ) /* apportion area differently */ { int fixcount = 0; for ( i = 0 ; i < FACET_VERTS ; i++ ) if ( get_vattr(v_id[i]) & (BOUNDARY|FIXED) ) fixcount++; for ( i = 0 ; i < FACET_VERTS ; i++ ) { vc[i] = v_curve+loc_ordinal(v_id[i]); if ( !(get_vattr(v_id[i]) & (BOUNDARY|FIXED)) ) vc[i]->a += 3*area/(3-fixcount); } } if ( area > 0.0 ) { if ( web.symmetry_flag ) { REAL f[FACET_VERTS][MAXCOORD]; REAL wforce[MAXCOORD]; /* unwrapped forces */ for ( i = 0 ; i < SDIM ; i++ ) { f[0][i] = -(t2t2*side[0][i]-t1t2*side[1][i])/4/area; f[1][i] = (t2t2*side[0][i]-t1t2*side[1][i])/4/area; f[2][i] = (t1t1*side[1][i]-t1t2*side[0][i])/4/area; f[1][i] -= (t1t1*side[1][i]-t1t2*side[0][i])/4/area; } for ( i = 0 ; i < FACET_VERTS ; i++ ) /* vertex loop */ { (*sym_form_pullback)(get_coord(v_id[i]),wforce,f[i],wraps[i]); for ( j = 0 ; j < SDIM ; j++ ) vc[i]->force[j] += wforce[j]; } } else for ( i = 0 ; i < SDIM ; i++ ) { vc[0]->force[i] -= (t2t2*side[0][i]-t1t2*side[1][i])/4/area; vc[1]->force[i] += (t2t2*side[0][i]-t1t2*side[1][i])/4/area; vc[2]->force[i] += (t1t1*side[1][i]-t1t2*side[0][i])/4/area; vc[1]->force[i] -= (t1t1*side[1][i]-t1t2*side[0][i])/4/area; } } /* accumulate normal vector at each vertex; should pullback */ { REAL normal[MAXCOORD]; cross_prod(side[0],side[1],normal); for ( i = 0 ; i < FACET_VERTS ; i ++ ) { if ( web.symmetry_flag && wraps[i] ) { REAL wnorm[MAXCOORD]; (*sym_form_pullback)(get_coord(v_id[i]),wnorm,normal,wraps[i]); for ( j = 0 ; j < SDIM ; j++ ) vc[i]->normal[j] += wnorm[j]; } else for ( j = 0 ; j < SDIM ; j++ ) vc[i]->normal[j] += normal[j]; } } } /************************************************************************ * * Function: sqcurve_grad_precalc() * * Purpose: Does square curvature grad calculation for a facet. * */ void sqcurve_grad_precalc(v_id,e_id,side,wraps) vertex_id *v_id; /* vertex list of facet */ edge_id *e_id; /* edge list */ REAL (*side)[MAXCOORD]; /* side vectors */ WRAPTYPE *wraps; { REAL det; struct v_curve_t *vc[FACET_VERTS]; int i,j,k; REAL force[FACET_VERTS][MAXCOORD]; REAL tt[FACET_VERTS][FACET_VERTS]; /* side dot products */ REAL area; struct e_curve_t *ec[FACET_EDGES]; int fixcount=0; for ( j = 0 ; j < FACET_VERTS ; j++ ) for ( k = 0 ; k <= j ; k++ ) tt[j][k] = tt[k][j] = SDIM_dot(side[j],side[k]); det = tt[0][0]*tt[1][1] - tt[0][1]*tt[0][1]; area = sqrt(det)/2; for ( i = 0 ; i < FACET_VERTS ; i++ ) { vc[i] = v_curve + loc_ordinal(v_id[i]); vc[i]->area += area; } if ( boundary_curvature_flag ) /* apportion area differently */ { fixcount = 0; for ( i = 0 ; i < FACET_VERTS ; i++ ) if ( get_vattr(v_id[i]) & (BOUNDARY|FIXED) ) fixcount++; for ( i = 0 ; i < FACET_VERTS ; i++ ) if ( !(get_vattr(v_id[i]) & (BOUNDARY|FIXED)) ) vc[i]->a += 3*area/(3-fixcount); } for ( i = 0 ; i < FACET_EDGES ; i++ ) ec[i] = e_curve + loc_ordinal(e_id[i]); memset((char*)force,0,sizeof(force)); for ( j = 0 ; j < FACET_VERTS ; j++ ) { int i1 = (j+1)%FACET_VERTS; int i2 = (j+2)%FACET_VERTS; for ( i = 0 ; i < SDIM ; i++ ) force[j][i] = (tt[i1][i1]*side[i2][i] - tt[i1][i2]*side[i1][i]) /4/area; } /* first and second derivatives at vertices; should add pullback */ for ( i = 0 ; i < FACET_VERTS ; i++ ) { int ii = (i+1)%FACET_VERTS; /* opposite side from vertex i */ for ( j = 0 ; j < SDIM ; j++ ) vc[i]->force[j] += force[i][j]; if ( boundary_curvature_flag && (fixcount != 3) ) for ( j = 0 ; j < SDIM ; j++ ) vc[i]->star_force[j] += 3*force[i][j]/(3-fixcount); for ( j = 0 ; j < SDIM ; j++ ) { vc[i]->deriv2[j][j] += tt[ii][ii]/4/area; for ( k = 0 ; k < SDIM ; k++ ) vc[i]->deriv2[j][k] -= (force[i][j]*force[i][k]+0.25*side[ii][j] *side[ii][k])/area; } } /* now first and second derivatives on edges; should add pullback */ for ( i = 0 ; i < FACET_EDGES ; i++ ) { int i1 = (i+1)%FACET_EDGES; int i2 = (i+2)%FACET_EDGES; for ( j = 0 ; j < SDIM ; j++ ) { ec[i]->deriv2[j][j] += tt[i1][i2]/4/area; for ( k = 0 ; k < SDIM ; k++ ) if ( inverted(e_id[i]) ) ec[i]->deriv2[k][j] += (-side[i1][j]*side[i2][k]/2 + side[i2][j]*side[i1][k]/4 -force[i1][j]*force[i][k])/area; else ec[i]->deriv2[j][k] += (-side[i1][j]*side[i2][k]/2 + side[i2][j]*side[i1][k]/4 -force[i1][j]*force[i][k])/area; if ( boundary_curvature_flag && (get_vattr(v_id[i2]) & (FIXED|BOUNDARY)) ) { if ( inverted(e_id[i]) ) { ec[i]->aderiv[0][j] += 0.5*force[i1][j]; ec[i]->aderiv[1][j] += 0.5*force[i][j]; } else { ec[i]->aderiv[0][j] += 0.5*force[i][j]; ec[i]->aderiv[1][j] += 0.5*force[i1][j]; } } if ( inverted(e_id[i]) ) { ec[i]->deriv[0][j] += force[i1][j]; ec[i]->deriv[1][j] += force[i][j]; } else { ec[i]->deriv[0][j] += force[i][j]; ec[i]->deriv[1][j] += force[i1][j]; } } if ( self_similar_flag ) { REAL cp[MAXCOORD]; int ii; cross_prod(get_coord(get_edge_tailv(e_id[i])), get_coord(get_edge_headv(e_id[i])), cp); ii = inverted(e_id[i1]) ? 0 : 1; for ( j = 0 ; j < SDIM ; j++ ) ec[i1]->volderiv[ii][j] += cp[j]; ii = inverted(e_id[i2]) ? 1 : 0; for ( j = 0 ; j < SDIM ; j++ ) ec[i2]->volderiv[ii][j] += cp[j]; } } /* accumulate normal vector at each vertex */ { REAL normal[MAXCOORD]; cross_prod(side[0],side[1],normal); for ( i = 0 ; i < 3 ; i ++ ) for ( j = 0 ; j < SDIM ; j++ ) vc[i]->normal[j] += normal[j]; } } /*********************************************************************** * * Function: sqcurve_method_cleanup() * * Purpose: Initializes data structures for square curvature. */ void sqcurve_method_cleanup() { if ( v_curve ) { temp_free((char*)v_curve); v_curve = NULL; } if ( e_curve ) { temp_free((char*)e_curve); e_curve = NULL; } } /************************************************************************* * * function: sqcurve_method_value() * * purpose: Calculate squared mean curvature of given vertex * from precalculated data. Does normal and eff_area versions also. * */ REAL sqcurve_method_value(v_info) struct qinfo *v_info; { vertex_id v_id = v_info->v[0]; REAL h,venergy; ATTR attr = get_vattr(v_id); int ordv = loc_ordinal(v_id); struct v_curve_t *vc; REAL denom,f; REAL area; /* curvature normalization area */ /* kludge check here, needed for info_only individual attributes */ if ( sqcurve_init_timestamp < global_timestamp ) sqcurve_method_init(METHOD_VALUE,METH_INSTANCE(v_info->method)); if ( v_curve == NULL ) sqcurve_method_init(METHOD_VALUE,NULL); vc = v_curve + ordv; if ((attr & BOUNDARY) && !(METH_INSTANCE(v_info->method)->flags & IGNORE_CONSTR)) return 0.0; if ((attr & FIXED) && !(METH_INSTANCE(v_info->method)->flags & IGNORE_FIXED)) return 0.0; if ( vc->area == 0.0 ) return 0.0; if ( METH_INSTANCE(v_info->method)->gen_method != sq_mean_curvature_mi ) { /* need to check facet orientation consistency */ edge_id eid,starteid; eid = starteid = get_vertex_edge(v_id); do { facetedge_id fa,fb; fa = get_edge_fe(eid); fb = get_next_facet(fa); if ( (fa != fb) && (inverted(get_fe_facet(fa))==inverted(get_fe_facet(fb))) ) { sprintf(errmsg, "Inconsistent orientation of \nfacets %s and %s at vertex %s.\n", ELNAME(get_fe_facet(fa)),ELNAME1(get_fe_facet(fb)), ELNAME(eid)); strcat(errmsg,"eff_area_sq_mean_curvature and normal_sq_mean_curvature need consistency.\n"); kb_error(2183,errmsg,RECOVERABLE); } eid = get_next_tail_edge(eid); } while ( !equal_element(eid,starteid) ); } if ( !boundary_curvature_flag ) { vc->a = vc->area; area = vc->area/3; } else { area = vc->area/3; } if ( (attr & CONSTRAINT) && !(METH_INSTANCE(v_info->method)->flags & IGNORE_CONSTR) ) { conmap_t * conmap = get_v_constraint_map(v_id); int j,oncount = 0; struct constraint *con[MAXCONPER]; REAL perp[MAXCOORD]; for ( j = 1 ; j <= (int)conmap[0] ; j++ ) if ( conmap[j] & CON_HIT_BIT ) con[oncount++] = get_constraint(conmap[j]); constr_proj(TANGPROJ,oncount,con,get_coord(v_id), vc->force,perp,NULL,NO_DETECT,v_id); for ( j = 0 ; j < SDIM ; j++ ) vc->force[j] -= perp[j]; constr_proj(TANGPROJ,oncount,con,get_coord(v_id), vc->normal,perp,NULL,NO_DETECT,v_id); for ( j = 0 ; j < SDIM ; j++ ) vc->normal[j] -= perp[j]; } if ( METH_INSTANCE(v_info->method)->gen_method == normal_sq_mean_curvature_mi ) { f = SDIM_dot(vc->force,vc->force); denom = SDIM_dot(vc->force,vc->normal); if ( denom == 0.0 ) h = 0.0; else h = 3*f/denom; /* mean, and normal was twice area */ switch ( h0_flag ) { case H0_IN_GLOBAL: h -= h0_value; break; case H0_IN_ATTR: h -= *VREAL(v_id,h0_attr); break; } venergy = h*h; } else if ( METH_INSTANCE(v_info->method)->gen_method==mix_sq_mean_curvature_mi ) { /* normal_sq_mean part */ f = SDIM_dot(vc->force,vc->force); denom = SDIM_dot(vc->force,vc->normal); if ( denom == 0.0 ) h = 0.0; else h = 3*f/denom; /* mean, and normal was twice area */ /* add other part */ vc->h = h = h*sq_mean_mix + SDIM_dot(vc->force,vc->normal)/ SDIM_dot(vc->normal,vc->normal)*3*(1-sq_mean_mix); switch ( h0_flag ) { case H0_IN_GLOBAL: h -= h0_value; break; case H0_IN_ATTR: h -= *VREAL(v_id,h0_attr); break; } venergy = h*h; } else if ( h0_flag ) { REAL term=0.0,sim; vc->h = h = SDIM_dot(vc->force,vc->normal)/ SDIM_dot(vc->normal,vc->normal)*3; /* since vc->normal = 6*volgrad */ switch ( h0_flag ) { case H0_IN_GLOBAL: term = h - h0_value; break; case H0_IN_ATTR: term = h - *VREAL(v_id,h0_attr); break; } if ( self_similar_flag ) { vc->vol = SDIM_dot(get_coord(v_id),vc->normal); sim = selfsim_coeff*vc->vol; term -= sim; } venergy = term*term; } else if ( METH_INSTANCE(v_info->method)->gen_method == eff_area_sq_mean_curvature_mi ) { f = SDIM_dot(vc->force,vc->force); denom = SDIM_dot(vc->normal,vc->normal); if ( denom == 0.0 ) venergy = 0.0; else venergy = 9*f/denom; /* 9 = 36/4 */ } else /* plain squared curvature */ venergy = SDIM_dot(vc->force,vc->force)/area/area/4; return venergy*vc->a/3; } /************************************************************************* * * function: sqcurve_method_grad() * * purpose: Convert square curvature data into forces for a vertex. * */ REAL sqcurve_method_grad(v_info) struct qinfo *v_info; { vertex_id v_id = v_info->v[0]; edge_id e_id,ee_id,eee_id; int i,j; REAL e,e1,e2,denom; REAL f[MAXCOORD]; REAL fudge1,fudge2,fudge3; /* combinations of values */ REAL fudge11=0.0,fudge12=0.0,fudge13=0.0; /* combinations of values */ REAL fudge21=0.0,fudge22=0.0,fudge23=0.0; /* combinations of values */ REAL h; /* curvature */ REAL area; /* curvature normalization area */ REAL a; /* integral area allocation */ struct v_curve_t *vc = v_curve + loc_ordinal(v_id); ATTR attr = get_vattr(v_id); REAL ad[MAXCOORD]; REAL venergy = 0.0; REAL *grad = v_info->grad[0]; if ( div_normal_curvature_flag ) kb_error(1761,"Force not implemented yet for div_normal_curvature.\n", RECOVERABLE ); for ( i = 0 ; i < SDIM ; i++ ) grad[i] = 0.0; if ((attr & BOUNDARY) && !(METH_INSTANCE(v_info->method)->flags & IGNORE_CONSTR)) return 0.0; if ((attr & FIXED) && !(METH_INSTANCE(v_info->method)->flags & IGNORE_FIXED)) return 0.0; if ( vc->area == 0.0 ) return 0.0; if ( !boundary_curvature_flag ) { area = a = vc->a/3; } else { a = vc->a/3; area = vc->area/3; } for ( i = 0 ; i < SDIM ;i++ ) /* alloc area deriv */ if ( boundary_curvature_flag ) ad[i] = vc->star_force[i]/3; else ad[i] = vc->force[i]/3; /* vertex self-second derivatives */ if ( METH_INSTANCE(v_info->method)->gen_method==normal_sq_mean_curvature_mi ) { e = SDIM_dot(vc->force,vc->force); denom = SDIM_dot(vc->force,vc->normal); if ( denom != 0.0 ) { h = 3*e/denom; switch ( h0_flag ) { case H0_IN_GLOBAL: h -= h0_value; break; case H0_IN_ATTR: h -= *VREAL(v_id,h0_attr); break; } venergy = h*h; fudge1 = 4*h/denom*vc->area; fudge2 = fudge1*e/denom/2; fudge3 = h*h/3; for ( i = 0 ; i < SDIM ; i++ ) f[i] = fudge1*SDIM_dot(vc->force,vc->deriv2[i]) - fudge2*SDIM_dot(vc->normal,vc->deriv2[i]) + fudge3*(boundary_curvature_flag ? vc->star_force[i] : vc->force[i]); } else for ( i = 0 ; i < SDIM ; i++ ) f[i] =0.0; } else if ( METH_INSTANCE(v_info->method)->gen_method==mix_sq_mean_curvature_mi ) { /* normal_sq_mean part */ e = SDIM_dot(vc->force,vc->force); denom = SDIM_dot(vc->force,vc->normal); h = 0.0; if ( denom != 0.0 ) { h = 3*e/denom; switch ( h0_flag ) { case H0_IN_GLOBAL: h -= h0_value; break; case H0_IN_ATTR: h -= *VREAL(v_id,h0_attr); break; } fudge1 = 4/denom; fudge2 = fudge1*e/denom/2; fudge3 = h*h/3; for ( i = 0 ; i < SDIM ; i++ ) f[i] = fudge1*SDIM_dot(vc->force,vc->deriv2[i]) - fudge2*SDIM_dot(vc->normal,vc->deriv2[i]); } else for ( i = 0 ; i < SDIM ; i++ ) f[i] =0.0; /* add other part */ { REAL net; REAL fd[MAXCOORD]; REAL aread[MAXCOORD]; for ( i = 0 ; i < SDIM ;i++ ) fd[i] = SDIM_dot(vc->normal,vc->deriv2[i])/vc->norm*3; for ( i = 0 ; i < SDIM ;i++ ) { aread[i] = vc->force[i]/3; if ( boundary_curvature_flag ) ad[i] = vc->star_force[i]/3; else ad[i] = aread[i]; } h = (h*sq_mean_mix+(1-sq_mean_mix)*vc->term); /* make combination */ for ( i = 0 ; i < SDIM ; i++ ) { net = 2*h*(f[i]*sq_mean_mix+(1-sq_mean_mix)*fd[i])*a + h*h*ad[i]; grad[i] += net; } } venergy = h*h; } else if ( METH_INSTANCE(v_info->method)->gen_method == eff_area_sq_mean_curvature_mi ) { e = SDIM_dot(vc->force,vc->force); denom = SDIM_dot(vc->normal,vc->normal); if ( denom != 0.0 ) { for ( i = 0 ; i < SDIM ; i++ ) f[i] = 18*SDIM_dot(vc->force,vc->deriv2[i])/denom*a + 9*e/denom*ad[i]; venergy = 9*e/denom; /* 9 = 36/4 */ } else for ( i = 0 ; i < SDIM ; i++ ) f[i] =0.0; } else /* squared curvature */ { e = SDIM_dot(vc->force,vc->force)/vc->area*3.0/4; for ( i = 0 ; i < SDIM ; i++ ) { f[i] = (2*SDIM_dot(vc->force,vc->deriv2[i]) - 4/3.0*e*vc->force[i])/vc->area*3.0/4; f[i] += vc->fpgradf[i]/vc->area*3.0/4; } venergy = e*3/vc->area/4; } if ( h0_flag && !(METH_INSTANCE(v_info->method)->gen_method==normal_sq_mean_curvature_mi) ) { REAL net,sim; REAL fd[MAXCOORD],simd[MAXCOORD]; REAL aread[MAXCOORD]; for ( i = 0 ; i < SDIM ;i++ ) fd[i] = SDIM_dot(vc->normal,vc->deriv2[i])/vc->norm*3; for ( i = 0 ; i < SDIM ;i++ ) { aread[i] = vc->force[i]/3; if ( boundary_curvature_flag ) ad[i] = vc->star_force[i]/3; else ad[i] = aread[i]; } if ( self_similar_flag ) { vc->vol = SDIM_dot(get_coord(v_id),vc->normal); sim = selfsim_coeff*vc->vol/area; vc->term -= sim/area; for ( i = 0 ; i < SDIM ;i++ ) { simd[i] = selfsim_coeff*vc->normal[i]; grad[i] += 2*vc->term* (- simd[i])*a; } } for ( i = 0 ; i < SDIM ; i++ ) { net = 2*vc->term*fd[i]*a + vc->term*vc->term*ad[i]; grad[i] += net; } } else for ( i = 0 ; i < SDIM ; i++ ) grad[i] += f[i]; ee_id = get_vertex_edge(v_id); eee_id = ee_id; if ( valid_id(eee_id) ) do { vertex_id headv; vertex_id tailv; struct e_curve_t *ec; struct v_curve_t *vc1; struct v_curve_t *vc2; REAL s[MAXCOORD],cross1[MAXCOORD],cross2[MAXCOORD]; REAL denom1=0.0,denom2=0.0; REAL s0[MAXCOORD]; REAL wa[MAXCOORD],wb[MAXCOORD],w[MAXCOORD],aw[MAXCOORD],vw[MAXCOORD]; facetedge_id fe; facet_id f_id; if ( inverted(eee_id) ) e_id = inverse_id(eee_id); else e_id = eee_id; headv = get_edge_headv(e_id); tailv = get_edge_tailv(e_id); ec = e_curve + loc_ordinal(e_id); vc1 = v_curve + loc_ordinal(tailv); vc2 = v_curve + loc_ordinal(headv); if ( (vc1->area == 0.0) || (vc2->area == 0.0) ) continue; if ( METH_INSTANCE(v_info->method)->gen_method == normal_sq_mean_curvature_mi ) { facetedge_id fe_a; facetedge_id fe_b; REAL sa[MAXCOORD],sb[MAXCOORD]; fe = get_edge_fe(e_id); fe_a = get_prev_edge(fe); fe_b = get_prev_edge(get_next_facet(fe)); get_edge_side(e_id,s0); get_edge_side(get_fe_edge(fe_a),sa); cross_prod(vc1->force,sa,cross1); if ( (assume_oriented_flag && inverted(get_fe_facet(fe_a))) || (!assume_oriented_flag && triple_prod(vc1->normal,sa,s0) < 0.0 ) ) for ( i = 0 ; i < SDIM ; i++ ) cross1[i] = -cross1[i]; if ( fe_b != fe_a ) /* check for single edge on constraint */ { get_edge_side(get_fe_edge(fe_b),sb); cross_prod(vc1->force,sb,s); if ( (assume_oriented_flag && inverted(get_fe_facet(fe_b))) || (!assume_oriented_flag && triple_prod(vc1->normal,sb,s0) < 0.0 ) ) for ( i = 0 ; i < SDIM ; i++ ) cross1[i] -= s[i]; else for ( i = 0 ; i < SDIM ; i++ ) cross1[i] += s[i]; } e1 = SDIM_dot(vc1->force,vc1->force); denom1 = SDIM_dot(vc1->force,vc1->normal); if ( denom1 != 0.0 ) { h = 3*e1/denom1; switch ( h0_flag ) { case H0_IN_GLOBAL: h -= h0_value; break; case H0_IN_ATTR: h -= *VREAL(headv,h0_attr); break; } fudge11 = 4*h/denom1*vc1->area; fudge12 = fudge11*e1/denom1/2; fudge13 = h*h/3; } else fudge11 = fudge12 = fudge13 = 0.0; e2 = SDIM_dot(vc2->force,vc2->force); denom2 = SDIM_dot(vc2->force,vc2->normal); if ( denom2 != 0.0 ) { h = 3*e2/denom2; switch ( h0_flag ) { case H0_IN_GLOBAL: h -= h0_value; break; case H0_IN_ATTR: h -= *VREAL(tailv,h0_attr); break; } fudge21 = 4*h/denom2*vc2->area; fudge22 = fudge21*e2/denom2/2; fudge23 = h*h/3; } else fudge21 = fudge22 = fudge23 = 0.0; cross_prod(vc2->force,sa,cross2); if ( (assume_oriented_flag && !inverted(get_fe_facet(fe_a))) || (!assume_oriented_flag && triple_prod(vc2->normal,sa,s0) > 0.0 ) ) for ( i = 0 ; i < SDIM ; i++ ) cross2[i] = -cross2[i]; if ( fe_b != fe_a ) /* check for single edge on constraint */ { cross_prod(vc2->force,sb,s); if ( (assume_oriented_flag && !inverted(get_fe_facet(fe_b))) || (!assume_oriented_flag && triple_prod(vc2->normal,sb,s0) > 0.0 ) ) for ( i = 0 ; i < SDIM ; i++ ) cross2[i] -= s[i]; else for ( i = 0 ; i < SDIM ; i++ ) cross2[i] += s[i]; } } else if ( METH_INSTANCE(v_info->method)->gen_method == eff_area_sq_mean_curvature_mi ) { facetedge_id fe_a; facetedge_id fe_b; REAL sa[MAXCOORD],sb[MAXCOORD]; fe = get_edge_fe(e_id); fe_a = get_prev_edge(fe); fe_b = get_prev_edge(get_next_facet(fe)); get_edge_side(e_id,s0); get_edge_side(get_fe_edge(fe_a),sa); cross_prod(vc1->normal,sa,cross1); if ( (assume_oriented_flag && inverted(get_fe_facet(fe_a))) || (!assume_oriented_flag && SDIM_dot(cross1,s0) < 0.0 ) ) for ( i = 0 ; i < SDIM ; i++ ) cross1[i] = -cross1[i]; if ( fe_b != fe_a ) /* check for single edge on constraint */ { get_edge_side(get_fe_edge(fe_b),sb); cross_prod(vc1->normal,sb,s); if ( (assume_oriented_flag && inverted(get_fe_facet(fe_b))) || (!assume_oriented_flag && SDIM_dot(s,s0) < 0.0 ) ) for ( i = 0 ; i < SDIM ; i++ ) cross1[i] -= s[i]; else for ( i = 0 ; i < SDIM ; i++ ) cross1[i] += s[i]; } e1 = SDIM_dot(vc1->force,vc1->force); denom1 = SDIM_dot(vc1->normal,vc1->normal); e2 = SDIM_dot(vc2->force,vc2->force); denom2 = SDIM_dot(vc2->normal,vc2->normal); cross_prod(vc2->normal,sa,cross2); if ( (assume_oriented_flag && !inverted(get_fe_facet(fe_a))) || (!assume_oriented_flag && SDIM_dot(cross2,s0) > 0.0 ) ) for ( i = 0 ; i < SDIM ; i++ ) cross2[i] = -cross2[i]; if ( fe_b != fe_a ) /* check for single edge on constraint */ { cross_prod(vc2->normal,sb,s); if ( (assume_oriented_flag && !inverted(get_fe_facet(fe_b))) || (!assume_oriented_flag && SDIM_dot(s,s0) > 0.0 ) ) for ( i = 0 ; i < SDIM ; i++ ) cross2[i] -= s[i]; else for ( i = 0 ; i < SDIM ; i++ ) cross2[i] += s[i]; } } else /* squared curvature */ { e1 = SDIM_dot(vc1->force,vc1->force)/vc1->area*3.0/4; e2 = SDIM_dot(vc2->force,vc2->force)/vc2->area*3.0/4; } if ( inverted(eee_id) ) { int tattr = get_vattr(tailv); if ( (!(tattr & FIXED) || (METH_INSTANCE(v_info->method)->flags & IGNORE_FIXED)) && (!(tattr & BOUNDARY) || (METH_INSTANCE(v_info->method)->flags & IGNORE_CONSTR)) ) { /* force on head due to curvature at tail */ if ( METH_INSTANCE(v_info->method)->gen_method == normal_sq_mean_curvature_mi ) for ( i = 0 ; i < SDIM ; i++ ) { f[i] = fudge11*SDIM_dot(vc1->force,ec->deriv2[i]) - fudge12*(cross1[i]+SDIM_dot(vc1->normal,ec->deriv2[i])) + fudge13*ec->deriv[1][i] ; } else if (METH_INSTANCE(v_info->method)->gen_method ==eff_area_sq_mean_curvature_mi ) for ( i = 0 ; i < SDIM ; i++ ) { if ( denom1 != 0.0 ) f[i] = 6*SDIM_dot(vc1->force,ec->deriv2[i])/denom1*vc1->area + 3*e1/denom1*ec->deriv[1][i] - 6*e1/denom1/denom1*cross1[i]*vc1->area; else f[i] = 0.0; } else /* squared curvature */ for ( i = 0 ; i < SDIM ; i++ ) f[i] = (- 4/3.0*e1*ec->deriv[1][i] + 2*SDIM_dot(vc1->force,ec->deriv2[i]))/vc1->area*3.0/4; if ( h0_flag && !(METH_INSTANCE(v_info->method)->gen_method == normal_sq_mean_curvature_mi) ) { REAL fd[MAXCOORD],net; fe = get_edge_fe(e_id); get_edge_side(get_fe_edge(get_next_edge(fe)),wa); get_edge_side(get_fe_edge(get_next_edge(get_next_facet(fe))),wb); f_id = get_fe_facet(fe); if ( inverted(f_id) ) for ( i = 0 ; i < SDIM ; i++ ) w[i] = wb[i] - wa[i]; else for ( i = 0 ; i < SDIM ; i++ ) w[i] = wa[i] - wb[i]; cross_prod(w,vc1->force,aw); cross_prod(w,vc1->normal,vw); for ( i = 0 ; i < SDIM ; i++ ) { fd[i] = (SDIM_dot(vc1->normal,ec->deriv2[i])/vc1->norm + aw[i]/vc1->norm - vc1->f*2*vw[i]/vc1->norm/vc1->norm)*3; ad[i] = ec->deriv[1][i]/3; } area = vc1->area/3; a = vc1->a/3; for ( i = 0 ; i < SDIM ; i++ ) { net = 2*vc1->term*fd[i]*a + vc1->term*vc1->term*(ad[i]+ec->aderiv[1][i]/3); if ( self_similar_flag ) net += 2*vc1->term*(-selfsim_coeff*ec->volderiv[1][i])*a; grad[i] += net; } } else for ( i = 0 ; i < SDIM ; i++ ) grad[i] += f[i]; } } else /* not inverted */ if ( (!(get_vattr(headv) & FIXED) || (METH_INSTANCE(v_info->method)->flags & IGNORE_FIXED)) && (!(get_vattr(headv) & BOUNDARY) || (METH_INSTANCE(v_info->method)->flags & IGNORE_CONSTR)) ) { /* force on tail due to curvature at head */ if ( METH_INSTANCE(v_info->method)->gen_method == normal_sq_mean_curvature_mi ) for ( i = 0 ; i < SDIM ; i++ ) { f[i] = fudge23*ec->deriv[0][i] - fudge22*cross2[i]; for ( j = 0 ; j < SDIM ; j++ ) f[i] += fudge21*vc2->force[j]*ec->deriv2[j][i] - fudge22*vc2->normal[j]*ec->deriv2[j][i]; } else if ( METH_INSTANCE(v_info->method)->gen_method == eff_area_sq_mean_curvature_mi ) for ( i = 0 ; i < SDIM ; i++ ) { if ( denom2 != 0.0 ) { f[i] = 3*e2/denom2*ec->deriv[0][i] - 6*e2/denom2/denom2*cross2[i]*vc2->area; for ( j = 0 ; j < SDIM ; j++ ) f[i] += 6*vc2->force[j]*ec->deriv2[j][i]/denom2*vc2->area; } else f[i] = 0.0; } else /* squared curvature */ for ( i = 0 ; i < SDIM ; i++ ) { f[i] = -4/3.0*e2*ec->deriv[0][i]/vc2->area*3.0/4; for ( j = 0 ; j < SDIM ; j++ ) f[i] += 2*vc2->force[j]*ec->deriv2[j][i]/vc2->area*3.0/4; } if ( h0_flag && !(METH_INSTANCE(v_info->method)->gen_method == normal_sq_mean_curvature_mi) ) { REAL fd[MAXCOORD],net; fe = get_edge_fe(e_id); get_edge_side(get_fe_edge(get_next_edge(fe)),wa); get_edge_side(get_fe_edge(get_next_edge(get_next_facet(fe))),wb); f_id = get_fe_facet(fe); if ( inverted(f_id) ) for ( i = 0 ; i < SDIM ; i++ ) w[i] = wb[i] - wa[i]; else for ( i = 0 ; i < SDIM ; i++ ) w[i] = wa[i] - wb[i]; cross_prod(vc2->force,w,aw); cross_prod(vc2->normal,w,vw); for ( i = 0 ; i < SDIM ; i++ ) { for ( j = 0, fd[i] = 0.0 ; j < SDIM ; j++ ) fd[i] += vc2->normal[j]*ec->deriv2[j][i]; fd[i] = (fd[i]/vc2->norm + aw[i]/vc2->norm - vc2->f*2*vw[i]/vc2->norm/vc2->norm)*3; ad[i] = ec->deriv[0][i]/3; } area = vc2->area/3; a = vc2->a/3; for ( i = 0 ; i < SDIM ; i++ ) { net = 2*vc2->term*fd[i]*a + vc2->term*vc2->term*(ad[i]+ec->aderiv[0][i]/3); if ( self_similar_flag ) net += 2*vc2->term*(-selfsim_coeff*ec->volderiv[0][i])*a; grad[i] += net; } } else for ( i = 0 ; i < SDIM ; i++ ) grad[i] += f[i]; } eee_id = get_next_tail_edge(eee_id); } while ( !equal_id(eee_id,ee_id) ); return venergy*vc->a/3; } /* end sqcurve_method_grad */ /************************************************************************* square mean curvature for string With optional power in variable curvature_power **************************************************************************/ /************************************************************************ * * function: sqcurve_string_init() * * purpose: */ void sqcurve_string_init(mode,mi) int mode; struct method_instance *mi; { if ( web.modeltype != LINEAR ) kb_error(2864,"Method sqcurve_string only for LINEAR model.\n", RECOVERABLE); sqcurve_energy_string_init(); /* sets up curve_power */ } void sqcurve_string_marked_init(mode,mi) int mode; struct method_instance *mi; { int eltype; if ( web.modeltype != LINEAR ) kb_error(2865,"Method sqcurve_string only for LINEAR model.\n", RECOVERABLE); marked_edge_attr = find_extra(MARKED_EDGE_ATTR_NAME,&eltype); if ( eltype != EDGE ) kb_error(2184,"sqcurve_string_mark should be an edge attribute.\n", RECOVERABLE); } /************************************************************************ * * function: sqcurve_string_value() * * purpose: Calculate square curvature energy for string model * Works locally vertex by vertex. Assumes two edges per vertex. * */ REAL sqcurve_string_value(v_info) struct qinfo *v_info; { REAL s1,s2,s1s2; /* edge lengths */ REAL *side1 = v_info->sides[0][0],*side2 = v_info->sides[0][1]; REAL energy; REAL hh,a1,a2; REAL power; if ( v_info->vcount < 3 ) return 0.; if ( METH_INSTANCE(v_info->method)->flags & METH_PARAMETER_1 ) power = METH_INSTANCE(v_info->method)->parameter_1; else power = curve_power; s1 = sqrt(SDIM_dot(side1,side1)); s2 = sqrt(SDIM_dot(side2,side2)); s1s2 = SDIM_dot(side1,side2); a1 = 1 + s1s2/s1/s2; a2 = (s1 + s2)/2; if ( (a1 <= 0.0) || (a2 == 0.0) ) return 0.0; hh = (2*a1+12*a1*a1/36)/a2/a2; energy = a2*pow(hh,power/2); return energy; } /************************************************************************ * * function: sqcurve_string_grad() * * purpose: Calculate square curvature force for string model * Works locally vertex by vertex. * */ REAL sqcurve_string_grad(v_info) struct qinfo *v_info; { REAL s1,s2,s1s2; /* edge lengths */ REAL *side1 = v_info->sides[0][0],*side2 = v_info->sides[0][1]; int i,k; REAL hh,a1,a2,energy; REAL a1g[2][MAXCOORD],a2g[2][MAXCOORD]; REAL s[2]; REAL *side[2]; REAL term; /* common factor */ REAL power; if ( v_info->vcount < 3 ) return 0.0; if ( METH_INSTANCE(v_info->method)->flags & METH_PARAMETER_1 ) power = METH_INSTANCE(v_info->method)->parameter_1; else power = curve_power; s1 = sqrt(SDIM_dot(side1,side1)); s2 = sqrt(SDIM_dot(side2,side2)); s1s2 = SDIM_dot(side1,side2); a1 = 2 + 2*s1s2/s1/s2; a2 = (s1 + s2)/2; if (a2 == 0.0) return 0.0; hh = (a1+3*a1*a1/36)/a2/a2; if ( hh <= 0.0 ) { hh = 0.0; energy = 0.0; } else energy = a2*pow(hh,power/2); side[0] = side1; side[1] = side2; s[0] = s1; s[1] = s2; for ( k = 0 ; k < 2 ; k++ ) { REAL coeff = 2*s1s2/s[k]/s[k]/s[k]/s[1-k]; for ( i = 0 ; i < SDIM ; i++ ) { a1g[k][i] = 2*side[1-k][i]/s1/s2 - coeff*side[k][i]; a2g[k][i] = 0.5*side[k][i]/s[k]; } } for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] = 0.0; term = a2*pow(hh,power/2-1); for ( k = 0 ; k < 2 ; k++ ) for ( i = 0 ; i < SDIM ; i++ ) { REAL f; f = a2g[k][i]*energy/a2 + term*power/2 *(a1g[k][i]*(1+6*a1/36)/a2/a2 - (2*a1+6*a1*a1/36)/a2/a2/a2*a2g[k][i]); v_info->grad[k+1][i] = f; v_info->grad[0][i] += -f; } return energy; } /************************************************************************ * * function: sqcurve_string_hess() * * purpose: Calculate square curvature hessian for string model * Works locally vertex by vertex. * */ REAL sqcurve_string_hess(v_info) struct qinfo *v_info; { REAL s1,s2,s1s2; /* edge lengths */ REAL *side1 = v_info->sides[0][0],*side2 = v_info->sides[0][1]; REAL *side[2]; int i,k,kk,ii; REAL a1,a2,energy; REAL a1g[2][MAXCOORD],a2g[2][MAXCOORD]; REAL a1h[2][2][MAXCOORD][MAXCOORD],a2h[2][2][MAXCOORD][MAXCOORD]; REAL s[2]; REAL ****h = v_info->hess; REAL term; /* common factor */ REAL power; if ( v_info->vcount < 3 ) return 0.0; if ( METH_INSTANCE(v_info->method)->flags & METH_PARAMETER_1 ) power = METH_INSTANCE(v_info->method)->parameter_1; else power = curve_power; if ( power != 2 ) kb_error(1762,"Hessian not implemented for curvature_power != 2.\n", RECOVERABLE); s1 = sqrt(SDIM_dot(side1,side1)); s2 = sqrt(SDIM_dot(side2,side2)); s1s2 = SDIM_dot(side1,side2); a1 = 1 + s1s2/s1/s2; a2 = (s1 + s2)/2; if ( a2 == 0.0 ) return 0.0; energy = 2*(a1+6*a1*a1/36)/a2; side[0] = side1; side[1] = side2; s[0] = s1; s[1] = s2; for ( k = 0 ; k < 2 ; k++ ) { term = s1s2/s[k]/s[k]/s[k]/s[1-k]; for ( i = 0 ; i < SDIM ; i++ ) { a1g[k][i] = side[1-k][i]/s1/s2 - term*side[k][i]; a2g[k][i] = 0.5*side[k][i]/s[k]; } } for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] = 0.0; term = (a1+6*a1*a1/36)/a2/a2; for ( k = 0 ; k < 2 ; k++ ) for ( i = 0 ; i < SDIM ; i++ ) { REAL f = 2*(a1g[k][i]*(1+12*a1/36)/a2 - term*a2g[k][i]); v_info->grad[k+1][i] = f; v_info->grad[0][i] += -f; } /* hessian */ memset((char*)a1h,0,sizeof(a1h)); memset((char*)a2h,0,sizeof(a2h)); for ( k = 0 ; k < 2 ; k++ ) { REAL terma = 3*s1s2/s[k]/s[k]/s[k]/s[k]/s[k]/s[1-k]; REAL termb = s1s2/s[k]/s[k]/s[k]/s[1-k]/s[1-k]/s[1-k]; REAL termc = 1/s[k]/s[1-k]/s[1-k]/s[1-k]; term = 1/s[k]/s[k]/s[k]/s[1-k]; for ( i = 0 ; i < SDIM ; i++ ) { a1h[k][1-k][i][i] += 1/s1/s2; for ( ii = 0 ; ii < SDIM ; ii++ ) { a1h[k][k][i][ii] += -side[1-k][i]*term*side[k][ii]; a1h[k][1-k][i][ii] += -side[1-k][i]*termc*side[1-k][ii]; } for ( ii = 0 ; ii < SDIM ; ii++ ) { a1h[k][k][i][ii] += -side[1-k][ii]*term*side[k][i]; a1h[k][1-k][i][ii] += -side[k][ii]*term*side[k][i]; a1h[k][k][i][ii] += terma*side[k][i]*side[k][ii]; a1h[k][1-k][i][ii] += termb*side[k][i]*side[1-k][ii]; } a1h[k][k][i][i] += - s1s2*term; a2h[k][k][i][i] += 0.5/s[k]; for ( ii = 0 ; ii < SDIM ; ii++ ) a2h[k][k][i][ii] += -0.5*side[k][i]/s[k]/s[k]/s[k]*side[k][ii]; } } for ( k = 0 ; k < 2 ; k++ ) for ( kk = 0 ; kk < 2 ; kk++ ) for ( i = 0 ; i < SDIM ; i++ ) for ( ii = 0 ; ii < SDIM ; ii++ ) { REAL f = 2*((a1h[k][kk][i][ii]*(1+12*a1/36)+12*a1g[k][i]*a1g[kk][ii]/36)/a2 - a1g[k][i]*(1+12*a1/36)*a2g[kk][ii]/a2/a2 - a1g[kk][ii]*(1+12*a1/36)*a2g[k][i]/a2/a2 - (a1+6*a1*a1/36)*a2h[k][kk][i][ii]/a2/a2 + 2*(a1+6*a1*a1/36)*a2g[k][i]*a2g[kk][ii]/a2/a2/a2); h[k+1][kk+1][i][ii] += f; h[k+1][0][i][ii] += -f; h[0][kk+1][i][ii] += -f; h[0][0][i][ii] += f; } return energy; } /************************************************************************* Square curvature for string with intrinsic curvature Plane only; intrinsic curvature in vertex attribute "h_zero" or variable h_zero. Edges assumed to all be oriented in the same direction. **************************************************************************/ /************************************************************************ * * function: sqcurve2_string_init() * * purpose: */ void sqcurve2_string_init(mode,mi) int mode; struct method_instance *mi; { int eltype; int k; if ( web.modeltype != LINEAR ) kb_error(3211,"Method sqcurve_string only for LINEAR model.\n", RECOVERABLE); /* see if using (H - H_0)^2 adjustment */ h0_flag = 0; h0_attr = find_extra("h_zero",&eltype); if ( h0_attr >= 0 ) h0_flag = H0_IN_ATTR; else { k = lookup_global("h_zero"); if ( k >= 0 ) { h0_flag = H0_IN_GLOBAL; h0_value = globals(k)->value.real; } } } /************************************************************************ * * function: sqcurve2_string_value() * * purpose: Calculate square curvature energy for string model * Works locally vertex by vertex. Assumes two edges per vertex. * */ REAL sqcurve2_string_value(v_info) struct qinfo *v_info; { REAL s1,s2,s1xs2; /* edge lengths */ REAL *side1 = v_info->sides[0][0],*side2 = v_info->sides[0][1]; REAL energy; REAL h,h0,hh,a1,a2; if ( v_info->vcount < 3 ) return 0.; s1 = sqrt(SDIM_dot(side1,side1)); s2 = sqrt(SDIM_dot(side2,side2)); s1xs2 = side1[0]*side2[1] - side1[1]*side2[0]; a1 = s1xs2/s1/s2; /* sin(theta) */ a2 = (s1 + s2)/2; if ( a2 == 0.0 ) return 0.0; h = a1/a2; /* curvature */ if ( h0_flag == H0_IN_ATTR ) h0 = *VREAL(v_info->id,h0_attr); else h0 = h0_value; hh = (h - h0)*(h - h0); /* square net curvature */ energy = a2*hh; return energy; } /************************************************************************ * * function: sqcurve2_string_grad() * * purpose: Calculate square curvature force for string model * Works locally vertex by vertex. * */ REAL sqcurve2_string_grad(v_info) struct qinfo *v_info; { REAL s1,s2,s1xs2; /* edge lengths */ REAL *side1 = v_info->sides[0][0],*side2 = v_info->sides[0][1]; int i,k; REAL hh,h,h0,a1,a2,energy; REAL a1g[2][MAXCOORD],a2g[2][MAXCOORD]; REAL s[2]; REAL *side[2]; if ( v_info->vcount < 3 ) return 0.; s1 = sqrt(SDIM_dot(side1,side1)); s2 = sqrt(SDIM_dot(side2,side2)); s1xs2 = side1[0]*side2[1] - side1[1]*side2[0]; a1 = s1xs2/s1/s2; /* sin(theta) */ a2 = (s1 + s2)/2; if ( a2 == 0.0 ) return 0.0; h = a1/a2; /* curvature */ if ( h0_flag == H0_IN_ATTR ) h0 = *VREAL(v_info->id,h0_attr); else h0 = h0_value; hh = (h - h0)*(h - h0); /* square net curvature */ energy = a2*hh; side[0] = side1; side[1] = side2; s[0] = s1; s[1] = s2; for ( k = 0 ; k < 2 ; k++ ) { REAL coeff = s1xs2/s[k]/s[k]/s[k]/s[1-k]; for ( i = 0 ; i < SDIM ; i++ ) { a1g[k][i] = (k==i?1:-1)*side[1-k][1-i]/s1/s2 - coeff*side[k][i]; a2g[k][i] = 0.5*side[k][i]/s[k]; } } for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] = 0.0; for ( k = 0 ; k < 2 ; k++ ) for ( i = 0 ; i < SDIM ; i++ ) { REAL f; f = a2g[k][i]*hh + a2*2*(h - h0)*(a1g[k][i]/a2 - a1/a2/a2*a2g[k][i]); v_info->grad[k+1][i] = f; v_info->grad[0][i] += -f; } return energy; } /************************************************************************* square mean curvature for string, version 3 Uses (1/L1 + 1/L2)/2 instead of 1/(L1+L2) for inverse length to encourage equal size edges. **************************************************************************/ /************************************************************************ * * function: sqcurve3_string_init() * * purpose: */ void sqcurve3_string_init(mode,mi) int mode; struct method_instance *mi; { if ( web.modeltype != LINEAR ) kb_error(4864,"Method sqcurve3_string only for LINEAR model.\n", RECOVERABLE); } /************************************************************************ * * function: sqcurve3_string_all() * * purpose: Calculate square curvature energy etc. for string model * Works locally vertex by vertex. Assumes two edges per vertex. * */ REAL sqcurve3_string_all(v_info,mode) struct qinfo *v_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, METHOD_HESSIAN */ { REAL s1,s2,s1s2; /* edge lengths */ REAL *side1 = v_info->sides[0][0],*side2 = v_info->sides[0][1]; REAL energy; REAL a1,a2; REAL da1[3][MAXCOORD],da2[3][MAXCOORD]; REAL dda1[3][3][MAXCOORD][MAXCOORD],dda2[3][3][MAXCOORD][MAXCOORD]; int i,j,k,kk; if ( v_info->vcount != 3 ) return 0.; s1 = sqrt(SDIM_dot(side1,side1)); s2 = sqrt(SDIM_dot(side2,side2)); s1s2 = SDIM_dot(side1,side2); if ( (s1 == 0.0) || (s2 == 0.0) ) return 0.0; a1 = 1 + s1s2/s1/s2; /* 1 - cos(theta)^2 */ a2 = (1/s1 + 1/s2)/2; /* inverse length approximation */ energy = (2*a1+a1*a1/3)*a2; if ( mode == METHOD_VALUE ) return energy; /* Gradient */ for ( i = 0 ; i < SDIM ; i++ ) { da1[1][i] = side2[i]/s1/s2 - s1s2/s1/s1/s1/s2*side1[i]; da1[2][i] = side1[i]/s1/s2 - s1s2/s1/s2/s2/s2*side2[i]; da1[0][i] = -da1[1][i] - da1[2][i]; da2[1][i] = -1./2/s1/s1/s1*side1[i]; da2[2][i] = -1./2/s2/s2/s2*side2[i]; da2[0][i] = -da2[1][i] - da2[2][i]; } for ( k = 0 ; k < v_info->vcount ; k++ ) for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[k][i] = (2 + 2*a1/3)*da1[k][i]*a2 + (2*a1+a1*a1/3)*da2[k][i]; if ( mode == METHOD_GRADIENT ) return energy; /* Hessian */ for ( i = 0 ; i < SDIM ; i++ ) { for ( j = 0 ; j < SDIM ; j++ ) { dda1[1][1][i][j] = -side2[i]/s1/s1/s1/s2*side1[j] - side2[j]/s1/s1/s1/s2*side1[i] + 3*s1s2/s1/s1/s1/s1/s1/s2*side1[i]*side1[j] + (j==i ? -s1s2/s1/s1/s1/s2 : 0.0); dda1[1][2][i][j] = -side2[i]/s1/s2/s2/s2*side2[j] - side1[j]/s1/s1/s1/s2*side1[i] + s1s2/s1/s1/s1/s2/s2/s2*side1[i]*side2[j] + (j==i ? 1/s1/s2 : 0.0); dda1[2][1][i][j] = -side1[i]/s1/s1/s1/s2*side1[j] - side2[j]/s1/s2/s2/s2*side2[i] + s1s2/s1/s1/s1/s2/s2/s2*side2[i]*side1[j] + (j==i ? 1/s1/s2 : 0.0); dda1[2][2][i][j] = -side1[i]/s1/s2/s2/s2*side2[j] - side1[j]/s1/s2/s2/s2*side2[i] + 3*s1s2/s1/s2/s2/s2/s2/s2*side2[i]*side2[j] + (j==i ? -s1s2/s1/s2/s2/s2 : 0.0); dda1[0][1][i][j] = -dda1[1][1][i][j] - dda1[2][1][i][j]; dda1[0][2][i][j] = -dda1[1][2][i][j] - dda1[2][2][i][j]; dda1[0][0][i][j] = -dda1[0][1][i][j] - dda1[0][2][i][j]; dda2[1][1][i][j] = 3./2/s1/s1/s1/s1/s1*side1[j]*side1[i] + (j==i ? -1./2/s1/s1/s1 : 0.0 ); dda2[1][2][i][j] = 0.0; dda2[2][1][i][j] = 0.0; dda2[2][2][i][j] = 3./2/s2/s2/s2/s2/s2*side2[j]*side2[i] + (j==i ? -1./2/s2/s2/s2 : 0.0 ); dda2[0][1][i][j] = -dda2[1][1][i][j] - dda2[2][1][i][j]; dda2[0][2][i][j] = -dda2[1][2][i][j] - dda2[2][2][i][j]; dda2[0][0][i][j] = -dda2[0][1][i][j] - dda2[0][2][i][j]; } } for ( k = 0 ; k < v_info->vcount ; k++ ) for ( kk = 0 ; kk < v_info->vcount ; kk++ ) for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) v_info->hess[k][kk][i][j] = (2*da1[kk][j]/3)*da1[k][i]*a2 + (2 + 2*a1/3)*dda1[k][kk][i][j]*a2 + (2 + 2*a1/3)*da1[k][i]*da2[kk][j] + (2 + 2*a1/3)*da1[kk][j]*da2[k][i] + (2*a1+a1*a1/3)*dda2[k][kk][i][j]; return energy; } REAL sqcurve3_string_value(v_info) struct qinfo *v_info; { return sqcurve3_string_all(v_info,METHOD_VALUE); } REAL sqcurve3_string_grad(v_info) struct qinfo *v_info; { return sqcurve3_string_all(v_info,METHOD_GRADIENT); } REAL sqcurve3_string_hess(v_info) struct qinfo *v_info; { return sqcurve3_string_all(v_info,METHOD_HESSIAN); } /* end sqcurve3_string method */ /************************************************************************* Square mean curvature for cylinder in string model Cylindrical axis is x axis. Includes 2*pi factor. Includes the 1/2 factor in the definition of mean curvature. Functions can actually do any power of (h-h0), so could set up other methods easily. **************************************************************************/ REAL sq_mean_curv_cyl_all ARGS(( struct qinfo *, int, REAL )); /************************************************************************ * * function: sq_mean_curv_cyl_init() * * purpose: */ void sq_mean_curv_cyl_init(mode,mi) int mode; struct method_instance *mi; { int k; int eltype; if ( web.modeltype != LINEAR ) kb_error(2486,"sq_mean_curv_cyl method only for LINEAR model.\n", RECOVERABLE); /* see if using (H - H_0)^2 adjustment */ h0_flag = 0; h0_attr = find_extra("h_zero",&eltype); if ( h0_attr >= 0 ) h0_flag = H0_IN_ATTR; else { k = lookup_global("h_zero"); if ( k >= 0 ) { h0_flag = H0_IN_GLOBAL; h0_value = globals(k)->value.real; } } } REAL sq_mean_curv_cyl_all(v_info,mode,power) struct qinfo *v_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, METHOD_HESSIAN */ REAL power; { REAL s1,s2; /* edge lengths */ REAL *side1 = v_info->sides[0][0],*side2 = v_info->sides[0][1]; REAL energy; REAL area; /* of entire pair of frustrums */ REAL h,hh; REAL r0,r1,r2; /* radii; r1 is middle radius, at varying vertex */ REAL dadx[3],dadr[3]; /* index refers to v_info order */ REAL ddadxdx[3][3],ddadxdr[3][3],ddadrdr[3][3],ddadrdx[3][3],dhdx[3],dhdr[3]; REAL ddhdxdx[3][3],ddhdxdr[3][3],ddhdrdr[3][3],ddhdrdx[3][3]; REAL dddadxdxdx[3][3][3]; REAL dddadxdxdr[3][3][3]; REAL dddadxdrdx[3][3][3]; REAL dddadxdrdr[3][3][3]; REAL dddadrdxdx[3][3][3]; REAL dddadrdxdr[3][3][3]; REAL dddadrdrdx[3][3][3]; REAL dddadrdrdr[3][3][3]; REAL root; int i,j; int sign; /* of curvature */ if ( (v_info->vcount == 2) && (fabs(v_info->x[0][1]) < 1e-10) ) { /* assume this is an endpoint on the axis, so we do special concentrated squared curvature */ edge_id e_id = get_vertex_edge(v_info->id); int sign = inverted(e_id) ? 1 : -1; /* in case of h0 */ REAL ss = SDIM_dot(side1,side1); REAL dx = v_info->x[1][0] - v_info->x[0][0]; REAL prod; /* full power */ REAL hterm; /* h - h0 */ REAL gradprod=0.0; /* one less power, for grad */ REAL hessprod=0.0; /* two less power, for hess */ int k; REAL dx_dx0=-1,dx_dx1=1,dx_dy0=0,dx_dy1=0; REAL dss_dx0,dss_dx1,dss_dy0,dss_dy1; REAL dhterm_dx0,dhterm_dx1,dhterm_dy0,dhterm_dy1; REAL dprod_dx0,dprod_dy0,dprod_dx1,dprod_dy1; REAL dss_dx0_dx0,dss_dx0_dx1,dss_dx0_dy0=0,dss_dx0_dy1=0; REAL dss_dx1_dx0,dss_dx1_dx1,dss_dx1_dy0=0,dss_dx1_dy1=0; REAL dss_dy0_dx0=0,dss_dy0_dx1=0,dss_dy0_dy0,dss_dy0_dy1; REAL dss_dy1_dx0=0,dss_dy1_dx1=0,dss_dy1_dy0,dss_dy1_dy1; REAL dhterm_dx0_dx0,dhterm_dx0_dx1,dhterm_dx0_dy0,dhterm_dx0_dy1; REAL dhterm_dx1_dx0,dhterm_dx1_dx1,dhterm_dx1_dy0,dhterm_dx1_dy1; REAL dhterm_dy0_dx0,dhterm_dy0_dx1,dhterm_dy0_dy0,dhterm_dy0_dy1; REAL dhterm_dy1_dx0,dhterm_dy1_dx1,dhterm_dy1_dy0,dhterm_dy1_dy1; REAL dprod_dx0_dx0,dprod_dx0_dx1,dprod_dx0_dy0,dprod_dx0_dy1; REAL dprod_dx1_dx0,dprod_dx1_dx1,dprod_dx1_dy0,dprod_dx1_dy1; REAL dprod_dy0_dx0,dprod_dy0_dx1,dprod_dy0_dy0,dprod_dy0_dy1; REAL dprod_dy1_dx0,dprod_dy1_dx1,dprod_dy1_dy0,dprod_dy1_dy1; hterm = sign*2*dx/ss - h0_value; for ( prod = 1.0, k = 0 ; k < power ; k++ ) { hessprod = gradprod; gradprod = prod; prod *= hterm; } energy = prod*M_PI*ss/4; if ( mode == METHOD_VALUE ) return energy; /* gradient */ dss_dx0 = -2*side1[0]; dss_dx1 = 2*side1[0]; dss_dy0 = -2*side1[1]; dss_dy1 = 2*side1[1]; dhterm_dx0 = sign*2*(-1/ss - dx/ss/ss*dss_dx0); dhterm_dy0 = sign*2*( - dx/ss/ss*dss_dy0); dhterm_dx1 = sign*2*( 1/ss - dx/ss/ss*dss_dx1); dhterm_dy1 = sign*2*( - dx/ss/ss*dss_dy1); dprod_dx0 = power*gradprod*dhterm_dx0; dprod_dx1 = power*gradprod*dhterm_dx1; dprod_dy0 = power*gradprod*dhterm_dy0; dprod_dy1 = power*gradprod*dhterm_dy1; v_info->grad[0][0] = dprod_dx0*M_PI*ss/4 +prod*M_PI*dss_dx0/4; v_info->grad[0][1] = 0.0; /* assuming constrained to axis */ v_info->grad[1][0] = dprod_dx1*M_PI*ss/4 +prod*M_PI*dss_dx1/4; v_info->grad[1][1] = dprod_dy1*M_PI*ss/4 +prod*M_PI*dss_dy1/4; if ( mode == METHOD_GRADIENT ) return energy; /* hessian */ dss_dx0_dx0 = 2; dss_dx0_dx1 = -2; dss_dx1_dx0 = -2; dss_dx1_dx1 = 2; dss_dy0_dy0 = 2; dss_dy0_dy1 = -2; dss_dy1_dy0 = -2; dss_dy1_dy1 = 2; dhterm_dx0_dx0 = sign*2*(1/ss/ss*dss_dx0 - dx_dx0/ss/ss*dss_dx0 +2*dx/ss/ss/ss*dss_dx0*dss_dx0 - dx/ss/ss*dss_dx0_dx0); dhterm_dx0_dx1 = sign*2*(1/ss/ss*dss_dx1 - dx_dx1/ss/ss*dss_dx0 +2*dx/ss/ss/ss*dss_dx1*dss_dx0 - dx/ss/ss*dss_dx0_dx1); dhterm_dx0_dy0 = sign*2*(1/ss/ss*dss_dy0 - dx_dy0/ss/ss*dss_dx0 +2*dx/ss/ss/ss*dss_dy0*dss_dx0 - dx/ss/ss*dss_dx0_dy0); dhterm_dx0_dy1 = sign*2*(1/ss/ss*dss_dy1 - dx_dy1/ss/ss*dss_dx0 +2*dx/ss/ss/ss*dss_dy1*dss_dx0 - dx/ss/ss*dss_dx0_dy1); dhterm_dy0_dx0 = sign*2*(-dx_dx0/ss/ss*dss_dy0 +2*dx/ss/ss/ss*dss_dx0*dss_dy0 - dx/ss/ss*dss_dy0_dx0); dhterm_dy0_dx1 = sign*2*(-dx_dx1/ss/ss*dss_dy0 +2*dx/ss/ss/ss*dss_dx1*dss_dy0 - dx/ss/ss*dss_dy0_dx1); dhterm_dy0_dy0 = sign*2*(-dx_dy0/ss/ss*dss_dy0 +2*dx/ss/ss/ss*dss_dy0*dss_dy0 - dx/ss/ss*dss_dy0_dy0); dhterm_dy0_dy1 = sign*2*(-dx_dy1/ss/ss*dss_dy0 +2*dx/ss/ss/ss*dss_dy1*dss_dy0 - dx/ss/ss*dss_dy0_dy1); dhterm_dx1_dx0 = sign*2*(-1/ss/ss*dss_dx0 - dx_dx0/ss/ss*dss_dx1 +2*dx/ss/ss/ss*dss_dx0*dss_dx1 - dx/ss/ss*dss_dx1_dx0); dhterm_dx1_dx1 = sign*2*(-1/ss/ss*dss_dx1 - dx_dx1/ss/ss*dss_dx1 +2*dx/ss/ss/ss*dss_dx1*dss_dx1 - dx/ss/ss*dss_dx1_dx1); dhterm_dx1_dy0 = sign*2*(-1/ss/ss*dss_dy0 - dx_dy0/ss/ss*dss_dx0 +2*dx/ss/ss/ss*dss_dy0*dss_dx1 - dx/ss/ss*dss_dx1_dy0); dhterm_dx1_dy1 = sign*2*(-1/ss/ss*dss_dy1 - dx_dy1/ss/ss*dss_dx0 +2*dx/ss/ss/ss*dss_dy1*dss_dx1 - dx/ss/ss*dss_dx1_dy1); dhterm_dy1_dx0 = sign*2*(-dx_dx0/ss/ss*dss_dy1 +2*dx/ss/ss/ss*dss_dx0*dss_dy1 - dx/ss/ss*dss_dy1_dx0); dhterm_dy1_dx1 = sign*2*(-dx_dx1/ss/ss*dss_dy1 +2*dx/ss/ss/ss*dss_dx1*dss_dy1 - dx/ss/ss*dss_dy1_dx1); dhterm_dy1_dy0 = sign*2*(-dx_dy0/ss/ss*dss_dy1 +2*dx/ss/ss/ss*dss_dy0*dss_dy1 - dx/ss/ss*dss_dy1_dy0); dhterm_dy1_dy1 = sign*2*(-dx_dy1/ss/ss*dss_dy1 +2*dx/ss/ss/ss*dss_dy1*dss_dy1 - dx/ss/ss*dss_dy1_dy1); dprod_dx0_dx0 = power*(power-1)*hessprod*dhterm_dx0*dhterm_dx0 + power*gradprod*dhterm_dx0_dx0; dprod_dx0_dx1 = power*(power-1)*hessprod*dhterm_dx0*dhterm_dx1 + power*gradprod*dhterm_dx0_dx1; dprod_dx0_dy0 = power*(power-1)*hessprod*dhterm_dx0*dhterm_dy0 + power*gradprod*dhterm_dx0_dy0; dprod_dx0_dy1 = power*(power-1)*hessprod*dhterm_dx0*dhterm_dy1 + power*gradprod*dhterm_dx0_dy1; dprod_dx1_dx0 = power*(power-1)*hessprod*dhterm_dx1*dhterm_dx0 + power*gradprod*dhterm_dx1_dx0; dprod_dx1_dx1 = power*(power-1)*hessprod*dhterm_dx1*dhterm_dx1 + power*gradprod*dhterm_dx1_dx1; dprod_dx1_dy0 = power*(power-1)*hessprod*dhterm_dx1*dhterm_dy0 + power*gradprod*dhterm_dx1_dy0; dprod_dx1_dy1 = power*(power-1)*hessprod*dhterm_dx1*dhterm_dy1 + power*gradprod*dhterm_dx1_dy1; dprod_dy0_dx0 = power*(power-1)*hessprod*dhterm_dy0*dhterm_dx0 + power*gradprod*dhterm_dy0_dx0; dprod_dy0_dx1 = power*(power-1)*hessprod*dhterm_dy0*dhterm_dx1 + power*gradprod*dhterm_dy0_dx1; dprod_dy0_dy0 = power*(power-1)*hessprod*dhterm_dy0*dhterm_dy0 + power*gradprod*dhterm_dy0_dy0; dprod_dy0_dy1 = power*(power-1)*hessprod*dhterm_dy0*dhterm_dy1 + power*gradprod*dhterm_dy0_dy1; dprod_dy1_dx0 = power*(power-1)*hessprod*dhterm_dy1*dhterm_dx0 + power*gradprod*dhterm_dy1_dx0; dprod_dy1_dx1 = power*(power-1)*hessprod*dhterm_dy1*dhterm_dx1 + power*gradprod*dhterm_dy1_dx1; dprod_dy1_dy0 = power*(power-1)*hessprod*dhterm_dy1*dhterm_dy0 + power*gradprod*dhterm_dy1_dy0; dprod_dy1_dy1 = power*(power-1)*hessprod*dhterm_dy1*dhterm_dy1 + power*gradprod*dhterm_dy1_dy1; v_info->hess[0][0][0][0] = M_PI/4*(dprod_dx0_dx0*ss + dprod_dx0*dss_dx0 + dprod_dx0*dss_dx0 + prod*dss_dx0_dx0); v_info->hess[0][0][0][1] = 0.0; v_info->hess[0][1][0][0] = -v_info->hess[0][0][0][0]; v_info->hess[0][1][0][1] = M_PI/4*(dprod_dx0_dy1*ss + dprod_dx0*dss_dy1 + dprod_dy1*dss_dx0 + prod*dss_dx0_dy1); v_info->hess[0][0][1][0] = 0.0; /* assuming constrained to axis */ v_info->hess[0][0][1][1] = 0.0; /* assuming constrained to axis */ v_info->hess[0][1][1][0] = 0.0; /* assuming constrained to axis */ v_info->hess[0][1][1][1] = 0.0; /* assuming constrained to axis */ v_info->hess[1][0][0][0] = v_info->hess[0][1][0][0]; v_info->hess[1][0][0][1] = 0.0; v_info->hess[1][1][0][0] = -v_info->hess[1][0][0][0]; v_info->hess[1][1][0][1] = M_PI/4*(dprod_dx1_dy1*ss + dprod_dx1*dss_dy1 + dprod_dy1*dss_dx1 + prod*dss_dx1_dy1); v_info->hess[1][0][1][0] = v_info->hess[0][1][0][1]; v_info->hess[1][0][1][1] = v_info->hess[0][1][1][1]; v_info->hess[1][1][1][0] = v_info->hess[1][1][0][1]; v_info->hess[1][1][1][1]= M_PI/4*(dprod_dy1_dy1*ss + dprod_dy1*dss_dy1 + dprod_dy1*dss_dy1 + prod*dss_dy1_dy1); return energy; } if ( v_info->vcount < 3 ) return 0.; /* now regular case */ r0 = v_info->x[1][1]; r1 = v_info->x[0][1]; r2 = v_info->x[2][1]; s1 = sqrt(SDIM_dot(side1,side1)); s2 = sqrt(SDIM_dot(side2,side2)); area = s1*(r0 + r1)/2 + s2*(r1 + r2)/2; dadx[0] = -side1[0]*(r0+r1)/2/s1 - side2[0]*(r1+r2)/2/s2; dadr[0] = (r1*r1 - r0*r0)/s1/2 + s1/2 + (r1*r1 - r2*r2)/s2/2 + s2/2; sign = ( dadr[0] < 0 ) ? -1 : 1; root = sqrt(dadx[0]*dadx[0] + dadr[0]*dadr[0]); h = sign*root/area; /* half factor included here */ switch ( h0_flag ) { case H0_IN_GLOBAL: hh = h - h0_value; break; case H0_IN_ATTR: hh = h - *VREAL(v_info->id,h0_attr); break; default: hh = h; } if ( power == 2.0 ) energy = 2*M_PI*hh*hh*area/2; else if ( power == 1.0 ) energy = 2*M_PI*hh*area/2; else energy = 2*M_PI*area/2*pow(fabs(hh),power); if ( mode == METHOD_VALUE ) return energy; /* Gradient */ /* Note: v_info->grad[i][j] is component j of gradient at vertex i */ dadx[1] = side1[0]*(r0+r1)/2/s1; dadr[1] = -(r1*r1 - r0*r0)/s1/2 + s1/2; dadx[2] = side2[0]*(r1+r2)/2/s2; dadr[2] = -(r1*r1 - r2*r2)/s2/2 + s2/2; ddadxdx[0][1] = -(r0+r1)/2/s1 + side1[0]*(r0+r1)/2/s1/s1/s1*side1[0]; ddadxdx[0][2] = -(r1+r2)/2/s2 + side2[0]*(r1+r2)/2/s2/s2/s2*side2[0]; ddadxdx[0][0] = -ddadxdx[0][1] - ddadxdx[0][2]; /* dadx[0] = -side1[0]*(r0+r1)/2/s1 - side2[0]*(r1+r2)/2/s2; */ ddadxdr[0][1] = -side1[0]/2/s1 - side1[0]*(r1*r1-r0*r0)/s1/s1/s1/2; ddadxdr[0][2] = -side2[0]/2/s2 - side2[0]*(r1*r1-r2*r2)/s2/s2/s2/2; ddadxdr[0][0] = -side1[0]/2/s1 + side1[0]*(r1*r1-r0*r0)/s1/s1/s1/2 -side2[0]/2/s2 + side2[0]*(r1*r1-r2*r2)/s2/s2/s2/2; ddadrdx[0][1] = ((r1*r1-r0*r0)/2*(-1)/s1/s1 + 1/2.)*(side1[0]/s1); ddadrdx[0][2] = ((r1*r1-r2*r2)/2*(-1)/s2/s2 + 1/2.)*(side2[0]/s2); ddadrdx[0][0] = -ddadrdx[0][1]-ddadrdx[0][2]; ddadrdr[0][0] = r1/s1 + ((r1*r1-r0*r0)/2*(-1)/s1/s1 + 0.5)/s1*(r1-r0) + r1/s2 + ((r1*r1-r2*r2)/2*(-1)/s2/s2 + 0.5)/s2*(r1-r2); ddadrdr[0][1] = -r0/s1 +((r1*r1-r0*r0)/2*(-1)/s1/s1 + 0.5)/s1*(r0-r1); ddadrdr[0][2] = -r2/s2 + ((r1*r1-r2*r2)/2*(-1)/s2/s2 + 0.5)/s2*(r2-r1); for ( i = 0 ; i < 3 ; i++ ) { dhdx[i] = sign*(.5/root/area* (2*dadx[0]*ddadxdx[0][i] + 2*dadr[0]*ddadrdx[0][i])) - h/area*dadx[i]; dhdr[i] = sign*(.5/root/area* (2*dadx[0]*ddadxdr[0][i] + 2*dadr[0]*ddadrdr[0][i])) - h/area*dadr[i]; } if ( power == 2.0 ) { for ( i = 0 ; i < 3 ; i++ ) { v_info->grad[i][0] = 2*M_PI*(2*hh*dhdx[i]*area + hh*hh*dadx[i])/2; v_info->grad[i][1] = 2*M_PI*(2*hh*dhdr[i]*area + hh*hh*dadr[i])/2; } } else if ( power == 1.0 ) { for ( i = 0 ; i < 3 ; i++ ) { v_info->grad[i][0] = 2*M_PI*(dhdx[i]*area + hh*dadx[i])/2; v_info->grad[i][1] = 2*M_PI*(dhdr[i]*area + hh*dadr[i])/2; } } else { REAL pp = pow(fabs(hh),power-1); for ( i = 0 ; i < 3 ; i++ ) { v_info->grad[i][0] = 2*M_PI*(power*pp*dhdx[i]*area + pp*hh*dadx[i])/2; v_info->grad[i][1] = 2*M_PI*(power*pp*dhdr[i]*area + pp*hh*dadr[i])/2; } } if ( mode == METHOD_GRADIENT ) return energy; /* Hessian */ /* Note: v_info->hess[i][ii][j][jj] is component j of vertex i and component jj of vertex ii */ /* dadx[1] = side1[0]*(r0+r1)/2/s1; */ ddadxdx[1][1] = (r0+r1)/2/s1 - side1[0]*(r0+r1)/2/s1/s1/s1*side1[0]; ddadxdx[1][2] = 0; ddadxdx[1][0] = -ddadxdx[1][1]; ddadxdr[1][1] = side1[0]/2/s1 + side1[0]*(r0+r1)/s1/s1/s1/2*(r1-r0); ddadxdr[1][2] = 0; ddadxdr[1][0] = side1[0]/2/s1 - side1[0]*(r0+r1)/s1/s1/s1/2*(r1-r0); /* dadr[1] = -(r1*r1 - r0*r0)/s1/2 + s1/2; */ ddadrdx[1][1] = (-(r1*r1-r0*r0)/2*(-1)/s1/s1 + 1/2.)*(side1[0]/s1); ddadrdx[1][2] = 0; ddadrdx[1][0] = -ddadrdx[1][1]; ddadrdr[1][0] = -r1/s1 + (-(r1*r1-r0*r0)/2*(-1)/s1/s1 + 0.5)/s1*(r1-r0); ddadrdr[1][1] = r0/s1 + (-(r1*r1-r0*r0)/2*(-1)/s1/s1 + 0.5)/s1*(r0-r1); ddadrdr[1][2] = 0; /* dadx[2] = side2[0]*(r1+r2)/2/s2; */ ddadxdx[2][1] = 0; ddadxdx[2][2] = (r1+r2)/2/s2 - side2[0]*(r1+r2)/2/s2/s2/s2*side2[0]; ddadxdx[2][0] = -ddadxdx[2][2]; ddadxdr[2][1] = 0; ddadxdr[2][2] = side2[0]/2/s2 + side2[0]*(r2+r1)/s2/s2/s2/2*(r1-r2); ddadxdr[2][0] = side2[0]/2/s2 - side2[0]*(r2+r1)/s2/s2/s2/2*(r1-r2); /* dadr[2] = -(r1*r1 - r2*r2)/s2/2 + s2/2; */ ddadrdx[2][1] = 0; ddadrdx[2][2] = (-(r1*r1-r2*r2)/2*(-1)/s2/s2 + 1/2.)*(side2[0]/s2); ddadrdx[2][0] = -ddadrdx[2][2]; ddadrdr[2][0] = -r1/s2 + (-(r1*r1-r2*r2)/2*(-1)/s2/s2 + 0.5)/s2*(r1-r2); ddadrdr[2][1] = 0; ddadrdr[2][2] = r2/s2 + (-(r1*r1-r2*r2)/2*(-1)/s2/s2 + 0.5)/s2*(r2-r1); /* ddadxdx[0][1] = -(r0+r1)/2/s1 + side1[0]*(r0+r1)/2/s1/s1/s1*side1[0]; */ dddadxdxdx[0][1][0] = -(r0+r1)/2/s1/s1/s1/(-2)*(-2*side1[0]) - 2*side1[0]*(r0+r1)/2/s1/s1/s1 - 1.5*side1[0]*side1[0]*(r0+r1)/2/s1/s1/s1/s1/s1*(-2*side1[0]); dddadxdxdx[0][1][1] = -dddadxdxdx[0][1][0]; dddadxdxdx[0][1][2] = 0; dddadxdxdr[0][1][0] = -1./2/s1 + side1[0]/2/s1/s1/s1*side1[0] + (-(r0+r1)/2*(-1)/s1/s1 + side1[0]*(r0+r1)/2*(-3)/s1/s1/s1/s1*side1[0]) *(r1-r0)/s1; dddadxdxdr[0][1][1] = -1./2/s1 + side1[0]/2/s1/s1/s1*side1[0] + (-(r0+r1)/2*(-1)/s1/s1 + side1[0]*(r0+r1)/2*(-3)/s1/s1/s1/s1*side1[0]) *(r0-r1)/s1; dddadxdxdr[0][1][2] = 0; /* ddadxdx[0][2] = -(r1+r2)/2/s2 + side2[0]*(r1+r2)/2/s2/s2/s2*side2[0]; */ dddadxdxdx[0][2][0] = + 2*side2[0]*(-1)*(r1+r2)/2/s2/s2/s2 + (-(r1+r2)/2*(-1)/s2/s2 + side2[0]*(r1+r2)/2*(-3)/s2/s2/s2/s2*side2[0]) *(-side2[0])/s2; dddadxdxdx[0][2][1] = 0; dddadxdxdx[0][2][2] = -dddadxdxdx[0][2][0]; dddadxdxdr[0][2][0] = -1./2/s2 + side2[0]/2/s2/s2/s2*side2[0] + (-(r1+r2)/2*(-1)/s2/s2 + side2[0]*(r1+r2)/2*(-3)/s2/s2/s2/s2*side2[0]) *(r1-r2)/s2; dddadxdxdr[0][2][1] = 0; dddadxdxdr[0][2][2] = -1./2/s2 + side2[0]/2/s2/s2/s2*side2[0] + (-(r1+r2)/2*(-1)/s2/s2 + side2[0]*(r1+r2)/2*(-3)/s2/s2/s2/s2*side2[0]) *(r2-r1)/s2; /* ddadxdx[0][0] = -ddadxdx[0][1] - ddadxdx[0][2]; */ dddadxdxdx[0][0][0] = -dddadxdxdx[0][1][0] - dddadxdxdx[0][2][0]; dddadxdxdx[0][0][1] = -dddadxdxdx[0][1][1] - dddadxdxdx[0][2][1]; dddadxdxdx[0][0][2] = -dddadxdxdx[0][1][2] - dddadxdxdx[0][2][2]; dddadxdxdr[0][0][0] = -dddadxdxdr[0][1][0] - dddadxdxdr[0][2][0]; dddadxdxdr[0][0][1] = -dddadxdxdr[0][1][1] - dddadxdxdr[0][2][1]; dddadxdxdr[0][0][2] = -dddadxdxdr[0][1][2] - dddadxdxdr[0][2][2]; /* ddadxdr[0][1] = -side1[0]/2/s1 - side1[0]*(r1*r1-r0*r0)/s1/s1/s1/2; */ dddadxdrdx[0][1][0] = dddadxdxdr[0][0][1]; dddadxdrdx[0][1][1] = dddadxdxdr[0][1][1]; dddadxdrdx[0][1][2] = dddadxdxdr[0][2][1]; dddadxdrdr[0][1][0] = -side1[0]*2*r1/s1/s1/s1/2 + (-side1[0]/2*(-1)/s1/s1 - side1[0]*(r1*r1-r0*r0)*(-3)/s1/s1/s1/s1/2) *(r1-r0)/s1; dddadxdrdr[0][1][1] = -side1[0]*(-2)*r0/s1/s1/s1/2 + (-side1[0]/2*(-1)/s1/s1 - side1[0]*(r1*r1-r0*r0)*(-3)/s1/s1/s1/s1/2) *(r0-r1)/s1; dddadxdrdr[0][1][2] = 0; /* ddadxdr[0][2] = -side2[0]/2/s2 - side2[0]*(r1*r1-r2*r2)/s2/s2/s2/2 */; dddadxdrdx[0][2][0] = dddadxdxdr[0][0][2]; dddadxdrdx[0][2][1] = dddadxdxdr[0][1][2]; dddadxdrdx[0][2][2] = dddadxdxdr[0][2][2]; dddadxdrdr[0][2][0] = -side2[0]*2*r1/s2/s2/s2/2 + + (-side2[0]/2*(-1)/s2/s2 - side2[0]*(r1*r1-r2*r2)*(-3)/s2/s2/s2/s2/2) * (r1-r2)/s2; dddadxdrdr[0][2][1] = 0; dddadxdrdr[0][2][2] = -side2[0]*(-2)*r2/s2/s2/s2/2 + + (-side2[0]/2*(-1)/s2/s2 - side2[0]*(r1*r1-r2*r2)*(-3)/s2/s2/s2/s2/2) * (r2-r1)/s2; /* ddadxdr[0][0] = -side1[0]/2/s1 + side1[0]*(r1*r1-r0*r0)/s1/s1/s1/2 -side2[0]/2/s2 + side2[0]*(r1*r1-r2*r2)/s2/s2/s2/2; */ dddadxdrdx[0][0][0] = dddadxdxdr[0][0][0]; dddadxdrdx[0][0][1] = dddadxdxdr[0][1][0]; dddadxdrdx[0][0][2] = dddadxdxdr[0][2][0]; dddadxdrdr[0][0][0] = side1[0]*2*r1/s1/s1/s1/2 + side2[0]*2*r1/s2/s2/s2/2 + (side1[0]/2/s1/s1 + side1[0]*(r1*r1-r0*r0)*(-3)/s1/s1/s1/s1/2)*(r1-r0)/s1 + (side2[0]/2/s2/s2 + side2[0]*(r1*r1-r2*r2)*(-3)/s2/s2/s2/s2/2)*(r1-r2)/s2; dddadxdrdr[0][0][1] = side1[0]*(-2)*r0/s1/s1/s1/2 + (side1[0]/2/s1/s1 + side1[0]*(r1*r1-r0*r0)*(-3)/s1/s1/s1/s1/2)*(r0-r1)/s1; dddadxdrdr[0][0][2] = side2[0]*(-2)*r2/s2/s2/s2/2 + (side2[0]/2/s2/s2 + side2[0]*(r1*r1-r2*r2)*(-3)/s2/s2/s2/s2/2)*(r2-r1)/s2; /* ddadrdx[0][1] = ((r1*r1-r0*r0)/2*(-1)/s1/s1 + 1/2.)*(side1[0]/s1); */ dddadrdxdx[0][1][0] = ((r1*r1-r0*r0)/2*(-1)/s1/s1 + 1/2.)*(-1/s1) + side1[0]*((r1*r1-r0*r0)/2*(-1)*(-3)/s1/s1/s1/s1 + 1/2.*(-1)/s1/s1) *(-side1[0])/s1; dddadrdxdx[0][1][1] = ((r1*r1-r0*r0)/2*(-1)/s1/s1 + 1/2.)*(1/s1) + side1[0]*((r1*r1-r0*r0)/2*(-1)*(-3)/s1/s1/s1/s1 + 1/2.*(-1)/s1/s1) *(side1[0])/s1; dddadrdxdx[0][1][2] = 0; dddadrdxdr[0][1][0] = 2*r1/2*(-1)/s1/s1*(side1[0]/s1) + side1[0]*((r1*r1-r0*r0)/2*(-1)*(-3)/s1/s1/s1/s1 + 1/2.*(-1)/s1/s1)*(r1-r0)/s1; dddadrdxdr[0][1][1] = -2*r0/2*(-1)/s1/s1*(side1[0]/s1) + side1[0]*((r1*r1-r0*r0)/2*(-1)*(-3)/s1/s1/s1/s1 + 1/2.*(-1)/s1/s1)*(r0-r1)/s1; dddadrdxdr[0][1][2] = 0; /* ddadrdx[0][2] = ((r1*r1-r2*r2)/2*(-1)/s2/s2 + 1/2.)*(side2[0]/s2); */ dddadrdxdx[0][2][0] = ((r1*r1-r2*r2)/2*(-1)/s2/s2 + 1/2.)*(-1/s2) + side2[0]*((r1*r1-r2*r2)/2*(-1)*(-3)/s2/s2/s2/s2 + 1/2.*(-1)/s2/s2) *(-side2[0])/s2; dddadrdxdx[0][2][1] = 0; dddadrdxdx[0][2][2] = ((r1*r1-r2*r2)/2*(-1)/s2/s2 + 1/2.)*(1/s2) + side2[0]*((r1*r1-r2*r2)/2*(-1)*(-3)/s2/s2/s2/s2 + 1/2.*(-1)/s2/s2) *(side2[0])/s2; dddadrdxdr[0][2][0] = 2*r1/2*(-1)/s2/s2*(side2[0]/s2) + side2[0]*((r1*r1-r2*r2)/2*(-1)*(-3)/s2/s2/s2/s2 + 1/2.*(-1)/s2/s2)*(r1-r2)/s2; dddadrdxdr[0][2][1] = 0; dddadrdxdr[0][2][2] = -2*r2/2*(-1)/s2/s2*(side2[0]/s2) + side2[0]*((r1*r1-r2*r2)/2*(-1)*(-3)/s2/s2/s2/s2 + 1/2.*(-1)/s2/s2)*(r2-r1)/s2; /* ddadrdx[0][0] = -ddadrdx[0][1]-ddadrdx[0][2]; */ dddadrdxdx[0][0][0] = -dddadrdxdx[0][1][0]-dddadrdxdx[0][2][0]; dddadrdxdx[0][0][1] = -dddadrdxdx[0][1][1]-dddadrdxdx[0][2][1]; dddadrdxdx[0][0][2] = -dddadrdxdx[0][1][2]-dddadrdxdx[0][2][2]; dddadrdxdr[0][0][0] = -dddadrdxdr[0][1][0]-dddadrdxdr[0][2][0]; dddadrdxdr[0][0][1] = -dddadrdxdr[0][1][1]-dddadrdxdr[0][2][1]; dddadrdxdr[0][0][2] = -dddadrdxdr[0][1][2]-dddadrdxdr[0][2][2]; /* ddadrdr[0][0] = r1/s1 + (-(r1*r1-r0*r0)/s1/s1/s1 + 1/s1)*(r1-r0)/2 + r1/s2 + (-(r1*r1-r2*r2)/s2/s2/s2 + 1/s2)*(r1-r2)/2; */ dddadrdrdx[0][0][0] = dddadrdxdr[0][0][0]; dddadrdrdx[0][0][1] = dddadrdxdr[0][1][0]; dddadrdrdx[0][0][2] = dddadrdxdr[0][2][0]; dddadrdrdr[0][0][0] = 1/s1 - 2*r1/s1/s1/s1*(r1-r0)/2 + (-(r1*r1-r0*r0)/s1/s1/s1 + 1/s1)/2 + (-r1/s1/s1 + (-(r1*r1-r0*r0)*(-3)/s1/s1/s1/s1 - 1/s1/s1)*(r1-r0)/2) *(r1-r0)/s1 + 1/s2 - 2*r1/s2/s2/s2*(r1-r2)/2 + (-(r1*r1-r2*r2)/s2/s2/s2 + 1/s2)/2 + (-r1/s2/s2 + (-(r1*r1-r2*r2)*(-3)/s2/s2/s2/s2 - 1/s2/s2)*(r1-r2)/2) *(r1-r2)/s2; dddadrdrdr[0][0][1] = 2*r0/s1/s1/s1*(r1-r0)/2 - (-(r1*r1-r0*r0)/s1/s1/s1 + 1/s1)/2 + (-r1/s1/s1 + (-(r1*r1-r0*r0)*(-3)/s1/s1/s1/s1 - 1/s1/s1)*(r1-r0)/2) *(r0-r1)/s1; dddadrdrdr[0][0][2] = 2*r2/s2/s2/s2*(r1-r2)/2 - (-(r1*r1-r2*r2)/s2/s2/s2 + 1/s2)/2 + (-r1/s2/s2 + (-(r1*r1-r2*r2)*(-3)/s2/s2/s2/s2 - 1/s2/s2)*(r1-r2)/2) *(r2-r1)/s2; /* ddadrdr[0][1] = -r0/s1 +(-(r1*r1-r0*r0)/s1/s1/s1 + 1/s1)*(r0-r1)/2; */ dddadrdrdx[0][1][0] = dddadrdxdr[0][0][1]; dddadrdrdx[0][1][1] = dddadrdxdr[0][1][1]; dddadrdrdx[0][1][2] = dddadrdxdr[0][2][1]; dddadrdrdr[0][1][0] = dddadrdrdr[0][0][1]; dddadrdrdr[0][1][1] = -1/s1 + 2*r0/s1/s1/s1*(r0-r1)/2 + (-(r1*r1-r0*r0)/s1/s1/s1 + 1/s1)/2 + (r0/s1/s1 + (-(r1*r1-r0*r0)*(-3)/s1/s1/s1/s1 + (-1)/s1/s1)*(r0-r1)/2) *(r0-r1)/s1; dddadrdrdr[0][1][2] = 0; /* ddadrdr[0][2] = -r2/s2 + ((r1*r1-r2*r2)/2*(-1)/s2/s2 + 0.5)/s2*(r2-r1); */ dddadrdrdx[0][2][0] = dddadrdxdr[0][0][2]; dddadrdrdx[0][2][1] = dddadrdxdr[0][1][2]; dddadrdrdx[0][2][2] = dddadrdxdr[0][2][2]; dddadrdrdr[0][2][0] = dddadrdrdr[0][0][2]; dddadrdrdr[0][2][1] = dddadrdrdr[0][1][2]; dddadrdrdr[0][2][2] = -1/s2 + 2*r2/s2/s2/s2*(r2-r1)/2 + (-(r1*r1-r2*r2)/s2/s2/s2 + 1/s2)/2 + (r2/s2/s2 + (-(r1*r1-r2*r2)*(-3)/s2/s2/s2/s2 + (-1)/s2/s2)*(r2-r1)/2) *(r2-r1)/s2; for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < 3 ; j++ ) { ddhdxdx[i][j] = sign*( (-.5)*.5/root/root/root/area* (2*dadx[0]*ddadxdx[0][i] + 2*dadr[0]*ddadrdx[0][i]) *(2*dadx[0]*ddadxdx[0][j] + 2*dadr[0]*ddadrdx[0][j]) + (-1)*.5/root/area/area*dadx[j]* (2*dadx[0]*ddadxdx[0][i] + 2*dadr[0]*ddadrdx[0][i]) + .5/root/area* (2*ddadxdx[0][j]*ddadxdx[0][i] + 2*ddadrdx[0][j]*ddadrdx[0][i]) + .5/root/area* (2*dadx[0]*dddadxdxdx[0][i][j] + 2*dadr[0]*dddadrdxdx[0][i][j]) ) - dhdx[j]/area*dadx[i] - (-1)*h/area/area*dadx[j]*dadx[i] - h/area*ddadxdx[i][j]; ddhdxdr[i][j] = sign*( (-.5)*.5/root/root/root/area* (2*dadx[0]*ddadxdx[0][i] + 2*dadr[0]*ddadrdx[0][i]) *(2*dadx[0]*ddadxdr[0][j] + 2*dadr[0]*ddadrdr[0][j]) + (-1)*.5/root/area/area*dadr[j]* (2*dadx[0]*ddadxdx[0][i] + 2*dadr[0]*ddadrdx[0][i]) + .5/root/area* (2*ddadxdr[0][j]*ddadxdx[0][i] + 2*ddadrdr[0][j]*ddadrdx[0][i]) + .5/root/area* (2*dadx[0]*dddadxdxdr[0][i][j] + 2*dadr[0]*dddadrdxdr[0][i][j]) ) - dhdr[j]/area*dadx[i] - (-1)*h/area/area*dadr[j]*dadx[i] - h/area*ddadxdr[i][j]; ddhdrdx[i][j] = sign*( (-.5)*.5/root/root/root/area* (2*dadx[0]*ddadxdr[0][i] + 2*dadr[0]*ddadrdr[0][i]) *(2*dadx[0]*ddadxdx[0][j] + 2*dadr[0]*ddadrdx[0][j]) + (-1)*.5/root/area/area*dadx[j]* (2*dadx[0]*ddadxdr[0][i] + 2*dadr[0]*ddadrdr[0][i]) + .5/root/area* (2*ddadxdx[0][j]*ddadxdr[0][i] + 2*ddadrdx[0][j]*ddadrdr[0][i]) + .5/root/area* (2*dadx[0]*dddadxdrdx[0][i][j] + 2*dadr[0]*dddadrdrdx[0][i][j]) ) - dhdx[j]/area*dadr[i] - (-1)*h/area/area*dadx[j]*dadr[i] - h/area*ddadrdx[i][j]; ddhdrdr[i][j] = sign*( (-.5)*.5/root/root/root/area* (2*dadx[0]*ddadxdr[0][i] + 2*dadr[0]*ddadrdr[0][i]) *(2*dadx[0]*ddadxdr[0][j] + 2*dadr[0]*ddadrdr[0][j]) + (-1)*.5/root/area/area*dadr[j]* (2*dadx[0]*ddadxdr[0][i] + 2*dadr[0]*ddadrdr[0][i]) + .5/root/area* (2*ddadxdr[0][j]*ddadxdr[0][i] + 2*ddadrdr[0][j]*ddadrdr[0][i]) + .5/root/area* (2*dadx[0]*dddadxdrdr[0][i][j] + 2*dadr[0]*dddadrdrdr[0][i][j]) ) - dhdr[j]/area*dadr[i] - (-1)*h/area/area*dadr[j]*dadr[i] - h/area*ddadrdr[i][j]; } if ( power == 2.0 ) { for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < 3 ; j++ ) { v_info->hess[i][j][0][0] = M_PI*( 2*dhdx[j]*dhdx[i]*area + 2*dhdx[j]*hh*dadx[i] + 2*hh*ddhdxdx[i][j]*area + hh*hh*ddadxdx[i][j] + 2*hh*dhdx[i]*dadx[j]); v_info->hess[i][j][0][1] = M_PI*( 2*dhdr[j]*dhdx[i]*area + 2*dhdr[j]*hh*dadx[i] + 2*hh*ddhdxdr[i][j]*area + hh*hh*ddadxdr[i][j] + 2*hh*dhdx[i]*dadr[j]); v_info->hess[i][j][1][0] = M_PI*( 2*dhdx[j]*dhdr[i]*area + 2*dhdx[j]*hh*dadr[i] + 2*hh*ddhdrdx[i][j]*area + hh*hh*ddadrdx[i][j] + 2*hh*dhdr[i]*dadx[j]); v_info->hess[i][j][1][1] = M_PI*( 2*dhdr[j]*dhdr[i]*area + 2*dhdr[j]*hh*dadr[i] + 2*hh*ddhdrdr[i][j]*area + hh*hh*ddadrdr[i][j] + 2*hh*dhdr[i]*dadr[j]); } } else if ( power == 1.0 ) { for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < 3 ; j++ ) { v_info->hess[i][j][0][0] = M_PI*(ddhdxdx[i][j]*area + dhdx[j]*dadx[i] + dhdx[i]*dadx[j] + hh*ddadxdx[i][j]); v_info->hess[i][j][0][1] = M_PI*(ddhdxdr[i][j]*area + dhdr[j]*dadx[i] + dhdx[i]*dadr[j] + hh*ddadxdr[i][j]); v_info->hess[i][j][1][0] = M_PI*(ddhdxdr[i][j]*area + dhdx[j]*dadr[i] + dhdr[i]*dadx[j] + hh*ddadxdr[i][j]); v_info->hess[i][j][1][1] = M_PI*(ddhdrdr[i][j]*area + dhdr[j]*dadr[i] + dhdr[i]*dadr[j] + hh*ddadrdr[i][j]); } } else { REAL pp = pow(fabs(hh),power-1); for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < 3 ; j++ ) { v_info->hess[i][j][0][0] = M_PI*( power*(power-1)*pp/hh*dhdx[j]*dhdx[i]*area + power*pp*dhdx[j]*dadx[i] + power*pp*ddhdxdx[i][j]*area + pp*hh*ddadxdx[i][j] + power*pp*dhdx[i]*dadx[j]); v_info->hess[i][j][0][1] = M_PI*( power*(power-1)*pp/hh*dhdr[j]*dhdx[i]*area + power*pp*dhdr[j]*dadx[i] + power*pp*ddhdxdr[i][j]*area + pp*hh*ddadxdr[i][j] + power*pp*dhdx[i]*dadr[j]); v_info->hess[i][j][1][0] = M_PI*( power*(power-1)*pp/hh*dhdx[j]*dhdr[i]*area + power*pp*dhdx[j]*dadr[i] + power*pp*ddhdxdr[i][j]*area + pp*hh*ddadxdr[i][j] + power*pp*dhdr[i]*dadx[j]); v_info->hess[i][j][1][1] = M_PI*( power*(power-1)*pp/hh*dhdr[j]*dhdr[i]*area + power*pp*dhdr[j]*dadr[i] + power*pp*ddhdrdr[i][j]*area + pp*hh*ddadrdr[i][j] + power*pp*dhdr[i]*dadr[j]); } } return energy; } /************************************************************************ * * function: sq_mean_curv_cyl_value() * * purpose: Calculate square curvature energy for string model * Works locally vertex by vertex. Assumes two edges per vertex. * */ REAL sq_mean_curv_cyl_value(v_info) struct qinfo *v_info; { return sq_mean_curv_cyl_all(v_info,METHOD_VALUE,2.0); } /************************************************************************ * * function: sq_mean_curv_cyl_grad() * * purpose: Calculate square curvature gradient for string model * Works locally vertex by vertex. Assumes two edges per vertex. * */ REAL sq_mean_curv_cyl_grad(v_info) struct qinfo *v_info; { return sq_mean_curv_cyl_all(v_info,METHOD_GRADIENT,2.0); } /************************************************************************ * * function: sq_mean_curv_cyl_hess() * * purpose: Calculate square curvature hessian for string model * Works locally vertex by vertex. Assumes two edges per vertex. * */ REAL sq_mean_curv_cyl_hess(v_info) struct qinfo *v_info; { return sq_mean_curv_cyl_all(v_info,METHOD_HESSIAN,2.0); } /************************************************************************* Square gaussian curvature for surface of revolution in string model. Contributed by John Frank, jrf@mit.edu Cylindrical axis is x axis. Includes 2*pi factor. **************************************************************************/ REAL sq_gauss_curv_cyl_all ARGS(( struct qinfo *, int)); /************************************************************************ * * function: sq_gauss_curv_cyl_init() * * purpose: Any overall initialization needed each time the method * is evaluated. */ void sq_gauss_curv_cyl_init(mode,mi) int mode; struct method_instance *mi; { if ( web.modeltype != LINEAR ) kb_error(4486,"sq_gaussian_curv_cyl method only for LINEAR model.\n", RECOVERABLE); } /************************************************************************ * * function: sq_gauss_curv_cyl_all() * * purpose: Combined calculations for one vertex for sq_gaussian_curv_cyl * method. */ REAL sq_gauss_curv_cyl_all(v_info,mode) struct qinfo *v_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ { REAL energy = 0.0; /* Notes on incoming data: v_info->vcount is number of vertices in v_info; it will be 3 for an interior vertex and 2 for an endpoint. v_info->x[0][] is the coordinates of the vertex in question. v_info->x[1][] is the coordinates of one neighbor vertex. v_info->x[2][] is the coordinates of the other neighbor vertex. v_info->sides[0][0][] is the vector from vertex 0 to vertex 1. v_info->sides[0][1][] is the vector from vertex 0 to vertex 2. */ /* calculate value for single vertex here */ if ( mode == METHOD_VALUE ) return energy; /* Gradient */ /* calculate gradient for the energy of the vertex here */ if ( mode == METHOD_GRADIENT ) return energy; /* Hessian */ /* calculate hessian for the energy of the vertex here */ return energy; } /************************************************************************ * * function: sq_gauss_curv_cyl_value() * * purpose: Calculate square gaussian curvature energy for surface * of revolution in string model. * Works locally vertex by vertex. Assumes two edges per vertex. * */ REAL sq_gauss_curv_cyl_value(v_info) struct qinfo *v_info; { return sq_gauss_curv_cyl_all(v_info,METHOD_VALUE); } /************************************************************************ * * function: sq_gauss_curv_cyl_grad() * * purpose: Calculate square gaussian curvature gradient for * surface of revolution in string model. * */ REAL sq_gauss_curv_cyl_grad(v_info) struct qinfo *v_info; { return sq_gauss_curv_cyl_all(v_info,METHOD_GRADIENT); } /************************************************************************ * * function: sq_gauss_curv_cyl_hess() * * purpose: Calculate square curvature hessian for surface of revolution * in string model. * */ REAL sq_gauss_curv_cyl_hess(v_info) struct qinfo *v_info; { return sq_gauss_curv_cyl_all(v_info,METHOD_HESSIAN); } evolver-2.30c.dfsg/src/inline.h0000644000175300017530000002123311410765113016645 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /*************************************************************** * File: inline.h * * Purpose: Functions eligible for inlining. Various systems * * have various things that need to be done to get inilning to * * work, * ***************************************************************/ #if defined(INLINE) INLINE void set_attr(id,attrib) element_id id; ATTR attrib; { elptr(id)->attr |= attrib; } INLINE void unset_attr(id,attrib) element_id id; ATTR attrib; { elptr(id)->attr &= ~attrib; } INLINE void set_fe_edge(fe_id,e_id) facetedge_id fe_id; edge_id e_id; { if ( inverted(fe_id) ) invert(e_id); feptr(fe_id)->fe_edge_id = e_id; top_timestamp = ++global_timestamp; } INLINE edge_id get_fe_edge(fe_id) facetedge_id fe_id; { edge_id e_id; e_id = feptr(fe_id)->fe_edge_id; /* if ( inverted(fe_id) ) invert(e_id); return e_id; */ return same_sign(e_id,fe_id); } INLINE facet_id get_fe_facet(fe_id) facetedge_id fe_id; { facet_id f_id; if ( !valid_id(fe_id) ) return NULLFACET; f_id = feptr(fe_id)->fe_facet_id; if ( inverted(fe_id) ) invert(f_id); return f_id; } INLINE facetedge_id get_prev_edge(fe_id) facetedge_id fe_id; { if ( inverted(fe_id) ) return inverse_id(feptr(fe_id)->nextedge[1]); else return feptr(fe_id)->nextedge[0]; } INLINE facetedge_id get_next_edge(fe_id) facetedge_id fe_id; { if ( inverted(fe_id) ) return inverse_id(feptr(fe_id)->nextedge[0]); else return feptr(fe_id)->nextedge[1]; } INLINE facetedge_id get_prev_facet(fe_id) facetedge_id fe_id; { if ( inverted(fe_id) ) return inverse_id(feptr(fe_id)->nextfacet[1]); else return feptr(fe_id)->nextfacet[0]; } INLINE facetedge_id get_next_facet(fe_id) facetedge_id fe_id; { if ( inverted(fe_id) ) return inverse_id(feptr(fe_id)->nextfacet[0]); else return feptr(fe_id)->nextfacet[1]; } INLINE void set_prev_edge(fe_id,fe) facetedge_id fe_id; facet_id fe; { if ( !valid_id(fe_id) ) return; if ( inverted(fe_id) ) { invert(fe); feptr(fe_id)->nextedge[1] = fe; } else feptr(fe_id)->nextedge[0] = fe; top_timestamp = ++global_timestamp; } INLINE void set_next_edge(fe_id,fe) facetedge_id fe_id,fe; { if ( !valid_id(fe_id) ) return; if ( inverted(fe_id) ) { invert(fe); feptr(fe_id)->nextedge[0] = fe; } else feptr(fe_id)->nextedge[1] = fe; } INLINE void set_prev_facet(fe_id,fe) facetedge_id fe_id,fe; { if ( !valid_id(fe_id) ) return; if ( inverted(fe_id) ) { invert(fe); feptr(fe_id)->nextfacet[1] = fe; } else feptr(fe_id)->nextfacet[0] = fe; top_timestamp = ++global_timestamp; } INLINE void set_next_facet(fe_id,fe) facetedge_id fe_id,fe; { if ( !valid_id(fe_id) ) return; if ( inverted(fe_id) ) { invert(fe); feptr(fe_id)->nextfacet[0] = fe; } else feptr(fe_id)->nextfacet[1] = fe; } INLINE void set_edge_wrap(e_id,wrap) edge_id e_id; WRAPTYPE wrap; { *EINT(e_id,E_WRAP_ATTR) = inverted(e_id) ? (*sym_inverse)(wrap) : wrap ; } INLINE WRAPTYPE get_edge_wrap(e_id) edge_id e_id; { WRAPTYPE wrap = *EINT(e_id,E_WRAP_ATTR) ; return ( inverted(e_id) ? (*sym_inverse)(wrap) : wrap ); } INLINE void set_edge_fe(e_id,fe) edge_id e_id; facetedge_id fe; { if ( inverted(e_id) ) invert(fe); eptr(e_id)->fe_id = fe; top_timestamp = ++global_timestamp; } INLINE facetedge_id get_edge_fe(e_id) edge_id e_id; { struct edge *ep; facetedge_id fe; ep = eptr(e_id); if ( !ep ) return NULLID; fe = ep->fe_id; if ( inverted(e_id) ) invert(fe); return fe; } INLINE vertex_id get_edge_tailv(e_id) edge_id e_id; { if ( inverted(e_id) ) return get_edge_vertices(e_id)[web.headvnum]; else return get_edge_vertices(e_id)[0]; } INLINE vertex_id get_edge_headv(e_id) edge_id e_id; { if ( inverted(e_id) ) return get_edge_vertices(e_id)[0]; else return get_edge_vertices(e_id)[web.headvnum]; } INLINE void set_edge_tailv(e_id,v_id) edge_id e_id; vertex_id v_id; { vertex_id oldv; /* make sure edge not in loop of old vertex */ oldv = get_edge_tailv(e_id); if ( valid_id(oldv) && !equal_id(oldv,v_id) ) remove_vertex_edge(oldv,e_id); if ( inverted(e_id) ) get_edge_vertices(e_id)[web.headvnum] = v_id; else get_edge_vertices(e_id)[0] = v_id; insert_vertex_edge(v_id,e_id); top_timestamp = ++global_timestamp; } INLINE void set_edge_headv(e_id,v_id) edge_id e_id; vertex_id v_id; { if ( inverted(e_id) ) get_edge_vertices(e_id)[0] = v_id; else get_edge_vertices(e_id)[web.headvnum] = v_id; insert_vertex_edge(v_id,inverse_id(e_id)); top_timestamp = ++global_timestamp; } INLINE void set_edge_midv(e_id,v_id) edge_id e_id; vertex_id v_id; { get_edge_vertices(e_id)[2] = v_id; set_vertex_edge(v_id,e_id); set_attr(v_id,Q_MIDPOINT); top_timestamp = ++global_timestamp; } INLINE body_id get_facet_body(f_id) facet_id f_id; { if ( web.skel[BODY].count == 0 ) return NULLID; if ( !valid_id(f_id) ) return NULLID; if ( inverted(f_id) ) return F_ELID(f_id,F_BODY_LIST_ATTR)[1]; else return F_ELID(f_id,F_BODY_LIST_ATTR)[0]; } INLINE facetedge_id get_facet_fe(f_id) facet_id f_id; { facetedge_id fe; if ( !valid_id(f_id) ) return NULLID; fe = fptr(f_id)->fe_id; if ( inverted(f_id) ) invert(fe); return fe; } INLINE edge_id get_next_tail_edge(e_id) edge_id e_id; { return eptr(e_id)->next_vedge[inverted(e_id) ?1: 0] ; } INLINE edge_id get_next_head_edge(e_id) edge_id e_id; { return inverse_id(eptr(e_id)->next_vedge[inverted(e_id) ?0: 1]); } INLINE void set_next_tail_edge(e_id,ee_id) edge_id e_id,ee_id; { eptr(e_id)->next_vedge[inverted(e_id) ?1: 0] = (ee_id); } INLINE facet_id get_body_facet(b_id) body_id b_id; { return ( valid_id(b_id) ? bptr(b_id)->f_id : NULLID ); } INLINE facetedge_id get_body_fe(b_id) body_id b_id; { facet_id f_id = valid_id(b_id) ? bptr(b_id)->f_id : NULLID ; return valid_id(f_id) ? get_facet_fe(f_id) : NULLID; } INLINE facetedge_id get_vertex_fe(v_id) vertex_id v_id; { edge_id xx_id=vptr(v_id)->e_id; return valid_id(xx_id)?same_sign(eptr(xx_id)->fe_id,xx_id):NULLID; } INLINE void set_body_facet(b_id,f_id) body_id b_id; facet_id f_id; { if ( valid_id(b_id) ) bptr(b_id)->f_id = (f_id); } INLINE REAL get_body_density(b_id) body_id b_id; { return ( valid_id(b_id) ? bptr(b_id)->density : 0.0 ) ; } INLINE REAL get_body_volume(b_id) body_id b_id; { return ( valid_id(b_id) ? bptr(b_id)->volume : 0.0 ) ; } INLINE REAL get_body_fixvol(b_id) body_id b_id; { return ( valid_id(b_id) ? bptr(b_id)->fixvol : 0.0 ) ; } INLINE REAL get_body_abstotal(b_id) body_id b_id; { return ( valid_id(b_id) ? bptr(b_id)->abstotal : 0.0 ) ; } INLINE REAL get_body_pressure(b_id) body_id b_id; { return ( valid_id(b_id) ? bptr(b_id)->pressure : 0.0 ) ; } INLINE REAL get_body_volconst(b_id) body_id b_id; { return ( valid_id(b_id) ? bptr(b_id)->volconst : 0.0 ) ; } INLINE void set_body_density(b_id,v) body_id b_id; REAL v; { ( valid_id(b_id) ? bptr(b_id)->density = (v) : 0.0 ) ; } INLINE void set_body_pressure(b_id,v) body_id b_id; REAL v; { ( valid_id(b_id) ? bptr(b_id)->pressure = (v) : 0.0 ) ; } INLINE void set_body_volconst(b_id,v) body_id b_id; REAL v; { if ( !valid_id(b_id) ) return; bptr(b_id)->volconst = v; if ( everything_quantities_flag ) { struct gen_quant *q = GEN_QUANT(get_body_volquant(b_id)); q->volconst = v; } } /************************************************************************* * * function: get_extra() * * purpose: return pointer to value of extra attribute of an element. */ INLINE char *get_extra(id,n) element_id id; int n; /* number of extra attribute */ { int type = id_type(id); return ( (char*)elptr(id) + EXTRAS(type)[n].offset); } /************************************************************************* * * function: get_extra_ptr() * * purpose: return pointer to value of extra attribute of an element. */ INLINE char *get_extra_ptr(id,ext) element_id id; struct extra *ext; /* extra attribute defining structure */ { return ( (char*)elptr(id) + ext->offset); } /*********************************************************************** * * function: get_meth_offset() * * purpose: return offset of method instance list in element structure. */ INLINE int get_meth_offset(type) int type; /* of element */ { int meth_offset; meth_offset = EXTRAS(type)[web.meth_attr[type]].offset; return meth_offset; } #endif evolver-2.30c.dfsg/src/query.c0000644000175300017530000004204111410765113016527 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************** * * File: query.c * * Contents: query interpreter for Surface Evolver */ #include "include.h" #include "ytab.h" int commandverb; /*********************************************************************** History mechanism. Command texts are stored in linear array history_space[], separated by nulls, most recent first. history_offsets[] contains indices of starts of commands, -1 for no command. history_number is permanent counter of commands. history_count is number of currently saved commands. **************************************************************************/ /************************************************************************** * * function: new_history() * * purpose: Appends command to history list, queue fashion. */ void new_history ARGS1((text), char *text) { size_t length; int k; if ( history_space == NULL ) { history_space = my_list_calloc(HISTORYSPACE,1,ETERNAL_BLOCK); for ( k = 0 ; k < MAXHISTORY ; k++ ) history_offsets[k] = -1; /* empty */ history_count = 0; } length = strlen(text)+1; if ( length >= HISTORYSPACE-5 ) { kb_error(1585,"Command too long to save in history list.\n",WARNING); return; } kb_memmove(history_space+length,history_space,HISTORYSPACE-length); for ( k = history_count-1 ; k >= 0 ; k-- ) { /* adjust pointers in history list */ history_offsets[k+1] = history_offsets[k] + (int)length; if ( history_offsets[k+1] >= HISTORYSPACE ) { history_offsets[k+1] = -1; history_count--; } } strcpy(history_space,text); history_offsets[0] = 0; if ( history_count < MAXHISTORY-1 ) history_count++; history_number++; } /************************************************************************** * * function: old_history() * * purpose: Checks commands beginning with ! for history list match. * return: retval from command() */ int old_history ARGS1((text), char *text) { char *h; int k; int hnum; int retval = 0; if ( history_space == NULL ) { history_space = my_list_calloc(HISTORYSPACE,1,ETERNAL_BLOCK); for ( k = 0 ; k < MAXHISTORY ; k++ ) history_offsets[k] = -1; /* empty */ history_count = 0; } if (text[1] == '!' ) { if ( history_count > 0 ) { k = 0; goto do_old; } else { kb_error(1586,"History list is empty.\n",WARNING); return retval; } } else if ( isdigit(text[1]) ) { hnum = atoi(text+1); k = history_number - hnum; if ( (k >= 0) && (k < history_count) ) goto do_old; else kb_error(1587,"Command not in history list.\n",RECOVERABLE); } else for ( k = 0 ; k < history_count ; k++ ) { h = history_space+history_offsets[k]; if ( strlen(h) < strlen(text+1) ) continue; /* prevent compare overrun */ if ( strncmp(h,text+1,strlen(text+1)) == 0 ) goto do_old; } kb_error(1588,"Command not found in history.\n",RECOVERABLE); do_old: h = history_space + history_offsets[k]; outstring(h);outstring("\n"); retval = command(h,ADD_TO_HISTORY); /* reinserts fulltext copy in history list */ return retval; } /************************************************************************** * * function: catfulltext() * * purpose: catenates line to full text of command */ void catfulltext ARGS1((stuff), char *stuff) { if ( fulltextsize > MAXCMDSIZE-3 ) return; /* too long */ if ( fulltextsize > 0 ) { strcat(fulltext," "); /* indentation */ fulltextsize += 2; } strncpy(fulltext+fulltextsize,stuff,MAXCMDSIZE-fulltextsize); fulltextsize = strlen(fulltext); if ( fulltext[fulltextsize-1] != '\n' ) fulltext[fulltextsize++] = '\n'; fulltext[fulltextsize] = '\0'; } /************************************************************************** * * function: command() * * purpose: Executes a text command string. * * return: END_COMMANDS if current command interpreter loop is to end. * 0 else */ int command ARGS2((text,mode), char *text, int mode) /* NO_HISTORY or ADD_TO_HISTORY */ { struct expnode qnode; /* for query expression */ int retval = 0; int old_scope; struct treenode *old_list; int old_listtop, old_listmax; int old_brace_depth = brace_depth; memset(&qnode,0,sizeof(struct expnode)); sprintf(qnode.name,"command"); old_listmax = listmax; old_list = list; old_listtop = listtop; /* for nested parsing */ listmax = 30; list = (struct treenode *)mycalloc(listmax,sizeof(struct treenode)); list[1].type = SETUP_FRAME_; listtop = 2; loopdepth = 0; parse_error_flag = 0; lists_flag = 0; cmdptr = text; old_scope = begin_scope(); #ifdef __cplusplus try { #else if ( setjmp(cmdbuf) ) { set_scope(old_scope); cmdptr = NULL; myfree((char *)list); list = NULL; memset(local_scope_bases,0,sizeof(local_scope_bases)); local_nest_depth = 0; return 0; } #endif verb_flag = 1; /* tell lex we need a verb first */ old_global_count = web.global_count; /* for error recovery */ old_perm_global_count = web.perm_global_count; /* for error recovery */ /* local_nest_depth = 0; */ init_local_scope(0); fulltextsize = 0; catfulltext(text); retval = yybegin(); /* 0 for accept, 1 for error */ qnode.locals = localbase; if ( localbase ) qnode.locals->flags |= LL_IN_USE; exit_local_scope(); cmdptr = NULL; if ( (retval == 1) || (parse_error_flag) ) { myfree((char *)list); if ( list == permlist ) permlist = NULL; list = NULL; set_scope(old_scope); return 0; } qnode.start=list; qnode.root = list + listtop - 1; if ( qnode.root->type == SETUP_FRAME_ ) /* empty command */ { retval = 0; goto command_exit; } if ( qnode.root->type != CMDLIST_ ) /* expression or something */ kb_error(1589,"Illegal command.\n",COMMAND_ERROR); list[0] = list[listtop-1]; /* root also in first spot */ if ( list[0].left ) list[0].left += listtop - 1; if ( list[0].right ) list[0].right += listtop - 1; /* put DONE marker after root */ list[listtop++].type = FINISHED; /* figure stack usage */ stack_usage(&qnode); /* set element loop flag bit in nodes */ if ( !function_kludge_flag ) mark_element_loops(&list[0],0); if ( logfd ) fprintf(logfd,"%s\n",text); /* add to history list */ if ( mode == ADD_TO_HISTORY ) new_history(fulltext); /* initialize current element to NULLID */ aggregate_depth = 0; calc_quant_flag = 0; exit_flag = 0; eval(&qnode,NULL,NULLID,NULL); retval = exit_flag ? END_COMMANDS : 1; exit_flag = 0; command_exit: qnode.flag = USERCOPY; /* so free_expr will work */ free_expr(&qnode); list = NULL; set_scope(old_scope); list = old_list; listtop = old_listtop; /* for nested parsing */ listmax = old_listmax; brace_depth = old_brace_depth; return retval; #ifdef __cplusplus } catch(cmd_excep c) { set_scope(old_scope); cmdptr = NULL; myfree((char *)list); list = NULL; memset(local_scope_bases,0,sizeof(local_scope_bases)); local_nest_depth = 0; return 0; } #endif } /************************************************************************* * * function: stack_usage() * * purpose: figure maximum stack usage of expression or command. * */ void stack_usage ARGS1((ex ), struct expnode *ex) { int usage = 0; struct treenode *node ; #ifdef _DEBUG int didwarn = 0; #endif ex->stack_max = 0; for ( node = ex->start+1 ; node->type != FINISHED ; node++ ) { usage += node->stack_delta; if ( usage > ex->stack_max ) ex->stack_max = usage; #ifdef _DEBUG if ( (usage < 0) && (!didwarn) ) { sprintf(errmsg,"Internal error: stack usage negative, node type %d.\n", node->type); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2670,errmsg,WARNING); didwarn = 1; } node->stack_spot = usage; #endif } } /*********************************************************************** * * Function: mark_element_loops() * * Purpose: Mark flags of expression nodes that are within * element loops, so can know when freeing discards is * ill-advised. Recursively transverses tree pre-order. */ void mark_element_loops(root,mark) struct treenode *root; int mark; /* whether root in loop */ { if ( mark ) root->flags |= IN_ELEMENT_LOOP; else mark = root->flags & IN_ELEMENT_LOOP; if ( root->left ) mark_element_loops(root+root->left,mark); if ( root->right ) mark_element_loops(root+root->right,mark); } /************************************************************************ * * function: print_profiling() * * purpose: Print profiling elapsed times. Prints comma form of clock 8 counts. */ void commaize(s) /* put commas in integers at end of string */ char *s; { int n,k,groups,j; k = strlen(s); while ( k > 0 ) { /* find last digit */ for ( n = k; n > 0 ; n-- ) if ( isdigit(s[n]) ) break; if ( !isdigit(s[n]) ) return; /* find first digit */ for ( k = n-1 ; k > 0 ; k-- ) if ( !isdigit(s[k]) ) break; groups = (n-k-1)/3; for ( j = k+1 ; j < n ; j++ ) { s[j-groups] = s[j]; if ( ((n-j+groups) % 4) == 0 ) { groups--; s[j-groups] = ','; } } } } void print_profiling() { #ifdef PROF_EVALS int i,k; int do_flag = 0; outstring("\nInclusive profiling counts: \n"); outstring(" Name CPU Cycles\n"); for ( i = 0 ; i < web.global_count ; i++ ) { if ( globals(i)->flags & (FUNCTION_NAME|PROCEDURE_NAME|SUBROUTINE) ) { sprintf(msg," %30s %15.f\n",globals(i)->name, globals(i)->value.proc.elapsed_time); commaize(msg); outstring(msg); } } for ( i = 0, do_flag = 0; i < NUMELEMENTS ; i++ ) if ( show_expr_table[i].root ) do_flag = 1; if ( do_flag ) { outstring("\nElement show expressions:\n"); outstring(" Element CPU Cycles\n"); for ( i = 0 ; i < NUMELEMENTS ; i++ ) { sprintf(msg," %30s %15.f\n",typenames[i],show_expr_table[i].elapsed_time); commaize(msg); outstring(msg); } } for ( i = LOW_INST, do_flag = 0 ; i < meth_inst_count ; i++ ) if ( METH_INSTANCE(i)->expr[0] ) do_flag = 1; if ( do_flag ) { outstring("\nMethod instance expressions:\n"); outstring(" Method instance CPU Cycles\n"); for ( i = LOW_INST; i < meth_inst_count ; i++ ) { REAL total_time = 0.0; struct method_instance *mi = METH_INSTANCE(i); for ( k = 0 ; k < MAXMEXPR ; k++ ) if ( mi->expr[k] ) total_time += mi->expr[k]->elapsed_time; else break; sprintf(msg," %40s %15.f\n",mi->name,total_time); commaize(msg); outstring(msg); } } for ( i = 0, do_flag = 0 ; i < web.bdrymax ; i++ ) if ( web.boundaries[i].coordf[0] ) do_flag = 1; if ( do_flag ) { outstring("\nBoundary expressions:\n"); outstring(" Boundary Formula Cycles Energy Cycles Content Cycles\n"); for ( i = 0 ; i < web.bdrymax ; i++ ) { REAL coord_time = 0.0; REAL energy_time = 0.0; REAL content_time = 0.0; struct boundary *b = web.boundaries + i; if ( !b->coordf[0] ) continue; for ( k = 0 ; k < SDIM ; k++ ) { if ( b->coordf[k] ) coord_time += b->coordf[k]->elapsed_time; if ( b->envect[k] ) energy_time += b->envect[k]->elapsed_time; if ( b->convect[k] ) content_time += b->convect[k]->elapsed_time; } sprintf(msg,"%30s %15.f %15.f %15.f\n",b->name,coord_time,energy_time,content_time); commaize(msg); outstring(msg); } } for ( i = 0, do_flag = 0 ; i < web.maxcon ; i++ ) if ( get_constraint(i)->formula ) do_flag = 1; if ( do_flag ) { outstring("\nConstraint expressions\n"); outstring(" Constraint Formula Cycles Energy Cycles Content Cycles\n"); for ( i = 0 ; i < web.maxcon ; i++ ) { struct constraint *con = get_constraint(i); REAL coord_time = 0.0; REAL energy_time = 0.0; REAL content_time = 0.0; if ( !con->formula ) continue; for ( k = 0 ; k < SDIM ; k++ ) { if ( con->formula ) coord_time += con->formula->elapsed_time; if ( con->envect[k] ) energy_time += con->envect[k]->elapsed_time; if ( con->convect[k] ) content_time += con->convect[k]->elapsed_time; } sprintf(msg,"%30s %15.f %15.f %15.f\n",con->name,coord_time,energy_time,content_time); commaize(msg); outstring(msg); } } #else outstring("Evaluation profiling not enabled; compile with PROF_EVALS.\n"); #endif } /************************************************************************ * * function: reset_profiling() * * purpose: Reset profiling elapsed times. */ void reset_profiling() { int i,k; for ( i = 0 ; i < web.global_count ; i++ ) { if ( globals(i)->flags & (FUNCTION_NAME|PROCEDURE_NAME|SUBROUTINE) ) globals(i)->value.proc.elapsed_time = 0.0; } for ( i = 0 ; i < NUMELEMENTS ; i++ ) { show_expr_table[i].elapsed_time = 0.0; } for ( i = LOW_INST; i < meth_inst_count ; i++ ) { struct method_instance *mi = METH_INSTANCE(i); for ( k = 0 ; k < MAXMEXPR ; k++ ) if ( mi->expr[k] ) mi->expr[k]->elapsed_time = 0.0; else break; } for ( i = 0 ; i < web.bdrymax ; i++ ) { struct boundary *b = web.boundaries + i; if ( !b->coordf[0] ) continue; for ( k = 0 ; k < SDIM ; k++ ) { b->coordf[k]->elapsed_time = 0.0; b->envect[k]->elapsed_time = 0.0; b->convect[k]->elapsed_time = 0.0; } } for ( i = 0 ; i < web.maxcon ; i++ ) { struct constraint *con = get_constraint(i); if ( !con->formula ) continue; for ( k = 0 ; k < SDIM ; k++ ) { con->formula->elapsed_time = 0.0; con->envect[k]->elapsed_time = 0.0; con->convect[k]->elapsed_time = 0.0; } } } /*************************************************************************** * * function: mark_recalc_params() * * purpose: Traverse quantities and expressions and things and find * variables whose change should cause a recalc(). */ void mark_recalc_params() { int n,i,k,j; /* Clear old recalc flags */ for ( n = 0 ; n < web.global_count ; n++ ) { struct global *g = globals(n); if ( g->flags & ALWAYS_RECALC ) g->flags |= RECALC_PARAMETER; else g->flags &= ~RECALC_PARAMETER; } /* Go through all expressions attached to various things. */ if ( torus_period_expr[0][0].root ) for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { mark_recalc_expr(&torus_period_expr[i][j]); mark_recalc_expr(&torus_display_period_expr[i][j]); } if ( mobility_flag ) mark_recalc_expr(&mobility_formula); if ( mobility_tensor_flag ) for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) mark_recalc_expr(&torus_period_expr[i][j]); if ( web.metric_flag ) for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) mark_recalc_expr(&web.metric[i][j]); for ( i = 0 ; i < NUMELEMENTS ; i++ ) mark_recalc_expr(show_expr[i]); for ( i = 0 ; i < web.bdrymax ; i++ ) { struct boundary *b = web.boundaries + i; if ( !(b->attr & IN_USE) ) continue; if ( b->attr & CON_ENERGY ) for ( j = 0 ; j < SDIM ; j++ ) { mark_recalc_expr(b->coordf[j]); mark_recalc_expr(b->envect[j]); mark_recalc_expr(b->convect[j]); } } for ( i = 0 ; i < web.maxcon ; i++ ) { struct constraint *con = get_constraint(i); if ( !(con->attr & IN_USE) ) continue; if ( con->attr & CON_ENERGY ) { mark_recalc_expr(con->formula); for ( j = 0 ; j < SDIM ; j++ ) { mark_recalc_expr(con->envect[j]); mark_recalc_expr(con->convect[j]); } } } for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { struct method_instance * mi = METH_INSTANCE(k); /* since some init may move things */ struct gen_quant *q; if ( mi->quant >= 0 ) q = GEN_QUANT(mi->quant); else continue; if ( !(q->flags & Q_ENERGY) ) continue; for ( i = 0 ; i < MAXMEXPR ; i++ ) mark_recalc_expr(mi->expr[i]); } } /* end mark_recalc_params() */ /************************************************************************* * * function: mark_recalc_expr() * * purpose: Mark the functions and variables in an expression known * to cause recalc. * * return: how many new ones marked */ int mark_recalc_expr(ex) struct expnode *ex; { int count = 0 ; struct treenode *node; if ( ex == NULL ) return 0; if ( ex->start == NULL ) return 0; for ( node = ex->start ; node != ex->root ; node ++ ) if ( node->type == PUSHGLOBAL_ ) { struct global *g; if ( (node->op1.name_id &(~GLOBMASK))==LOCALVAR ) continue; g = globals(node->op1.name_id); if ( g->flags & RECALC_PARAMETER ) continue; g->flags |= RECALC_PARAMETER; count ++; } return count; } /* end mark_recalc_expr() */ evolver-2.30c.dfsg/src/lex.h0000644000175300017530000000167111410765113016163 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************ * * File: lex.h * * Contents: Definitions for lexical analyzer for reading * data files. */ #ifndef _LEX_H_ #define _LEX_H_ /* Data type for yylval and parse semantics stack. */ typedef struct { int i; /* standard integer yylval */ int qnum; REAL r; int etype; /* element type */ int datatype; /* expression datatype */ struct sym *symptr; /* iteration variable names */ char lexeme[32]; /* for identifiers */ } yystype; #define YYSTYPE yystype extern YYSTYPE yylval; /* parser terminal value */ #endif evolver-2.30c.dfsg/src/hessian3.c0000644000175300017530000016524311410765113017111 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /***************************************************************** * * file: hessian3.c * * Contents: hessian related routines. * Old hessian calculators. * Some YSMP drivers. * Metric setup routines. */ #include "include.h" /******************************************************************** * * function: area_hessian() * * purpose: fill in hessian matrix with area derivatives * also does volume constraints * */ void area_hessian(S,rhs) struct linsys *S; REAL *rhs; { int i,j,k; facet_id f_id; MAT2D(self2,MAXCOORD,MAXCOORD); MAT2D(otherD,MAXCOORD,MAXCOORD); /* fill in sparse matrix rows and volume constraint rows */ FOR_ALL_FACETS(f_id) { REAL side[FACET_EDGES][MAXCOORD]; /* 3 sides of facet */ REAL ss[FACET_EDGES]; /* squares of sides */ REAL sd[FACET_EDGES]; /* dot of side with next side */ facetedge_id fe_id; struct hess_verlist *v[FACET_VERTS]; REAL two_area; /* twice area of facet */ REAL first[FACET_VERTS][MAXCOORD]; /* first partials */ REAL density = get_facet_density(f_id); vertex_id v_id[FACET_VERTS]; if ( density == 0.0 ) continue; fe_id = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { v_id[i] = get_fe_tailv(fe_id); v[i] = get_vertex_vhead(v_id[i]); get_fe_side(fe_id,side[i]); fe_id = get_next_edge(fe_id); } for ( i = 0 ; i < FACET_EDGES ; i++ ) { ss[i] = SDIM_dot(side[i],side[i]); sd[i] = -SDIM_dot(side[(i+1)%FACET_EDGES],side[(i+2)%FACET_EDGES]); } two_area = sqrt(ss[1]*ss[2] - sd[0]*sd[0]); /* first derivatives of area */ for ( i = 0 ; i < FACET_EDGES ; i++ ) { int ii,jj; REAL grad[MAXCOORD]; if ( v[i]->freedom <= 0 ) continue; ii = (i+2)%FACET_EDGES; /* side previous vertex i */ jj = (i+1)%FACET_EDGES; /* side opposite vertex i, next vertex */ for ( k = 0 ; k < SDIM ; k++ ) { first[i][k] = (side[ii][k]*ss[jj] - sd[i]*(-side[jj][k]))/2/two_area; grad[k] = density*first[i][k]; } fill_grad(S,v[i],grad,rhs); } /* second derivatives */ if ( hess_flag ) for ( i = 0 ; i < FACET_EDGES ; i++ ) { REAL self,other; int ii,jj; if ( v[i]->freedom <= 0 ) continue; ii = (i+2)%FACET_EDGES; /* side previous vertex i */ jj = (i+1)%FACET_EDGES; /* side opposite vertex i, next vertex */ /* for now, all degrees of freedom are coordinate */ for ( j = 0 ; j < SDIM ; j++ ) for ( k = 0 ; k < SDIM ; k++ ) { self = -(side[jj][j]*side[jj][k])/2; if ( j == k ) self += ss[jj]/2; self2[j][k] = density*(self - 2*first[i][j]*first[i][k])/two_area; if ( v[jj]->freedom <= 0 ) continue; other = -side[ii][j]*side[jj][k] - side[jj][j]*(-side[ii][k])/2; if ( j == k ) other -= sd[i]/2; otherD[j][k] = density*(other - 2*first[i][j]*first[jj][k])/two_area; } fill_self_entry(S,v_id[i],self2); if ( v[jj]->freedom > 0 ) fill_mixed_entry(S,v_id[i],v_id[jj],otherD); } } } /* end area_hessian() */ /******************************************************************** * * function: edge_energy_hessian() * * purpose: edge constraint hessian * */ void edge_energy_hessian(S,rhs) struct linsys *S; REAL *rhs; { int i,j; int head,tail; edge_id e_id; MAT2D(first,2,MAXCOORD); MAT4D(second,2,2,MAXCOORD,MAXCOORD); /* do edges */ FOR_ALL_EDGES(e_id) { struct hess_verlist *v[MAXCOORD]; vertex_id *v_id = get_edge_vertices(e_id); if ( ! get_e_constraint_map(e_id)[0] ) continue; for ( i = 0 ; i < 2 ; i++ ) v[i] = get_vertex_vhead(v_id[i]); memset((char*)first[0],0,sizeof(REAL)*2*MAXCOORD); memset((char*)second[0][0][0],0,sizeof(REAL)*2*2*MAXCOORD*MAXCOORD); edge_constr_hessian(S,e_id,first,second); /* first derivatives on right hand side */ for ( i = 0 ; i < 2 ; i++ ) fill_grad(S,v[i],first[i],rhs); /* second derivatives */ if ( hess_flag ) for ( i = 0 ; i < 2 ; i++ ) for ( j = i ; j < 2 ; j++ ) { if ( (v[i]->freedom==0) || (v[j]->freedom==0) ) continue; if ( loc_ordinal(v_id[i]) > loc_ordinal(v_id[j]) ) { tail = i; head = j; } else { tail = j; head = i; } fill_mixed_entry(S,v_id[tail],v_id[head],second[tail][head]); } } } /* end edge_energy_hessian() */ /******************************************************************** * * function: edge_constr_hessian() * * purpose: fill in hessian matrix for constraint energy of one edge * */ void edge_constr_hessian(S,e_id,first,second) struct linsys *S; edge_id e_id; REAL **first; REAL ****second; { REAL *tcoord,*hcoord; struct constraint *constr; int i,m; REAL side[MAXCOORD]; REAL green[MAXCOORD]; REAL green_deriv_space[MAXCOORD][MAXCOORD]; REAL *green_deriv[MAXCOORD]; int j,sign; conmap_t *conmap; REAL midpt[MAXCOORD]; REAL grad[MAXCOORD]; conmap = get_e_constraint_map(e_id); if ( !conmap[0] ) return; if ( get_eattr(e_id) & NEGBOUNDARY ) sign = -1; else sign = 1; if ( inverted(e_id) ) sign = -sign; tcoord = get_coord(get_edge_tailv(e_id)); hcoord = get_coord(get_edge_headv(e_id)); for ( j = 0 ; j < SDIM ; j++ ) { side[j] = hcoord[j] - tcoord[j]; green_deriv[j] = green_deriv_space[j]; } for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { constr = get_constraint(conmap[j]); if ( !(constr->attr & CON_ENERGY) || (constr->compcount != SDIM) ) continue; for ( m = 0 ; m < gauss1D_num ; m++ ) { for ( i = 0 ; i < SDIM ; i++ ) midpt[i] = gauss1Dpt[m]*hcoord[i] + (1 - gauss1Dpt[m])*tcoord[i]; for ( i = 0 ; i < SDIM ; i++ ) eval_all(constr->envect[i],midpt,SDIM,&green[i],green_deriv[i],e_id); vec_mat_mul(side,green_deriv,grad,SDIM,SDIM); for ( i = 0 ; i < SDIM ; i++ ) { int jj; first[0][i] += sign*gauss1Dwt[m]*((1-gauss1Dpt[m])*grad[i]-green[i]); first[1][i] += sign*gauss1Dwt[m]*(gauss1Dpt[m]*grad[i] + green[i]); if ( hess_flag ) for ( jj = 0 ; jj < SDIM ; jj++ ) { second[0][0][i][jj] -= sign*gauss1Dwt[m]* (green_deriv[i][jj]+green_deriv[jj][i])*(1-gauss1Dpt[m]); second[0][1][i][jj] += sign*gauss1Dwt[m]* (-green_deriv[i][jj]*(1-gauss1Dpt[m]) + green_deriv[jj][i]*gauss1Dpt[m]); second[1][0][i][jj] += sign*gauss1Dwt[m]* (-green_deriv[jj][i]*(1-gauss1Dpt[m]) + green_deriv[i][jj]*gauss1Dpt[m]); second[1][1][i][jj] += sign*gauss1Dwt[m]* (green_deriv[i][jj]+green_deriv[jj][i])*gauss1Dpt[m]; } } } } } /* end edge_constr_hessian() */ /******************************************************************** * * function: body_hessian() * * purpose: fill in hessian matrix with body volume constraints * * return: number of bodies that have volume constraints */ int body_hessian(S,rhs) struct linsys *S; REAL *rhs; { int i,j; int count = 0; facet_id f_id; body_id b_id; REAL *Z; REAL coe; MAT2D(otherD,MAXCOORD,MAXCOORD); MAT2D(self,MAXCOORD,MAXCOORD); REAL zsum,ssum; Z = (REAL *)temp_calloc(web.skel[BODY].max_ord+1,sizeof(REAL)); FOR_ALL_BODIES(b_id) if ( get_battr(b_id) & FIXEDVOL ) Z[loc_ordinal(b_id)] = get_body_pressure(b_id); /* Z is vector of coefficients for adding constraint hessians */ if ( hess_debug ) { printf("Z vector: \n"); for ( i = 0 ; i < web.skel[BODY].max_ord+1 ; i++ ) printf("%d %g\n",i,(DOUBLE)Z[i]); } /* rhs for body constraint rows */ if ( rhs_flag && !everything_quantities_flag ) FOR_ALL_BODIES(b_id) { REAL *current = rhs + S->bodyrowstart + loc_ordinal(b_id); if ( get_battr(b_id) & FIXEDVOL ) *current = -(get_body_fixvol(b_id) - get_body_volume(b_id)); } /* body volume constraints, linear part */ FOR_ALL_FACETS(f_id) { REAL side[FACET_EDGES][MAXCOORD]; /* 3 sides of facet */ facetedge_id fe_id; struct hess_verlist *v[FACET_VERTS]; REAL *x[FACET_VERTS]; /* for volume calculation */ body_id bb_id; int do_b, do_bb; /* flags for whether bodies fixed */ int a; if ( get_attr(f_id) & NONCONTENT ) continue; b_id = get_facet_body(f_id); if ( valid_id(b_id) ) { a = get_battr(b_id); do_b = (a & FIXEDVOL) && !(a & REDUNDANT_BIT); } else do_b = 0; bb_id = get_facet_body(inverse_id(f_id)); if ( valid_id(bb_id) ) { a = get_battr(bb_id); do_bb = (a & FIXEDVOL) && !(a & REDUNDANT_BIT); } else do_bb = 0; if ( !do_b && !do_bb ) continue; fe_id = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { v[i] = get_vertex_vhead(get_fe_tailv(fe_id)); x[i] = get_coord(get_fe_tailv(fe_id)); get_fe_side(fe_id,side[i]); fe_id = get_next_edge(fe_id); } coe = 0.0; if ( valid_id(b_id) ) coe += Z[loc_ordinal(b_id)]; if ( valid_id(bb_id) ) coe -= Z[loc_ordinal(bb_id)]; zsum = (x[0][2]+x[1][2]+x[2][2]); ssum = side[0][0]*side[1][1] - side[0][1]*side[1][0]; for ( i = 0 ; i < FACET_VERTS ; i++ ) { int currentrow; REAL g[MAXCOORD],gg[MAXCOORD],*ggg; REAL grad[MAXCOORD]; int ii = (i+1)%3; int iii = (i+2)%3; if ( web.symmetric_content ) cross_prod(x[ii],x[iii],g); else { g[0] = zsum*(x[ii][1]-x[iii][1]); g[1] = zsum*(x[iii][0]-x[ii][0]); g[2] = ssum; } for ( j = 0 ; j < SDIM ; j++ ) grad[j] = -coe*g[j]/6; fill_grad(S,v[i],grad,rhs); if ( !hess_flag ) continue; if ( v[i]->proj ) { vec_mat_mul(g,v[i]->proj,gg,SDIM,v[i]->freedom); ggg = gg; } else ggg = g; if ( do_b ) { currentrow = S->bodyrowstart + loc_ordinal(b_id); for ( j = 0 ; j < v[i]->freedom ; j++ ) { sp_hash_search(S,v[i]->rownum+j,currentrow,ggg[j]/6); } } if ( do_bb ) { currentrow = S->bodyrowstart + loc_ordinal(bb_id); for ( j = 0 ; j < v[i]->freedom ; j++ ) { sp_hash_search(S,v[i]->rownum+j,currentrow,-ggg[j]/6); } } } } /* Now quadratic part */ otherD[0][0] = otherD[1][1] = otherD[2][2] = 0.0; self[0][0] = self[1][1] = self[2][2] = 0.0; self[0][1] = self[1][0] = 0.0; /* now add constraint hessians to energy hessian */ if ( hess_flag ) FOR_ALL_FACETS(f_id) { facetedge_id fe_id; struct hess_verlist *v[FACET_VERTS]; REAL *x[FACET_VERTS]; /* for volume calculation */ vertex_id v_id[FACET_VERTS]; if ( get_attr(f_id) & NONCONTENT ) continue; coe = 0.0; b_id = get_facet_body(f_id); if ( valid_id(b_id) ) coe += Z[loc_ordinal(b_id)]; b_id = get_facet_body(inverse_id(f_id)); if ( valid_id(b_id) ) coe -= Z[loc_ordinal(b_id)]; coe /= 6; /* tetrahedron factor */ if ( coe == 0.0 ) continue; fe_id = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { v_id[i] = get_fe_tailv(fe_id); v[i] = get_vertex_vhead(v_id[i]); x[i] = get_coord(v_id[i]); fe_id = get_next_edge(fe_id); } zsum = x[0][2] + x[1][2] + x[2][2]; /* second derivatives */ if ( hess_flag ) for ( i = 0 ; i < FACET_EDGES ; i++ ) { int ii,jj; jj = (i+1)%FACET_EDGES; /* next vertex */ ii = (i+2)%FACET_EDGES; /* vertex previous vertex i */ if ( v[i]->freedom <= 0 ) continue; if ( !web.symmetric_content ) { self[0][2] = self[2][0] = coe*(x[jj][1]-x[ii][1]); self[1][2] = self[2][1] = coe*(x[ii][0]-x[jj][0]); fill_self_entry(S,v_id[i],self); } if ( v[jj]->freedom <= 0 ) continue; /* for now, all degrees of freedom are coordinate */ if ( web.symmetric_content ) { otherD[0][1] = -coe*x[ii][2]; otherD[1][0] = coe*x[ii][2]; otherD[0][2] = coe*x[ii][1]; otherD[2][0] = -coe*x[ii][1]; otherD[2][1] = coe*x[ii][0]; otherD[1][2] = -coe*x[ii][0]; } else { otherD[0][1] = -coe*zsum; otherD[1][0] = coe*zsum; otherD[0][2] = -coe*(x[jj][1]-x[ii][1]); otherD[2][0] = -coe*(x[ii][1]-x[i ][1]); otherD[1][2] = -coe*(x[ii][0]-x[jj][0]); otherD[2][1] = -coe*(x[i ][0]-x[ii][0]); } fill_mixed_entry(S,v_id[i],v_id[jj],otherD); } } temp_free((char*)Z); count = 0; if ( !everything_quantities_flag ) FOR_ALL_BODIES(b_id) { if ( get_battr(b_id) & FIXEDVOL ) count++; } return count; } /* end body_hessian() */ /*****************************************************************************/ #include "f2c.h" /* C declarations of the YSMP routines */ int odrv_ ARGS(( integer *, integer *,integer *,REAL *, integer *,integer *, integer *,integer *, integer *, integer *)); int sdrvmd_ ARGS(( integer *, integer *,integer *, integer *,integer *,REAL *, REAL *,REAL *, integer *,integer *,REAL *,integer *, integer *, integer *, REAL *)); void sdrv_flag_check ARGS((integer , integer , integer )); void odrv_flag_check ARGS((integer , integer )); /************************************************************************* * * function: ysmp_factor() * * purpose: Do YSMP factoring in linear system * */ void ysmp_factor(S) struct linsys *S; { int i,j; int PATH,FLAG=0,ESP=0; REAL *RSP,EMAX; PROF_START(hessian_factor); /* need just enough at first to do symbolic factorization */ if ( S->NSP == 0 ) { S->NSP = (4 + (int)(log(S->N+1)))*(S->N + S->IA[S->N]); S->ISP = (int *)temp_calloc(S->NSP,sizeof(REAL)); } else memset((char*)S->ISP,0,S->NSP*sizeof(REAL)); if ( S->N == 0 ) return; /* empty hessian */ /* subtract lambda from diagonal */ if ( S->lambda != 0.0 ) for ( i = 0 ; i < S->A_rows ; i++ ) { for ( j = S->IA[i]-A_OFF ; j < S->IA[i+1]-A_OFF ; j++ ) if ( S->JA[j]-A_OFF == i ) { S->A[j] -= S->lambda; break; } } /* Call ODRV to perform the reordering on A, if not done yet */ if ( !(S->flags & S_ODRV_REORDERED) ) { PATH = (S->flags & S_ORDERFOUND) ? 3 : 2; if ( S->P == NULL ) S->P = (int *)temp_calloc(S->N,sizeof(int)); if ( S->IP == NULL ) S->IP = (int *)temp_calloc(S->N,sizeof(int)); odrv_( &S->N, S->IA,S->JA,S->A, S->P,S->IP, &S->NSP,S->ISP,&PATH,&FLAG ); odrv_flag_check(FLAG,S->N); S->flags |= S_ODRV_REORDERED; } /* Call SDRVMD to factor symbolically and numerically */ PATH = 4; /* symbolic factor */ RSP = (REAL *) S->ISP; sdrvmd_(&S->N,S->P,S->IP,S->IA,S->JA,S->A,NULL,NULL,&S->NSP, S->ISP,RSP,&ESP, &PATH,&FLAG,&EMAX); sdrv_flag_check(0,FLAG,S->N); PATH = 6; /* numeric factor */ if ( !hessian_quiet_flag ) { if ( ESP < 0 ) sprintf(msg,"YSMP allocation at %p of %d*8 short by %d*8. Expanding.\n", S->ISP,S->NSP,-ESP); else sprintf(msg,"YSMP allocation at %p of %d*8 over by %d*8. Reducing.\n", S->ISP,S->NSP,ESP); outstring(msg); } S->ISP = (int*)temp_realloc((char*)(S->ISP), (S->NSP-ESP+10)*sizeof(REAL)); if ( !hessian_quiet_flag ) { sprintf(msg,"New allocation at %p.\n",S->ISP); outstring(msg); } S->NSP += -ESP + 10; RSP = (REAL *) S->ISP; sdrvmd_(&S->N,S->P,S->IP,S->IA,S->JA,S->A,NULL,NULL,&S->NSP, S->ISP,RSP,&ESP, &PATH,&FLAG,&EMAX); sdrv_flag_check(ESP,FLAG,S->N); S->neg = mat_index; S->zero = mat_null; S->pos = S->N - mat_index - mat_null; /* add lambda back to diagonal */ if ( S->lambda != 0.0 ) for ( i = 0 ; i < S->A_rows ; i++ ) { for ( j = S->IA[i]-A_OFF ; j < S->IA[i+1]-A_OFF ; j++ ) if ( S->JA[j]-A_OFF == i ) { S->A[j] += S->lambda; break; } } PROF_FINISH(hessian_factor); } /* end ysmp_factor() */ /************************************************************************* * * function: ysmp_solve() * * purpose: Do YSMP solve in linear system * */ void ysmp_solve(S,b,x) struct linsys *S; REAL *b; /* rhs */ REAL *x; /* solution, may be B */ { int PATH,FLAG=0,ESP; REAL *RSP,EMAX; PROF_START(hessian_factor); if ( S->N == 0 ) return; RSP = (REAL *) S->ISP; PATH = 3; /* solution only */ sdrvmd_(&S->N,S->P,S->IP,S->IA,S->JA,S->A,b,x,&S->NSP,S->ISP,RSP,&ESP, &PATH,&FLAG,&EMAX); sdrv_flag_check(ESP,FLAG,S->N); PROF_FINISH(hessian_factor); } /********************************************************************** * * function: ysmp_solve_multi() * * purpose: solve for multiple right hand sides. * */ void ysmp_solve_multi(S,b,x,rk) struct linsys *S; REAL **b; /* rhs */ REAL **x; /* solution, may be B */ int rk; /* number of rhs */ { int k; for ( k = 0 ; k < rk ; k++ ) ysmp_solve(S,b[k],x[k]); } /************************************************************************** * * function: odrv_flag_check() * * purpose: check error return from odrv(). */ void odrv_flag_check(FLAG, N) integer FLAG,N; { if (!FLAG) return; /* Fatal error */ sprintf(errmsg,"Internal error after ODRV: N = %d FLAG = %d -- ",N,FLAG); if ( 9*N 1 ) { linear_metric_setup(S,M); return; } if ( ysmp_flag != MINDEG_FACTORING ) { ysmp_flag = MINDEG_FACTORING; sp_mul_func = bk_mul; sp_AIJ_setup_func = bk_AIJ_setup; sp_constraint_setup_func = bk_constraint_setup; sp_hess_project_setup_func= BK_hess_project_setup; sp_factor_func = xmd_factor; sp_solve_func = xmd_solve; sp_solve_multi_func = xmd_solve_multi; sp_ordering_func = NULL; sp_CHinvC_func = sp_CHinvC; outstring("Using alternate minimal degree with area normalization.\n"); } M->N = S->N; M->flags &= ~ S_ODRV_REORDERED; Total_entries = S->N; /* pure diagonal */ /* allocate storage for arrays */ M->IA = (int *)temp_calloc(M->N+1,sizeof(int)); M->JA = (int *)temp_calloc(Total_entries,sizeof(int)); M->A = (REAL *)temp_calloc(Total_entries,sizeof(REAL)); if ( M->P == NULL ) M->P = (int *)temp_calloc(M->N,sizeof(int)); if ( M->IP == NULL ) M->IP = (int *)temp_calloc(M->N,sizeof(int)); /* go through, vertex by vertex */ FOR_ALL_VERTICES(v_id) { REAL area; vh = get_vertex_vhead(v_id); if ( vh->freedom == 0 ) continue; area = ((web.representation==STRING)?get_vertex_length_star(v_id): get_vertex_area_star(v_id))/star_fraction; for ( j = 0, k = vh->rownum ; j < vh->freedom ; j++,k++ ) { M->IA[k] = M->JA[k] = k + A_OFF; M->A[k] = area; M->IP[k] = M->P[k] = k; /* unpermuted */ } } for ( i = S->optparamrowstart ; i < S->bodyrowstart ; i++ ) { /* metric 1 for optimizing parameters */ M->IA[i] = M->JA[i] = i + A_OFF; M->A[i] = 1.0; M->IP[i] = M->P[i] = i; /* unpermuted */ } for ( i = S->bodyrowstart ; i < S->N ; i++ ) { /* make sure rest of diagonal exists, but no shift */ M->IA[i] = M->JA[i] = i + A_OFF; M->A[i] = 0.0; M->IP[i] = M->P[i] = i; /* unpermuted */ } M->IA[M->N] = M->N + A_OFF; M->apinv = (REAL *)temp_calloc(M->N,sizeof(REAL)); /* sums of rows */ for ( i = 0 ; i < M->N ; i++ ) { int start = M->IA[i] - A_OFF; int end = M->IA[i+1] - A_OFF; M->apinv[i] = M->A[start]; for ( j = start+1 ; j < end ; j++ ) { M->apinv[i] += M->A[j]; M->apinv[M->JA[j]] += M->A[j]; } } for ( i = 0 ; i < M->N ; i++ ) M->apinv[i] = (M->apinv[i]==0.0) ? 0.0 : 1/M->apinv[i]; } /* end star_metric_setup() */ /************************************************************************ * * function: linear_metric_setup() * * purpose: set up vector-to-form metric sparse matrix for local * linear interpolation metric. */ void linear_metric_setup(S,M) struct linsys *S; /* hessian to build metric for */ struct linsys *M; /* pointer to empty structure */ { int i,jj,k,kk,m; size_t j; int sum; edge_id e_id; vertex_id v_id; int ti=0,hi=0; REAL len; MAT2D(weights,MAXCOORD+1,MAXCOORD+1); MAT2D(temp_mat,MAXCOORD,MAXCOORD); REAL **mat; int col,end; int ii[MAXCOORD+1]; int dim; if ( optparamcount > 0 ) kb_error(2444,"Sorry; linear_metric not working with optimizing parameters.\n",RECOVERABLE); if ( web.modeltype == QUADRATIC ) { linear_metric_setup_quadratic(S,M); goto apinv_setup; } if ( web.modeltype == LAGRANGE ) { linear_metric_setup_lagrange(S,M); goto apinv_setup; } if ( ysmp_flag != MINDEG_FACTORING ) { ysmp_flag = MINDEG_FACTORING; sp_mul_func = bk_mul; sp_AIJ_setup_func = bk_AIJ_setup; sp_constraint_setup_func = bk_constraint_setup; sp_hess_project_setup_func = BK_hess_project_setup; sp_factor_func = xmd_factor; sp_solve_func = xmd_solve; sp_solve_multi_func = xmd_solve_multi; sp_ordering_func = NULL; sp_CHinvC_func = sp_CHinvC; outstring( "Using alternate minimal degree with linear interpolation metric.\n"); } M->N = S->N; M->flags &= ~ S_ODRV_REORDERED; /* allocate storage for arrays */ M->IA = (int *)temp_calloc(M->N+1,sizeof(int)); if ( M->P == NULL ) M->P = (int *)temp_calloc(M->N,sizeof(int)); if ( M->IP == NULL ) M->IP = (int *)temp_calloc(M->N,sizeof(int)); /* have to count number of higher-ordinal neighbors of each vertex */ if ( web.representation == SIMPLEX ) { vertex_id *nbrlist; int nbralloc = 1000; int nbrcount; facet_id f_id,start_f; vertex_id *v; int m; nbrlist = (vertex_id *)temp_calloc(nbralloc,sizeof(vertex_id)); FOR_ALL_VERTICES(v_id) { struct hess_verlist *vh = get_vertex_vhead(v_id); i = loc_ordinal(v_id); nbrcount = 1; nbrlist[0] = v_id; f_id = start_f = get_vertex_first_facet(v_id); do { v = get_facet_vertices(f_id); for ( k = 0 ; k <= web.dimension ; k++ ) if ( loc_ordinal(v[k]) > i ) { for ( m = 0 ; m < nbrcount ; m++ ) if ( nbrlist[m] == v[k] ) break; if ( m == nbrcount ) { if ( nbrcount >= nbralloc ) kb_error(1849,"Too many neighbors in linear_metric_setup()\n", RECOVERABLE); nbrlist[nbrcount++] = v[k]; } } f_id = get_next_vertex_facet(v_id,f_id); } while ( !equal_element(f_id,start_f)); /* now count total degrees of freedom */ for ( j = vh->rownum, jj = 0 ; jj < vh->freedom ; jj++,j++ ) M->IA[j] += vh->freedom-jj; for ( m = 1 ; m < nbrcount ; m++ ) { struct hess_verlist *vhi = get_vertex_vhead(nbrlist[m]); for ( j = vh->rownum, jj = 0 ; jj < vh->freedom ; j++,jj++ ) M->IA[j] += vhi->freedom; } } } else { /* fe model */ FOR_ALL_VERTICES(v_id) { struct hess_verlist *vh = get_vertex_vhead(v_id); for ( j = vh->rownum, jj = 0 ; jj < vh->freedom ; jj++,j++ ) M->IA[j] += vh->freedom-jj; } FOR_ALL_EDGES(e_id) { vertex_id tailv = get_edge_tailv(e_id); vertex_id headv = get_edge_headv(e_id); struct hess_verlist *vt = get_vertex_vhead(tailv),*vhtmp; struct hess_verlist *vh = get_vertex_vhead(headv); ti = loc_ordinal(tailv); hi = loc_ordinal(headv); if ( ti > hi ) { int tt = ti; ti = hi ; hi = tt; vhtmp = vh; vh = vt; vt = vhtmp; } if ( vh->freedom == 0 ) continue; for ( j = vt->rownum, jj = 0 ; jj < vt->freedom ; j++,jj++ ) M->IA[j] += vh->freedom; } } for ( i = S->optparamrowstart ; i < S->N ; i++ ) M->IA[i] = 1; /* diagonal elements */ for ( i = 0, sum = 0 ; i < M->N ; i++ ) /* add to running totals */ { int tmp = sum; sum += M->IA[i] + optparamcount; M->IA[i] = tmp + A_OFF; } M->IA[M->N] = sum + A_OFF; /* now can allocate space */ M->JA = (int *)temp_calloc(sum,sizeof(int)); M->A = (REAL *)temp_calloc(sum,sizeof(REAL)); for ( i = 0 ; i < sum ; i++ ) M->JA[i] = -1; /* vacant */ for ( i = 0 ; i < M->N ; i++ ) { M->JA[M->IA[i]-A_OFF] = i + A_OFF; /* diagonal entry at start */ M->IP[i] = M->P[i] = i; /* unpermuted */ } /* set up interpolation weight matrix */ for ( i = 0 ; i < optparamcount ; i++ ) M->A[M->IA[S->optparamrowstart+i]-A_OFF] = 1.0; /* leave rest of diagonal zero in case of augmented hessian */ dim = web.dimension; for ( i = 0 ; i <= dim ; i++ ) /* index for vertices of element */ for ( m = 0 ; m <= dim ; m++ ) { if ( i == m ) weights[i][m] = linear_metric_mix*2.0/(dim+1.0)/(dim+2.0) + (1.0 - linear_metric_mix)/(dim+1.0); else weights[i][m] = linear_metric_mix/(dim+1.0)/(dim+2.0); } if ( web.representation == STRING ) { /* edge by edge */ FOR_ALL_EDGES(e_id) { struct hess_verlist *vh[2]; vertex_id tailv = get_edge_tailv(e_id); vertex_id headv = get_edge_headv(e_id); if ( loc_ordinal(tailv) > loc_ordinal(headv) ) { vh[0] = get_vertex_vhead(headv); vh[1] = get_vertex_vhead(tailv); } else { vh[0] = get_vertex_vhead(tailv); vh[1] = get_vertex_vhead(headv); } len = get_edge_length(e_id); for ( i = 0 ; i < 2 ; i++ ) { struct hess_verlist *v = vh[i]; REAL fudge = v->slant*v->slant; /* self */ if ( v->proj ) { tr_mat_mul(v->proj,v->proj,temp_mat,SDIM,v->freedom,v->freedom); mat = temp_mat; } else mat = identmat; for ( jj = 0 ; jj < v->freedom ; jj++ ) for ( k = jj ; k < v->freedom ; k++ ) { col = v->rownum + k + A_OFF; end = M->IA[v->rownum+jj+1]-A_OFF; for ( m = M->IA[v->rownum+jj]-A_OFF ; m < end ; m++ ) if ( (M->JA[m] < A_OFF) || (M->JA[m] == col) ) { M->JA[m] = col; M->A[m] += fudge*(3-linear_metric_mix)*len/6*mat[jj][k]; break; } if ( m == end ) kb_error(1851,"Internal error in linear_metric_setup.\n",RECOVERABLE); } /* cross terms */ for ( k = i+1 ; k < 2 ; k++ ) { struct hess_verlist *vv = vh[k]; REAL cfudge = v->slant*vv->slant; if ( v->proj && vv->proj ) { tr_mat_mul(v->proj,vv->proj,temp_mat,SDIM,v->freedom, vv->freedom); mat = temp_mat; } else if ( v->proj ) { tr_mat_mul(v->proj,identmat,temp_mat,SDIM,v->freedom, vv->freedom); mat = temp_mat; } else if ( vv->proj ) mat = vv->proj; else mat = identmat; for ( jj = 0 ; jj < v->freedom ; jj++ ) for ( kk = 0 ; kk < vv->freedom ; kk++ ) { col = vv->rownum + kk + A_OFF ; end = M->IA[v->rownum+jj+1]-A_OFF; for ( j = M->IA[v->rownum+jj]+1-A_OFF ; (int)j < end ; j++ ) if ( (M->JA[j] < A_OFF) || (M->JA[j] == col) ) { M->JA[j] = col; M->A[j] += cfudge*mat[jj][kk]*len/6*linear_metric_mix; break; } if ( j == end ) kb_error(1852,"Internal error in linear_metric_setup.\n",RECOVERABLE); } } } } } else { facet_id f_id; REAL area; /* facet by facet */ FOR_ALL_FACETS(f_id) { vertex_id ix[FACET_VERTS]; facetedge_id fe; REAL density = get_facet_density(f_id); REAL normal[MAXCOORD]; struct hess_verlist *vh[MAXCOORD+1]; area = get_facet_area(f_id); if ( density != 0.0 ) area *= density; if ( hessian_normal_perp_flag ) { REAL d; get_facet_normal(f_id,normal); d = sqrt(SDIM_dot(normal,normal)); for ( i = 0 ; i < SDIM ; i++ ) normal[i] /= d; } fe = get_facet_fe(f_id); ix[0] = get_fe_tailv(fe); ix[1] = get_fe_headv(fe); fe = get_next_edge(fe); ix[2] = get_fe_headv(fe); for ( i = 0 ; i <= dim ; i++ ) /* bubble sort vertices */ { j = loc_ordinal(ix[i]); for ( k = i ; k > 0 ; k-- ) if ( ii[k-1] > (int)j ) { ii[k] = ii[k-1]; vh[k] = vh[k-1]; } else break; ii[k] = (int)j; vh[k] = get_vertex_vhead(ix[i]); } for ( i = 0 ; i <= dim ; i++ ) { struct hess_verlist *v = vh[i]; REAL fudge = v->slant*v->slant; /* self */ if ( v->proj ) { if ( hessian_normal_perp_flag ) { REAL w[MAXCOORD], *wp=w; vec_mat_mul(normal,v->proj,w,SDIM,v->freedom); tr_mat_mul(&wp,&wp,temp_mat,1,v->freedom,v->freedom); fudge = 1.0; } else { tr_mat_mul(v->proj,v->proj,temp_mat,SDIM,v->freedom,v->freedom); } mat = temp_mat; } else mat = identmat; for ( jj = 0 ; jj < v->freedom ; jj++ ) for ( k = jj ; k < v->freedom ; k++ ) { col = v->rownum + k + A_OFF; end = M->IA[v->rownum+jj+1]-A_OFF; for ( j = M->IA[v->rownum+jj]-A_OFF ; (int)j < end ; j++ ) if ( (M->JA[j] < A_OFF) || (M->JA[j] == col) ) { M->JA[j] = col; M->A[j] += fudge*weights[i][i]*area*mat[jj][k]; break; } if ( j == end ) kb_error(1853,"Internal error in linear_metric_setup.\n",RECOVERABLE); } /* cross terms */ for ( k = i+1 ; k <= dim ; k++ ) { struct hess_verlist *vv = vh[k]; REAL cfudge = v->slant*vv->slant; if ( v->proj && vv->proj ) { if ( hessian_normal_perp_flag ) { REAL w[MAXCOORD],ww[MAXCOORD],*wp=w,*wwp=ww; vec_mat_mul(normal,v->proj,w,SDIM,v->freedom); vec_mat_mul(normal,vv->proj,ww,SDIM,vv->freedom); tr_mat_mul(&wp,&wwp,temp_mat,1,v->freedom,vv->freedom); cfudge = 1.0; } else tr_mat_mul(v->proj,vv->proj,temp_mat,SDIM,v->freedom,vv->freedom); mat = temp_mat; } else if ( v->proj ) { if ( hessian_normal_perp_flag ) { REAL w[MAXCOORD],*wp=w,*np=normal; vec_mat_mul(normal,v->proj,w,SDIM,v->freedom); tr_mat_mul(&wp,&np,temp_mat,1,v->freedom,vv->freedom); cfudge = 1.0; } else tr_mat_mul(v->proj,identmat,temp_mat,SDIM,v->freedom,vv->freedom); mat = temp_mat; } else if ( vv->proj ) { if ( hessian_normal_perp_flag ) { REAL ww[MAXCOORD],*wwp=ww,*np=normal; vec_mat_mul(normal,vv->proj,ww,SDIM,vv->freedom); tr_mat_mul(&np,&wwp,temp_mat,1,v->freedom,vv->freedom); cfudge = 1.0; mat = temp_mat; } else mat = vv->proj; } else { if ( hessian_normal_perp_flag ) { REAL *np=normal; tr_mat_mul(&np,&np,temp_mat,1,v->freedom,vv->freedom); cfudge = 1.0; mat = temp_mat; } else mat = identmat; } for ( jj = 0 ; jj < v->freedom ; jj++ ) for ( kk = 0 ; kk < vv->freedom ; kk++ ) { col = vv->rownum + kk + A_OFF ; end = M->IA[v->rownum+jj+1]-A_OFF; for ( j = M->IA[v->rownum+jj]+1-A_OFF ; (int)j < end ; j++ ) if ( (M->JA[j] < A_OFF) || (M->JA[j] == col) ) { M->JA[j] = col; M->A[j] += cfudge*mat[jj][kk]*weights[i][k]*area; break; } if ( j == end ) kb_error(1854,"Internal error: Bad j in linear_metric_setup().\n",RECOVERABLE); } } } } } /* compact matrix by looking for -1 in M->JA */ for ( i = 0 ; i < M->IA[M->N]-A_OFF ; i++ ) if ( M->JA[i] < A_OFF ) break; if ( i < M->IA[M->N]-A_OFF ) { int *ja_to_spot = M->JA,*ja_from_spot = M->JA; REAL *a_to_spot= M->A,*a_from_spot= M->A; for ( i = 0 ; i < M->N ; i++ ) { end = M->IA[i+1]-A_OFF; for ( j = ja_from_spot - M->JA ; (int)j < end ; j++,ja_from_spot++,a_from_spot++ ) { if ( *ja_from_spot >= A_OFF ) { *ja_to_spot = *ja_from_spot; *a_to_spot = *a_from_spot; a_to_spot++; ja_to_spot++; } } M->IA[i+1] = (int)(ja_to_spot - M->JA + A_OFF); } } apinv_setup: /* approximate inverse, as diagonal inverse sum of row */ M->apinv = (REAL *)temp_calloc(M->N,sizeof(REAL)); /* sums of rows */ for ( i = 0 ; i < M->N ; i++ ) { int start = M->IA[i] - A_OFF; end = M->IA[i+1] - A_OFF; M->apinv[i] += M->A[start]; for ( j = start+1 ; (int)j < end ; j++ ) { M->apinv[i] += M->A[j]; M->apinv[M->JA[j]-A_OFF] += M->A[j]; } } for ( i = 0 ; i < M->N ; i++ ) M->apinv[i] = (M->apinv[i]==0.0) ? 0.0 : 1/M->apinv[i]; if ( hess_debug) { printf("Metric matrix:\n"); printf("IA: "); for ( i = 0 ; i <= M->N ; i++ ) printf("%d ",M->IA[i]); printf("\nJA: "); for ( i = 0 ; i < M->IA[M->N]-A_OFF ; i++ ) printf("%d ",M->JA[i]); printf("\nA: \n"); for ( i = 0 ; i < M->N ; i++ ) { int k,m; for ( m = 0 ; m < i ; m++ ) printf(" "); for ( m = i, k = M->IA[i]-A_OFF ; m < M->N ; m++ ) if ( (m == M->JA[k]-A_OFF) && (k < M->IA[i+1]-A_OFF) ) { printf(" %9.6f",(DOUBLE)M->A[k]); k++; } else printf(" %9.6f",0.0); printf("\n"); } printf("\n"); } } /* end linear_metric_setup() */ /************************************************************************ * * function: linear_metric_setup_quadratic() * * purpose: set up vector-to-form metric sparse matrix for local * linear interpolation metric. Quadratic mode. */ void linear_metric_setup_quadratic(S,M) struct linsys *S; /* hessian to set up metric for */ struct linsys *M; /* pointer to empty structure */ { int i,j,jj,k,kk,m,n; int sum; edge_id e_id; vertex_id v_id; facet_id f_id; int hi; REAL len; MAT2D(weights,FACET_CTRL,FACET_CTRL); MAT2D(temp_mat,MAXCOORD,MAXCOORD); REAL **mat; int col,end; int ii[FACET_CTRL]; vertex_id vv[FACET_CTRL]; int lo; if ( ysmp_flag != MINDEG_FACTORING ) { ysmp_flag = MINDEG_FACTORING; sp_mul_func = bk_mul; sp_AIJ_setup_func = bk_AIJ_setup; sp_constraint_setup_func = bk_constraint_setup; sp_hess_project_setup_func= BK_hess_project_setup; sp_factor_func = xmd_factor; sp_solve_func = xmd_solve; sp_solve_multi_func = xmd_solve_multi; sp_ordering_func = NULL; sp_CHinvC_func = sp_CHinvC; outstring( "Using alternate minimal degree with linear interpolation metric.\n"); } M->N = S->N; M->flags &= ~ S_ODRV_REORDERED; /* allocate storage for arrays */ M->IA = (int *)temp_calloc(M->N+1,sizeof(int)); if ( M->P == NULL ) M->P = (int *)temp_calloc(M->N,sizeof(int)); if ( M->IP == NULL ) M->IP = (int *)temp_calloc(M->N,sizeof(int)); for ( i = S->optparamrowstart ; i < S->N ; i++ ) { /* make sure rest of diagonal exists, but no shift */ M->IA[i] = 1; } /* have to count number of higher-ordinal neighbors of each vertex */ FOR_ALL_VERTICES(v_id) { struct hess_verlist *vh = get_vertex_vhead(v_id); for ( j = vh->rownum, jj = 0 ; jj < vh->freedom ; jj++,j++ ) M->IA[j] += vh->freedom-jj; } FOR_ALL_EDGES(e_id) { vertex_id v[3]; struct hess_verlist *vh[3]; v[0] = get_edge_tailv(e_id); v[1] = get_edge_headv(e_id); v[2] = get_edge_midv(e_id); for ( i = 0 ; i < 3 ; i ++ ) vh[i] = get_vertex_vhead(v[i]); for ( i = 0 ; i < 3 ; i ++ ) for ( k = 0 ; k < 3 ; k++ ) if ( loc_ordinal(v[i]) < loc_ordinal(v[k]) ) for ( j = vh[i]->rownum, jj = 0 ; jj < vh[i]->freedom ; j++,jj++ ) M->IA[j] += vh[k]->freedom; } if ( web.representation == SOAPFILM ) FOR_ALL_FACETS(f_id) { vertex_id vv[FACET_CTRL]; struct hess_verlist *vh[FACET_CTRL]; facetedge_id fe_id = get_facet_fe(f_id); int v[FACET_CTRL]; for ( i = 0 ; i < FACET_EDGES ; i++ ) { vv[i] = get_fe_tailv(fe_id); vv[i+3] = get_fe_midv(fe_id); fe_id = get_next_edge(fe_id); } for ( i = 0 ; i < FACET_CTRL ; i++ ) { v[i] = loc_ordinal(vv[i]); vh[i] = get_vertex_vhead(v[i]); } #define ADDON(a,b) \ if ( v[a] > v[b] ) {lo = b; hi = a; } else { lo= a; hi=b;}\ for ( j = vh[lo]->rownum, jj = 0 ; jj < vh[lo]->freedom ; j++,jj++ )\ M->IA[j] += vh[hi]->freedom; ADDON(0,4); ADDON(1,5); ADDON(2,3); ADDON(4,5); ADDON(3,4); ADDON(3,5); } for ( i = 0, sum = 0 ; i < M->N ; i++ ) /* add to running totals */ { int tmp = sum; sum += M->IA[i]; M->IA[i] = tmp + A_OFF; } M->IA[M->N] = sum + A_OFF; /* now can allocate space */ M->JA = (int *)temp_calloc(sum,sizeof(int)); M->A = (REAL *)temp_calloc(sum,sizeof(REAL)); for ( i = 0 ; i < sum ; i++ ) M->JA[i] = -1; /* vacant */ for ( i = 0 ; i < M->N ; i++ ) { M->JA[M->IA[i]-A_OFF] = i + A_OFF; /* diagonal entry at start */ M->IP[i] = M->P[i] = i; /* unpermuted */ } /* set up interpolation weight matrix */ for ( i = 0 ; i < optparamcount ; i++ ) M->A[M->IA[S->optparamrowstart+i]-A_OFF] = 1.0; /* leave rest of diagonal zero in case of augmented hessian */ if ( web.representation == STRING ) { MAT2D(x,EDGE_CTRL,MAXCOORD); REAL tang[MAXCOORD]; /* edge by edge */ FOR_ALL_EDGES(e_id) { vv[0] = get_edge_tailv(e_id); vv[1] = get_edge_midv(e_id); vv[2] = get_edge_headv(e_id); ii[0] = loc_ordinal(vv[0]); ii[1] = loc_ordinal(vv[1]); ii[2] = loc_ordinal(vv[2]); get_edge_verts(e_id,x,NULL); len = get_edge_length(e_id); /* weight matrix */ for ( i = 0 ; i < edge_ctrl ; i++ ) for ( j = 0 ; j < edge_ctrl ; j++ ) weights[i][j] = 0.0; for ( m = 0 ; m < gauss1D_num ; m++ ) { REAL value; for ( n = 0 ; n < SDIM ; n ++ ) { tang[n] = 0.0; for ( k = 0 ; k < edge_ctrl ; k++ ) tang[n] += gauss1polyd[k][m]*x[k][n]; } value = gauss1Dwt[m]*sqrt(SDIM_dot(tang,tang)); for ( i = 0 ; i < edge_ctrl ; i++ ) { for ( j = 0 ; j < edge_ctrl ; j++ ) weights[i][j] += quadratic_metric_mix*value *gauss1poly[i][m]*gauss1poly[j][m]; } } /* simple vertex weighting part */ for ( i = 0 ; i < edge_ctrl ; i++ ) weights[i][i] += (1-quadratic_metric_mix)*((i==1) ? 0.5*len : 0.25*len); /* fill in metric matrix */ for ( i = 0 ; i < 3 ; i++ ) { struct hess_verlist *vh = get_vertex_vhead(vv[i]); /* self */ if ( vh->proj ) { tr_mat_mul(vh->proj,vh->proj,temp_mat,SDIM,vh->freedom,vh->freedom); mat = temp_mat; } else mat = identmat; for ( jj = 0 ; jj < vh->freedom ; jj++ ) for ( k = jj ; k < vh->freedom ; k++ ) { col = vh->rownum + k + A_OFF; end = M->IA[vh->rownum+jj+1]-A_OFF; for ( j = M->IA[vh->rownum+jj]-A_OFF ; j < end ; j++ ) if ( (M->JA[j] < A_OFF) || (M->JA[j] == col) ) { M->JA[j] = col; M->A[j] += vh->slant*vh->slant*weights[i][i]*mat[jj][k]; break; } if ( j == end ) kb_error(1856,"Internal error in linear_metric_setup.\n", RECOVERABLE); } /* cross terms */ for ( k = 0 ; k < 3 ; k++ ) { struct hess_verlist *vvh = get_vertex_vhead(vv[k]); if ( ii[k] <= ii[i] ) continue; if ( vh->proj && vvh->proj ) { tr_mat_mul(vh->proj,vvh->proj,temp_mat,SDIM,vh->freedom, vvh->freedom); mat = temp_mat; } else if ( vh->proj ) { tr_mat_mul(vh->proj,identmat,temp_mat,SDIM,vh->freedom,vvh->freedom); mat = temp_mat; } else if ( vvh->proj ) mat = vvh->proj; else mat = identmat; for ( jj = 0 ; jj < vh->freedom ; jj++ ) for ( kk = 0 ; kk < vvh->freedom ; kk++ ) { col = vvh->rownum + kk + A_OFF ; end = M->IA[vh->rownum+jj+1]-A_OFF; for ( j = M->IA[vh->rownum+jj]+1-A_OFF ; ; j++ ) if ( (M->JA[j] < A_OFF) || (M->JA[j] == col) ) { M->JA[j] = col; M->A[j] += vh->slant*vvh->slant*mat[jj][kk]*weights[i][k]; break; } if ( j == end ) kb_error(1857,"Internal error in linear_metric_setup.\n", RECOVERABLE); } } } } } else { REAL area; MAT2D(tang,2,MAXCOORD); MAT2D(x,FACET_CTRL,MAXCOORD); /* facet by facet */ FOR_ALL_FACETS(f_id) { vertex_id ix[FACET_CTRL]; REAL density = get_facet_density(f_id); facetedge_id fe_id = get_facet_fe(f_id); if ( density == 0.0 ) density = 1.0; for ( i = 0 ; i < FACET_EDGES ; i++ ) { ix[2*i] = get_fe_tailv(fe_id); ix[2*i+1] = get_fe_midv(fe_id); fe_id = get_next_edge(fe_id); } area = get_facet_area(f_id); for ( i = 0 ; i < FACET_CTRL ; i++ ) ii[i] = loc_ordinal(ix[i]); /* weight matrix */ get_facet_verts(f_id,x,NULL); for ( i = 0 ; i < FACET_CTRL ; i++ ) for ( j = 0 ; j < FACET_CTRL ; j++ ) weights[i][j] = 0.0; /* simple vertex weighting part */ for ( i = 0 ; i < 3 ; i++ ) { weights[2*i][2*i] = (1-quadratic_metric_mix)/12*area; weights[2*i+1][2*i+1] = (1-quadratic_metric_mix)/4*area; } for ( m = 0 ; m < gauss2D_num ; m++ ) { REAL value,ss,st,tt,det; mat_mult(gpolypartial[m],x,tang,web.dimension,FACET_CTRL,SDIM); ss = SDIM_dot(tang[0],tang[0]); st = SDIM_dot(tang[0],tang[1]); tt = SDIM_dot(tang[1],tang[1]); det = ss*tt - st*st; value = gauss2Dwt[m]*sqrt(det)/2; /* with triangle factor */ for ( i = 0 ; i < FACET_CTRL ; i++ ) { for ( j = 0 ; j < FACET_CTRL ; j++ ) weights[i][j] += quadratic_metric_mix*value*gpoly[m][i]*gpoly[m][j]; } } for ( i = 0 ; i < FACET_CTRL ; i++ ) for ( j = 0 ; j < FACET_CTRL ; j++ ) weights[i][j] *= density; for ( i = 0 ; i < FACET_CTRL ; i++ ) { struct hess_verlist *v = get_vertex_vhead(ix[i]); /* self */ if ( v->proj ) { tr_mat_mul(v->proj,v->proj,temp_mat,SDIM,v->freedom,v->freedom); mat = temp_mat; } else mat = identmat; for ( jj = 0 ; jj < v->freedom ; jj++ ) for ( k = jj ; k < v->freedom ; k++ ) { col = v->rownum + k + A_OFF; end = M->IA[v->rownum+jj+1]-A_OFF; for ( j = M->IA[v->rownum+jj]-A_OFF ; j < end ; j++ ) if ( (M->JA[j] < A_OFF) || (M->JA[j] == col) ) { M->JA[j] = col; M->A[j] += v->slant*v->slant*weights[i][i]*mat[jj][k]; break; } if ( j == end ) kb_error(1858,"Internal error in linear_metric_setup.\n", RECOVERABLE); } /* cross terms */ for ( k = 0 ; k < FACET_CTRL ; k++ ) { struct hess_verlist *vv = get_vertex_vhead(ix[k]); if ( ii[k] <= ii[i] ) continue; if ( v->proj && vv->proj ) { tr_mat_mul(v->proj,vv->proj,temp_mat,SDIM,v->freedom,vv->freedom); mat = temp_mat; } else if ( v->proj ) { tr_mat_mul(v->proj,identmat,temp_mat,SDIM,v->freedom,vv->freedom); mat = temp_mat; } else if ( vv->proj ) mat = vv->proj; else mat = identmat; for ( jj = 0 ; jj < v->freedom ; jj++ ) for ( kk = 0 ; kk < vv->freedom ; kk++ ) { col = vv->rownum + kk + A_OFF ; end = M->IA[v->rownum+jj+1]-A_OFF; for ( j = M->IA[v->rownum+jj]+1-A_OFF ; j < end ; j++ ) if ( (M->JA[j] < A_OFF) || (M->JA[j] == col) ) { M->JA[j] = col; M->A[j] += v->slant*vv->slant*mat[jj][kk]*weights[i][k]; break; } if ( j == end ) kb_error(1859, "Internal error: Bad j in linear_metric_setup_quadratic().\n",RECOVERABLE); } } } } } /* compact matrix by looking for -1 in M->JA */ for ( i = 0 ; i < M->IA[M->N]-A_OFF ; i++ ) if ( M->JA[i] < A_OFF ) { kb_error(1860,"Internal error: Empty spot in Met.\n",WARNING); break; } if ( i < M->IA[M->N]-A_OFF ) { int *ja_to_spot = M->JA,*ja_from_spot = M->JA; REAL *a_to_spot= M->A,*a_from_spot= M->A; for ( i = 0 ; i < M->N ; i++ ) { end = M->IA[i+1]-A_OFF; for ( j = M->IA[i]-A_OFF ; j < end ; j++,ja_from_spot++,a_from_spot++ ) { if ( M->JA[j] >= A_OFF ) { *ja_to_spot = *ja_from_spot; *a_to_spot = *a_from_spot; a_to_spot++; ja_to_spot++; } } } } } /* end linear_metric_setup_quadratic() */ /************************************************************************ * * function: linear_metric_setup_lagrange() * * purpose: set up vector-to-form metric sparse matrix for local * linear interpolation metric. Lagrange model. */ void linear_metric_setup_lagrange(S,M) struct linsys *S; /* hessian to set up metric for */ struct linsys *M; /* pointer to empty structure */ { int i,j,jj,k,kk,m,n; edge_id e_id; facet_id f_id; MAT2D(temp_mat,MAXCOORD,MAXCOORD); REAL **mat=NULL; int col,row; int *ii; REAL **weights=NULL; REAL **x = NULL; struct mentry { int row; int col; REAL val; } *temp,*mlist; int maxcount; int count,total; if ( ysmp_flag != MINDEG_FACTORING ) { ysmp_flag = MINDEG_FACTORING; sp_mul_func = bk_mul; sp_AIJ_setup_func= bk_AIJ_setup; sp_constraint_setup_func = bk_constraint_setup; sp_hess_project_setup_func= BK_hess_project_setup; sp_factor_func = xmd_factor; sp_solve_func = xmd_solve; sp_solve_multi_func = xmd_solve_multi; sp_ordering_func = NULL; sp_CHinvC_func = sp_CHinvC; outstring( "Using alternate minimal degree with Lagrange linear interpolation metric.\n"); } M->N = S->N; M->flags &= ~ S_ODRV_REORDERED; /* allocate storage for arrays */ M->IA = (int *)temp_calloc(M->N+1,sizeof(int)); if ( M->P == NULL ) M->P = (int *)temp_calloc(M->N,sizeof(int)); if ( M->IP == NULL ) M->IP = (int *)temp_calloc(M->N,sizeof(int)); for ( i = 0 ; i < M->N ; i++ ) M->IP[i] = M->P[i] = i; /* unpermuted */ if ( web.representation == STRING ) { REAL tang[MAXCOORD]; int ctrl = web.skel[EDGE].ctrlpts; struct gauss_lag *gl = &gauss_lagrange[web.dimension][web.gauss1D_order]; ii = (int*)temp_calloc(ctrl,sizeof(int)); weights = dmatrix(0,ctrl,0,ctrl); x = dmatrix(0,ctrl,0,SDIM); maxcount = web.skel[EDGE].count*ctrl*(ctrl+1)/2*SDIM*SDIM; maxcount += M->N - M->optparamrowstart + 10; mlist = (struct mentry *)temp_calloc(maxcount,sizeof(struct mentry)); count = 0; /* edge by edge */ FOR_ALL_EDGES(e_id) { vertex_id *v = get_edge_vertices(e_id); for ( i = 0 ; i < ctrl ; i++ ) ii[i] = loc_ordinal(v[i]); get_edge_verts(e_id,x,NULL); /* weight matrix */ for ( i = 0 ; i < ctrl ; i++ ) for ( j = 0 ; j < ctrl ; j++ ) weights[i][j] = 0.0; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL value; for ( n = 0 ; n < SDIM ; n ++ ) { tang[n] = 0.0; for ( k = 0 ; k < ctrl ; k++ ) tang[n] += gl->gpolypart[m][0][k]*x[k][n]; } value = gl->gausswt[m]*sqrt(SDIM_dot(tang,tang)); for ( i = 0 ; i < ctrl ; i++ ) { for ( j = 0 ; j < ctrl ; j++ ) weights[i][j] += quadratic_metric_mix*value *gl->gpoly[m][i]*gl->gpoly[m][j]; } } /* simple vertex weighting part */ weights[0][0] += (1-quadratic_metric_mix)*.5/(ctrl-1); weights[ctrl][ctrl] = (1-quadratic_metric_mix)*.5/(ctrl-1); for ( i = 1 ; i < edge_ctrl-1 ; i++ ) weights[i][i] += (1-quadratic_metric_mix)/(ctrl-1); /* fill in metric matrix */ for ( i = 0 ; i < ctrl ; i++ ) { struct hess_verlist *vh = get_vertex_vhead(v[i]); /* self */ if ( vh->proj ) { tr_mat_mul(vh->proj,vh->proj,temp_mat,SDIM,vh->freedom,vh->freedom); mat = temp_mat; } else mat = identmat; for ( jj = 0 ; jj < vh->freedom ; jj++ ) for ( k = jj ; k < vh->freedom ; k++ ) { col = vh->rownum + k; row = vh->rownum + jj; mlist[count].col = col; mlist[count].row = row; mlist[count++].val = vh->slant*vh->slant*weights[i][i]*mat[jj][k]; } /* cross terms */ for ( k = 0 ; k < ctrl ; k++ ) { struct hess_verlist *vv = get_vertex_vhead(v[k]); if ( ii[k] <= ii[i] ) continue; if ( vh->proj && vv->proj ) { tr_mat_mul(vh->proj,vv->proj,temp_mat,SDIM,vh->freedom,vv->freedom); mat = temp_mat; } else if ( vh->proj ) { tr_mat_mul(vh->proj,identmat,temp_mat,SDIM,vh->freedom,vv->freedom); mat = temp_mat; } else if ( vv->proj ) mat = vv->proj; else mat = identmat; for ( jj = 0 ; jj < vh->freedom ; jj++ ) for ( kk = 0 ; kk < vv->freedom ; kk++ ) { col = vv->rownum + kk; row = vh->rownum+jj; mlist[count].col = col; mlist[count].row = row; mlist[count++].val = vh->slant*vv->slant*mat[jj][kk]*weights[i][k]; } } } } } else /* facets */ { MAT2D(tang,MAXCOORD,MAXCOORD); int ctrl = web.skel[FACET].ctrlpts; struct gauss_lag *gl = &gauss_lagrange[web.dimension][web.gauss2D_order]; MAT2D(matt,MAXCOORD,MAXCOORD); ii = (int*)temp_calloc(ctrl,sizeof(int)); weights = dmatrix(0,ctrl,0,ctrl); x = dmatrix(0,ctrl,0,SDIM); maxcount = web.skel[FACET].count*ctrl*(ctrl+1)/2*SDIM*SDIM; maxcount += M->N - M->optparamrowstart + 10; count = 0; mlist = (struct mentry *)temp_calloc(maxcount,sizeof(struct mentry)); /* facet by facet */ FOR_ALL_FACETS(f_id) { vertex_id *ix = get_facet_vertices(f_id); REAL density = get_facet_density(f_id); for ( i = 0 ; i < ctrl ; i++ ) ii[i] = loc_ordinal(ix[i]); /* weight matrix */ get_facet_verts(f_id,x,NULL); for ( i = 0 ; i < ctrl ; i++ ) for ( j = 0 ; j < ctrl ; j++ ) weights[i][j] = 0.0; /* simple vertex weighting part */ for ( i = 0 ; i < ctrl ; i++ ) /* not very precise */ { int attr = get_vattr(ix[i]); if ( attr & Q_MIDFACET ) weights[i][i] = (1-quadratic_metric_mix)/ctrl; else if ( attr & Q_MIDEDGE ) weights[i][i] = (1-quadratic_metric_mix)/ctrl; else weights[i][i] = (1-quadratic_metric_mix)/ctrl; } for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL value,det; mat_mult(gl->gpolypart[m],x,tang,web.dimension,ctrl,SDIM); for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j <= i ; j++ ) matt[i][j] = matt[j][i] = SDIM_dot(tang[i],tang[j]); det = det_adjoint(matt,web.dimension); if ( det <= 0.0 ) value = 0.0; else value = gl->gausswt[m]*sqrt(det)/factorial[web.dimension]; for ( i = 0 ; i < ctrl ; i++ ) { for ( j = 0 ; j < ctrl ; j++ ) weights[i][j] += quadratic_metric_mix*value *gl->gpoly[m][i]*gl->gpoly[m][j]; } } for ( i = 0 ; i < ctrl ; i++ ) for ( j = 0 ; j < ctrl ; j++ ) weights[i][j] *= density; for ( i = 0 ; i < ctrl ; i++ ) { struct hess_verlist *v = get_vertex_vhead(ix[i]); /* self */ if ( v->proj ) { tr_mat_mul(v->proj,v->proj,temp_mat,SDIM,v->freedom,v->freedom); mat = temp_mat; } else mat = identmat; for ( jj = 0 ; jj < v->freedom ; jj++ ) for ( k = jj ; k < v->freedom ; k++ ) { col = v->rownum + k; row = v->rownum+jj; mlist[count].col = col; mlist[count].row = row; mlist[count++].val = v->slant*v->slant*weights[i][i]*mat[jj][k]; } /* cross terms */ for ( k = 0 ; k < ctrl ; k++ ) { struct hess_verlist *vv = get_vertex_vhead(ix[k]); if ( ii[k] <= ii[i] ) continue; if ( v->proj && vv->proj ) { tr_mat_mul(v->proj,vv->proj,temp_mat,SDIM,v->freedom,vv->freedom); mat = temp_mat; } else if ( v->proj ) { tr_mat_mul(v->proj,identmat,temp_mat,SDIM,v->freedom,vv->freedom); mat = temp_mat; } else if ( vv->proj ) mat = vv->proj; else mat = identmat; for ( jj = 0 ; jj < v->freedom ; jj++ ) for ( kk = 0 ; kk < vv->freedom ; kk++ ) { col = vv->rownum + kk; row = v->rownum+jj; mlist[count].col = col; mlist[count].row = row; mlist[count++].val = v->slant*vv->slant*mat[jj][kk]*weights[i][k]; } } } } } if ( count > maxcount ) {kb_error(1869, "Internal error in linear_metric setup Lagrange: count > maxcount", RECOVERABLE); } /* non-element stuff */ for ( i = 0 ; i < optparamcount ; i++ ) { mlist[count].row = S->optparamrowstart+i; mlist[count].col = S->optparamrowstart+i; mlist[count].val = 1.0; count++; } /* leave rest of diagonal zero in case of augmented hessian */ for ( i = S->optparamrowstart ; i < S->N ; i++ ) { /* make sure rest of diagonal exists, but no shift */ mlist[count].row = i; mlist[count].col = i; mlist[count].val = 0.0; count++; } /* sort and combine entries */ temp = (struct mentry *)temp_calloc(count,sizeof(struct mentry)); /* radix sort, first on columns */ /* count columns */ for ( i = 0 ; i < count ; i++ ) M->IA[mlist[i].col]++; for ( n = 0, total = 0 ; n < M->N ; n++ ) { int tmp = M->IA[n]; M->IA[n] = total; total += tmp; } M->IA[M->N] = total; /* sort into temp */ for ( i = 0 ; i < count ; i++ ) temp[M->IA[mlist[i].col]++] = mlist[i]; /* now sort on rows */ for ( n = 0; n < M->N ; n++ )M->IA[n] = 0; for ( i = 0 ; i < count ; i++ ) M->IA[temp[i].row]++; for ( n = 0, total = 0 ; n < M->N ; n++ ) { int tmp = M->IA[n]; M->IA[n] = total; total += tmp; } M->IA[M->N] = total; /* sort into mlist */ for ( i = 0 ; i < count ; i++ ) mlist[M->IA[temp[i].row]++] = temp[i]; /* compress */ for ( i = 1, j = 0 ; i < count ; i++ ) { if ( (mlist[j].row == mlist[i].row) && (mlist[j].col == mlist[i].col) ) mlist[j].val += mlist[i].val; else { mlist[++j] = mlist[i]; } } /* put into M structure */ count = j+1; M->JA = (int*)temp_calloc(count,sizeof(int)); M->A = (REAL *)temp_calloc(count,sizeof(REAL)); M->IA[0] = A_OFF; for ( j = 0 ; j < count ; j++ ) { M->IA[mlist[j].row+1] = j + 1 + A_OFF; M->JA[j] = mlist[j].col + A_OFF; M->A[j] = mlist[j].val; } M->IA[M->N] = count + A_OFF; /* free working space */ temp_free((char*)temp); temp_free((char*)mlist); if ( ii ) temp_free((char*)ii); if ( weights ) free_matrix(weights); if ( x ) free_matrix(x); } /* linear_metric_setup_lagrange() */ evolver-2.30c.dfsg/src/registry.c0000644000175300017530000001400111410765113017225 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************** * * File: registry.c * * Purpose: Declare lists of functions availiable for * symmetry groups. */ /**************************************************************** * * SYMMETRY GROUP FUNCTIONS * * Instructions to those who wish to add a new set of symmetry * functions: Write a source file (like quotient.c) with * the necessary symmetry functions. Be sure your functions * have unique names. Then create an additional structure in * the array sym_functions below. This array will be searched * for a name matching the symmetry group name in the datafile. * Add the name of your source file to the files in Makefile. * * Registry structure: (for documentation only; actual globally * visible declaration is in extern.h) */ #ifdef DONT_COMPILE_THIS struct sym_registry { char *name; /* to be matched in datafile */ int flags; /* see bits below */ void (*wrapper)(REAL *in,REAL *out,int wrap); /* point wrapper */ int (*compose)(int wrap1,int wrap2); /* group composition */ int (*inverse)(int wrap); /* group inverse */ void (*pullback)(REAL *x,REAL *xform,REAL *yform,int wrap); /* pullback of yform to xform at x */ } sym_register[]; /* flag bits */ /* for group with fixed points, which should be given attribute AXIAL_POINT */ #define HAS_FIXED_PTS 1 /* for group which has nontrivial form pullback */ #define NEED_FORM_UNWRAPPING 2 /* for group with period two points on rotation axis, which should be given attribute AXIAL_POINT */ #define DOUBLE_AXIAL 4 #endif /****************************************************************/ /****************************************************************/ /* Prototypes: For this registry to work, your functions must */ /* be declared before the registry. So add your declarations */ /* to the list below. */ /****************************************************************/ #include "include.h" /* Declarations, using appropriate typedefs */ /* Torus, from torus.c */ extern SYM_WRAP torus_wrap; extern WRAPTYPE torus_compose ARGS((WRAPTYPE,WRAPTYPE)); extern WRAPTYPE torus_inverse ARGS((WRAPTYPE)); extern SYM_FORM torus_form_pullback; /* XYZ Rotation, from quotient.c */ extern SYM_WRAP xyz_wrap; extern WRAPTYPE xyz_compose ARGS((WRAPTYPE,WRAPTYPE)); extern WRAPTYPE xyz_inverse ARGS((WRAPTYPE)); extern SYM_FORM xyz_form_pullback; /* Rotation, from quotient.c */ extern SYM_WRAP rot_wrap; extern WRAPTYPE rot_compose ARGS((WRAPTYPE,WRAPTYPE)); extern WRAPTYPE rot_inverse ARGS((WRAPTYPE)); extern SYM_FORM rot_form_pullback; /* Cube point group, from quotient.c */ extern SYM_WRAP pgcube_wrap; extern WRAPTYPE pgcube_compose ARGS((WRAPTYPE,WRAPTYPE)); extern WRAPTYPE pgcube_inverse ARGS((WRAPTYPE)); extern SYM_FORM pgcube_form_pullback; /* Full cube lattice group, from quotient.c */ extern SYM_WRAP cubel_wrap; extern WRAPTYPE cubel_compose ARGS((WRAPTYPE,WRAPTYPE)); extern WRAPTYPE cubel_inverse ARGS((WRAPTYPE)); extern SYM_FORM cubel_form_pullback; /* Flip-Rotation, from quotient.c */ extern SYM_WRAP frot_wrap; extern SYM_FORM frot_form_pullback; /* Central symmetry group, from quotient.c */ extern SYM_WRAP central_wrap; extern WRAPTYPE central_compose ARGS((WRAPTYPE,WRAPTYPE)); extern WRAPTYPE central_inverse ARGS((WRAPTYPE)); extern SYM_FORM central_form_pullback; /* Genus 2 hyperbolic space, Klein model, from khyp.c */ extern SYM_WRAP khyp_wrap; extern WRAPTYPE khyp_compose ARGS((WRAPTYPE,WRAPTYPE)); extern WRAPTYPE khyp_inverse ARGS((WRAPTYPE)); extern SYM_FORM khyp_form_pullback; /* hyperbolic space, Klein model, from dodecGroup.c (Ken Bromberg) */ extern SYM_WRAP dodec_wrap; extern WRAPTYPE dodec_compose ARGS((WRAPTYPE,WRAPTYPE)); extern WRAPTYPE dodec_inverse ARGS((WRAPTYPE)); extern SYM_FORM dodec_form_pullback; /* screw rotation, from quotient.c */ extern SYM_WRAP screw_wrap; extern WRAPTYPE screw_compose ARGS((WRAPTYPE,WRAPTYPE)); extern WRAPTYPE screw_inverse ARGS((WRAPTYPE)); extern SYM_FORM screw_form_pullback; /* quarter_turn torus, from quotient.c */ extern SYM_WRAP quarter_turn_wrap; extern WRAPTYPE quarter_turn_compose ARGS((WRAPTYPE,WRAPTYPE)); extern WRAPTYPE quarter_turn_inverse ARGS((WRAPTYPE)); extern SYM_FORM quarter_turn_form_pullback; /* symmetry function registry array */ struct sym_registry sym_register[] = { {"torus",0,torus_wrap,torus_compose,torus_inverse,torus_form_pullback}, {"genus2",NEED_FORM_UNWRAPPING, khyp_wrap,khyp_compose,khyp_inverse,khyp_form_pullback}, {"dodecahedron",NEED_FORM_UNWRAPPING, dodec_wrap,dodec_compose,dodec_inverse,dodec_form_pullback}, {"xyz",HAS_FIXED_PTS|NEED_FORM_UNWRAPPING, xyz_wrap,xyz_compose,xyz_inverse,xyz_form_pullback}, {"rotate",HAS_FIXED_PTS|NEED_FORM_UNWRAPPING, rot_wrap,rot_compose,rot_inverse,rot_form_pullback}, {"cubocta",HAS_FIXED_PTS|NEED_FORM_UNWRAPPING, pgcube_wrap,pgcube_compose,pgcube_inverse,pgcube_form_pullback}, {"cubelat",HAS_FIXED_PTS|NEED_FORM_UNWRAPPING, cubel_wrap,cubel_compose,cubel_inverse,cubel_form_pullback}, {"flip_rotate",HAS_FIXED_PTS|DOUBLE_AXIAL|NEED_FORM_UNWRAPPING, frot_wrap,rot_compose,rot_inverse,frot_form_pullback}, {"central_symmetry",HAS_FIXED_PTS|NEED_FORM_UNWRAPPING, central_wrap,central_compose,central_inverse,central_form_pullback}, {"screw_symmetry",HAS_FIXED_PTS|NEED_FORM_UNWRAPPING, screw_wrap,screw_compose,screw_inverse,screw_form_pullback}, {"quarter_turn",NEED_FORM_UNWRAPPING, quarter_turn_wrap,quarter_turn_compose,quarter_turn_inverse, quarter_turn_form_pullback}, {NULL,0,NULL,NULL,NULL,NULL} /* NULL name ends search */ }; evolver-2.30c.dfsg/src/hessian.c0000644000175300017530000022766211410765113017032 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************* * * File: hessian.c * * Purpose: Main file for hessian commands. * */ #define GDEBUG int hess_debug; /* debugging flag */ /* use ysmp */ #include "include.h" #include "f2c.h" int hmode; /* mode of motion */ struct hess_verlist *vhead = NULL; /* main vertex list */ int vhead_count; /* total number of vertices */ #ifdef XXX REAL *rhs = NULL; /* right side of augmented matrix */ REAL *X = NULL; /* solution to augmented matrix */ #endif REAL *conrhs = NULL; /* right side of augmented matrix for constraints */ int vertex_rows; /* number of rows for vertex degrees of freedom */ REAL **vproj_base; /* vertex projection matrix row pointers */ REAL *vproj_space; /* vertex projection matrix arena */ REAL ***conhess_base; /* vertex constraint hessian arena */ REAL rhs_norm; static int ritz_done_flag; REAL **ritzvecs; static int ritzdim; void optparamhess ARGS((struct linsys *,REAL *)); /* right side */ #ifdef OLDHASH /******************************************************************** * * function: hess_hash_search() * * purpose: Finds existing entry or allocates entry. * Installs key values, and adds hessian value. * return: Pointer to entry. */ void hess_hash_search(col,row,value) int row,col; /* meant to do upper triangle */ REAL value; /* value to add */ { struct hess_entry *e; int spot; if ( row > col ) return; if ( (value == 0.0) && (row != col) ) return; if ( col >= S->A_rows ) /* a constraint grad */ { if ( col == goner ) return; if ( !array[col].u.congrad ) array[col].u.congrad = (REAL *)mycalloc(S->A_rows,sizeof(REAL)); e = (struct hess_entry *)(array[col].u.congrad+row); e->value += value; if ( !sparse_constraints_flag ) return; } if ( hashcount >= max_fill ) hess_hash_expand(); /* search hash table */ spot = hash(row,col) % table_size; e = hashtable + spot; while ( e->row != HASHEMPTY ) { if ( (e->row == row) && (e->col == col) ) { e->value += value; return; } spot++; if ( spot >= table_size ) spot -= table_size; e = hashtable + spot; hash_extraprobes++; } /* if here, then have empty slot and need to insert */ e->col = col; e->row = row; hashcount++; e->value = value; } #endif /*********************************************************************** * * function: fill_grad() * * purpose: process gradient of function at constraint */ void fill_grad(S,v,grad,rhs) struct linsys *S; struct hess_verlist *v; REAL *grad; REAL *rhs; { REAL g[MAXCOORD]; int k,a,b; if ( rhs_flag ) { if ( v->proj ) { vec_mat_mul(grad,v->proj,g,SDIM,v->freedom); for ( k = 0 ; k < v->freedom ; k++ ) rhs[v->rownum+k] -= g[k]; } else for ( k = 0 ; k < v->freedom ; k++ ) rhs[v->rownum+k] -= grad[k]; } if ( hess_flag && v->conhess ) { for ( a = 0 ; a < v->freedom ; a++ ) for ( b = 0 ; b <= a ; b++ ) sp_hash_search(S,v->rownum+b,v->rownum+a, SDIM_dot(grad,v->conhess[a][b])); } } /************************************************************************* * * function: fill_self_entry() * * purpose: fill proper hessian matrix spot for single vertex second derivs */ void fill_self_entry(S,v_id,self) struct linsys *S; vertex_id v_id; REAL **self; /* full dim values */ { int j,k; struct hess_verlist *v; v = get_vertex_vhead(v_id); if ( v->proj ) { MAT2D(temp_mat,MAXCOORD,MAXCOORD); MAT2D(temp_mat2,MAXCOORD,MAXCOORD); tr_mat_mul(v->proj,self,temp_mat,SDIM,v->freedom,SDIM); mat_mult(temp_mat,v->proj,temp_mat2,v->freedom,SDIM,v->freedom); for ( j = 0 ; j < v->freedom ; j++ ) for ( k = 0 ; k <= j ; k++ ) sp_hash_search(S,v->rownum+k,v->rownum+j, temp_mat2[j][k]); } else { for ( j = 0 ; j < v->freedom ; j++ ) for ( k = 0 ; k <= j ; k++ ) sp_hash_search(S,v->rownum+k,v->rownum+j,self[j][k]); } } /************************************************************************* * * function: fill_mixed_entry() * * purpose: fill proper hessian matrix spot for mixed vertex second derivs */ void fill_mixed_entry(S,v_id1,v_id2,mixed) struct linsys *S; vertex_id v_id1,v_id2; REAL **mixed; /* full dim values */ { int k,j; REAL **oo; MAT2D(temp_mat,MAXCOORD,MAXCOORD); MAT2D(temp_mat2,MAXCOORD,MAXCOORD); struct hess_verlist *v1,*v2; if ( equal_id(v_id1,v_id2) ) { fill_self_entry(S,v_id1,mixed); return; } v1 = get_vertex_vhead(v_id1); v2 = get_vertex_vhead(v_id2); if ( v1->proj ) { tr_mat_mul(v1->proj,mixed,temp_mat,SDIM,v1->freedom,SDIM); oo = temp_mat; } else oo = mixed; if ( v2->proj ) { mat_mult(oo,v2->proj,temp_mat2,v1->freedom,SDIM,v2->freedom); oo = temp_mat2; } if ( v1->rownum < v2->rownum ) for ( j = 0 ; j < v1->freedom ; j++ ) for ( k = 0 ; k < v2->freedom ; k++ ) sp_hash_search(S,v1->rownum+j,v2->rownum+k,oo[j][k]); else for ( j = 0 ; j < v1->freedom ; j++ ) for ( k = 0 ; k < v2->freedom ; k++ ) sp_hash_search(S,v2->rownum+k,v1->rownum+j,oo[j][k]); } #ifdef OLDHASH /******************************************************************** * * function: hess_hash_sort() * * purpose: Create sorted list of hessian entries, row major order * Does 2-digit radix sort on row,col with count sort on each. * input: total_rows - upper bound for row,col. * output: *nptr - number of entries * **listptr - allocated list (temp_calloc) */ void hess_hash_sort(hessian_rows,nptr,listptr) int hessian_rows; /* rows in hessian */ int *nptr; /* for return of total entries */ struct hess_entry **listptr; /* list in, for return of sorted list */ { int i; struct hess_entry *e; int *counts; int *starts; int sum,oldsum; counts = (int *)temp_calloc(2*hessian_rows+1,sizeof(int)); starts = counts + hessian_rows; /* count entries in row */ for ( i = 0, e = hashtable ; i < table_size ; i++, e++ ) if ( e->row != HASHEMPTY ) counts[e->row]++; for ( i = 0 ; i < hessian_rows ; i++ ) array[i].count = counts[i]; /* get starting points of each row */ for ( i = 0, sum = 0 ; i < hessian_rows ; i++ ) { oldsum = sum; sum += counts[i]; counts[i] = starts[i] = oldsum; } starts[hessian_rows] = sum; /* sort into row bins */ for ( i = 0, e = hashtable ; i < table_size ; i++,e++ ) { struct hess_entry eorig; struct hess_entry etmp; if ( e->row == HASHEMPTY ) continue; if ( i < counts[e->row] ) continue; eorig = *e; e->row = HASHEMPTY; do { /* follow chain of replacements */ int spot; int k; spot = counts[eorig.row]; etmp = hashtable[spot]; /* insertion sort into row */ for ( k = spot ; k > starts[eorig.row] ; k-- ) { if ( hashtable[k-1].col > eorig.col ) hashtable[k] = hashtable[k-1]; else break; } hashtable[k] = eorig; counts[eorig.row]++; eorig = etmp; } while ( eorig.row != HASHEMPTY ); /* stop when get back to empty */ } *listptr = hashtable; *nptr = hashcount; temp_free((char*)counts); hash_per_row = 1 + (5*hashcount)/(4*S->A_rows+1); /* for next time */ if ( !hessian_quiet_flag ) { sprintf(msg,"Hessian entries: %d Final hashtable size: %d next hash_per_row: %d.\n",hashcount, table_size,hash_per_row); outstring(msg); sprintf(msg,"Hash extra probes: %d\n",hash_extraprobes); outstring(msg); } } /******************************************************************** * * function: hess_hash_end() * * purpose: Deallocate hessian hash table. */ void hess_hash_end() { if ( hashtable ) myfree((char*)hashtable); hashtable = NULL; } #endif /******************************************************************** End Hessian hash routines. ********************************************************************/ /************************************************************** * * Function: hessian_auto() * * Purpose: Global minimization of energy by finding * critical point of quadratic approximation * to total energy function. * Using only lower left triangle of symmetric matrix. * Uses whatever sparse matrix solver is in effect at the moment. * */ void hessian_auto() /* automatic hessian */ { REAL old_energy = web.total_energy; REAL *rhs = NULL; REAL *X = NULL; struct linsys S; hmode = hessian_normal_flag; hessian_cleanup(); /* cleanup from previous round */ hessian_init(&S,&rhs); hess_flag = 1; rhs_flag = 1; hessian_fill(&S,&rhs); sp_Hessian_solver(&S,rhs,&X); hessian_move((REAL)1.0,ACTUAL_MOVE,X); if ( check_increase_flag && (web.total_energy > (1+100*machine_eps)*old_energy) ) { kb_error(1596,"Hessian move would have raised energy. Restoring coords.\n", WARNING); restore_coords(&saved,SAVE_IN_ATTR); } hessian_exit(X); free_system(&S); temp_free((char*)rhs); temp_free((char*)X); } /************************************************************** * * Function: hessian_seek() * * Purpose: Same as hessian_auto(), but uses direction of minimum * as seek direction, and moves down it. * * Return: best stepsize */ REAL hessian_seek(maxscale) /* automatic hessian */ REAL maxscale; { REAL best_scale; REAL *rhs=NULL,*X=NULL; struct linsys S; hmode = hessian_normal_flag; hessian_init(&S,&rhs); hess_flag = 1; rhs_flag = 1; hessian_fill(&S,&rhs); if ( hessian_linear_metric_flag ) linear_metric_setup(&S,&Met); else if ( web.area_norm_flag ) star_metric_setup(&S,&Met); sp_Hessian_solver(&S,rhs,&X); best_scale = hessian_line_seek(&S,maxscale,X); hessian_exit(X); free_system(&S); if ( hessian_linear_metric_flag || web.area_norm_flag ) free_system(&Met); temp_free((char*)rhs); temp_free((char*)X); return best_scale; } /***************************************************************************** * * function: square_grad() * * purpose: calculates gradients for rhs and returns square of gradient * */ REAL square_grad() { REAL sum; vertex_id v_id; REAL *f; int i; int oldflag = min_square_grad_flag; calc_all_grads(CALC_VOLGRADS); volume_restore(1.,ACTUAL_MOVE); min_square_grad_flag = 0; /* so calc_force does regular forces */ calc_force(); min_square_grad_flag = oldflag; pressure_forces(); fix_vertices(); /* project forces to constraints, etc */ calc_lagrange(); lagrange_adjust(); vgrad_end(); sum = 0.0; FOR_ALL_VERTICES(v_id) { REAL star; f = get_force(v_id); if ( hessian_linear_metric_flag ) star = Met.apinv[get_vertex_vhead(v_id)->rownum]; else star = 1.0; if ( hessian_normal_flag ) { struct hess_verlist *v; /* current vertex */ REAL r[MAXCOORD]; REAL *rr = r; v = get_vertex_vhead(v_id); vec_mat_mul(f,v->proj,rr,SDIM,v->freedom); sum += dot(r,r,v->freedom)*star; } else sum += SDIM_dot(f,f)*star; } for ( i = 0 ; i < optparamcount ; i++ ) sum += optparam[i].grad*optparam[i].grad; return sum; } /***************************************************************************** * * function: square_grad_forces() * * purpose: calculates gradients for rhs and returns square of gradient * and calculates forces as hessian times rhs. * */ void square_grad_forces() { REAL *rhs=NULL,*X=NULL; struct linsys S; hmode = hessian_normal_flag; hessian_init(&S,&rhs); hess_flag = 1; rhs_flag = 1; hessian_fill(&S,&rhs); /* ? if ( hessian_linear_metric_flag ) linear_metric_setup(&S,&Met); else if ( web.area_norm_flag ) star_metric_setup(&S,&Met); */ X = (REAL*)temp_calloc(S.N,sizeof(REAL)); sp_hessian_mult(&S,rhs,X); hessian_move(0,SET_VELOCITY,X); hessian_exit(X); free_system(&S); if ( hessian_linear_metric_flag || web.area_norm_flag ) free_system(&Met); temp_free((char*)rhs); temp_free((char*)X); } /***************************************************************************** * * function: hessian_line_seek() * * purpose: seek optimum motion in downhill direction, after downhill * found. * save_coords() assumed to have been done in hessian_init(); * step size bounded by web.max_scale. * If input parameter maxscale is nonzero, will obey. * * return: optimal stepsize */ REAL hessian_line_seek(S,maxscale,X) struct linsys *S; REAL maxscale; REAL *X; { REAL scale0=0.0,scale1,scale2=0.0; REAL energy0,energy1,energy2=0.0; int seekcount = 0; int dirflag = 1; /* for positive or negative direction */ REAL stepsize; REAL typescale; if ( maxscale <= 0.0 ) return 0.0; typescale = maxscale/100; ENTER_GRAPH_MUTEX; /* base energy; may be volume adjustments, so can't just take current values */ hessian_move(0.0,ACTUAL_MOVE/*TEST_MOVE*/,X); energy0 = min_square_grad_flag ? square_grad(): web.total_energy; restore_coords(&saved,SAVE_IN_ATTR); if ( itdebug ) { sprintf(msg,"stepsize 0 energy0 %18.15g\n",energy0); outstring(msg); } hessian_move(typescale*1e-3,ACTUAL_MOVE/*TEST_MOVE*/,X); energy1 = min_square_grad_flag ? square_grad(): web.total_energy; restore_coords(&saved,SAVE_IN_ATTR); if ( itdebug ) { sprintf(msg,"stepsize %f energy1 %18.15g\n",typescale*1e-3,energy1); outstring(msg); } hessian_move(-typescale*1e-3,ACTUAL_MOVE/*TEST_MOVE*/,X); energy2 = min_square_grad_flag ? square_grad(): web.total_energy; restore_coords(&saved,SAVE_IN_ATTR); if ( itdebug ) { sprintf(msg,"stepsize %f energy2 %18.15g\n",-typescale*1e-3,energy2); outstring(msg); } LEAVE_GRAPH_MUTEX; if ( (energy1 >= energy0) && (energy2 >= energy0) ) return 0.0; if ( energy1 > energy2 ) dirflag = -1; else dirflag = 1; ENTER_GRAPH_MUTEX; stepsize = scale1 = 0.499999999*typescale; hessian_move(dirflag*stepsize,ACTUAL_MOVE/*TEST_MOVE*/,X); energy1 = min_square_grad_flag ? square_grad(): web.total_energy; restore_coords(&saved,SAVE_IN_ATTR); if ( itdebug ) { sprintf(msg,"stepsize %f energy1 %18.15g\n",dirflag*stepsize,energy1); outstring(msg); } if ( energy1 < energy0 ) { do { stepsize *= 2; if ( stepsize > maxscale ) { stepsize = maxscale; /*break;*/ } hessian_move(dirflag*stepsize,ACTUAL_MOVE/*TEST_MOVE*/,X); energy2 = min_square_grad_flag ? square_grad(): web.total_energy; scale2 = stepsize; restore_coords(&saved,SAVE_IN_ATTR); if ( itdebug ) { sprintf(msg,"stepsize %f energy2 %18.15g\n",dirflag*stepsize,energy2); outstring(msg); } if ( energy2 > energy1 ) { stepsize /= 2; break; } else { energy0 = energy1; scale0 = scale1; energy1 = energy2; scale1 = scale2; } } while ( stepsize < maxscale ); } else /* energy1 >= energy0 */ { seekcount = 0; do { if ( seekcount++ > 20 ) { stepsize = 0.0; break; } energy2 = energy1; scale2 = scale1; stepsize = scale2/2; hessian_move(dirflag*stepsize,ACTUAL_MOVE/*TEST_MOVE*/,X); energy1 = min_square_grad_flag ? square_grad(): web.total_energy; scale1 = stepsize; restore_coords(&saved,SAVE_IN_ATTR); if ( itdebug ) { sprintf(msg,"stepsize %f energy1 %18.15g\n",dirflag*stepsize,energy1); outstring(msg); } } while ( energy1 > energy0 ); } if ( (stepsize > 0.0) && (energy2 > energy1) /*(stepsize < maxscale)*/ ) { REAL denom; /* now quadratic interpolation for minimum energy */ denom = energy0*(scale1-scale2)+energy1*(scale2-scale0) + energy2*(scale0 - scale1); if ( denom == 0.0 ) stepsize = 0.0; else { stepsize = ((energy0-energy2)*scale1*scale1 +(energy1-energy0)*scale2*scale2 +(energy2-energy1)*scale0*scale0)/2/denom; } if ( stepsize < scale0 ) stepsize = scale0; if ( stepsize > scale2 ) stepsize = scale2; } else if ( stepsize < 0.0 ) stepsize = 0.0; else if ( stepsize > maxscale ) stepsize = maxscale; hessian_move(dirflag*stepsize,ACTUAL_MOVE,X); LEAVE_GRAPH_MUTEX; if ( min_square_grad_flag ) { energy1 = square_grad(); #ifdef LONGDOUBLE sprintf(msg,"square gradient: %3.*Lg\n",DPREC,energy1); #else sprintf(msg,"square gradient: %3.15g\n",energy1); #endif outstring(msg); } return dirflag*stepsize; } /* end hessian_line_seek() */ /*************************************************************************** * * function: hessian_saddle() * * purpose: move downhill optimally if at saddle point */ void hessian_saddle(maxscale) REAL maxscale; { REAL low; REAL stepsize; REAL *rhs=NULL,*X=NULL; struct linsys S; hmode = hessian_normal_flag; hessian_init(&S,&rhs); hess_flag = 1; rhs_flag = 1; hessian_fill(&S,&rhs); (*sp_AIJ_setup_func)(S.A_rows,&S); (*sp_constraint_setup_func) (web.skel[BODY].max_ord+1 + gen_quant_count,&S); if ( sp_ordering_func ) (*sp_ordering_func)(&S); if ( hessian_linear_metric_flag ) { linear_metric_setup(&S,&Met); } else if ( web.area_norm_flag ) { star_metric_setup(&S,&Met); } X = (REAL*)temp_calloc(S.N+S.concount,sizeof(REAL)); low = lowest_eigenpair(&S,X); if ( low >= 0.0 ) { #ifdef LONGDOUBLE sprintf(msg,"Lowest eigenvalue %2.*Lg. Not a saddle point.\n",DPREC,low); #else sprintf(msg,"Lowest eigenvalue %2.15g. Not a saddle point.\n",low); #endif outstring(msg); goto saddle_exit; } #ifdef LONGDOUBLE sprintf(msg,"Lowest eigenvalue %2.*Lg\n",DPREC,low); #else sprintf(msg,"Lowest eigenvalue %2.15g\n",low); #endif outstring(msg); stepsize = hessian_line_seek(&S,maxscale,X); sprintf(msg,"stepsize %g\n",(DOUBLE)stepsize); outstring(msg); #ifdef LONGDOUBLE sprintf(msg,"1. energy: %17.*Lg \n", DPREC,web.total_energy); #else sprintf(msg,"1. %s: %17.15g energy: %17.15g \n", areaname,web.total_area,web.total_energy); #endif outstring(msg); update_display(); reset_conj_grad(); saddle_exit: hessian_exit(X); /* cleanup */ free_system(&S); if ( hessian_linear_metric_flag || web.area_norm_flag ) free_system(&Met); temp_free((char*)rhs); temp_free((char*)X); } /* end hessian_saddle() */ /*************************************************************************** * * function: print_hessian_menu() * * purpose: Prints full menu in hessian_menu command. */ void print_hessian_menu() { outstring("\n"); outstring("1. Fill in hessian matrix.\n"); outstring("2. Fill in right side. (Do 1 first)\n"); outstring("3. Solve. (Do 2 first)\n"); outstring("4. Move. (Do 3,V,E,F,C,B or X first)\n"); outstring("7. Restore original coordinates.\n"); outstring("9. Toggle debugging. (Don't do this!)\n"); outstring("B. Chebychev (For Hessian solution ).\n"); outstring("C. Chebychev (For most negative eigenvalue eigenvector).\n"); outstring("E. Lowest eigenvalue. (By factoring. Do 1 first)\n"); outstring("F. Lowest eigenvalue. (By conjugate gradient. Do 1 first)\n"); outstring("L. Lanczos. (Finds eigenvalues near probe value. )\n"); outstring("R. Lanczos with selective reorthogonalization.\n"); outstring("Z. Ritz subspace iteration for eigenvalues. (Do 1 first)\n"); outstring("X. Pick Ritz vector for motion. (Do Z first)\n"); outstring("P. Eigenvalue probe. (By factoring. Do 1 first)\n"); outstring("V. Eigenvalue probe with eigenvector. (By factoring. Do 1 first)\n"); outstring("S. Seek along direction. (Do 3,V,E,F,C,B or X first)\n"); outstring("Y. Toggle YSMP/alternate minimal degree factoring.\n"); outstring("U. Toggle Bunch-Kaufman version of min deg.\n"); outstring("M. Toggle projecting to global constraints in move. (Leave this alone!)\n"); outstring("G. Toggle minimizing square gradient in seek.\n"); outstring("D. Dump Hessian to text file.\n"); outstring("=. Execute Evolver commands in subprompt.\n"); #ifdef MPI_EVOLVER outstring("A. Build corona (MPI version only).\n"); outstring("J. Dissolve corona (MPI version only).\n"); outstring("I. Free discard lists (MPI version only).\n"); #endif outstring("0. Exit hessian.\n"); } /*********************************************************************** * * function: hessian_menu() * * purpose: give user more interactive control over Hessian move * and debugging. */ void hessian_menu() { char response[30]; int left_flag = 0; /* whether hessian filled */ int right_flag = 0; /* whether rhs filled in */ int dir_flag = 0 ; /* whether direction filled in */ REAL stepsize; REAL *rhs = NULL,*X = NULL; struct linsys S; hessian_legal(); save_coords(&saved,SAVE_IN_ATTR); memset(&S,0,sizeof(struct linsys)); hess_debug = 0; hmode = hessian_normal_flag; for(;;) { #ifdef MPI_EVOLVER prompt("Choice(?,1,2,3,4,7,9,B,C,E,F,L,R,Z,X,P,V,S,Y,U,M,G,D,A,=,0,q): ", response,sizeof(response)); #else prompt("Choice(?,1,2,3,4,7,9,B,C,E,F,L,R,Z,X,P,V,S,Y,U,M,G,D,=,0,q): ", response,sizeof(response)); #endif switch ( toupper(response[0]) ) { case '?': print_hessian_menu(); break; case '1': hessian_cleanup(); /* cleanup from previous round */ hessian_init(&S,&rhs); hess_flag = 1; rhs_flag = 1; hessian_fill(&S,&rhs); X = (REAL*)temp_calloc(S.N,sizeof(REAL)); (*sp_AIJ_setup_func)(S.A_rows,&S); (*sp_constraint_setup_func) ( web.skel[BODY].max_ord+1 + gen_quant_count,&S); if ( sp_ordering_func ) (*sp_ordering_func)(&S); if ( hessian_linear_metric_flag ) { linear_metric_setup(&S,&Met); } else if ( web.area_norm_flag ) { star_metric_setup(&S,&Met); } left_flag = 1; break; case '2': if ( left_flag == 0 ) { outstring("Error: Must do step 1 first.\n"); break; } if ( hess_debug ) { int i; for ( i = 0 ; i < S.N ; i++ ) printf("B[%d] %20.15f\n",i,(DOUBLE)(rhs[i])); } rhs_norm = dot(rhs,rhs,S.N); #ifdef LONGDOUBLE sprintf(msg,"RHS norm: %20.*Lg\n",DPREC, rhs_norm); #else sprintf(msg,"RHS norm: %20.15g\n", rhs_norm); #endif outstring(msg); right_flag = 1; break; case '3': if ( left_flag == 0 ) { outstring("Error: Must do step 1 first.\n"); break; } if ( right_flag == 0 ) { outstring("Error: Must do step 2 first.\n"); break; } if ( !(S.flags & S_FACTORED) ) sp_factor(&S); if ( !(S.flags & S_PROJECTED)) (*sp_hess_project_setup_func)(&S); sp_hessian_solve(&S,rhs,X,SET_PRESSURE); outstring("Motion found. Pressures adjusted.\n"); dir_flag = 1; break; case '4': if ( dir_flag == 0 ) { outstring("Error: Must do step 3,V,E,F,C,B or X first.\n"); break; } prompt("Step size: ",response,sizeof(response)); stepsize = atof(response); hessian_move(stepsize,ACTUAL_MOVE,X); #ifdef LONGDOUBLE sprintf(msg,"1. energy: %*.*Lg \n",DWIDTH,DPREC,web.total_energy); #else sprintf(msg,"1. %s: %17.15f energy: %17.15f \n", areaname,web.total_area,web.total_energy); #endif outstring(msg); update_display(); break; case '7': restore_coords(&saved,SAVE_IN_ATTR); update_display(); break; case '8': if ( left_flag == 0 ) { outstring("Error: Must do step 1 first.\n"); break; } write_hessian(&S); break; case '=': hessian_subshell_flag = 1; subshell_depth++; setjmp(jumpbuf[subshell_depth]); /* command read and execute loop */ exec_commands(NULL,"hessian_menu subcommand: "); subshell_depth-- ; hessian_subshell_flag = 0; break; case 'Q': case '0': case 0: hessian_exit(X); free_system(&S); if ( hessian_linear_metric_flag || web.area_norm_flag ) free_system(&Met); if ( rhs ) temp_free((char*)rhs); if ( X ) temp_free((char*)X); return; case '9': hess_debug = !hess_debug; if ( hess_debug ) outstring("Hessian debug ON.\n"); else outstring("Hessian debug OFF.\n"); break; case 'K': /* experimdntal multigrid */ if ( left_flag == 0 ) { outstring("Error: Must do step 1 first.\n"); break; } if ( right_flag == 0 ) { outstring("Error: Must do step 2 first.\n"); break; } do_multigrid(&S); break; case 'Z': /* ritz */ { REAL probe; if ( left_flag == 0 ) { outstring("Error: Must do step 1 first.\n"); break; } prompt("Ritz probe value: ",response,sizeof(response)); probe = atof(response); prompt("Number of eigenvalues: ",response,sizeof(response)); ritzdim = atoi(response); if ( ritzvecs ) free_matrix(ritzvecs); ritzvecs = dmatrix(0,ritzdim-1,0,S.N+S.concount); do_ritz(&S,probe,ritzdim,ritzvecs); ritz_done_flag = 1; break; } case 'X': /* pick ritz vector for motion */ { int ritznum; char *spot; int i; if ( ritz_done_flag == 0 ) { outstring("Error: Must do step Z first.\n"); break; } sprintf(msg,"Eigenvector number (1 to %d): ",ritzdim); prompt(msg,response,sizeof(response)); /* parse response for possible linear combo of vectors */ for ( spot = response ; *spot ; spot++ ) *spot = tolower(*spot); if ( strchr(response,'v') ) { spot = response; memset((char*)X,0,S.N*sizeof(REAL)); while ( strchr(spot,'v') ) { REAL coeff; int sign = 1; while ( *spot==' ' || *spot=='+' || *spot=='-' ) { if ( *spot=='-' ) sign = -sign; spot++; } coeff = sign*atof(spot); spot = strchr(spot,'v')+1; ritznum = atoi(spot); while ( isdigit(*spot) ) spot++; if ( (ritznum < 0) || (ritznum > ritzdim) ) { outstring("Error: Invalid eigenvector number.\n"); break; } ritznum--; for ( i = 0 ; i < S.N ; i++ ) X[i] += coeff*ritzvecs[ritznum][i]; } } else { ritznum = atoi(response); if ( (ritznum < 0) || (ritznum > ritzdim) ) { outstring("Error: Invalid eigenvector number.\n"); break; } ritznum--; for ( i = 0 ; i < S.N ; i++ ) X[i] = ritzvecs[ritznum][i]; } dir_flag = 1; break; } case 'L': { int nprint,i; REAL evalues[NPRINT]; if ( left_flag == 0 ) { outstring("Error: Must do step 1 first.\n"); break; } if ( !(S.flags & S_FACTORED) ) sp_factor(&S); if ( !(S.flags & S_PROJECTED)) (*sp_hess_project_setup_func)(&S); nprint = lanczos(&S,KRYLOVDIM,evalues,NPRINT); /* list, ones near probe value */ for ( i = 0 ; i < nprint ; i++ ) { #ifdef LONGDOUBLE sprintf(msg,"%d %*.*Lf\n",i+1,DWIDTH,DPREC,evalues[i]); #else sprintf(msg,"%d %20.15f\n",i+1,evalues[i]); #endif outstring(msg); } } break; case 'R': { int nprint,i; REAL evalues[NPRINT]; if ( left_flag == 0 ) { outstring("Error: Must do step 1 first.\n"); break; } if ( !(S.flags & S_FACTORED) ) sp_factor(&S); if ( !(S.flags & S_PROJECTED)) (*sp_hess_project_setup_func)(&S); nprint = selective_lanczos(&S,KRYLOVDIM,evalues,NPRINT); /* list, ones near probe value */ for ( i = 0 ; i < nprint ; i++ ) { #ifdef LONGDOUBLE sprintf(msg,"%d %*.*Lf\n",i+1,DWIDTH,DPREC,evalues[i]); #else sprintf(msg,"%d %20.15f\n",i+1,evalues[i]); #endif outstring(msg); } } break; case 'C': if ( left_flag == 0 ) { outstring("Error: Must do step 1 first.\n"); break; } chebychev_ev(&S); dir_flag = 1; break; case 'B': if ( left_flag == 0 ) { outstring("Error: Must do step 1 first.\n"); break; } chebychev_hess(&S); dir_flag = 1; break; case 'P': /* probe for eigenvalues */ if ( left_flag == 0 ) { outstring("Error: Must do step 1 first.\n"); break; } bk_eigenprobe(&S); break; case 'V': /* probe for eigenvalues, with eigenvector */ if ( left_flag == 0 ) { outstring("Error: Must do step 1 first.\n"); break; } bk_inverse_it(&S,X); dir_flag = 1; break; case 'E': /* lowest eigenvalue */ { REAL low; if ( left_flag == 0 ) { outstring("Error: Must do step 1 first.\n"); break; } low = lowest_eigenpair(&S,X); #ifdef LONGDOUBLE sprintf(msg,"Lowest eigenvalue %2.*Lg\n",DPREC,low); #else sprintf(msg,"Lowest eigenvalue %2.15g\n",low); #endif outstring(msg); dir_flag = 1; break; } case 'd': /* lowest eigenvalue, by old conjugate gradient */ { REAL low; if ( left_flag == 0 ) { outstring("Error: Must do step 1 first.\n"); break; } low = old_cg_lowest_eigenpair(&S,X); #ifdef LONGDOUBLE sprintf(msg,"Lowest eigenvalue %2.*Lg\n",DPREC,low); #else sprintf(msg,"Lowest eigenvalue %2.15g\n",low); #endif outstring(msg); dir_flag = 1; break; } case 'F': /* lowest eigenvalue, by conjugate gradient */ { REAL low; if ( left_flag == 0 ) { outstring("Error: Must do step 1 first.\n"); break; } low = cg_lowest_eigenpair(&S,X); #ifdef LONGDOUBLE sprintf(msg,"Lowest eigenvalue %2.*Lg\n",DPREC,low); #else sprintf(msg,"Lowest eigenvalue %2.15g\n",low); #endif outstring(msg); dir_flag = 1; break; } case 'S': if ( dir_flag == 0 ) { outstring("Error: Must get direction first.\n"); break; } stepsize = hessian_line_seek(&S,10.0,X); sprintf(msg,"stepsize %g\n",(DOUBLE)stepsize); outstring(msg); #ifdef LONGDOUBLE sprintf(msg,"1. energy: %*.*Lg \n",DWIDTH,DPREC,web.total_energy); #else sprintf(msg,"1. %s: %17.15g energy: %17.15g \n", areaname,web.total_area,web.total_energy); #endif outstring(msg); update_display(); break; case 'Y': /* toggle YSMP */ if ( ysmp_flag != MINDEG_FACTORING ) { ysmp_flag = MINDEG_FACTORING; sp_mul_func = bk_mul; sp_AIJ_setup_func= bk_AIJ_setup; sp_constraint_setup_func = bk_constraint_setup; sp_hess_project_setup_func= BK_hess_project_setup; sp_factor_func = xmd_factor; sp_solve_func = xmd_solve; sp_solve_multi_func = xmd_solve_multi; sp_ordering_func = NULL; sp_CHinvC_func = sp_CHinvC; outstring("Using alternate minimal degree factoring.\n"); if ( left_flag ) outstring("Must do step 1 again.\n"); dir_flag = left_flag = right_flag = 0; } else { ysmp_flag = YSMP_FACTORING; sp_mul_func = bk_mul; sp_AIJ_setup_func= bk_AIJ_setup; sp_constraint_setup_func = bk_constraint_setup; sp_hess_project_setup_func= BK_hess_project_setup; sp_factor_func = ysmp_factor; sp_CHinvC_func = NULL; sp_solve_func = ysmp_solve; sp_solve_multi_func = ysmp_solve_multi; sp_ordering_func = NULL; outstring("Using YSMP.\n"); } break; case 'U': /* toggle BK version of min degree */ BK_flag = !BK_flag; if ( BK_flag ) outstring("Using Bunch-Kaufman version of minimal degree.\n"); else outstring("Using non-Bunch-Kaufman version of minimal degree.\n"); break; case 'M': hess_move_con_flag = !hess_move_con_flag; if ( hess_move_con_flag ) outstring("Projecting global constraints in move.\n"); else outstring("Not projecting global constraints in move.\n"); break; case 'G': min_square_grad_flag = !min_square_grad_flag; if ( min_square_grad_flag ) outstring("Minimizing square gradient in seek.\n"); else outstring("Minimizing regular energy in seek.\n"); break; case 'D': /* dump Hessian, 1-based indexing */ { FILE *fd; char hname[200]; size_t baselength; int i,j,k; gethname: outstring("Hessian dump. Note indexing is 1-based.\n"); prompt("Enter path and base name of Hessian dump files: ", hname,200); if ( hname[0] == 0 ) break; baselength = strlen(hname); strcat(hname,"_H.dat"); fd = fopen(hname,"w"); if ( fd == NULL ) { perror(hname); goto gethname; } sprintf(msg, "Dumping Hessian upper triangle in row col value format to %s.\n", hname); outstring(msg); for ( i = 0 ; i < S.N ; i++ ) for ( j = S.IA[i]-A_OFF ; j < S.IA[i+1]-A_OFF ; j++ ) { fprintf(fd,"%6d %6d %20.15f\n",i+1,S.JA[j]-A_OFF+1,S.A[j]); } fclose(fd); if ( ysmp_flag != MINDEG_FACTORING ) { outstring("Further dumping of factors in minimal degree mode (ysmp off) only.\n"); break; } if ( !S.LA ) { outstring("Need to do step 3, P, V, or Z before dumping factors.\n"); break; } strcpy(hname+baselength,"_L.dat"); sprintf(msg, "Dumping L factor in row col value format to %s.\n",hname); outstring(msg); if ( hname[0] == 0 ) break; fd = fopen(hname,"w"); if ( fd == NULL ) { perror(hname); break; } for ( i = 0 ; i < S.N ; i++ ) { fprintf(fd,"%6d %6d %20.15f\n",i+1,i+1,1.0); for ( j = S.LIA[i]+S.psize[i], k = S.LIJA[i]+S.psize[i] ; j < S.LIA[i+1] ; j++,k++ ) { fprintf(fd,"%6d %6d %20.15f\n",i+1,S.LJA[k]+1,S.LA[j]); } } fclose(fd); strcpy(hname+baselength,"_D.dat"); sprintf(msg, "Dumping diagonal factor in row col value format to %s.\n",hname); outstring(msg); if ( hname[0] == 0 ) break; fd = fopen(hname,"w"); if ( fd == NULL ) { perror(hname); break; } for ( i = 0 ; i < S.N ; i++ ) { j = S.LIA[i]; k = S.LIJA[i]; if ( S.psize[i] == 1 ) { fprintf(fd,"%6d %6d %20.15f\n",i+1,S.LJA[k]+1,S.LA[j]); } else { fprintf(fd,"%6d %6d %20.15f\n",i+1,S.LJA[k]+1,S.LA[j]); fprintf(fd,"%6d %6d %20.15f\n",i+1,S.LJA[k+1]+1,S.LA[j+1]); fprintf(fd,"%6d %6d %20.15f\n",S.LJA[k+1]+1,i+1,S.LA[j+1]); i++; j = S.LIA[i]; k = S.LIJA[i]; fprintf(fd,"%6d %6d %20.15f\n",i+1,S.LJA[k]+1,S.LA[j]); } } fclose(fd); if ( !augmented_hessian_mode ) { strcpy(hname+baselength,"_C.dat"); sprintf(msg, "Dumping constraint matrix in row col value format to %s.\n",hname); outstring(msg); fd = fopen(hname,"w"); if ( fd == NULL ) { perror(hname); break; } for ( i = 0 ; i < S.CN ; i++ ) { if ( S.CIA ) for ( j = S.CIA[i] ; j < S.CIA[i+1] ; j++ ) { fprintf(fd,"%6d %6d %20.15f\n",i+1,S.CJA[j]+1,S.CA[j]); } else /* dense */ for ( j = 0 ; j < S.N ; j++ ) if ( S.C[i][j] ) fprintf(fd,"%6d %6d %20.15f\n",i+1,j+1,S.C[i][j]); } fclose(fd); } strcpy(hname+baselength,"_P.dat"); sprintf(msg, "Dumping permutation in one line format to %s.\n",hname); outstring(msg); fd = fopen(hname,"w"); if ( fd == NULL ) { perror(hname); break; } for ( i = 0 ; i < S.N ; i++ ) { fprintf(fd,"%6d",S.P[i]+1); } fprintf(fd,"\n"); fclose(fd); } break; #ifdef MPI_EVOLVER case 'A' : mpi_hessian_fill_distrib(Mpi_RHS_MOVE); printf("Done fill. Enter shift: "); scanf("%lf",&S.lambda); mpi_hessian_factor_distrib(&S); printf("Done factoring. Inertia: pos %d, neg %d, zero %d\n", S.pos,S.neg,S.zero); mpi_solve_distrib(Mpi_LINSYS_HESSIAN, Mpi_RHS_MOVE, Mpi_SOLUTION_MOVE); mpi_hessian_distrib_move(1.0,ACTUAL_MOVE); #ifdef RANDOMRHS mpi_create_random_rhs(); printf("Created random rhs\n"); mpi_solve_distrib(Mpi_LINSYS_HESSIAN, Mpi_RHS_RANDOM, Mpi_SOLUTION_RANDOM); printf("Solved random rhs\n"); if ( S.lambda == 0.0 ) { mpi_mult_hess_distrib(); /* check */ printf("Multiplied solution by original\n"); } #endif break; #endif default : outstring("Illegal choice.\n"); } } } /* end hessian_menu() */ /************************************************************************** * * function: hessian_legal() * * purpose: see if Hessian is legal for this surface. */ void hessian_legal() { int i; body_id b_id; if ( !quantities_only_flag ) { if ( square_curvature_flag ) { if ( auto_convert_flag ) convert_to_quantities(); else kb_error(2077,"The type of squared mean curvature in effect does not permit Hessian.\n", RECOVERABLE); } if ( optparamcount > 0 ) { if ( auto_convert_flag ) convert_to_quantities(); else kb_error(2078, "Hessian for optimizing parameters requires convert_to_quantities.\n", RECOVERABLE); } if ( web.representation == SIMPLEX ) { if ( auto_convert_flag ) convert_to_quantities(); else kb_error(1597,"Hessian for simplex model requires convert_to_quantities.\n", RECOVERABLE); } for ( i = 0 ; i < web.maxcon ; i++ ) if ( get_constraint(i)->attr & CON_CONTENT ) { if ( auto_convert_flag ) { convert_to_quantities(); break; } else kb_error(1843,"Hessian for constraint content requires convert_to_quantities.\n",RECOVERABLE); } if ( web.symmetry_flag ) FOR_ALL_BODIES(b_id) { if ( get_battr(b_id) & FIXEDVOL ) { if ( auto_convert_flag ) { convert_to_quantities(); break; } else kb_error(1844,"Cannot do torus body volumes with hessian. Do convert_to_quantities.\n",RECOVERABLE); } } if ( web.modeltype != LINEAR ) { if ( auto_convert_flag ) convert_to_quantities(); else kb_error(1598,"Hessian for non-LINEAR models requires convert_to_quantities.\n", RECOVERABLE); } if ( web.representation == STRING ) { if ( auto_convert_flag ) convert_to_quantities(); else kb_error(1599, "Hessian for string model requires convert_to_quantities.\n", RECOVERABLE); } if ( web.gravflag && (web.grav_const != 0.0)) { if ( auto_convert_flag ) convert_to_quantities(); else kb_error(1601, "Hessian method for gravity requires convert_to_quantities.\n", RECOVERABLE); } if ( web.pressflag || web.pressure_flag ) { if ( auto_convert_flag ) convert_to_quantities(); else kb_error(1602,"Hessian with pressure in energy requires convert_to_quantities.\n", RECOVERABLE); } if ( web.wulff_flag ) kb_error(1603,"Cannot do Hessian method with crystalline integrand.\n", RECOVERABLE); if ( web.convex_flag ) kb_error(1604,"Cannot do gap energy Hessian, since it would need third derivatives.\n", RECOVERABLE); if ( web.metric_flag ) { if ( auto_convert_flag ) convert_to_quantities(); else kb_error(1605,"Hessian with Riemannian metric requires convert_to_quantities.\n", RECOVERABLE); } } if ( count_fixed_vol() && !pressure_set_flag && !web.pressure_flag ) kb_error(1606,"Pressures invalid. Do regular iteration before Hessian to set pressures.\n", RECOVERABLE); } /***************************************************************************** * * function: hessian_init() * * purpose: Allocate Hessian */ void hessian_init(S,rhs) struct linsys *S; REAL **rhs; /* right side, allocated by hessian_init */ { vertex_id v_id; int j,i,gam,a,b,m,n; struct hess_verlist *v; /* current vertex */ int currow = 0; MAT2D(grad,MAXCOORD,MAXCOORD); MAT2D(gradcopy,MAXCOORD,MAXCOORD); MAT2D(cc,MAXCOORD,MAXCOORD); MAT2D(pp,MAXCOORD,MAXCOORD); MAT2D(qq,MAXCOORD,MAXCOORD); MAT2D(gg,MAXCOORD,MAXCOORD); MAT3D(second,MAXCOORD,MAXCOORD,MAXCOORD); REAL dummy; int total_proj,vproj_count,total_conhess,vproj_alloc,conhess_alloc; int vproj_spot,vcount; REAL ***v_normal = NULL; memset(S,0,sizeof(struct linsys)); hessian_legal(); /* in case this doesn't work */ if ( saved.coord == NULL ) local_save_coords(&saved,SAVE_IN_ATTR); /* vertex attribute to hold vhead index */ vhead_attr = find_attribute(VERTEX,VHEAD_ATTR_NAME); if ( vhead_attr < 0 ) { int one = 1; vhead_attr = add_attribute(VERTEX,VHEAD_ATTR_NAME,INTEGER_TYPE, 0,&one,0,NULL); } vhead_count = web.skel[VERTEX].max_ord+1; vhead_count += optparamcount; vhead_count += gen_quant_count; if ( !everything_quantities_flag ) { vhead_count += web.skel[BODY].max_ord+1; bhead_attr = find_attribute(BODY,BHEAD_ATTR_NAME); if ( bhead_attr < 0 ) { int one = 1; bhead_attr = add_attribute(BODY,BHEAD_ATTR_NAME,INTEGER_TYPE, 0,&one,0,NULL); } } vhead = (struct hess_verlist*)temp_calloc(vhead_count, sizeof(struct hess_verlist)); if ( hmode == NORMAL_MOTION ) v_normal = dmatrix3(vhead_count,SDIM,SDIM); /* v_normal will use vhead_attr as index */ /* populate vertex list and count degrees of freedom */ total_proj = 0; /* count columns needed */ vproj_count = 0; /* number of vertices needing projection */ total_conhess = 0; /* count rows needed */ vcount = 0; MFOR_ALL_VERTICES(v_id) { conmap_t * conmap; int oncount; int kk; set_vertex_vhead(v_id,vcount); /* new way */ vcount++; if ( get_vattr(v_id) & FIXED ) continue; v = get_vertex_vhead(v_id); /* old way */ v->v_id = v_id; if ( hmode == SINGLE_DEGREE ) v->freedom = 1; else if ( get_vattr(v_id) & BOUNDARY ) { struct boundary *bdry = get_boundary(v_id); v->freedom = bdry->pcount; total_proj += v->freedom; vproj_count++; total_conhess += v->freedom; } else /* possible level set constraints */ { if ( hmode == NORMAL_MOTION ) { REAL **norm = get_vertex_v_normal(v_id); kk = new_calc_vertex_normal(v_id,norm); kk = project_vertex_normals(v_id,norm,kk); if ( kk < SDIM ) { v->freedom = kk; total_proj += kk; vproj_count++; } else v->freedom = SDIM; } else /* v-proj still identity */ v->freedom = SDIM; conmap = get_v_constraint_map(v_id); if ( conmap[0] ) { /* calculate size of projection matrix to fewer degrees of freedom */ for ( j = 1, oncount = 0; j <= (int)conmap[0] ; j++ ) { if ( !(conmap[j] & CON_HIT_BIT) ) continue; oncount++; } if ( v->freedom + oncount > SDIM ) { total_proj += v->freedom; vproj_count++; } /* now constraint hessian */ if ( oncount ) total_conhess += v->freedom; } } } /* end FOR_ALL_VERTICES first time through */ vproj_base = (REAL**)mycalloc(vproj_count*SDIM,sizeof(REAL*)); vproj_space = (REAL*)mycalloc(total_proj*SDIM,sizeof(REAL));; conhess_base = dmatrix3(total_conhess,SDIM-1,SDIM); vproj_spot = vproj_alloc = conhess_alloc = 0; /* populate vertex list vproj and conhess */ MFOR_ALL_VERTICES(v_id) { conmap_t * conmap; int oncount; v = get_vertex_vhead(v_id); /* v->rownum = currow; */ v->slant = 1.0; if ( v->freedom == 0 ) continue; if ( hmode == SINGLE_DEGREE ) {} else if ( get_vattr(v_id) & BOUNDARY ) { struct boundary *bdry = get_boundary(v_id); v->proj = vproj_base + vproj_alloc; vproj_alloc += SDIM; for ( j = 0 ; j < SDIM ; j++ ) { v->proj[j] = vproj_space + vproj_spot; vproj_spot+= v->freedom; } v->conhess = conhess_base + conhess_alloc; conhess_alloc += v->freedom; for ( j = 0 ; j < SDIM ; j++ ) { eval_second(bdry->coordf[j],get_param(v_id),bdry->pcount,&dummy, v->proj[j],second[j],v_id); for ( m = 0 ; m < bdry->pcount ; m++ ) for ( n = 0 ; n < bdry->pcount ; n++ ) v->conhess[m][n][j] = second[j][m][n]; } } else /* possible level set constraints */ { if (v->freedom < SDIM) /* copy over normal basis */ { REAL **vn = get_vertex_v_normal(v_id); v->proj = vproj_base + vproj_alloc; vproj_alloc += SDIM; for ( j = 0 ; j < SDIM ; j++ ) { v->proj[j] = vproj_space + vproj_spot; vproj_spot += v->freedom; } for ( j = 0 ; j < SDIM ; j++ ) for ( i = 0 ; i < v->freedom ; i++ ) v->proj[j][i] = vn[i][j]; } conmap = get_v_constraint_map(v_id); oncount = 0; if ( conmap[0] ) { /* calculate projection matrix to fewer degrees of freedom */ for ( j = 1; j <= (int)conmap[0] ; j++ ) { struct constraint *constr; if ( !(conmap[j] & CON_HIT_BIT) ) continue; constr = get_constraint(conmap[j]); eval_second(constr->formula,get_coord(v_id),SDIM, &dummy, grad[oncount],second[oncount],v_id); /* constraint value and derivs and hessian */ oncount++; } oncount = gram_schmidt(grad,oncount,SDIM); if ( (v->freedom < SDIM) && (oncount > 0) ) /* normal or something */ { /* project basis to constraints */ mat_mul_tr(grad,grad,gg,oncount,SDIM,oncount); mat_inv(gg,oncount); mat_mult(grad,v->proj,pp,oncount,SDIM,v->freedom); mat_mult(gg,pp,qq,oncount,oncount,v->freedom); tr_mat_mul(grad,qq,pp,oncount,SDIM,v->freedom); for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < v->freedom ; j++ ) v->proj[i][j] -= pp[i][j]; } else if ( oncount > 0 ) { v->freedom = SDIM - oncount; if ( v->freedom <= 0 ) continue; v->proj = vproj_base + vproj_alloc; vproj_alloc += SDIM; for ( j = 0 ; j < SDIM ; j++ ) { v->proj[j] = vproj_space + vproj_spot; vproj_spot += v->freedom; } for ( m = 0 ; m < oncount ; m++ ) for ( n = 0 ; n < SDIM ; n++ ) gradcopy[m][n] = grad[m][n]; kernel_basis(gradcopy,v->proj,oncount,SDIM); } } /* orthonormalize proj */ if ( v->proj ) { for ( i = 0 ; i < v->freedom ; i++ ) { /* subtract previous components */ REAL sum; for ( j = 0 ; j < i ; j++ ) { for ( n = 0, sum = 0.0 ; n < SDIM ; n++ ) sum += v->proj[n][j]*v->proj[n][i]; for ( n = 0 ; n < SDIM ; n++ ) v->proj[n][i] -= sum*v->proj[n][j]; } /* normalize */ for ( n = 0, sum = 0.0 ; n < SDIM ; n++ ) sum += v->proj[n][i]*v->proj[n][i]; sum = sqrt(sum); if ( sum > hessian_epsilon ) for ( n = 0 ; n < SDIM ; n++ ) v->proj[n][i] /= sum; else /* have to skip this direction */ { for ( n = 0 ; n < SDIM ; n++ ) v->proj[n][i] = v->proj[n][v->freedom-1]; v->freedom--; i--; } } if ( (hessian_normal_flag && (v->freedom == 1)) || hessian_double_normal_flag) { /* get cosine of normal and degree of freedom */ REAL *nor = get_vertex_v_normal(v_id)[0]; v->slant = 0.0; for ( i = 0 ; i < SDIM ; i++ ) v->slant += v->proj[i][0]*nor[i]; v->slant /= sqrt(SDIM_dot(nor,nor)); if ( v->slant < hessian_slant_cutoff ) v->freedom = 0; } } /* now constraint hessian */ if ( oncount ) { v->conhess = conhess_base + conhess_alloc; conhess_alloc += v->freedom; mat_tsquare(grad,cc,oncount,SDIM); mat_inv(cc,oncount); for ( gam = 0 ; gam < SDIM ; gam++ ) for ( a = 0 ; a < v->freedom ; a++ ) for ( b = 0 ; b < v->freedom ; b++ ) { REAL sum; for ( sum = 0.0, i = 0 ; i < oncount ; i++ ) for ( j = 0 ; j < oncount ; j++ ) for ( m = 0 ; m < SDIM ; m++ ) for ( n = 0 ; n < SDIM ; n++ ) sum += grad[j][gam]*cc[j][i]*v->proj[m][a]*second[i][m][n] *v->proj[n][b]; v->conhess[a][b][gam] = -sum; } } } /* end level set constraints */ if ( hessian_double_normal_flag && v->proj ) { REAL sum; int k; /* special kludge to give double-dimension perturbations the same normal space as regular dimensions. See spin4d.fe for example of use. */ for ( i = 0 ; i < v->freedom ; i++ ) { /* assume low dim normals are first */ for ( j = 0, sum = 0.0 ; j < SDIM/2 ; j++ ) sum += v->proj[j][i]*v->proj[j][i]; if ( sum < 1e-6 ) break; } /* copy over low dim basis to high dim, with transform */ for ( k = 0 ; k < i ; k++ ) for ( j = 0 ; j < SDIM/2 ; j++ ) { v->proj[j][i+k] = v->proj[j+SDIM/2][k]; v->proj[j+SDIM/2][i+k] = v->proj[j][k]; } v->freedom = 2*i; } if ( hess_debug && v->proj ) { printf("v_id %s proj\n",ELNAME(v_id)); for ( j = 0 ; j < v->freedom ; j++ ) { for ( m = 0 ; m < SDIM ; m++ ) printf("%f ",(DOUBLE)v->proj[m][j]); printf("\n"); } } } /* end FOR_ALL_VERTICES second time through */ if ( v_normal ) free_matrix3(v_normal); /* assign row numbers */ for ( i = 0, v = vhead, currow = 0 ; i < vhead_count ; i++,v++ ) { v->rownum = currow; currow += v->freedom; } /* check */ if ( (vproj_alloc > vproj_count*SDIM) || (vproj_spot > total_proj*SDIM) || (conhess_alloc > total_conhess) ) kb_error(1608,"Internal error, hessian_init(): Overflow of vproj or conhess arena.\n",RECOVERABLE); /* low_rank update stuff */ if ( quantity_function_sparse_flag ) { for ( i = LOW_INST ; i < meth_inst_count ; i++ ) { struct method_instance *mi = METH_INSTANCE(i); if ( mi->flags & Q_COMPOUND ) if ( GEN_QUANT(mi->quant)->flags & (Q_ENERGY|Q_FIXED|Q_CONSERVED) ) { mi->global_low_rank = S->low_rank; S->low_rank++; } } S->low_rank_vecsize = currow; S->low_rank_vectors = dmatrix(0,S->low_rank,0,currow); S->low_rank_form = dmatrix(0,S->low_rank,0,S->low_rank); S->low_rank_inverse_form = dmatrix(0,S->low_rank,0,S->low_rank); } /* rows for optimizing parameters */ S->optparamrowstart = vertex_rows = currow; for ( i = 0 ; i < optparamcount ; i++) { optparam[i].vhead_index = i + web.skel[VERTEX].max_ord+1; v = vhead + optparam[i].vhead_index; v->rownum = currow; v->freedom = 1; optparam[i].rownum = currow++; } /* allocate matrix */ S->A_rows = S->bodyrowstart = currow; /* S->A_rows is variables, before constraints */ if ( everything_quantities_flag ) S->quanrowstart = currow; else S->quanrowstart = currow + web.skel[BODY].max_ord+1; S->total_rows = S->quanrowstart + gen_quant_count; /* take care of torus_filled */ if ( web.full_flag ) /* pretend one body info only */ { body_id b_id; int found = 0; FOR_ALL_BODIES(b_id) { if ( (get_battr(b_id) & FIXEDVOL) && !found ) { if ( everything_quantities_flag ) GEN_QUANT(get_body_volquant(b_id))->flags |= Q_REDUNDANT; else set_attr(b_id,REDUNDANT_BIT); found = 1; } else { if ( everything_quantities_flag ) GEN_QUANT(get_body_volquant(b_id))->flags &= ~Q_REDUNDANT; else unset_attr(b_id,REDUNDANT_BIT); } } } /* figure out which bodies and quantities are constraints */ S->concount = web.skel[BODY].max_ord+1 + gen_quant_count; S->coninx = (int*)temp_calloc(S->concount,sizeof(int)); S->coninxinv = (int*)temp_calloc(S->concount,sizeof(int)); S->CN = 0; { if ( !everything_quantities_flag ) { body_id b_id; FOR_ALL_BODIES(b_id) { int a = get_battr(b_id); set_body_vhead(b_id,web.skel[VERTEX].max_ord+1 + loc_ordinal(b_id)); v = get_body_vhead(b_id); if ( (a & (FIXEDVOL|PRESSURE)) && !(a & REDUNDANT_BIT) ) { v->v_id = b_id; v->freedom = 1; S->coninx[loc_ordinal(b_id)] = S->CN; S->coninxinv[S->CN] = loc_ordinal(b_id); S->CN++; } else S->coninx[loc_ordinal(b_id)] = -1; } FOR_ALL_BODIES(b_id) { v = get_body_vhead(b_id); v->rownum = currow; currow += v->freedom; } } else S->concount = gen_quant_count; for ( n = 0 ; n < gen_quant_count ; n++,v++ ) { int nn = everything_quantities_flag ? n : web.skel[BODY].max_ord+1 + n ; GEN_QUANT(n)->vhead_index = web.skel[VERTEX].max_ord+1+optparamcount+nn; v = vhead + GEN_QUANT(n)->vhead_index; v->rownum = currow; v->v_id = n; if ( (GEN_QUANT(n)->flags & (Q_FIXED|Q_CONSERVED) ) && !(GEN_QUANT(n)->flags & Q_REDUNDANT) ) { v->freedom = 1; currow++; S->coninx[nn] = S->CN; S->coninxinv[S->CN] = nn; S->CN++; } else S->coninx[nn] = -1; } } /* figure whether to do constraints as augmentation or separately */ if ( augmented_hessian_flag < 0 ) { augmented_hessian_mode = (sparse_constraints_flag && (S->CN >= 50)) ? 1 : 0; } else augmented_hessian_mode = augmented_hessian_flag; pressures = (REAL*)temp_calloc(web.skel[BODY].max_ord+1+gen_quant_count, sizeof(REAL)); conrhs = (REAL*)temp_calloc(web.skel[BODY].max_ord+1+gen_quant_count, sizeof(REAL)); /* allocate right hand side and solution of augmented matrix */ if ( rhs ) *rhs = (REAL *)temp_calloc(S->total_rows,sizeof(REAL)); S->P = (int *)temp_calloc(S->total_rows,sizeof(int)); S->IP = (int *)temp_calloc(S->total_rows,sizeof(int)); /* initialize hash table */ sp_hash_init(S,0); /* tell solvers element data is appropriate */ S->flags |= S_USE_ELEMENTS; } /* end hessian_init */ /************************************************************************* * * function: hessian_fill() * * purpose: call routines that calculate hessian. */ void hessian_fill(S,rhsptr) struct linsys *S; REAL **rhsptr; { int k; body_id b_id; REAL *rhs = rhsptr ? *rhsptr : NULL; #ifdef MPI_EVOLVER if ( this_task == 0 ) { mpi_hessian_fill(S,rhsptr); temp_free((char*)S->hashtable); /* since not calling hash_end() */ rhs = rhsptr ? *rhsptr : NULL; /* mpi_hessian_fill reallocates it */ goto rhs_constraints; } #endif /* clear entries */ if ( rhs_flag ) memset((char*)rhs,0,S->total_rows*sizeof(REAL)); /* fill in hessian */ if ( hessian_by_diff_flag ) { /* hessian by crude numeric difference formulas */ difference_hessian(S,vhead,rhs); if ( optparamcount ) optparamhess(S,rhs); } else /* hessian by explicit formulas */ { if ( !quantities_only_flag ) { if ( web.symmetry_flag && !web.torus_flag ) { if ( auto_convert_flag ) {convert_to_quantities(); goto quantplace;} else kb_error(2079, "Must do convert_to_quantities to do Hessian with symmetry group.\n", RECOVERABLE); } if ( web.representation == SIMPLEX ) simplex_hessian(S,rhs); else { if ( web.representation == SOAPFILM ) area_hessian(S,rhs); edge_energy_hessian(S,rhs); } } quantplace: if ( gen_quant_count ) calc_quant_hess(S,rhs_flag,hess_flag,rhs); if ( !quantities_only_flag ) if ( web.bodycount > web.full_flag ) body_hessian(S,rhs); if ( optparamcount ) optparamhess(S,rhs); } if ( hess_flag ) { /* extract stuff from hash table */ if ( sparse_constraints_flag ) sp_hash_end(S,S->total_rows,S->total_rows,A_OFF); else sp_hash_end(S,S->A_rows,S->total_rows,A_OFF); } #ifdef MPI_EVOLVER rhs_constraints: #endif /* rhs for body constraint rows */ if ( rhs && !everything_quantities_flag ) FOR_ALL_BODIES(b_id) { REAL *current = rhs + S->bodyrowstart + loc_ordinal(b_id); if ( get_battr(b_id) & FIXEDVOL ) *current = -(get_body_fixvol(b_id) - get_body_volume(b_id)); } /* rhs for quantity constraint rows */ if ( rhs ) for ( k = 0 ; k < gen_quant_count ; k++ ) { struct gen_quant *q = GEN_QUANT(k); if ( q->flags & Q_FIXED ) rhs[S->quanrowstart + k] = -(q->target - q->value); } } /* end hessian_fill() */ /*********************************************************************** * * function: hessian_move() * * purpose: move vertices by given stepsize times current vector */ void hessian_move(stepsize,mode,X) REAL stepsize; int mode; /* ACTUAL_MOVE or TEST_MOVE or SET_VELOCITY */ REAL *X; /* direction */ { int i,j; struct hess_verlist *vc; /* current vertex */ vertex_id v_id; last_hessian_scale = stepsize; /* change optimizing parameters */ for ( i = 0 ; i < optparamcount ; i++ ) { optparam[i].velocity = X[optparam[i].rownum]; if ( mode != SET_VELOCITY ) globals(optparam[i].pnum)->value.real += stepsize*X[optparam[i].rownum]; } /* move vertices */ #ifdef MPI_EVOLVER if ( this_task == 0 ) mpi_hessian_move(stepsize,mode,X); else #endif FOR_ALL_VERTICES(v_id) { REAL *coord; REAL *velocity; vc = get_vertex_vhead(v_id); if ( vc->freedom <= 0 ) continue; coord = get_coord(v_id); velocity = get_velocity(v_id); if ( get_vattr(v_id) & BOUNDARY ) { struct boundary *boundary = get_boundary(v_id); int pcount = boundary->pcount; REAL *param = get_param(v_id); for ( i = 0 ; i < pcount ; i++ ) { velocity[i] = X[vc->rownum + i]; if ( mode != SET_VELOCITY ) param[i] += stepsize*X[vc->rownum + i]; } if ( mode != SET_VELOCITY ) for ( i = 0 ; i < SDIM ; i++ ) coord[i] = eval(boundary->coordf[i],param,v_id,NULL); } else { for ( j = 0 ; j < SDIM ; j++ ) if ( vc->proj ) { velocity[j] = dot(vc->proj[j],X+vc->rownum,vc->freedom); if ( mode != SET_VELOCITY ) coord[j] += stepsize*dot(vc->proj[j],X+vc->rownum,vc->freedom); } else { velocity[j] = X[vc->rownum + j]; if ( mode != SET_VELOCITY ) coord[j] += stepsize*X[vc->rownum + j]; } } } #ifdef MPI_EVOLVER if ( this_task != 0 ) return; #endif if ( mode == SET_VELOCITY ) return; if ( hess_move_con_flag ) { calc_all_grads(CALC_VOLGRADS); project_all(hess_move_con_flag /* all constraints */,mode); vgrad_end(); } else project_all(hess_move_con_flag /* all constraints ? */,mode); global_timestamp++; calc_energy(); /* energy after motion */ } /* end hessian_move() */ /************************************************************************** * * function: hessian_exit() * * purpose: clean up after hessian stuff. */ void hessian_exit(X) REAL *X; /* direction, if any */ { int j; struct hess_verlist *vc; /* current vertex */ vertex_id v_id; hess_debug = 0; /* store move in vertex force */ if ( X && vhead ) FOR_ALL_VERTICES(v_id) { REAL *f; vc = get_vertex_vhead(v_id); if ( vc->freedom <= 0 ) continue; f = get_force(v_id); if ( get_vattr(v_id) & BOUNDARY ) { struct boundary *boundary = get_boundary(v_id); int pcount = boundary->pcount; for ( j = 0 ; j < pcount ; j++ ) f[j] = X[vc->rownum + j]; } else { for ( j = 0 ; j < SDIM ; j++ ) if ( vc->proj ) f[j] = dot(vc->proj[j],X+vc->rownum,vc->freedom); else f[j] = X[vc->rownum + j]; } } hessian_cleanup(); } /************************************************************************ * * function: sp_hessian_mult * * purpose: multiply Hessian times vector */ void sp_hessian_mult(S,B,C) struct linsys *S; REAL *B; /* incoming vector */ REAL *C; /* product */ { int i,j; if ( S->lambda != 0.0 ) { if ( hessian_linear_metric_flag ) { for ( j = 0 ; j < Met.N ; j++ ) C[j] = 0.0; for ( i = 0 ; i < Met.N ; i++ ) { for ( j = Met.IA[i] ; j < Met.IA[i+1] ; j++ ) { int jj = Met.JA[j-A_OFF] - A_OFF; C[i] -= S->lambda*Met.A[j-A_OFF]*B[jj]; if ( i != jj ) C[jj] -= S->lambda*Met.A[j-A_OFF]*B[i]; } } } else if ( augmented_hessian_mode ) { for ( i = 0 ; i < S->A_rows ; i++ ) C[i] = -S->lambda*B[i]; for ( ; i < S->N ; i++ ) C[i] = 0.0; } else for ( i = 0 ; i < S->N ; i++ ) C[i] = -S->lambda*B[i]; } for ( i = 0 ; i < S->N ; i++ ) { for ( j = S->IA[i] ; j < S->IA[i+1] ; j++ ) { int jj = S->JA[j-A_OFF] - A_OFF; C[i] += S->A[j-A_OFF]*B[jj]; if ( i != jj ) C[jj] += S->A[j-A_OFF]*B[i]; } } /* Add low-rank stuff */ if ( S->low_rank ) { REAL *Y = (REAL*)temp_calloc(S->low_rank,sizeof(REAL)); REAL *W = (REAL*)temp_calloc(S->low_rank,sizeof(REAL)); REAL *Z = (REAL*)temp_calloc(S->N,sizeof(REAL)); matvec_mul(S->low_rank_vectors,B,Y,S->low_rank,S->low_rank_vecsize); matvec_mul(S->low_rank_form,Y,W,S->low_rank,S->low_rank); vec_mat_mul(W,S->low_rank_vectors,Z,S->low_rank,S->low_rank_vecsize); for ( i = 0 ; i < S->low_rank_vecsize ; i++ ) C[i] += Z[i]; temp_free((char*)Y); temp_free((char*)W); temp_free((char*)Z); } } /********************************************************************* * * function: hessian_cleanup() * * purpose: free memory allocated for hessian stuff. * */ void hessian_cleanup() { #ifdef MPI_EVOLVER if ( this_task == 0 ) { mpi_hessian_cleanup(); } #endif if ( vhead ) temp_free((char *)vhead); vhead = NULL; if ( vproj_base ) myfree((char*)vproj_base); vproj_base = NULL; if ( vproj_space ) myfree((char*)vproj_space); vproj_space = NULL; if ( conhess_base ) free_matrix3(conhess_base); conhess_base = NULL; local_unsave_coords(&saved,SAVE_IN_ATTR); if ( pressures ) temp_free((char*)pressures); pressures = NULL; if ( conrhs ) temp_free((char*)conrhs); conrhs = NULL; if ( ritzvecs ) free_matrix(ritzvecs); ritz_done_flag = 0; ritzvecs = NULL; return; } /******************************************************************** * * function: write_hessian() * * purpose: Write hessian to file in format suitable for * wet cone investigations. */ void write_hessian(S) struct linsys *S; { FILE *fd; char name[100]; int i,j,n,row; struct hess_verlist *v; vertex_id v_id; prompt("Enter hessian file name: ",name,sizeof(name)); fd = fopen(name,"w"); if ( fd == NULL ) { perror(name); return; } /* size info */ fprintf(fd,"%d space dimension\n",SDIM); fprintf(fd,"%d rows\n",S->N); fprintf(fd,"%d entries\n",S->IA[S->N]-A_OFF); if ( hessian_linear_metric_flag ) { fprintf(fd,"%d metric rows\n",Met.N); fprintf(fd,"%d metric entries\n",Met.IA[S->N]-A_OFF); } else { fprintf(fd,"%d metric rows\n",0); fprintf(fd,"%d metric entries\n",0); } fprintf(fd,"%d constraints\n",S->CN); /* per row info: point coords, basis vectors for each degree of freedom, */ FOR_ALL_VERTICES(v_id) { REAL *x = get_coord(v_id); v = get_vertex_vhead(v_id); for ( j = 0 ; j < v->freedom ; j++ ) { fprintf(fd,"%d ",v->rownum+j); for ( n = 0 ; n < SDIM ; n++ ) fprintf(fd," %21.15e",(DOUBLE)x[n]); if ( v->proj ) for ( n = 0 ; n < SDIM ; n++ ) fprintf(fd," %21.15e",(DOUBLE)v->proj[n][j]); else for ( n = 0 ; n < SDIM ; n++ ) fprintf(fd," %d",n==j); fprintf(fd,"\n"); } } /* the Hessian */ FOR_ALL_VERTICES(v_id) { v = get_vertex_vhead(v_id); if ( v->freedom == 0 ) continue; for ( row = v->rownum ; row < v->rownum + v->freedom ; row++ ) { int start = S->IA[row]-A_OFF; int end = S->IA[row+1] - A_OFF; for ( j = start ; j < end ; j++ ) fprintf(fd,"%d %d %21.15e\n",row,S->JA[j]-A_OFF,(DOUBLE)S->A[j]); } } /* the metric */ if (hessian_linear_metric_flag) FOR_ALL_VERTICES(v_id) { v = get_vertex_vhead(v_id); if ( v->freedom == 0 ) continue; for ( row = v->rownum ; row < v->rownum + v->freedom ; row++ ) { int start = Met.IA[row]-A_OFF; int end = Met.IA[row+1] - A_OFF; for ( j = start ; j < end ; j++ ) fprintf(fd,"%d %d %21.15e\n",row,Met.JA[j]-A_OFF,(DOUBLE)Met.A[j]); } } /* constraints */ for ( i = 0 ; i < S->CN ; i++ ) for ( j = 0 ; j < S->N ; j++ ) fprintf(fd,"%d %d %21.15e\n",i,j,(DOUBLE)S->C[i][j]); fclose(fd); } /********************************************************************** * * function: optparamhess() * * purpose: fill in hessian and right side for optimizing parameter rows. * * method: finite differences */ void optparamhess(S,rhs) struct linsys *S; REAL *rhs; /* right side */ { int i,j,m; struct oldcoord csaved; REAL *fake_rhs; /* for grad differences */ REAL *temprow; /* for accumulating row values */ struct gen_quant *q; if ( optparamcount <= 0 ) return; if ( !everything_quantities_flag ) kb_error(2080,"Hessian for optimizing parameters requires convert_to_quantities.\n", RECOVERABLE); fake_rhs = (REAL*)temp_calloc(S->total_rows,sizeof(REAL)); temprow = (REAL*)temp_calloc(S->total_rows,sizeof(REAL)); csaved.coord = NULL; save_coords(&csaved,SAVE_SEPARATE); for ( i = 0 ; i < optparamcount ; i++ ) { REAL dp; REAL emid = web.total_energy; REAL eleft,eright; restore_coords(&csaved,SAVE_SEPARATE); calc_energy(); calc_content(Q_FIXED); emid = web.total_energy; dp = globals(optparam[i].pnum)->attr.varstuff.delta; memset((char*)fake_rhs,0,S->total_rows*sizeof(REAL)); memset((char*)temprow ,0,S->total_rows*sizeof(REAL)); /* for optimizing parameter self hessian */ for ( m = 0 ; m < gen_quant_count ; m++ ) { q = GEN_QUANT(m); if ( q->flags & Q_FIXED ) { sp_hash_search(S,optparam[i].rownum,optparam[i].rownum, -q->pressure*( - 2*q->value )/dp/dp); } } /* right difference */ globals(optparam[i].pnum)->value.real += dp; project_all(0, TEST_MOVE); if ( fixed_constraint_flag || web.pressure_flag || web.pressflag ) calc_content(Q_FIXED); calc_energy(); /* energy after motion */ calc_quant_hess(S,1,0,temprow); eright = web.total_energy; calc_volgrads(NO_OPTS); /* global constraint hessians */ /* for global constraints */ for ( m = 0 ; m < gen_quant_count ; m++ ) { q = GEN_QUANT(m); if ( q->flags & Q_FIXED ) { sp_hash_search(S,optparam[i].rownum,optparam[i].rownum, -q->pressure*( q->value )/dp/dp); sp_hash_search(S,optparam[i].rownum,S->quanrowstart+m, q->value/dp/2); if ( rhs ) rhs[optparam[i].rownum] += q->pressure*q->value/dp/2; } } restore_coords(&csaved,SAVE_SEPARATE); /* also restores opt params */ /* left difference */ globals(optparam[i].pnum)->value.real -= dp; project_all(0, TEST_MOVE); if ( fixed_constraint_flag || web.pressure_flag || web.pressflag ) calc_content(Q_FIXED); calc_energy(); /* energy after motion */ calc_quant_hess(S,1,0,fake_rhs); eleft = web.total_energy; calc_volgrads(NO_OPTS); /* global constraint hessians */ /* for global constraints */ for ( m = 0 ; m < gen_quant_count ; m++ ) { q = GEN_QUANT(m); if ( q->flags & Q_FIXED ) { sp_hash_search(S,optparam[i].rownum,optparam[i].rownum, -q->pressure*( q->value )/dp/dp); sp_hash_search(S,optparam[i].rownum,S->quanrowstart+m, -q->value/dp/2); if ( rhs ) rhs[optparam[i].rownum] -= q->pressure*q->value/dp/2; } } vgrad_end(); restore_coords(&csaved,SAVE_SEPARATE); /* also restores opt params */ web.total_energy = emid; /* restore */ if ( rhs ) rhs[optparam[i].rownum] += -(eright - eleft)/2/dp; /* rhs is neg grad */ /* vertex-param cross terms */ for ( j = 0 ; j < vertex_rows ; j++ ) sp_hash_search(S,j,optparam[i].rownum,(fake_rhs[j]-temprow[j])/2/dp); sp_hash_search(S,optparam[i].rownum,optparam[i].rownum, (eright - 2*emid + eleft)/dp/dp); } temp_free((char*)fake_rhs); temp_free((char*)temprow); /* optimizing parameter mixed terms */ for ( i = 0 ; i < optparamcount ; i++ ) for ( j = 0 ; j < i ; j++ ) { REAL dp; REAL vmid = globals(optparam[i].pnum)->value.real; REAL emid = web.total_energy; REAL ehihi,ehilo,elohi,elolo; if ( fabs(vmid) > 1. ) dp = fabs(vmid)*1e-3; else dp = 1e-3; /* + + difference */ globals(optparam[i].pnum)->value.real += dp; globals(optparam[j].pnum)->value.real += dp; project_all(0, TEST_MOVE); if ( fixed_constraint_flag || web.pressure_flag || web.pressflag ) calc_content(Q_FIXED); calc_energy(); /* energy after motion */ ehihi = web.total_energy; for ( m = 0 ; m < gen_quant_count ; m++ ) { q = GEN_QUANT(m); if ( q->flags & Q_FIXED ) sp_hash_search(S,optparam[j].rownum,optparam[i].rownum, -q->pressure*( q->value )/dp/dp/4); } restore_coords(&csaved,SAVE_SEPARATE); /* also restores opt params */ /* + - difference */ globals(optparam[i].pnum)->value.real += dp; globals(optparam[j].pnum)->value.real -= dp; project_all(0, TEST_MOVE); if ( fixed_constraint_flag || web.pressure_flag || web.pressflag ) calc_content(Q_FIXED); calc_energy(); /* energy after motion */ ehilo = web.total_energy; for ( m = 0 ; m < gen_quant_count ; m++ ) { q = GEN_QUANT(m); if ( q->flags & Q_FIXED ) sp_hash_search(S,optparam[j].rownum,optparam[i].rownum, -q->pressure*(-q->value )/dp/dp/4); } restore_coords(&csaved,SAVE_SEPARATE); /* also restores opt params */ /* - + difference */ globals(optparam[i].pnum)->value.real -= dp; globals(optparam[j].pnum)->value.real += dp; project_all(0, TEST_MOVE); if ( fixed_constraint_flag || web.pressure_flag || web.pressflag ) calc_content(Q_FIXED); calc_energy(); /* energy after motion */ elohi = web.total_energy; for ( m = 0 ; m < gen_quant_count ; m++ ) { q = GEN_QUANT(m); if ( q->flags & Q_FIXED ) sp_hash_search(S,optparam[j].rownum,optparam[i].rownum, -q->pressure*(-q->value )/dp/dp/4); } restore_coords(&csaved,SAVE_SEPARATE); /* also restores opt params */ /* - - difference */ globals(optparam[i].pnum)->value.real -= dp; globals(optparam[j].pnum)->value.real -= dp; project_all(0, TEST_MOVE); if ( fixed_constraint_flag || web.pressure_flag || web.pressflag ) calc_content(Q_FIXED); calc_energy(); /* energy after motion */ elolo = web.total_energy; for ( m = 0 ; m < gen_quant_count ; m++ ) { q = GEN_QUANT(m); if ( q->flags & Q_FIXED ) sp_hash_search(S,optparam[j].rownum,optparam[i].rownum, -q->pressure*( q->value )/dp/dp/4); } restore_coords(&csaved,SAVE_SEPARATE); /* also restores opt params */ web.total_energy = emid; /* restore */ sp_hash_search(S,optparam[j].rownum,optparam[i].rownum, (ehihi-ehilo-elohi+elolo)/dp/dp/4); } unsave_coords(&csaved,SAVE_SEPARATE); } /* end optparamhess() */ evolver-2.30c.dfsg/src/torus.c0000644000175300017530000001314711410765113016543 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************* * * file: torus.c * * Purpose: Functions dealing with symmetries of surface. * Has the three functions for torus. * If user wants to define his/her own symmetry * group, write a file like this and put function * names in registry.c. * */ #include "include.h" /* Group elements are encoded in two-bit fields in "wrap", one for each dimension, with "000001b" for positive wrap and "011111b" for negative wrap. You are free to encode any way you want, but use wrap = 0 for the identity. */ /* specific case of torus symmetry representation */ /* these also defined in extern.h since torus built-in */ #ifndef TWRAPBITS #define TWRAPBITS 6 #define POSWRAP 1 #define WRAPMASK 037 #define ALLWRAPMASK 03737373737 #define NEGWRAP WRAPMASK #define WRAPNUM(x) ( (x)>(1<<(TWRAPBITS-2)) ? (x)-(1<<(TWRAPBITS-1)) : (x)) #endif #define TERRDETECT 06060606060 /******************************************************************* * * function: torus_wrap * * purpose: Provide adjusted coordinates for vertices that get * wrapped around torus. Serves as example for user-written * symmetry function. * * This function uses the values of the torus periods read * in from the data file. */ void torus_wrap(x,y,wrap) REAL *x; /* original coordinates */ REAL *y; /* wrapped coordinates */ WRAPTYPE wrap; /* encoded symmetry group element, TWRAPBITS bits per dimension */ { int i,j; int wrapnum; if ( x != y ) for ( i = 0 ; i < SDIM ; i++ ) y[i] = x[i]; for ( i = 0 ; wrap != 0 ; i++, wrap >>= TWRAPBITS ) { wrapnum = WRAPNUM(wrap & WRAPMASK); if ( wrapnum ) for ( j = 0 ; j < SDIM ; j++ ) y[j] += wrapnum*web.torus_period[i][j]; } } /******************************************************************* * * function: torus_form_pullback * * purpose: Pull back differentail forms at vertices that get * wrapped around torus. Serves as example for user-written * symmetry function. * * Torus is flat, so implementation is trivial copy. */ void torus_form_pullback(x,xform,yform,wrap) REAL *x; /* original coordinates */ REAL *xform; /* pulled back form */ REAL *yform; /* wrapped form, input */ WRAPTYPE wrap; /* encoded symmetry group element, 3 bits per dimension */ { memcpy((char*)xform,(char*)yform,SDIM*sizeof(REAL)); } /******************************************************************** * * function: torus_compose() * * purpose: do composition of two group elements * */ WRAPTYPE torus_compose(wrap1,wrap2) WRAPTYPE wrap1,wrap2; /* the elements to compose */ { int w = (wrap1 + wrap2) & ALLWRAPMASK; #ifdef _DEBUG int i; int ww = w; if ( verbose_flag ) for ( i = 0, ww=w ; i < SDIM ; i++ ) { switch(ww&WRAPMASK) { case 0: case POSWRAP: case NEGWRAP: break; default: kb_error(3917,"Bad wrap as result of torus_compose.\n", WARNING); } ww >>= TWRAPBITS; } #endif if ( ~(wrap1|wrap2) & TERRDETECT & w ) kb_error(4553,"Wrap out of bounds as result of torus_compose.\n", WARNING); return w; } /******************************************************************** * * function: torus_inverse() * * purpose: return inverse of group element. * */ WRAPTYPE torus_inverse(wrap) WRAPTYPE wrap; /* the element invert */ { return ((~ALLWRAPMASK)-wrap) & ALLWRAPMASK; } /********************************************************************* * * function: torus_edge_unwrap() * * purpose: get wrap of edge to 0 (preliminary to deletion or other * topology change) so endpoints near as possible in * a fundamental region. */ void torus_unwrap_edge(e_id) edge_id e_id; { edge_id pos_e = positive_id(e_id); vertex_id tailv,headv; REAL *x; WRAPTYPE wrap = get_edge_wrap(pos_e); WRAPTYPE tailwrap,headwrap; REAL u[MAXCOORD]; /* tail unit cell coords */ int i; if ( wrap == 0 ) return; tailv = get_edge_tailv(pos_e); headv = get_edge_headv(pos_e); x = get_coord(tailv); matvec_mul(web.inverse_periods,x,u,SDIM,SDIM); tailwrap = 0; for ( i = SDIM-1 ; i >= 0 ; i-- ) { tailwrap <<= TWRAPBITS; tailwrap |= ((int)(floor(u[i]))) & WRAPMASK; } tailwrap = torus_inverse(tailwrap); headwrap = torus_compose(tailwrap,wrap); wrap_vertex(tailv,tailwrap); wrap_vertex(headv,headwrap); } /************************************************************* * * Functions: Identity symmetry functions. */ void identity_wrap(x,y,wrap) REAL *x; /* original coordinates */ REAL *y; /* wrapped coordinates */ WRAPTYPE wrap; /* encoded symmetry group element, TWRAPBITS bits per dimension */ { int i; if ( x != y ) for ( i = 0 ; i < SDIM ; i++ ) y[i] = x[i]; } void identity_form_pullback(x,xform,yform,wrap) REAL *x; /* original coordinates */ REAL *xform; /* pulled back form */ REAL *yform; /* wrapped form, input */ WRAPTYPE wrap; /* encoded symmetry group element, 3 bits per dimension */ { memcpy((char*)xform,(char*)yform,SDIM*sizeof(REAL)); } WRAPTYPE identity_compose(wrap1,wrap2) WRAPTYPE wrap1,wrap2; /* the elements to compose */ { return 0; } WRAPTYPE identity_inverse(wrap) WRAPTYPE wrap; /* the element invert */ { return 0; } void identity_unwrap_edge(e_id) edge_id e_id; {}evolver-2.30c.dfsg/src/method2.c0000644000175300017530000012545011410765113016732 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************* * * file: method2.c * * contents: quantities for facets */ #include "include.h" /********************************************************************* Facet area quantity Linear simplex volume: Let S[i][j] be side i vector. Let U[i][j] = L[i][k]*S[k][j] be orthogonalized version. Let diag[i] = U[i]*U[i]. Let det = det(diag). Let D = diag^-1. Then area = sqrt(det)/n! d(area)/dS[i][j] = area*(L^T D U)[i][j] dd(area)/dS[i][j]dS[k][l] = area*( (L^T D L)[i][k]*I[j][l] - (L^T D L)[i][k]*(U^T D U)[j][l] + (L^T D U)[i][j]*(L^T D U)[k][l] - (L^T D U)[k][j]*(L^T D U)[i][l]) *********************************************************************/ /********************************************************************* * * function: q_facet_tension_init() * * purpose: initialize web.total_area to 0. */ void q_facet_tension_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { /* if ( everything_quantities_flag && (mode==METHOD_VALUE) ) web.total_area = 0.0; */ } /********************************************************************* * * function: q_facet_tension_value() * * purpose: General quantity value of facet tension. */ REAL q_facet_tension_value(f_info) struct qinfo *f_info; { REAL area; int i,j,k; REAL diag[MAXCOORD]; REAL D[MAXCOORD]; /* diag^-1 */ REAL U[MAXCOORD][MAXCOORD]; if ( web.modeltype == QUADRATIC ) return q_facet_tension_q(f_info); if ( web.modeltype == LAGRANGE ) return lagrange_facet_tension_value(f_info); /* new way, using Gram-Schmidt */ area = 1.0; for ( i = 0 ; i < web.dimension ; i++ ) { for ( j = 0 ; j < SDIM ; j++ ) U[i][j] = f_info->sides[0][i][j]; for ( j = 0 ; j < i ; j++ ) { REAL su = SDIM_dot(f_info->sides[0][i],U[j])*D[j]; for ( k = 0 ; k < SDIM ; k++ ) U[i][k] -= su*U[j][k]; } diag[i] = SDIM_dot(U[i],U[i]); if ( diag[i] > 0.0 ) D[i] = 1/diag[i]; else D[i] = 1.0; area *= diag[i]; } if ( area > 0.0 ) area = sqrt(area)/web.simplex_factorial; else area = 0.0; if ( METH_INSTANCE(f_info->method)->flags & DEFAULT_INSTANCE ) { set_facet_area(f_info->id,area); #ifdef SHARED_MEMORY if ( nprocs > 1 ) proc_total_area[GET_THREAD_ID] += area; else #endif binary_tree_add(web.total_area_addends,area); } if ( METH_INSTANCE(f_info->method)->flags & USE_DENSITY ) area *= get_facet_density(f_info->id); return area; } /********************************************************************* * * function: q_facet_tension_gradient() * * purpose: General quantity value of facet tension. */ REAL q_facet_tension_gradient(f_info) struct qinfo *f_info; { REAL area; int i,j; REAL fudge; if ( web.modeltype == QUADRATIC ) return q_facet_tension_q_grad(f_info); if ( web.modeltype == LAGRANGE ) return lagrange_facet_tension_grad(f_info); #define OLDWAY #ifdef OLDWAY mat_tsquare(f_info->sides[0],f_info->ss,web.dimension,SDIM); area = det_adjoint(f_info->ss,web.dimension); if ( area > 0.0 ) { area = sqrt(area); fudge = 1/(area*web.simplex_factorial); area /= web.simplex_factorial; } else fudge = area = 0.0; if ( METH_INSTANCE(f_info->method)->flags & USE_DENSITY ) { REAL density = get_facet_density(f_info->id); area *= density; fudge *= density; } for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < web.dimension ; j++ ) f_info->ss[i][j] *= fudge; mat_mult(f_info->ss,f_info->sides[0],f_info->grad+1,web.dimension, web.dimension, SDIM); /* head forces */ memset((char*)f_info->grad[0],0,SDIM*sizeof(REAL)); for ( i = 0 ; i < web.dimension ; i++ ) /* tail forces */ vector_sub(f_info->grad[0],f_info->grad[i+1],SDIM); #else /* new way, using Gram-Schmidt */ { REAL diag[MAXCOORD]; REAL D[MAXCOORD]; REAL U[MAXCOORD][MAXCOORD]; REAL L[MAXCOORD][MAXCOORD]; REAL DL[MAXCOORD][MAXCOORD]; REAL LDU[MAXCOORD][MAXCOORD]; REAL det; int k; det = 1.0; for ( i = 0 ; i < web.dimension ; i++ ) { REAL invd; for ( j = 0 ; j < SDIM ; j++ ) U[i][j] = f_info->sides[0][i][j]; for ( j = 0 ; j < i ; j++ ) { REAL su = SDIM_dot(f_info->sides[0][i],U[j])*D[j]; for ( k = 0 ; k < SDIM ; k++ ) U[i][k] -= su*U[j][k]; L[i][j] = -su; } diag[i] = SDIM_dot(U[i],U[i]); if ( diag[i] <= 0.0 ) invd = 1.0; else invd = 1/diag[i]; D[i] = invd; DL[i][i] = invd; for ( k = 0 ; k < i ; k++ ) DL[i][k] = invd*L[i][k]; det *= diag[i]; } area = sqrt(det)/web.simplex_factorial; if ( METH_INSTANCE(f_info->method)->flags & USE_DENSITY ) area *= get_facet_density(f_info->id); for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { REAL sum = DL[i][i]*U[i][j]; for ( k = i+1 ; k < web.dimension ; k++ ) sum += DL[k][i]*U[k][j]; LDU[i][j] = sum; } for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[0][j] = 0.0; for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { REAL gr = area*LDU[i][j]; f_info->grad[i+1][j] = gr; f_info->grad[0][j] -= gr; } } #endif return area; } /********************************************************************* * * function: q_facet_tension_hessian() * * purpose: General quantity value, gradient and hessian of facet area. */ REAL q_facet_tension_hessian(f_info) struct qinfo *f_info; { int i,j,k,m; REAL val; REAL area; REAL ssdet,fudge,energy; if ( dirichlet_flag ) { if ( web.modeltype != LINEAR ) kb_error(2144,"Dirichlet_mode requires linear model.\n",RECOVERABLE); return dirichlet_area_hess(f_info); } if ( sobolev_flag ) { if ( web.modeltype != LINEAR ) kb_error(2145,"Sobolev_mode requires linear model.\n",RECOVERABLE); return sobolev_area_hess(f_info); } if ( web.modeltype == QUADRATIC ) return q_facet_tension_q_hess(f_info); if ( web.modeltype == LAGRANGE ) return lagrange_facet_tension_hess(f_info); #ifdef OLDWAY { MAT2D(AS,MAXCOORD,MAXCOORD); MAT2D(SAS,MAXCOORD,MAXCOORD); #define S (f_info->sides[0]) #define A (f_info->ss) /* area derivatives */ mat_tsquare(S,A,web.dimension,SDIM); ssdet = det_adjoint(A,web.dimension); if ( ssdet <= 0.0 ) { return 0.0; } energy = area = sqrt(ssdet)/web.simplex_factorial; fudge = 1/ssdet; if ( METH_INSTANCE(f_info->method)->flags & USE_DENSITY ) { REAL density = get_facet_density(f_info->id); energy *= density; area *= density; } for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < web.dimension ; j++ ) A[i][j] *= fudge; /* now inverse of ss, times density */ mat_mult(A,S,AS,web.dimension,web.dimension, SDIM); /* head forces */ for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[i+1][j] = area*AS[i][j]; /* tail forces */ memset((char*)f_info->grad[0],0,SDIM*sizeof(REAL)); for ( i = 0 ; i < web.dimension ; i++ ) vector_sub(f_info->grad[0],f_info->grad[i+1],SDIM); /* hessian */ tr_mat_mul(S,AS,SAS,web.dimension,SDIM,SDIM); for ( m = 0 ; m < web.dimension ; m++ ) for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( k = 0 ; k < SDIM ; k++ ) { val = area*(AS[m][j]*AS[i][k] + (j==k?A[i][m]:0.0) - A[i][m]*SAS[j][k] - AS[i][j]*AS[m][k]); f_info->hess[m+1][i+1][j][k] = val; f_info->hess[0][0][j][k] += val; f_info->hess[0][i+1][j][k] -= val; f_info->hess[m+1][0][j][k] -= val; } return energy; #undef S #undef A } #else /* new way, using Gram-Schmidt */ { REAL diag[MAXCOORD]; REAL U[MAXCOORD][MAXCOORD]; REAL D[MAXCOORD]; REAL L[MAXCOORD][MAXCOORD]; /* only subdiagonal explicit */ REAL DL[MAXCOORD][MAXCOORD]; REAL LDU[MAXCOORD][MAXCOORD]; REAL LDL[MAXCOORD][MAXCOORD]; REAL UDU[MAXCOORD][MAXCOORD]; REAL det; det = 1.0; for ( i = 0 ; i < web.dimension ; i++ ) { REAL invd; for ( j = 0 ; j < SDIM ; j++ ) U[i][j] = f_info->sides[0][i][j]; for ( j = 0 ; j < i ; j++ ) { REAL su = SDIM_dot(f_info->sides[0][i],U[j])*D[j]; for ( k = 0 ; k < SDIM ; k++ ) U[i][k] -= su*U[j][k]; L[i][j] = -su; } diag[i] = SDIM_dot(U[i],U[i]); if ( diag[i] <= 0.0 ) invd = 0.0; else invd = 1/diag[i]; D[i] = invd; DL[i][i] = invd; for ( k = 0 ; k < i ; k++ ) DL[i][k] = invd*L[i][k]; det *= diag[i]; } area = sqrt(det)/web.simplex_factorial; if ( METH_INSTANCE(f_info->method)->flags & USE_DENSITY ) area *= get_facet_density(f_info->id); /* form LDL, LDU, UDU */ for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j <= i ; j++ ) { REAL sum = DL[i][j]; for ( k = i+1 ; k < web.dimension ; k++ ) sum += L[k][i]*DL[k][j]; LDL[i][j] = LDL[j][i] = sum; } for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { REAL sum = DL[i][i]*U[i][j]; for ( k = i+1 ; k < web.dimension ; k++ ) sum += DL[k][i]*U[k][j]; LDU[i][j] = sum; } for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j <= i ; j++ ) { REAL sum = 0.0; for ( k = 0 ; k < web.dimension ; k++ ) sum += U[k][i]*D[k]*U[k][j]; UDU[i][j] = UDU[j][i] = sum; } for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { REAL gr = area*LDU[i][j]; f_info->grad[i+1][j] = gr; f_info->grad[0][j] -= gr; } for ( m = 0 ; m < web.dimension ; m++ ) for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( k = 0 ; k < SDIM ; k++ ) { val = area*(LDU[m][j]*LDU[i][k] + (j==k?LDL[i][m]:0.0) - LDL[i][m]*UDU[j][k] - LDU[i][j]*LDU[m][k]) ; f_info->hess[m+1][i+1][j][k] = val; f_info->hess[0][0][j][k] += val; f_info->hess[0][i+1][j][k] -= val; f_info->hess[m+1][0][j][k] -= val; } return area; } #endif } /********************************************************************* facet_scalar_integral method 2D facets only, in any space dimension *********************************************************************/ /********************************************************************* * * function: facet_scalar_integral_init() * * purpose: check illegalities * */ void facet_scalar_integral_init(mode,mi) int mode; struct method_instance *mi; { if ( web.dimension != 2 ) kb_error(1768,"facet_scalar_integral method only for SOAPFILM model.\n", RECOVERABLE); } /********************************************************************* * * function: facet_scalar_integral() * * purpose: method value * */ REAL facet_scalar_integral(f_info) struct qinfo *f_info; { int m; REAL value = 0.0; REAL area; REAL ss,st,tt; if ( web.modeltype == QUADRATIC ) return facet_scalar_integral_q(f_info); if ( web.modeltype == LAGRANGE ) return facet_scalar_integral_lagr(f_info); ss = SDIM_dot(f_info->sides[0][0],f_info->sides[0][0]); st = SDIM_dot(f_info->sides[0][0],f_info->sides[0][1]); tt = SDIM_dot(f_info->sides[0][1],f_info->sides[0][1]); area = ss*tt-st*st; if ( area <= 0.0 ) return 0.0; area = sqrt(area)/2; for ( m = 0 ; m < gauss2D_num ; m++ ) { f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ value += gauss2Dwt[m]* eval(METH_INSTANCE(f_info->method)->expr[0],f_info->gauss_pt[m],f_info->id,NULL); } value *= area; return value; } /********************************************************************* * * function: facet_scalar_integral_grad() * * purpose: method gradient * */ REAL facet_scalar_integral_grad(f_info) struct qinfo *f_info; { int m,i,j; REAL value = 0.0; REAL val; REAL derivs[MAXCOORD]; REAL area; REAL ss,st,tt; if ( web.modeltype == QUADRATIC ) return facet_scalar_integral_q_grad(f_info); if ( web.modeltype == LAGRANGE ) return facet_scalar_integral_lagr_grad(f_info); for ( m = 0 ; m < FACET_VERTS ; m++ ) for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[m][j] = 0.0; ss = SDIM_dot(f_info->sides[0][0],f_info->sides[0][0]); st = SDIM_dot(f_info->sides[0][0],f_info->sides[0][1]); tt = SDIM_dot(f_info->sides[0][1],f_info->sides[0][1]); area = ss*tt-st*st; if ( area <= 0.0 ) return 0.0; area = sqrt(area)/2; for ( m = 0 ; m < gauss2D_num ; m++ ) { f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ eval_all(METH_INSTANCE(f_info->method)->expr[0],f_info->gauss_pt[m],SDIM,&val, derivs,f_info->id); value += gauss2Dwt[m]*val; for ( i = 0 ; i < FACET_VERTS ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[i][j] += gauss2Dwt[m]*gauss2Dpt[m][i]*derivs[j]*area; } for ( j = 0 ; j < SDIM ; j++ ) { REAL tmp1,tmp2; tmp1 = tt*f_info->sides[0][0][j] - st*f_info->sides[0][1][j]; tmp2 = ss*f_info->sides[0][1][j] - st*f_info->sides[0][0][j]; f_info->grad[0][j] -= value*(tmp1+tmp2)/4/area; f_info->grad[1][j] += value*tmp1/4/area; f_info->grad[2][j] += value*tmp2/4/area; } return area*value; } /********************************************************************* * * function: facet_scalar_integral_hess() * * purpose: method gradient and hessian * */ REAL facet_scalar_integral_hess(f_info) struct qinfo *f_info; { int n,m,j,k,i; REAL value = 0.0; REAL sum,val; REAL derivs[MAXCOORD]; REAL areagrad[MAXCOORD][MAXCOORD],sumgrad[MAXCOORD][MAXCOORD]; REAL areahess[MAXCOORD][MAXCOORD][MAXCOORD][MAXCOORD], sumhess[MAXCOORD][MAXCOORD][MAXCOORD][MAXCOORD]; REAL area; REAL ssdet; MAT2D(AS,MAXCOORD,MAXCOORD); MAT2D(SAS,MAXCOORD,MAXCOORD); MAT2D(second,MAXCOORD,MAXCOORD); #define S (f_info->sides[0]) #define A (f_info->ss) if ( web.modeltype == QUADRATIC ) return facet_scalar_integral_q_hess(f_info); if ( web.modeltype == LAGRANGE ) return facet_scalar_integral_lagr_hess(f_info); /* first, area derivatives */ mat_tsquare(S,A,web.dimension,SDIM); ssdet = det_adjoint(A,web.dimension); if ( ssdet <= 0.0 ) { return 0.0; } area = sqrt(ssdet)/web.simplex_factorial; for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < web.dimension ; j++ ) A[i][j] /= ssdet; /* now inverse of ss */ mat_mult(A,S,AS,web.dimension,web.dimension, SDIM); /* head forces */ for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) areagrad[i+1][j] = area*AS[i][j]; /* tail forces */ memset((char*)areagrad[0],0,SDIM*sizeof(REAL)); for ( i = 0 ; i < web.dimension ; i++ ) vector_sub(areagrad[0],areagrad[i+1],SDIM); /* hessian */ tr_mat_mul(S,AS,SAS,web.dimension,SDIM,SDIM); for ( j = 0 ; j < SDIM ; j++ ) for ( k = 0 ; k < SDIM ; k++ ) for ( m = 0 ; m <= web.dimension ; m++ ) { areahess[m][0][j][k] = 0.0; areahess[0][m][j][k] = 0.0; } for ( m = 0 ; m < web.dimension ; m++ ) for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( k = 0 ; k < SDIM ; k++ ) { val = area*(AS[m][j]*AS[i][k] + (j==k?A[i][m]:0.0) - A[i][m]*SAS[j][k] - AS[i][j]*AS[m][k]); areahess[m+1][i+1][j][k] = val; areahess[0][0][j][k] += val; areahess[0][i+1][j][k] -= val; areahess[m+1][0][j][k] -= val; } /* gaussian sum derivatives */ memset((char*)sumgrad,0,sizeof(sumgrad)); memset((char*)sumhess,0,sizeof(sumhess)); for ( m = 0, sum = 0.0 ; m < gauss2D_num ; m++ ) { f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ eval_second(METH_INSTANCE(f_info->method)->expr[0],f_info->gauss_pt[m],SDIM,&val, derivs,second,f_info->id); sum += gauss2Dwt[m]*val; for ( i = 0 ; i <= web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) sumgrad[i][j] += gauss2Dwt[m]*gauss2Dpt[m][i]*derivs[j]; for ( i = 0 ; i <= web.dimension ; i++ ) for ( n = 0 ; n <= web.dimension ; n++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( k = 0 ; k < SDIM ; k++ ) sumhess[i][n][j][k] += gauss2Dwt[m]*gauss2Dpt[m][i]*gauss2Dpt[m][n] *second[j][k]; } /* final values */ value = area*sum; for ( m = 0 ; m <= web.dimension ; m++ ) for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[m][j] = areagrad[m][j]*sum + area*sumgrad[m][j]; for ( m = 0 ; m <= web.dimension ; m++ ) for ( i = 0 ; i <= web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( k = 0 ; k < SDIM ; k++ ) f_info->hess[m][i][j][k] = areahess[m][i][j][k]*sum + areagrad[m][j]*sumgrad[i][k] + sumgrad[m][j]*areagrad[i][k] + area*sumhess[m][i][j][k]; return value; } /********************************************************************* quadratic facet_scalar_integral method 2D facets only, in any space dimension *********************************************************************/ /********************************************************************* * * function: facet_scalar_integral_q() * * purpose: method value * */ REAL facet_scalar_integral_q(f_info) struct qinfo *f_info; { int m; REAL value = 0.0; REAL area; REAL ss,st,tt; for ( m = 0 ; m < gauss2D_num ; m++ ) { REAL **tang = f_info->sides[m]; ss = SDIM_dot(tang[0],tang[0]); st = SDIM_dot(tang[0],tang[1]); tt = SDIM_dot(tang[1],tang[1]); area = ss*tt-st*st; if ( area <= 0.0 ) continue; f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ value += gauss2Dwt[m]*sqrt(area)* eval(METH_INSTANCE(f_info->method)->expr[0],f_info->gauss_pt[m],f_info->id,NULL); } return value/2; /* triangle factor */ } /********************************************************************* * * function: facet_scalar_integral_q_grad() * * purpose: method gradient * */ REAL facet_scalar_integral_q_grad(f_info) struct qinfo *f_info; { int m,j,k; REAL value = 0.0; REAL val; REAL derivs[MAXCOORD]; REAL detgrad[FACET_CTRL][MAXCOORD]; REAL area,det; REAL ss,st,tt; for ( m = 0 ; m < FACET_CTRL ; m++ ) for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[m][j] = 0.0; for ( m = 0 ; m < gauss2D_num ; m++ ) { REAL **tang = f_info->sides[m]; ss = SDIM_dot(tang[0],tang[0]); st = SDIM_dot(tang[0],tang[1]); tt = SDIM_dot(tang[1],tang[1]); det = ss*tt-st*st; if ( det <= 0.0 ) continue; area = sqrt(det)/2; f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ eval_all(METH_INSTANCE(f_info->method)->expr[0],f_info->gauss_pt[m],SDIM,&val, derivs,f_info->id); value += gauss2Dwt[m]*area*val; for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) detgrad[k][j] = 2*gpolypartial[m][0][k]*tang[0][j]*tt + ss*2*gpolypartial[m][1][k]*tang[1][j] - 2*st*(gpolypartial[m][0][k]*tang[1][j] + gpolypartial[m][1][k]*tang[0][j]); for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[k][j] += gauss2Dwt[m]* (val/sqrt(det)/4*detgrad[k][j] + area*derivs[j]*gpoly[m][k]); } return value; } /********************************************************************* * * function: facet_scalar_integral_q_hess() * * purpose: method gradient and hessian * */ REAL facet_scalar_integral_q_hess(f_info) struct qinfo *f_info; { int m,j,k,kk,jj; REAL value = 0.0; REAL val; REAL derivs[MAXCOORD]; REAL detgrad[FACET_CTRL][MAXCOORD]; REAL dethess[FACET_CTRL][FACET_CTRL][MAXCOORD][MAXCOORD]; REAL area,det,ss,st,tt; MAT2D(second,MAXCOORD,MAXCOORD); for ( m = 0 ; m < gauss2D_num ; m++ ) { REAL **gpp = gpolypartial[m]; REAL **tang = f_info->sides[m]; ss = SDIM_dot(tang[0],tang[0]); st = SDIM_dot(tang[0],tang[1]); tt = SDIM_dot(tang[1],tang[1]); det = ss*tt-st*st; if ( det <= 0.0 ) continue; area = sqrt(det)/2; f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ eval_second(METH_INSTANCE(f_info->method)->expr[0],f_info->gauss_pt[m],SDIM,&val, derivs,second,f_info->id); value += gauss2Dwt[m]*area*val; /* gradients */ for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) detgrad[k][j] = 2*gpp[0][k]*tang[0][j]*tt + ss*2*gpp[1][k]*tang[1][j] - 2*st*(gpp[0][k]*tang[1][j] + gpp[1][k]*tang[0][j]); for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[k][j] += gauss2Dwt[m]* (val/sqrt(det)/4*detgrad[k][j] + area*derivs[j]*gpoly[m][k]); /* hessian */ for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( kk = 0 ; kk < FACET_CTRL ; kk++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) { dethess[k][kk][j][jj] = 2*gpp[0][k]*tang[0][j]*2*gpp[1][kk]*tang[1][jj] + 2*gpp[0][kk]*tang[0][jj]*2*gpp[1][k]*tang[1][j] - 2*(gpp[0][kk]*tang[1][jj] + gpp[1][kk]*tang[0][jj]) *(gpp[0][k]*tang[1][j] + gpp[1][k]*tang[0][j]); if (j==jj) dethess[k][kk][j][jj] += 2*gpp[0][k]*gpp[0][kk]*tt + ss*2*gpp[1][k]*gpp[1][kk] - 2*st*(gpp[0][k]*gpp[1][kk] + gpp[1][k]*gpp[0][kk]); } for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( kk = 0 ; kk < FACET_CTRL ; kk++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) f_info->hess[k][kk][j][jj] += gauss2Dwt[m]* (derivs[jj]*gpoly[m][kk]/sqrt(det)/4*detgrad[k][j] -0.5*val/sqrt(det)/det/4*detgrad[kk][jj]*detgrad[k][j] + val/sqrt(det)/4*dethess[k][kk][j][jj] + 1/sqrt(det)/4*detgrad[kk][jj]*derivs[j]*gpoly[m][k] + area*second[j][jj]*gpoly[m][k]*gpoly[m][kk]); } return value; } /********************************************************************* Lagrange facet_scalar_integral method 2 D facets only, in any space dimension *********************************************************************/ /********************************************************************* * * function: facet_scalar_integral_lagr() * * purpose: method value * */ REAL facet_scalar_integral_lagr(f_info) struct qinfo *f_info; { int m; REAL value = 0.0; REAL area; REAL ss,st,tt; struct gauss_lag *gl = &gauss_lagrange[web.dimension][web.gauss2D_order]; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL **tang = f_info->sides[m]; ss = SDIM_dot(tang[0],tang[0]); st = SDIM_dot(tang[0],tang[1]); tt = SDIM_dot(tang[1],tang[1]); area = ss*tt-st*st; if ( area <= 0.0 ) continue; f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ value += gl->gausswt[m]*sqrt(area)* eval(METH_INSTANCE(f_info->method)->expr[0],f_info->gauss_pt[m],f_info->id,NULL); } return value/2; /* triangle factor */ } /********************************************************************* * * function: facet_scalar_integral_lagr_grad() * * purpose: method gradient * */ REAL facet_scalar_integral_lagr_grad(f_info) struct qinfo *f_info; { int m,j,k; REAL value = 0.0; REAL val; REAL derivs[MAXCOORD]; REAL detgrad; REAL area,det; REAL ss,st,tt; struct gauss_lag *gl = &gauss_lagrange[web.dimension][web.gauss2D_order]; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL **tang = f_info->sides[m]; ss = SDIM_dot(tang[0],tang[0]); st = SDIM_dot(tang[0],tang[1]); tt = SDIM_dot(tang[1],tang[1]); det = ss*tt-st*st; if ( det <= 0.0 ) continue; area = sqrt(det)/2; f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ eval_all(METH_INSTANCE(f_info->method)->expr[0],f_info->gauss_pt[m],SDIM,&val, derivs,f_info->id); value += gl->gausswt[m]*area*val; for ( k = 0 ; k < gl->lagpts ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) { detgrad = 2*gl->gpolypart[m][0][k]*tang[0][j]*tt + ss*2*gl->gpolypart[m][1][k]*tang[1][j] - 2*st*(gl->gpolypart[m][0][k]*tang[1][j] + gl->gpolypart[m][1][k]*tang[0][j]); f_info->grad[k][j] += gl->gausswt[m]* (val/sqrt(det)/4*detgrad + area*derivs[j]*gl->gpoly[m][k]); } } return value; } /********************************************************************* * * function: facet_scalar_integral_lagr_hess() * * purpose: method gradient and hessian * */ REAL facet_scalar_integral_lagr_hess(f_info) struct qinfo *f_info; { int m,j,k,kk,jj; REAL value = 0.0; REAL val; REAL derivs[MAXCOORD]; REAL detgrad[MAXVCOUNT][MAXCOORD]; REAL dethess; REAL area,det,ss,st,tt; MAT2D(second,MAXCOORD,MAXCOORD); struct gauss_lag *gl = &gauss_lagrange[web.dimension][web.gauss2D_order]; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL **gpp = gl->gpolypart[m]; REAL **tang = f_info->sides[m]; ss = SDIM_dot(tang[0],tang[0]); st = SDIM_dot(tang[0],tang[1]); tt = SDIM_dot(tang[1],tang[1]); det = ss*tt-st*st; if ( det <= 0.0 ) continue; area = sqrt(det)/2; f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ eval_second(METH_INSTANCE(f_info->method)->expr[0],f_info->gauss_pt[m],SDIM,&val, derivs,second,f_info->id); value += gl->gausswt[m]*area*val; /* gradients */ for ( k = 0 ; k < gl->lagpts ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) { detgrad[k][j] = 2*gpp[0][k]*tang[0][j]*tt + ss*2*gpp[1][k]*tang[1][j] - 2*st*(gpp[0][k]*tang[1][j] + gpp[1][k]*tang[0][j]); f_info->grad[k][j] += gl->gausswt[m]* (val/sqrt(det)/4*detgrad[k][j] + area*derivs[j]*gl->gpoly[m][k]); } /* hessian */ for ( k = 0 ; k < gl->lagpts ; k++ ) for ( kk = 0 ; kk < gl->lagpts ; kk++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) { dethess = 2*gpp[0][k]*tang[0][j]*2*gpp[1][kk]*tang[1][jj] + 2*gpp[0][kk]*tang[0][jj]*2*gpp[1][k]*tang[1][j] - 2*(gpp[0][kk]*tang[1][jj] + gpp[1][kk]*tang[0][jj]) *(gpp[0][k]*tang[1][j] + gpp[1][k]*tang[0][j]); if (j==jj) dethess += 2*gpp[0][k]*gpp[0][kk]*tt + ss*2*gpp[1][k]*gpp[1][kk] - 2*st*(gpp[0][k]*gpp[1][kk] + gpp[1][k]*gpp[0][kk]); f_info->hess[k][kk][j][jj] += gl->gausswt[m]* (derivs[jj]*gl->gpoly[m][kk]/sqrt(det)/4*detgrad[k][j] -0.5*val/sqrt(det)/det/4*detgrad[kk][jj]*detgrad[k][j] + val/sqrt(det)/4*dethess + 1/sqrt(det)/4*detgrad[kk][jj]*derivs[j]*gl->gpoly[m][k] + area*second[j][jj]*gl->gpoly[m][k]*gl->gpoly[m][kk]); } } return value; } /********************************************************************* facet_vector_integral method Integral of vectorfield over facet. 2D facet in 3D only. *********************************************************************/ /********************************************************************* * * function: facet_vector_integral_init() * * purpose: Check illegalities * */ void facet_vector_integral_init(mode,mi) int mode; struct method_instance *mi; { if ( web.dimension != 2 ) kb_error(1772,"facet_vector_integral method only for 2D facets.\n",RECOVERABLE); if ( SDIM != 3 ) kb_error(1773,"facet_vector_integral method only for 3D space.\n",RECOVERABLE); } /********************************************************************* * * function: facet_vector_integral() * * purpose: method value * */ REAL facet_vector_integral(f_info) struct qinfo *f_info; { int m,j; REAL value=0.0; if ( web.modeltype == QUADRATIC ) return facet_vector_integral_q(f_info); if ( web.modeltype == LAGRANGE ) return lagrange_vector_integral(f_info); for ( m = 0 ; m < gauss2D_num ; m++ ) { f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( j = 0 ; j < SDIM ; j++ ) { REAL green = gauss2Dwt[m]* eval(METH_INSTANCE(f_info->method)->expr[j],f_info->gauss_pt[m],f_info->id,NULL); value += f_info->normal[j]*green; } } return value/2; /* 2 is triangle factor for normal */ } /********************************************************************* * * function: facet_vector_integral_grad() * * purpose: method gradient * */ REAL facet_vector_integral_grad(f_info) struct qinfo *f_info; { int m,j,k; REAL value = 0.0; REAL val[MAXCOORD]; REAL derivs[MAXCOORD][MAXCOORD]; REAL sum; REAL cross0[MAXCOORD],cross1[MAXCOORD]; REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; if ( web.modeltype == QUADRATIC ) return facet_vector_integral_q_grad(f_info); if ( web.modeltype == LAGRANGE ) return lagrange_vector_integral_grad(f_info); for ( j = 0 ; j < SDIM ; j++ ) for ( m = 0 ; m < FACET_VERTS ; m++ ) f_info->grad[m][j] = 0.0; for ( m = 0 ; m < gauss2D_num ; m++ ) { REAL weight = sign*gauss2Dwt[m]; f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( j = 0 ; j < SDIM ; j++ ) eval_all(METH_INSTANCE(f_info->method)->expr[j],f_info->gauss_pt[m],SDIM,val+j, derivs[j],f_info->id); value += weight*SDIM_dot(val,f_info->normal); cross_prod(val,f_info->sides[0][0],cross0); cross_prod(val,f_info->sides[0][1],cross1); for ( k = 0 ; k < SDIM ; k++ ) { for ( sum = 0.0, j = 0 ; j < SDIM ; j++ ) sum += derivs[j][k]*f_info->normal[j]; f_info->grad[0][k] += weight*(gauss2Dpt[m][0]*sum + cross1[k] - cross0[k])/2; f_info->grad[1][k] += weight*(gauss2Dpt[m][1]*sum - cross1[k])/2; f_info->grad[2][k] += weight*(gauss2Dpt[m][2]*sum + cross0[k])/2; } } return value/2; } /********************************************************************* * * function: facet_vector_integral_hess() * * purpose: method gradient and hessian * */ REAL facet_vector_integral_hess(f_info) struct qinfo *f_info; { int m,i,j,k; REAL value = 0.0; REAL val[MAXCOORD]; REAL derivs[MAXCOORD][MAXCOORD]; REAL sum; REAL cross0[MAXCOORD],cross1[MAXCOORD]; MAT3D(second,MAXCOORD,MAXCOORD,MAXCOORD); REAL *s1 = f_info->sides[0][0]; REAL *s2 = f_info->sides[0][1]; int p,q,r,s; REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; if ( web.modeltype == QUADRATIC ) return facet_vector_integral_q_hess(f_info); if ( web.modeltype == LAGRANGE ) return lagrange_vector_integral_hess(f_info); for ( m = 0 ; m < gauss2D_num ; m++ ) { REAL weight = sign*gauss2Dwt[m]; f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( j = 0 ; j < SDIM ; j++ ) eval_second(METH_INSTANCE(f_info->method)->expr[j],f_info->gauss_pt[m],SDIM,val+j, derivs[j],second[j],f_info->id); value += weight*SDIM_dot(val,f_info->normal); cross_prod(val,f_info->sides[0][0],cross0); cross_prod(val,f_info->sides[0][1],cross1); for ( k = 0 ; k < SDIM ; k++ ) { for ( sum = 0.0, j = 0 ; j < SDIM ; j++ ) sum += derivs[j][k]*f_info->normal[j]; f_info->grad[0][k] += weight*(gauss2Dpt[m][0]*sum + cross1[k] - cross0[k])/2; f_info->grad[1][k] += weight*(gauss2Dpt[m][1]*sum - cross1[k])/2; f_info->grad[2][k] += weight*(gauss2Dpt[m][2]*sum + cross0[k])/2; } for ( r = 0 ; r < FACET_VERTS ; r++ ) for ( s = 0 ; s < FACET_VERTS ; s++ ) for ( p = 0 ; p < SDIM ; p++ ) for ( q = 0 ; q < SDIM ; q++ ) { sum = 0.0; for ( i = 0 ; i < 3 ; i++ ) { REAL tmp; j = (i+1)%3; k = (j+1)%3; if ( (r==1) && (p==i) ) { sum += s2[j]*gauss2Dpt[m][s]*derivs[k][q]; if ( (s==2) && (q==j) ) sum += val[k]; if ( (s==0) && (q==j) ) sum -= val[k]; } if ( (r==0) && (p==i) ) { sum -= s2[j]*gauss2Dpt[m][s]*derivs[k][q]; if ( (s==2) && (q==j) ) sum -= val[k]; if ( (s==0) && (q==j) ) sum += val[k]; } if ( (r==2) && (p==j) ) { sum += s1[i]*gauss2Dpt[m][s]*derivs[k][q]; if ( (s==1) && (q==i) ) sum += val[k]; if ( (s==0) && (q==i) ) sum -= val[k]; } if ( (r==0) && (p==j) ) { sum -= s1[i]*gauss2Dpt[m][s]*derivs[k][q]; if ( (s==1) && (q==i) ) sum -= val[k]; if ( (s==0) && (q==i) ) sum += val[k]; } if ( (s==1) && (q==i) ) sum += s2[j]*gauss2Dpt[m][r]*derivs[k][p]; if ( (s==0) && (q==i) ) sum -= s2[j]*gauss2Dpt[m][r]*derivs[k][p]; if ( (s==2) && (q==j) ) sum += s1[i]*gauss2Dpt[m][r]*derivs[k][p]; if ( (s==0) && (q==j) ) sum -= s1[i]*gauss2Dpt[m][r]*derivs[k][p]; tmp = s1[i]*s2[j]*gauss2Dpt[m][r]*gauss2Dpt[m][s]; sum += tmp*second[k][p][q]; } f_info->hess[r][s][p][q] += weight * sum/2; sum = 0.0; for ( i = 0 ; i < 3 ; i++ ) { REAL tmp; j = (i+2)%3; k = (j+2)%3; if ( (r==1) && (p==i) ) { sum += s2[j]*gauss2Dpt[m][s]*derivs[k][q]; if ( (s==2) && (q==j) ) sum += val[k]; if ( (s==0) && (q==j) ) sum -= val[k]; } if ( (r==0) && (p==i) ) { sum -= s2[j]*gauss2Dpt[m][s]*derivs[k][q]; if ( (s==2) && (q==j) ) sum -= val[k]; if ( (s==0) && (q==j) ) sum += val[k]; } if ( (r==2) && (p==j) ) { sum += s1[i]*gauss2Dpt[m][s]*derivs[k][q]; if ( (s==1) && (q==i) ) sum += val[k]; if ( (s==0) && (q==i) ) sum -= val[k]; } if ( (r==0) && (p==j) ) { sum -= s1[i]*gauss2Dpt[m][s]*derivs[k][q]; if ( (s==1) && (q==i) ) sum -= val[k]; if ( (s==0) && (q==i) ) sum += val[k]; } if ( (s==1) && (q==i) ) sum += s2[j]*gauss2Dpt[m][r]*derivs[k][p]; if ( (s==0) && (q==i) ) sum -= s2[j]*gauss2Dpt[m][r]*derivs[k][p]; if ( (s==2) && (q==j) ) sum += s1[i]*gauss2Dpt[m][r]*derivs[k][p]; if ( (s==0) && (q==j) ) sum -= s1[i]*gauss2Dpt[m][r]*derivs[k][p]; tmp = s1[i]*s2[j]*gauss2Dpt[m][r]*gauss2Dpt[m][s]; sum += tmp*second[k][p][q]; } f_info->hess[r][s][p][q] -= weight * sum/2; } } return value/2; } /********************************************************************* quadratic facet_vector_integral method Integral of vectorfield over facet. 2D facet in 3D only. *********************************************************************/ /********************************************************************* * * function: facet_vector_integral_q() * * purpose: method value * */ REAL facet_vector_integral_q(f_info) struct qinfo *f_info; { int m,j; REAL value=0.0,val[MAXCOORD]; REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; for ( m = 0 ; m < gauss2D_num ; m++ ) { REAL **tang = f_info->sides[m]; f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( j = 0 ; j < SDIM ; j++ ) { int jj = (j+1)%SDIM; int jjj = (j+2)%SDIM; val[j] = gauss2Dwt[m]* eval(METH_INSTANCE(f_info->method)->expr[j],f_info->gauss_pt[m],f_info->id,NULL); value += val[j]*(tang[0][jj]*tang[1][jjj] - tang[0][jjj]*tang[1][jj]); } } return sign*value/2; /* 2 is triangle factor */ } /********************************************************************* * * function: facet_vector_integral_q_grad() * * purpose: method gradient * */ REAL facet_vector_integral_q_grad(f_info) struct qinfo *f_info; { int m,i,j,k; REAL value = 0.0; REAL val; REAL derivs[MAXCOORD]; REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[k][j] = 0.0; for ( m = 0 ; m < gauss2D_num ; m++ ) { REAL **gpp = gpolypartial[m]; REAL wt = sign*0.5*gauss2Dwt[m]; /* include triangle factor */ REAL **tang = f_info->sides[m]; f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( j = 0 ; j < SDIM ; j++ ) { int jj = (j+1)%SDIM; int jjj = (j+2)%SDIM; eval_all(METH_INSTANCE(f_info->method)->expr[j],f_info->gauss_pt[m],SDIM,&val, derivs,f_info->id); value += wt*val*(tang[0][jj]*tang[1][jjj] - tang[0][jjj]*tang[1][jj]); for ( k = 0 ; k < FACET_CTRL ; k++ ) { f_info->grad[k][jj] += wt*val *(gpp[0][k]*tang[1][jjj] - tang[0][jjj]*gpp[1][k]); f_info->grad[k][jjj] += wt*val *(tang[0][jj]*gpp[1][k] - gpp[0][k]*tang[1][jj]); for ( i = 0 ; i < SDIM ; i++ ) f_info->grad[k][i] += wt*derivs[i]*gpoly[m][k] *(tang[0][jj]*tang[1][jjj] - tang[0][jjj]*tang[1][jj]); } } } return value; } /********************************************************************* * * function: facet_vector_integral_q_hess() * * purpose: method gradient and hessian * */ REAL facet_vector_integral_q_hess(f_info) struct qinfo *f_info; { int m,i,j,k,ii,kk; REAL value = 0.0; REAL val; REAL derivs[MAXCOORD]; MAT2D(second,MAXCOORD,MAXCOORD); REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; for ( m = 0 ; m < gauss2D_num ; m++ ) { REAL **gpp = gpolypartial[m]; REAL wt = sign*0.5*gauss2Dwt[m]; /* include triangle factor */ REAL **tang = f_info->sides[m]; f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( j = 0 ; j < SDIM ; j++ ) { int jj = (j+1)%SDIM; int jjj = (j+2)%SDIM; eval_second(METH_INSTANCE(f_info->method)->expr[j],f_info->gauss_pt[m],SDIM, &val, derivs,second,f_info->id); value += wt*val*(tang[0][jj]*tang[1][jjj] - tang[0][jjj]*tang[1][jj]); for ( k = 0 ; k < FACET_CTRL ; k++ ) { f_info->grad[k][jj] += wt*val *(gpp[0][k]*tang[1][jjj] - tang[0][jjj]*gpp[1][k]); f_info->grad[k][jjj] += wt*val *(tang[0][jj]*gpp[1][k] - gpp[0][k]*tang[1][jj]); for ( i = 0 ; i < SDIM ; i++ ) f_info->grad[k][i] += wt*derivs[i]*gpoly[m][k] *(tang[0][jj]*tang[1][jjj] - tang[0][jjj]*tang[1][jj]); } /* hessian */ for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( kk = 0 ; kk < FACET_CTRL ; kk++ ) { f_info->hess[k][kk][jj][jjj] += wt*val *(gpp[0][k]*gpp[1][kk] - gpp[0][kk]*gpp[1][k]); f_info->hess[k][kk][jjj][jj] += wt*val *(gpp[0][kk]*gpp[1][k] - gpp[0][k]*gpp[1][kk]); for ( ii = 0 ; ii < SDIM ; ii++ ) { f_info->hess[k][kk][jj][ii] += wt*derivs[ii]*gpoly[m][kk] *(gpp[0][k]*tang[1][jjj] - tang[0][jjj]*gpp[1][k]); f_info->hess[k][kk][jjj][ii] += wt*derivs[ii]*gpoly[m][kk] *(tang[0][jj]*gpp[1][k] - gpp[0][k]*tang[1][jj]); } for ( i = 0 ; i < SDIM ; i++ ) { for ( ii = 0 ; ii < SDIM ; ii++ ) f_info->hess[k][kk][i][ii] += wt*second[i][ii]*gpoly[m][k]*gpoly[m][kk] *(tang[0][jj]*tang[1][jjj] - tang[0][jjj]*tang[1][jj]); f_info->hess[k][kk][i][jj] += wt*derivs[i]*gpoly[m][k] *(gpp[0][kk]*tang[1][jjj] - tang[0][jjj]*gpp[1][kk]); f_info->hess[k][kk][i][jjj] += wt*derivs[i]*gpoly[m][k] *(tang[0][jj]*gpp[1][kk] - gpp[0][kk]*tang[1][jj]); } } } } return value; } /*********************************************************************** Named Method spherical_area Assumes vertices of a facet are on a unit sphere (in any dimension). Value is the area of the spherical triangle. ***********************************************************************/ REAL spherical_area_value(f_info) struct qinfo *f_info; { int i; REAL area; REAL a[FACET_EDGES+2]; /* squares of side lengths */ REAL *b; /* Value done by angle excess */ a[0] = SDIM_dot(f_info->sides[0][0],f_info->sides[0][0]); a[2] = SDIM_dot(f_info->sides[0][1],f_info->sides[0][1]); a[1] = a[0] + a[2] - 2*SDIM_dot(f_info->sides[0][0],f_info->sides[0][1]); a[FACET_EDGES] = a[0]; a[FACET_EDGES+1] = a[1]; for ( area = -M_PI, i = 0, b = a ; i < FACET_VERTS ; i++,b++ ) { REAL v = sqrt(b[1]*b[2]*(1-b[1]/4)*(1-b[2]/4)); REAL w = (b[1]+b[2]-b[0]-b[1]*b[2]/2); REAL u = w/2/v; area += acos(u); } return area; } REAL spherical_area_grad(f_info) struct qinfo *f_info; { int i,j; REAL area; REAL a[FACET_EDGES+2]; /* squares of side lengths */ REAL *b; REAL *x[FACET_VERTS+2]; /* Value done by angle excess */ a[0] = SDIM_dot(f_info->sides[0][0],f_info->sides[0][0]); a[2] = SDIM_dot(f_info->sides[0][1],f_info->sides[0][1]); a[1] = a[0] + a[2] - 2*SDIM_dot(f_info->sides[0][0],f_info->sides[0][1]); a[FACET_EDGES] = a[0]; a[FACET_EDGES+1] = a[1]; for ( i = 0 ; i < FACET_VERTS ; i++ ) x[i] = f_info->x[i]; x[FACET_VERTS] = x[0]; x[FACET_VERTS+1] = x[1]; for ( area = -M_PI, i = 0, b = a ; i < FACET_VERTS ; i++,b++ ) { REAL v = sqrt(b[1]*b[2]*(1-b[1]/4)*(1-b[2]/4)); REAL w = (b[1]+b[2]-b[0]-b[1]*b[2]/2); REAL u = w/2/v; REAL dudb0 = -1/2./v; REAL dudb1 = (1-b[2]/2)/2./v - w/4/v/v/v*(b[2]*(1-b[1]/4)*(1-b[2]/4)-b[1]*b[2]*(1./4)*(1-b[2]/4)); REAL dudb2 = (1-b[1]/2)/2./v - w/4/v/v/v*(b[1]*(1-b[1]/4)*(1-b[2]/4)-b[1]*b[2]*(1./4)*(1-b[1]/4)); area += acos(u); for ( j = 0 ; j < SDIM ; j++ ) { f_info->grad[i][j] += -1/sqrt(1-u*u)*2*(dudb2*(x[i][j]-x[i+2][j])+dudb0*(x[i][j]-x[i+1][j])); f_info->grad[(i+1)%3][j] += -1/sqrt(1-u*u)*2*(dudb1*(x[i+1][j]-x[i+2][j])+dudb0*(x[i+1][j]-x[i][j])); f_info->grad[(i+2)%3][j] += -1/sqrt(1-u*u)*2*(dudb1*(x[i+2][j]-x[i+1][j])+dudb2*(x[i+2][j]-x[i][j])); } } return area; } evolver-2.30c.dfsg/src/zoom.c0000644000175300017530000000764611410765113016362 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /****************************************************************** * * File: zoom.c * * Contents: Routines to zoom in on vertex. * */ #include "include.h" /****************************************************************** * * Function: zoom_vertex() * * Purpose: Zoom in on a vertex by chopping off everything beyond * a given distance and fixing in place the jagged edges. */ void zoom_vertex(v_id,radius) vertex_id v_id; /* vertex to zoom on */ REAL radius; /* cutoff distance from v_id */ { vertex_id vv_id; edge_id e_id; facet_id f_id; facetedge_id fe_id,next,prev; int i; if ( (web.bodycount != 0) && (web.representation == SOAPFILM) ) kb_error(1540,"Zoom is not implemented for bodies.\n",RECOVERABLE); if ( radius <= 0.0 ) kb_error(1541,"Must have positive cut-off radius.\n",RECOVERABLE); if ( !valid_id(v_id) || !valid_element(v_id) ) { sprintf(errmsg,"Vertex %s is not valid.\n",ELNAME(v_id)); kb_error(1542,errmsg,RECOVERABLE); } /* eliminate all vertices beyond cutoff */ FOR_ALL_VERTICES(vv_id) { if ( distance(v_id,vv_id) > radius ) set_attr(vv_id,DISSOLVED); } /* eliminate all edges connected to gone vertices */ FOR_ALL_EDGES(e_id) { int val_head,val_tail; val_tail = !(get_vattr(get_edge_tailv(e_id)) & DISSOLVED); val_head = !(get_vattr(get_edge_headv(e_id)) & DISSOLVED); if ( !val_head || !val_tail ) { if (val_head) { set_attr(get_edge_headv(e_id),FIXED); } if (val_tail) { set_attr(get_edge_tailv(e_id),FIXED); } set_attr(e_id,DISSOLVED); } } /* eliminate all facets verging on gone edges */ FOR_ALL_FACETS(f_id) { fe_id = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { if ( get_eattr(get_fe_edge(fe_id)) & DISSOLVED ) { set_attr(f_id,DISSOLVED); break; } fe_id = get_next_edge(fe_id); } } /* eliminate all facet-edges on gone facets */ FOR_ALL_FACETEDGES(fe_id) { e_id = get_fe_edge(fe_id); if ( get_eattr(e_id) & DISSOLVED ) { set_attr(fe_id,DISSOLVED); continue; } f_id = get_fe_facet(fe_id); if ( valid_id(f_id) && (get_fattr(f_id) & DISSOLVED) ) { /* have kept edge on removed facet */ /* patch up ragged edges */ next = get_next_facet(fe_id); prev = get_prev_facet(fe_id); if ( equal_id(next,fe_id) ) { /* was only facet on edge */ set_edge_fe(e_id,NULLFACETEDGE); } else { /* close ranks */ set_next_facet(prev,next); set_prev_facet(next,prev); set_edge_fe(e_id,next); } if ( web.representation == SOAPFILM) { /* fix edge in place */ set_attr(e_id,FIXED); set_attr(get_edge_tailv(e_id),FIXED); set_attr(get_edge_headv(e_id),FIXED); } set_attr(fe_id,DISSOLVED); } } FOR_ALL_VERTICES(v_id) if ( get_vattr(v_id) & DISSOLVED ) free_element(v_id); FOR_ALL_EDGES(e_id) if ( get_eattr(e_id) & DISSOLVED ) free_element(e_id); FOR_ALL_FACETS(f_id) if ( get_fattr(f_id) & DISSOLVED ) free_element(f_id); FOR_ALL_FACETEDGES(fe_id) if ( get_attr(fe_id) & DISSOLVED ) free_element(fe_id); } evolver-2.30c.dfsg/src/lexyy.c0000644000175300017530000041253511410765113016545 0ustar hazelscthazelsct/* A lexical scanner generated by flex */ /* Scanner skeleton version: * $Header: /home/daffy/u0/vern/flex/RCS/flex.skl,v 2.85 95/04/24 10:48:47 vern Exp $ */ #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 5 #include /* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ #ifdef c_plusplus #ifndef __cplusplus #define __cplusplus #endif #endif #ifdef __cplusplus #include #include /* Use prototypes in function declarations. */ #define YY_USE_PROTOS /* The "const" storage-class-modifier is valid. */ #define YY_USE_CONST #else /* ! __cplusplus */ #if __STDC__ #define YY_USE_PROTOS #define YY_USE_CONST #endif /* __STDC__ */ #endif /* ! __cplusplus */ #ifdef __TURBOC__ #pragma warn -rch #pragma warn -use #include #include #define YY_USE_CONST #define YY_USE_PROTOS #endif #ifdef YY_USE_CONST #define yyconst const #else #define yyconst #endif #ifdef YY_USE_PROTOS #define YY_PROTO(proto) proto #else #define YY_PROTO(proto) () #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN yy_start = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START ((yy_start - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE yyrestart( yyin ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #define YY_BUF_SIZE 16384 typedef struct yy_buffer_state *YY_BUFFER_STATE; extern int yyleng; extern FILE *yyin, *yyout; #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 /* The funky do-while in the following #define is used to turn the definition * int a single C statement (which needs a semi-colon terminator). This * avoids problems with code like: * * if ( condition_holds ) * yyless( 5 ); * else * do_something_else(); * * Prior to using the do-while the compiler would get upset at the * "else" because it interpreted the "if" statement as being all * done when it reached the ';' after the yyless() call. */ /* Return all but the first 'n' matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ *yy_cp = yy_hold_char; \ yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up yytext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, yytext_ptr ) /* The following is because we cannot portably get our hands on size_t * (without autoconf's help, which isn't available because we want * flex-generated scanners to compile on their own). */ typedef unsigned int yy_size_t; struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ yy_size_t yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via yyrestart()), so that the user can continue scanning by * just pointing yyin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; static YY_BUFFER_STATE yy_current_buffer = 0; /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". */ #define YY_CURRENT_BUFFER yy_current_buffer /* yy_hold_char holds the character lost when yytext is formed. */ static char yy_hold_char; static int yy_n_chars; /* number of characters read into yy_ch_buf */ int yyleng; /* Points to current character in buffer. */ static char *yy_c_buf_p = (char *) 0; static int yy_init = 1; /* whether we need to initialize */ static int yy_start = 0; /* start state number */ /* Flag which is used to allow yywrap()'s to do buffer switches * instead of setting up a fresh yyin. A bit of a hack ... */ static int yy_did_buffer_switch_on_eof; void yyrestart YY_PROTO(( FILE *input_file )); void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer )); void yy_load_buffer_state YY_PROTO(( void )); YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size )); void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b )); void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file )); void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b )); #define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer ) YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size )); YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *str )); YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len )); static void *yy_flex_alloc YY_PROTO(( yy_size_t )); static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t )); static void yy_flex_free YY_PROTO(( void * )); #define yy_new_buffer yy_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! yy_current_buffer ) \ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ yy_current_buffer->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! yy_current_buffer ) \ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ yy_current_buffer->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (yy_current_buffer->yy_at_bol) typedef unsigned char YY_CHAR; FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; typedef int yy_state_type; extern char *yytext; #define yytext_ptr yytext static yy_state_type yy_get_previous_state YY_PROTO(( void )); static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state )); static int yy_get_next_buffer YY_PROTO(( void )); static void yy_fatal_error YY_PROTO(( yyconst char msg[] )); /* Done after the current pattern has been matched and before the * corresponding action - sets up yytext. */ #define YY_DO_BEFORE_ACTION \ yytext_ptr = yy_bp; \ yyleng = (int) (yy_cp - yy_bp); \ yy_hold_char = *yy_cp; \ *yy_cp = '\0'; \ yy_c_buf_p = yy_cp; #define YY_NUM_RULES 68 #define YY_END_OF_BUFFER 69 static yyconst short int yy_accept[227] = { 0, 0, 0, 69, 67, 65, 3, 56, 25, 67, 41, 67, 67, 46, 47, 41, 41, 40, 41, 41, 7, 7, 7, 66, 42, 41, 45, 41, 63, 63, 48, 43, 57, 44, 65, 41, 31, 41, 5, 5, 5, 0, 0, 28, 0, 51, 0, 0, 55, 0, 49, 36, 34, 35, 23, 1, 2, 37, 22, 7, 7, 11, 0, 0, 0, 32, 33, 52, 50, 53, 58, 63, 63, 0, 60, 54, 0, 0, 28, 0, 5, 0, 0, 30, 5, 20, 19, 5, 5, 0, 0, 0, 6, 26, 27, 6, 29, 0, 0, 0, 64, 0, 2, 22, 0, 0, 24, 10, 0, 38, 59, 63, 0, 6, 5, 20, 19, 0, 0, 14, 13, 0, 0, 19, 0, 8, 0, 21, 17, 16, 0, 9, 0, 0, 0, 23, 0, 12, 12, 0, 22, 39, 63, 14, 13, 0, 0, 19, 0, 8, 0, 21, 0, 13, 0, 0, 15, 0, 20, 0, 12, 0, 19, 0, 16, 0, 0, 18, 0, 0, 0, 22, 63, 0, 13, 0, 0, 15, 0, 20, 0, 0, 19, 0, 14, 0, 0, 13, 0, 19, 0, 17, 0, 0, 16, 0, 0, 63, 0, 14, 0, 0, 13, 0, 19, 0, 13, 0, 16, 61, 0, 63, 0, 13, 0, 63, 0, 0, 63, 0, 62, 63, 63, 63, 63, 4, 0 } ; static yyconst int yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 4, 5, 6, 7, 1, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 21, 21, 21, 21, 21, 21, 21, 22, 23, 24, 25, 26, 8, 27, 28, 29, 28, 30, 30, 28, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 8, 1, 8, 8, 32, 33, 34, 35, 36, 37, 38, 39, 40, 40, 41, 40, 40, 42, 40, 43, 44, 40, 40, 40, 40, 45, 46, 40, 40, 47, 40, 40, 48, 49, 50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst int yy_meta[51] = { 0, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 1, 1, 1, 1, 1, 4, 4, 5, 6, 7, 1, 5, 5, 5, 5, 5, 5, 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1 } ; static yyconst short int yy_base[233] = { 0, 0, 49, 281, 825, 57, 825, 255, 825, 15, 825, 256, 0, 825, 825, 42, 239, 228, 60, 69, 78, 204, 72, 32, 225, 218, 213, 49, 0, 195, 68, 825, 175, 825, 124, 102, 129, 65, 138, 175, 169, 116, 84, 206, 142, 825, 173, 162, 825, 188, 825, 825, 825, 825, 163, 825, 0, 825, 183, 150, 198, 825, 158, 0, 89, 825, 825, 825, 825, 825, 169, 0, 151, 102, 825, 825, 0, 213, 220, 132, 225, 228, 237, 825, 249, 252, 264, 145, 0, 240, 277, 257, 286, 56, 825, 308, 825, 289, 152, 151, 825, 298, 0, 311, 323, 301, 331, 0, 156, 825, 825, 135, 334, 339, 361, 364, 376, 342, 351, 388, 391, 403, 416, 419, 427, 354, 384, 431, 434, 439, 447, 454, 130, 128, 459, 462, 470, 473, 481, 485, 488, 825, 125, 491, 494, 506, 519, 522, 530, 497, 534, 537, 547, 550, 558, 562, 565, 570, 573, 581, 589, 592, 595, 603, 606, 614, 618, 621, 121, 114, 626, 629, 124, 637, 640, 648, 652, 655, 660, 663, 671, 674, 677, 680, 683, 691, 694, 697, 700, 703, 706, 709, 717, 720, 723, 109, 105, 97, 726, 729, 737, 740, 743, 746, 749, 752, 755, 758, 761, 825, 96, 88, 764, 767, 210, 81, 309, 106, 62, 90, 825, 58, 45, 20, 30, 0, 825, 785, 791, 799, 805, 808, 816 } ; static yyconst short int yy_def[233] = { 0, 226, 1, 226, 226, 226, 226, 226, 226, 226, 226, 226, 227, 226, 226, 226, 226, 226, 226, 226, 226, 20, 226, 226, 226, 226, 226, 226, 228, 228, 226, 226, 226, 226, 226, 226, 226, 226, 226, 38, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 229, 226, 226, 20, 226, 226, 226, 230, 226, 226, 226, 226, 226, 226, 226, 228, 228, 226, 226, 226, 34, 226, 43, 226, 226, 226, 226, 226, 226, 226, 226, 38, 40, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 229, 231, 226, 226, 226, 230, 226, 226, 226, 228, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 231, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 231, 137, 226, 226, 226, 228, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 137, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 228, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 228, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 228, 226, 226, 226, 228, 226, 232, 228, 232, 226, 228, 228, 228, 228, 228, 0, 226, 226, 226, 226, 226, 226 } ; static yyconst short int yy_nxt[876] = { 0, 4, 5, 6, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 10, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 4, 28, 28, 28, 28, 29, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 31, 32, 33, 34, 46, 34, 64, 50, 47, 65, 93, 41, 93, 41, 225, 35, 224, 36, 37, 51, 38, 39, 40, 42, 73, 43, 69, 70, 44, 44, 44, 54, 54, 54, 55, 74, 85, 85, 85, 56, 223, 58, 222, 60, 60, 60, 57, 58, 220, 59, 59, 60, 221, 91, 62, 92, 92, 92, 73, 61, 62, 62, 62, 108, 220, 61, 109, 62, 62, 74, 41, 81, 41, 82, 82, 82, 218, 63, 76, 52, 76, 215, 42, 83, 43, 83, 214, 44, 44, 44, 77, 211, 78, 79, 210, 80, 80, 80, 81, 209, 84, 84, 84, 115, 115, 115, 53, 86, 197, 87, 87, 88, 196, 44, 44, 44, 195, 89, 172, 61, 90, 97, 169, 168, 105, 61, 105, 90, 90, 106, 106, 106, 142, 141, 54, 54, 54, 63, 86, 133, 88, 88, 88, 132, 226, 101, 111, 110, 89, 226, 100, 90, 101, 101, 103, 103, 103, 99, 90, 90, 93, 94, 93, 98, 216, 104, 216, 58, 217, 60, 60, 60, 104, 104, 226, 91, 75, 95, 95, 95, 62, 72, 112, 96, 113, 113, 113, 62, 62, 112, 68, 114, 114, 114, 116, 67, 80, 80, 80, 119, 119, 119, 66, 226, 117, 53, 120, 118, 82, 82, 82, 125, 125, 125, 118, 118, 52, 48, 120, 121, 84, 84, 84, 85, 85, 85, 121, 121, 128, 128, 128, 121, 45, 226, 122, 123, 123, 123, 121, 121, 226, 122, 122, 126, 226, 126, 124, 226, 127, 127, 127, 226, 226, 124, 124, 129, 226, 92, 92, 92, 131, 131, 131, 216, 134, 216, 134, 217, 130, 135, 135, 135, 106, 106, 106, 130, 130, 129, 226, 95, 95, 95, 103, 103, 103, 226, 226, 97, 226, 139, 130, 139, 226, 136, 140, 140, 140, 130, 130, 226, 138, 138, 106, 106, 106, 143, 143, 143, 144, 226, 113, 113, 113, 149, 149, 149, 226, 150, 226, 150, 226, 145, 151, 151, 151, 125, 125, 125, 145, 145, 144, 226, 114, 114, 114, 115, 115, 115, 226, 226, 97, 226, 226, 145, 226, 226, 146, 147, 147, 147, 145, 145, 226, 146, 146, 127, 127, 127, 148, 119, 119, 119, 153, 153, 153, 148, 148, 226, 226, 155, 152, 155, 226, 154, 156, 156, 156, 152, 152, 226, 154, 154, 157, 226, 157, 226, 226, 158, 158, 158, 123, 123, 123, 161, 226, 161, 226, 226, 162, 162, 162, 159, 127, 127, 127, 128, 128, 128, 160, 160, 164, 164, 164, 166, 226, 166, 163, 226, 167, 167, 167, 165, 226, 163, 163, 131, 131, 131, 165, 165, 135, 135, 135, 135, 135, 135, 170, 226, 170, 226, 226, 171, 171, 171, 226, 226, 226, 170, 226, 170, 226, 226, 171, 171, 171, 226, 140, 140, 140, 140, 140, 140, 143, 143, 143, 174, 174, 174, 149, 149, 149, 226, 176, 173, 176, 226, 175, 177, 177, 177, 173, 173, 226, 175, 175, 178, 226, 178, 226, 226, 179, 179, 179, 147, 147, 147, 181, 226, 181, 226, 226, 182, 182, 182, 180, 151, 151, 151, 151, 151, 151, 180, 180, 183, 226, 183, 226, 226, 184, 184, 184, 153, 153, 153, 186, 226, 186, 226, 226, 187, 187, 187, 185, 156, 156, 156, 156, 156, 156, 185, 185, 158, 158, 158, 158, 158, 158, 188, 226, 188, 226, 226, 189, 189, 189, 188, 226, 188, 226, 226, 189, 189, 189, 162, 162, 162, 162, 162, 162, 190, 226, 190, 226, 226, 191, 191, 191, 164, 164, 164, 193, 226, 193, 226, 226, 194, 194, 194, 192, 167, 167, 167, 167, 167, 167, 192, 192, 171, 171, 171, 171, 171, 171, 198, 226, 198, 226, 226, 199, 199, 199, 174, 174, 174, 201, 226, 201, 226, 226, 202, 202, 202, 200, 177, 177, 177, 177, 177, 177, 200, 200, 179, 179, 179, 179, 179, 179, 203, 226, 203, 226, 226, 204, 204, 204, 182, 182, 182, 182, 182, 182, 184, 184, 184, 184, 184, 184, 205, 226, 205, 226, 226, 206, 206, 206, 187, 187, 187, 187, 187, 187, 189, 189, 189, 189, 189, 189, 191, 191, 191, 191, 191, 191, 207, 226, 207, 226, 226, 208, 208, 208, 194, 194, 194, 194, 194, 194, 199, 199, 199, 199, 199, 199, 212, 226, 212, 226, 226, 213, 213, 213, 202, 202, 202, 202, 202, 202, 204, 204, 204, 204, 204, 204, 206, 206, 206, 206, 206, 206, 208, 208, 208, 208, 208, 208, 213, 213, 213, 213, 213, 213, 49, 49, 49, 226, 49, 71, 71, 71, 71, 71, 71, 102, 226, 102, 102, 102, 102, 102, 102, 107, 107, 107, 137, 226, 137, 226, 226, 137, 219, 219, 219, 219, 219, 219, 219, 219, 3, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226 } ; static yyconst short int yy_chk[876] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 9, 2, 23, 15, 9, 23, 93, 5, 93, 5, 224, 2, 223, 2, 2, 15, 2, 2, 2, 5, 30, 5, 27, 27, 5, 5, 5, 18, 18, 18, 19, 30, 37, 37, 37, 19, 222, 22, 221, 22, 22, 22, 19, 20, 219, 20, 20, 20, 218, 42, 22, 42, 42, 42, 73, 20, 20, 22, 22, 64, 217, 20, 64, 20, 20, 73, 41, 35, 41, 35, 35, 35, 215, 20, 34, 35, 34, 211, 41, 36, 41, 36, 210, 41, 41, 41, 34, 197, 34, 34, 196, 34, 34, 34, 36, 195, 36, 36, 36, 79, 79, 79, 36, 38, 172, 38, 38, 38, 169, 44, 44, 44, 168, 38, 142, 38, 38, 44, 133, 132, 62, 38, 62, 38, 38, 62, 62, 62, 111, 108, 54, 54, 54, 38, 40, 99, 40, 40, 40, 98, 87, 54, 72, 70, 40, 59, 49, 40, 54, 54, 58, 58, 58, 47, 40, 40, 43, 43, 43, 46, 214, 58, 214, 60, 214, 60, 60, 60, 58, 58, 39, 43, 32, 43, 43, 43, 60, 29, 77, 43, 77, 77, 77, 60, 60, 78, 26, 78, 78, 78, 80, 25, 80, 80, 80, 81, 81, 81, 24, 21, 80, 17, 82, 80, 82, 82, 82, 89, 89, 89, 80, 80, 16, 11, 84, 82, 84, 84, 84, 85, 85, 85, 82, 82, 91, 91, 91, 84, 7, 3, 85, 86, 86, 86, 84, 84, 0, 85, 85, 90, 0, 90, 86, 0, 90, 90, 90, 0, 0, 86, 86, 92, 0, 92, 92, 92, 97, 97, 97, 216, 101, 216, 101, 216, 92, 101, 101, 101, 105, 105, 105, 92, 92, 95, 0, 95, 95, 95, 103, 103, 103, 0, 0, 95, 0, 104, 95, 104, 0, 103, 104, 104, 104, 95, 95, 0, 103, 103, 106, 106, 106, 112, 112, 112, 113, 0, 113, 113, 113, 117, 117, 117, 0, 118, 0, 118, 0, 113, 118, 118, 118, 125, 125, 125, 113, 113, 114, 0, 114, 114, 114, 115, 115, 115, 0, 0, 114, 0, 0, 114, 0, 0, 115, 116, 116, 116, 114, 114, 0, 115, 115, 126, 126, 126, 116, 119, 119, 119, 120, 120, 120, 116, 116, 0, 0, 121, 119, 121, 0, 120, 121, 121, 121, 119, 119, 0, 120, 120, 122, 0, 122, 0, 0, 122, 122, 122, 123, 123, 123, 124, 0, 124, 0, 0, 124, 124, 124, 123, 127, 127, 127, 128, 128, 128, 123, 123, 129, 129, 129, 130, 0, 130, 128, 0, 130, 130, 130, 129, 0, 128, 128, 131, 131, 131, 129, 129, 134, 134, 134, 135, 135, 135, 136, 0, 136, 0, 0, 136, 136, 136, 137, 137, 137, 138, 0, 138, 0, 0, 138, 138, 138, 137, 139, 139, 139, 140, 140, 140, 143, 143, 143, 144, 144, 144, 149, 149, 149, 0, 145, 143, 145, 0, 144, 145, 145, 145, 143, 143, 0, 144, 144, 146, 0, 146, 0, 0, 146, 146, 146, 147, 147, 147, 148, 0, 148, 0, 0, 148, 148, 148, 147, 150, 150, 150, 151, 151, 151, 147, 147, 152, 0, 152, 0, 0, 152, 152, 152, 153, 153, 153, 154, 0, 154, 0, 0, 154, 154, 154, 153, 155, 155, 155, 156, 156, 156, 153, 153, 157, 157, 157, 158, 158, 158, 159, 0, 159, 0, 0, 159, 159, 159, 160, 0, 160, 0, 0, 160, 160, 160, 161, 161, 161, 162, 162, 162, 163, 0, 163, 0, 0, 163, 163, 163, 164, 164, 164, 165, 0, 165, 0, 0, 165, 165, 165, 164, 166, 166, 166, 167, 167, 167, 164, 164, 170, 170, 170, 171, 171, 171, 173, 0, 173, 0, 0, 173, 173, 173, 174, 174, 174, 175, 0, 175, 0, 0, 175, 175, 175, 174, 176, 176, 176, 177, 177, 177, 174, 174, 178, 178, 178, 179, 179, 179, 180, 0, 180, 0, 0, 180, 180, 180, 181, 181, 181, 182, 182, 182, 183, 183, 183, 184, 184, 184, 185, 0, 185, 0, 0, 185, 185, 185, 186, 186, 186, 187, 187, 187, 188, 188, 188, 189, 189, 189, 190, 190, 190, 191, 191, 191, 192, 0, 192, 0, 0, 192, 192, 192, 193, 193, 193, 194, 194, 194, 198, 198, 198, 199, 199, 199, 200, 0, 200, 0, 0, 200, 200, 200, 201, 201, 201, 202, 202, 202, 203, 203, 203, 204, 204, 204, 205, 205, 205, 206, 206, 206, 207, 207, 207, 208, 208, 208, 212, 212, 212, 213, 213, 213, 227, 227, 227, 0, 227, 228, 228, 228, 228, 228, 228, 229, 0, 229, 229, 229, 229, 229, 229, 230, 230, 230, 231, 0, 231, 0, 0, 231, 232, 232, 232, 232, 232, 232, 232, 232, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226 } ; static yy_state_type yy_last_accepting_state; static char *yy_last_accepting_cpos; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 char *yytext; #define INITIAL 0 /******************************************************************** * * File: datafile.lex, lexyy.c * * Contents: lexical analyzer for evolver data files. * Constructed from datafile.lex by lex. */ /* will have my own input() and output() */ #ifdef FLEX_SCANNER #define YY_INPUT(buf,result,max_size) \ { \ int c = kb_input(); \ result = (c == 0) ? YY_NULL : (buf[0] = c, 1); \ } #else /* lex scanner */ #undef input #undef unput #define input kb_input #define unput rawunput #endif #include "include.h" #include "lex.h" #include "ytab.h" static int previous_char = 0; /* for CR conversion */ static int BUFFSIZE = 0; /* size of lex buffer */ static char *buff = NULL; /* lex buffer, expandable */ static int spot = 0; #define ERRBUFFSIZE 80 static char errbuff[ERRBUFFSIZE+4]; /* for reporting spot of error */ static int errspot; extern int ubuff_spot; struct ckey { char *name; int token; } const_expr_keywords[] = { {"pi",PI_}, {"e",E_}, {"g",G_} }; void savein ARGS((int)); void get_more_input ARGS((void)); int keyword_compare ARGS(( struct ckey *, struct ckey *)); /* Wrapper for yylex() to buffer look-ahead token */ #define UNPUTMAX 10 int unputted_tok[UNPUTMAX]; /* unput token number */ int unput_tok_count; /* 1 if token was unput */ YYSTYPE yylval; char kb_input_new ARGS((void)); void kb_unput ARGS((char)); int kblex ARGS((void)); #define KB_INPUT kb_input_new #define KB_UNPUT kb_unput /* for generation of bad tokens for error message generation */ /* for use with -E command line option */ int next_err_token = 0; /* token in file for which to generate error */ int token_count; /* resets on file initialization */ int err_tok_gen_flag; /* whether -E option in effect */ int kb_yylex(lvalp) YYSTYPE *lvalp; /* for pure parser destination for yylval */ { if ( lvalp ) { PROF_FINISH(yyparse); /* exclude from yyparse time */ } PROF_START(yylex); if ( unput_tok_count ) { tok = unputted_tok[--unput_tok_count]; } else { /* clean out previous data */ /* memset(&yylval,0,sizeof(yylval)); time hog*/ /* tok = yylex(); */ if ( err_tok_gen_flag ) { if ( token_count == next_err_token ) { tok = GEN_ERROR_TOKEN; if ( yytext ) strcpy(yytext,"generated error"); next_err_token++; token_count += 10000000; /* just one error per file */ return tok; } token_count++; } tok = kblex(); } switch ( tok ) { case '(' : parens++; break; case ')' : parens--; break; case '{' : brace_depth++; break; case '}' : brace_depth--; break; } if ( lvalp ) { { /* non-movsd substitute for *lvalp = yylval; */ int i; int *dest = (int*)lvalp; int *src = (int*)&yylval; for ( i = 0 ; i < sizeof(yylval)/sizeof(int) ; i++ ) *(dest++) = *(src++); } PROF_START(yyparse); } PROF_FINISH(yylex); return tok; } /* For unputting unneeded look-ahead token */ void unput_tok() { if ( unput_tok_count >= UNPUTMAX ) kb_error(2324,"Internal error: unputted_tok stack overflow.\n",RECOVERABLE); unputted_tok[unput_tok_count++] = tok; /* fprintf(stderr,"UNPUT %3d %s %s\n",tok,tokname(tok),yytext); */ /* tok = UNPUTTED_; */ /* some places depend on knowing unput lookahead */ } #ifndef NOPROTO int rawinput ARGS((void)); int yyback(int *,int); /* #define yyoutput yyout_unused */ /* #define yyunput yyunput_unused */ #endif extern FILE *data_fd; extern char *cmdptr; int line_no = 1; int macro_flag; /* tells macro() identifier is being #define'd */ /* return values for macro() */ #define NO_MACRO 0 #define WAS_MACRO_DEF 1 #define MACRO_EXPANDED 2 char *whitespace = " \t\r,:;"; /* whitespace */ #define SUBMAX 500 int macro_max; int macro_subs_top; /* index of top of string space */ int macro_subs_max; /* space allocated for strings */ struct dkey { char *name; int token; } datafile_keywords[] = { {"content_rank",CONTENT_RANK_}, {"clip_coeff",CLIP_COEFF}, {"slice_coeff",SLICE_COEFF}, {"high_constraint",V_HIGH_CONSTRAINT}, {"high_boundary",V_HIGH_BOUNDARY}, {"suppress_warning",SUPPRESS_WARNING_}, {"unsuppress_warning",UNSUPPRESS_WARNING_}, {"volume_method_name",VOLUME_METHOD_NAME_}, {"partner_hitting",PARTNER_HITTING_}, {"procedure",PROCEDURE_WORD_}, {"display_origin",DISPLAY_ORIGIN_}, {"length_method_name",LENGTH_METHOD_NAME_}, {"area_method_name",AREA_METHOD_NAME_}, {"hessian_special_normal_vector",HESSIAN_SPECIAL_NORMAL_VECTOR_}, {"keep_originals",KEEP_ORIGINALS_}, {"element_modulus",ELEMENT_MODULUS_}, {"swap_colors",SWAP_COLORS_}, {"self",SELF_}, {"conserved",CONSERVED_}, {"actual_volume",ACTUAL_VOLUME_}, {"keep_macros",KEEP_MACROS_}, {"pdelta",DELTA_ }, {"tolerance",TOLERANCE_}, {"lagrange_multiplier",LAGRANGE_MULTIPLIER_ }, {"evolver_version",VERSION_}, {"orientation",ORIENTATION_}, {"ignore_constraints",IGNORE_CONSTRAINTS_}, {"ignore_fixed",IGNORE_FIXED_}, {"load_library",LOAD_LIBRARY_}, {"interp_bdry_param",INTERP_BDRY_PARAM_}, {"axial_point",AXIAL_POINT_}, {"lagrange",LAGRANGE_}, {"lagrange_order",LAGRANGE_ORDER_}, {"parameter_1",PARAMETER_1_}, {"optimizing_parameter",OPTIMIZING_PARAMETER_}, {"optimising_parameter",OPTIMIZING_PARAMETER_}, {"everything_quantities",EVERYTHING_QUANTITIES_}, {"value",VALUE_}, {"target",TARGET_}, {"define",DEFINE_}, {"attribute",ATTRIBUTE_}, {"method_instance",METHOD_INSTANCE_}, {"method",METHOD_}, {"scalar_integrand",SCALAR_INTEGRAND_}, {"vector_integrand",VECTOR_INTEGRAND_}, {"k_vector_order",K_VEC_ORDER_}, {"form_integrand",FORM_INTEGRAND_}, {"mobility",MOBILITY_}, {"mobility_tensor",MOBILITY_TENSOR_}, {"bare",BARE_}, {"boundary_curvature",BOUNDARY_CURVATURE_}, {"modulus",MODULUS_}, {"info_only",INFO_ONLY_}, {"global_method",GLOBAL_METHOD_}, {"global",GLOBAL_}, {"area_fixed",AREA_FIXED_}, {"fixed_area",AREA_FIXED_}, {"view_matrix",VIEW_MATRIX_}, {"view_transforms",VIEW_TRANSFORMS_}, {"view_transform_generators",VIEW_TRANSFORM_GENS_}, {"homothety",HOMOTHETY_}, {"approximate_curvature",APPROX_CURV_}, {"approx_curvature",APPROX_CURV_}, {"phasefile",PHASEFILE_}, {"phase",PHASE_}, {"autopop",AUTOPOP_}, {"autopop_quartic",AUTOPOP_QUARTIC_}, {"autochop",AUTOCHOP_}, {"total_time",TOTAL_TIME_}, {"effective_area",EFFECTIVE_AREA_}, {"runge_kutta",RUNGE_KUTTA_}, {"color",COLOR_}, {"backcolor",BACKCOLOR_}, {"frontcolor",FRONTCOLOR_}, {"mean_curvature_integral",MEAN_CURV_INT_}, {"normal_curvature",NORMAL_CURVATURE_}, {"square_curvature",SQUARE_CURVATURE_}, {"squared_curvature",SQUARE_CURVATURE_}, {"square_gaussian_curvature",SQGAUSS_}, {"squared_gaussian_curvature",SQGAUSS_}, {"gauss_curvature",GAUSS_CURVATURE_}, {"insulating_knot_energy",INSULATING_KNOT_ENERGY_}, {"conducting_knot_energy",CONDUCTING_KNOT_ENERGY_}, {"space_dimension",SPACE_DIMENSION_}, {"surface_dimension",SURFACE_DIMENSION_}, {"simplex_representation",SIMPLEX_REP_}, {"metric",METRIC_}, {"klein_metric",KLEIN_METRIC_}, {"conformal_metric",CONFORMAL_}, {"fixed",FIXED_}, {"no_refine",NO_REFINE_}, {"hit_partner",HIT_PARTNER_}, {"no_display",NODISPLAY_}, {"noncontent",NONCONTENT_}, {"efixed",EFIXED_}, {"symmetry_group",SYMMETRY_GROUP_}, {"wrap",WRAP_}, {"torus",TORUS_}, {"torus_filled",TORUS_FILLED_}, {"torus_periods",TORUS_PERIODS_}, {"periods",PERIODS_}, {"display_periods",DISPLAY_PERIODS_}, {"string",STRING_}, {"soapfilm",SOAPFILM_}, {"wulff",WULFF_}, {"boundary",BOUNDARY_}, {"boundaries",BOUNDARY_}, {"constraint",CONSTRAINT_}, {"constraints",CONSTRAINT_}, {"surface_energy",SURFACE_ENERGY_}, {"formula",FUNCTION_}, {"function",FUNCTION_}, {"parameter",PARAMETERS_}, {"parameters",PARAMETERS_}, {"parameter_file",PARAMETER_FILE_}, {"symmetric_content",SYMMETRIC_CONTENT_}, {"integral_order",V_INTEGRAL_ORDER}, {"integral_order_1d",V_INTEGRAL_ORDER_1D}, {"integral_order_2d",V_INTEGRAL_ORDER_2D}, {"integration_order",V_INTEGRAL_ORDER}, {"integration_order_1d",V_INTEGRAL_ORDER_1D}, {"integration_order_2d",V_INTEGRAL_ORDER_2D}, {"constraint_tolerance",CONSTRAINT_TOLERANCE_ }, {"convex",CONVEX_}, {"nonwall",NONWALL_}, {"nonnegative",NONNEGATIVE_}, {"nonpositive",NONPOSITIVE_}, {"global",GLOBAL_}, {"energy",ENERGY_}, {"content",CONTENT_}, {"quadratic",QUADRATIC_}, {"linear",LINEAR_}, {"area_normalization",MEAN_CURV_}, {"jiggle",JIGGLE_}, {"diffusion",DIFFUSION_}, {"merit_factor",MERITFACTOR_}, {"gravity_constant",GRAV_CONST_}, {"spring_constant",SPRING_CONSTANT_}, {"gap_constant",GAP_CONSTANT_}, {"scale",SCALE_}, {"pscale",SCALE_}, {"temperature",TEMPERATURE_}, {"pressure",PRESSURE_}, {"volume",VOLUME_}, {"density",DENSITY_}, {"tension",DENSITY_}, {"nodisplay",NODISPLAY_}, {"scale_limit",SCALE_LIMIT_}, {"zoom_vertex",ZOOM_VERTEX_}, {"zoom_radius",ZOOM_RADIUS_}, {"quantity",QUANTITY_ }, {"volconst",VOLCONST_ }, {"read",READ_}, {"and",AND_}, {"or",OR_}, {"not",NOT_}, {"mod",'%'}, {"imod",IMOD_}, {"idiv",IDIV_}, {"dot_product",DOT_}, {"pi",PI_}, {"e",E_}, {"g",G_}, {"tag",TAG_}, {"original",ORIGINAL_}, {"vertices",VERTICES_}, {"vertex",VERTICES_}, {"edges",EDGES_}, {"edge",EDGES_}, {"faces",FACES_}, {"face",FACES_}, {"facets",FACES_}, {"facet",FACES_}, {"bodies",BODIES_}, {"body",BODIES_}, {"facet_edges",FACETEDGES_}, {"facet_edge",FACETEDGES_}, {"facetedges",FACETEDGES_}, {"facetedge",FACETEDGES_} }; struct ckey mathfunc_keywords[] = { {"wrap_inverse",WRAP_INVERSE_}, {"ellipticK",ELLIPTICK}, {"ellipticE",ELLIPTICE}, {"log",LOG}, {"exp",EXP}, {"sin",SIN}, {"cos",COS}, {"tan",TAN}, {"asin",ASIN}, {"acos",ACOS}, {"atan",ATAN}, {"sinh",SINH}, {"cosh",COSH}, {"tanh",TANH}, {"asinh",ASINH}, {"acosh",ACOSH}, {"atanh",ATANH}, {"sqrt",SQRT}, {"sqr",SQR}, {"ceil",CEIL_}, {"floor",FLOOR_}, {"abs",ABS} }; struct ckey mathfunc2_keywords[] = { /* two arguments */ {"wrap_compose",WRAP_COMPOSE_}, {"atan2",ATAN2_}, {"incompleteEllipticF",INCOMPLETE_ELLIPTICF}, {"incompleteEllipticE",INCOMPLETE_ELLIPTICE}, {"maximum",MAXIMUM_}, {"minimum",MINIMUM_}, {"pow",POW} }; struct ckey command_keywords[] = { {"valid_constraint",VALID_CONSTRAINT_}, {"valid_boundary",VALID_BOUNDARY_}, {"profiling",PROFILING_}, {"reset_profiling",RESET_PROFILING_}, {"suppress_warning",SUPPRESS_WARNING_}, {"unsuppress_warning",UNSUPPRESS_WARNING_}, {"delete_text",DELETE_TEXT_}, {"display_text", DISPLAY_TEXT_ }, {"simplex_to_fe", SIMPLEX_TO_FE_ }, {"addload", ADDLOAD_ }, {"whereami",WHEREAMI_}, {"breakpoint",BREAKPOINT_}, {"breakpoints",BREAKPOINT_}, {"abort",ABORT_}, {"subcommand",SUBCOMMAND_}, {"global",GLOBAL_}, {"repartition",REPARTITION_}, {"free_discards",FREE_DISCARDS_}, {"dump_memlist",DUMP_MEMLIST_}, {"reverse_orientation",REVERSE_ORIENTATION_}, {"matrix_multiply",MATRIX_MULTIPLY_}, {"matrix_inverse",MATRIX_INVERSE_}, {"matrix_determinant",MATRIX_DETERMINANT_}, {"mid_edge",MID_EDGE_}, {"mid_facet",MID_FACET_}, {"flush_counts",FLUSH_COUNTS_}, {"valid_element",VALID_ELEMENT_}, {"reset_counts",RESET_COUNTS_}, {"vertex_merge",MERGE_VERTEX_}, {"edge_merge",MERGE_EDGE_}, {"facet_merge",MERGE_FACET_}, {"mpi_task",MPI_TASK_ATTR_}, {"mean_curvature",MEAN_CURVATURE_}, {"element_modulus",ELEMENT_MODULUS_}, {"ignore_constraints",IGNORE_CONSTRAINTS_}, {"ignore_fixed",IGNORE_FIXED_}, {"global_method",GLOBAL_METHOD_}, {"scalar_integrand",SCALAR_INTEGRAND_}, {"vector_integrand",VECTOR_INTEGRAND_}, {"k_vector_order",K_VEC_ORDER_}, {"form_integrand",FORM_INTEGRAND_}, {"info_only",INFO_ONLY_}, {"method",METHOD_}, {"parallel_exec",PARALLEL_EXEC_}, {"task_exec",TASK_EXEC_}, {"pop",POP_}, {"pop_tri_to_edge",POP_TRI_TO_EDGE_}, {"pop_edge_to_tri",POP_EDGE_TO_TRI_}, {"pop_quad_to_quad",POP_QUAD_TO_QUAD_}, {"local",LOCAL_}, {"for",FOR_}, {"warning_messages",WARNING_MESSAGES_}, {"equiangulate",EQUIANGULATE_}, {"no_refine",NO_REFINE_}, {"hit_partner",HIT_PARTNER_}, {"no_display",NODISPLAY_}, {"vertexnormal",VERTEXNORMAL_}, {"colorfile",COLORFILE_}, {"date_and_time",DATE_AND_TIME_}, {"exprint",EXPRINT_}, {"energy",ENERGY_}, {"info_only",INFO_ONLY_}, {"conserved",CONSERVED_}, {"exec",EXEC_}, {"wrap_vertex",WRAP_VERTEX_}, {"self",SELF_}, {"is_defined",IS_DEFINED_}, {"nodisplay",NODISPLAY_}, {"no_display",NODISPLAY_}, {"function",FUNCTION_}, {"reorder_storage",REORDER_STORAGE_}, {"renumber_all",RENUMBER_ALL_}, /* {"view_matrix",VIEW_MATRIX_}, */ {"pause",PAUSE_}, {"pdelta",DELTA_ }, {"pscale",SCALE_ }, {"scale",SCALE_ }, {"tolerance",TOLERANCE_}, {"method_instance",METHOD_INSTANCE_}, {"logfile",LOGFILE_}, {"keylogfile",KEYLOGFILE_}, {"frontbody",FRONTBODY_}, {"backbody",BACKBODY_}, {"new_vertex",NEWVERTEX_}, {"new_edge",NEWEDGE_}, {"new_facet",NEWFACET_}, {"new_body",NEWBODY_}, {"noncontent",NONCONTENT_}, /* {"inverse_periods",INVERSE_PERIODS_}, */ {"return",RETURN_}, {"postscript",POSTSCRIPT_}, {"ooglfile",OOGLFILE_}, {"binary_off_file",BINARY_OFF_FILE_}, {"vertex_average",VERTEX_AVERAGE_}, {"raw_vertex_average",RAW_VERTEX_AVERAGE_}, {"rawest_vertex_average",RAWEST_VERTEX_AVERAGE_}, {"axial_point",AXIAL_POINT_}, {"metis_factor",METIS_FACTOR_}, {"lagrange",LAGRANGE_}, {"metis",METIS_}, {"metis_readjust",METIS_READJUST_}, {"body_metis",BODY_METIS_}, {"kmetis",KMETIS_}, {"ometis",OMETIS_}, {"sizeof",SIZEOF_}, {"move",MOVE_}, {"geompipe",GEOMPIPE_}, {"convert_to_quantities",CONVERT_TO_QUANTS_}, {"edgeswap",EDGESWAP_}, {"t1_edgeswap",T1_EDGESWAP_}, /* {"torus_periods",TORUS_PERIODS_}, */ {"break",BREAK_}, {"volfixed",FIXEDVOL_}, {"continue",CONTINUE_}, {"volconst",VOLCONST_ }, {"ritz",RITZ_}, {"orientation",ORIENTATION_}, {"eigenprobe",EIGENPROBE_}, {"lanczos",LANCZOS_}, {"tetra_point",TETRA_POINT_}, {"triple_point",TRIPLE_POINT_}, {"value",VALUE_}, {"target",TARGET_}, {"datafilename",DATAFILENAME_}, {"geomview",GEOMVIEW_}, {"saddle",HESSIAN_SADDLE_}, {"midv",MIDV_}, {"define",DEFINE_}, {"attribute",ATTRIBUTE_}, {"attributes",ATTRIBUTE_}, {"string",STRING_}, {"sobolev",SOBOLEV_}, {"sobolev_seek",SOBOLEV_SEEK_}, {"dirichlet",DIRICHLET_}, {"dirichlet_seek",DIRICHLET_SEEK_}, {"wrap",WRAP_}, {"total",TOTAL_}, {"history",HISTORY_}, {"fix",FIX_}, {"unfix",UNFIX_}, {"bare",BARE_}, {"phase",PHASE_}, {"foreach",FOREACH_}, {"rebody", REBODY_}, {"burchard", BURCHARD_}, {"close_show",CLOSE_SHOW_}, {"show_off",CLOSE_SHOW_}, /* {"view_transforms",VIEW_TRANSFORMS_}, */ /* {"view_transform_swap_colors",VIEW_TRANSFORM_SWAP_COLORS_}, */ /* {"view_transform_parity",VIEW_TRANSFORM_PARITY_}, */ {"transform_depth",TRANSFORM_DEPTH_}, {"transform_expr",TRANSFORM_EXPR_}, {"modulus",MODULUS_}, {"boundary",BOUNDARY_}, {"on_constraint",ON_CONSTRAINT_}, {"hit_constraint",HIT_CONSTRAINT_}, {"on_boundary",ON_BOUNDARY_}, {"on_quantity",ON_QUANTITY_}, {"on_method_instance",ON_METHOD_INSTANCE_}, {"hessian",HESSIAN_}, {"hessian_seek",HESSIAN_SEEK_}, {"hessian_menu",HESSIAN_MENU_}, {"help",HELP_}, {"quit",QUIT_}, {"exit",QUIT_}, {"bye",QUIT_}, {"dump",DUMP_}, {"load",LOAD_}, {"permload",PERMLOAD_}, {"quantity",QUANTITY_}, {"spring_constant",GAP_CONSTANT_}, {"gap_constant",GAP_CONSTANT_}, {"notch",NOTCH_}, {"while",WHILE_}, {"do",DO_}, {"if",IF_}, {"then",THEN_}, {"else",ELSE_}, {"histogram",HISTOGRAM_}, {"loghistogram",LOGHISTOGRAM_}, {"show_expr",SHOW_EXPR_}, {"show_trans",SHOW_TRANS_}, {"dihedral",DIHEDRAL_}, {"max",MAX_}, {"min",MIN_}, {"avg",AVG_}, {"sum",SUM_}, {"count",COUNT_}, {"print",PRINT_}, {"eprint",EPRINT_}, {"printf",PRINTF_}, {"errprintf",ERRPRINTF_}, {"sprintf",SPRINTF_}, {"binary_printf",BINARY_PRINTF_}, {"procedures",PROCEDURES_}, {"procedure",PROCEDURE_WORD_}, {"on",ON_}, {"counts",COUNTS_}, {"extrapolate",EXTRAPOLATE_}, {"off",OFF_}, {"areaweed",AREAWEED_}, {"edgeweed",EDGEWEED_}, {"edge_divide",EDGEDIVIDE_}, {"alice",ALICE_}, {"stability_test",STABILITY_TEST_}, {"zoom",ZOOM_}, {"utest",UTEST_}, {"system",SYSTEM_}, {"chdir",CHDIR_}, {"longj",LONG_JIGGLE_}, {"rawv",RAW_VERAVG_}, {"rawestv",RAWEST_VERAVG_}, {"go",GO_}, {"refine",REFINE_}, {"check",CHECK_}, {"show_vol",SHOW_VOL_}, {"id",ID_}, {"oid",OID_}, {"and",AND_}, {"or",OR_}, {"not",NOT_}, {"mod",'%'}, {"imod",IMOD_}, {"idiv",IDIV_}, {"dot_product",DOT_}, {"pi",PI_}, {"e",E_}, {"g",G_}, {"tag",TAG_}, {"original",ORIGINAL_}, {"fixed",FIXED_}, {"vertices",VERTICES_}, {"vertex",VERTICES_}, {"edges",EDGES_}, {"edge",EDGES_}, {"facets",FACETS_}, {"facet",FACETS_}, {"faces",FACETS_}, {"face",FACETS_}, {"facet_edges",FACETEDGES_}, {"facet_edge",FACETEDGES_}, {"facetedges",FACETEDGES_}, {"facetedge",FACETEDGES_}, {"bodies",BODIES_}, {"body",BODIES_}, {"topinfo",TOPINFO_}, {"bottominfo",BOTTOMINFO_}, {"length",LENGTH_ }, {"valence",VALENCE_ }, {"area",AREA_ }, {"volume",VOLUME_ }, {"sqcurve",SQ_MEAN_CURV_}, {"where",WHERE_ }, {"list",LIST_ }, {"show",SHOW_}, {"showq",SHOWQ_}, {"delete",DELETE_ }, {"dissolve",DISSOLVE_ }, {"refine",REFINE_ }, {"shell",SHELL_}, {"constraint",CONSTRAINT_}, {"pressure",PRESSURE_}, {"color",COLOR_}, {"backcolor",BACKCOLOR_}, {"frontcolor",FRONTCOLOR_}, {"opacity",OPACITY_}, {"volume",VOLUME_}, {"density",DENSITY_}, {"tension",DENSITY_}, {"set",SET_}, {"read",READ_}, {"unset",UNSET_}, {"recalc",RECALC_}, {"optimize",OPTIMIZE_}, {"optimise",OPTIMIZE_}, {"autochop",AUTOCHOP_}, }; struct ckey togglenames[] = { {"immediate_autopop",IMMEDIATE_AUTOPOP_}, {"star_finagling",STAR_FINAGLING_}, {"force_deletion",FORCE_DELETION_}, {"function_quantity_sparse",FUNCTION_QUANTITY_SPARSE_}, {"slice_view",SLICE_VIEW_}, {"clip_view",CLIP_VIEW_}, {"quietload",QUIETLOAD_}, {"big_endian",BIG_ENDIAN_}, {"little_endian",LITTLE_ENDIAN_}, {"full_bounding_box",FULL_BOUNDING_BOX_}, {"pop_disjoin",POP_DISJOIN_}, {"pop_enjoin",POP_ENJOIN_}, {"pop_to_edge",POP_TO_EDGE_}, {"pop_to_face",POP_TO_FACE_}, {"mpi_debug",MPI_DEBUG_}, {"smooth_graph",SMOOTH_GRAPH_}, {"bezier_basis",BEZIER_BASIS_}, {"break_after_warning",BREAK_AFTER_WARNING_}, {"blas_flag",BLAS_FLAG_}, {"diffusion",DIFFUSION_}, {"augmented_hessian",AUGMENTED_HESSIAN_}, {"sparse_constraints",SPARSE_CONSTRAINTS_}, {"visibility_test", VISIBILITY_TEST_}, {"circular_arc_draw",CIRCULAR_ARC_DRAW_}, {"rgb_colors",RGB_COLORS_FLAG_}, {"kraynikpopvertex",KRAYNIKPOPVERTEX_FLAG_}, {"kraynikpopedge",KRAYNIKPOPEDGE_FLAG_}, {"sobolev_mode",SOBOLEV_MODE_}, {"dirichlet_mode",DIRICHLET_MODE_}, {"verbose",VERBOSE_}, {"torus_filled",TORUS_FILLED_}, {"ambient_pressure",AMBIENT_PRESSURE_}, {"backcull",BACKCULL_}, {"interp_normals",INTERP_NORMALS_}, {"volgrads_every",VOLGRADS_EVERY_}, {"zener_drag",ZENER_DRAG_}, {"hessian_special_normal",HESSIAN_SPECIAL_NORMAL_}, {"hessian_normal_perp",HESSIAN_NORMAL_PERP_}, {"show_all_quantities",SHOW_ALL_QUANTITIES_}, {"pscolorflag",PSCOLORFLAG_}, {"ps_colorflag",PSCOLORFLAG_}, {"ps_gridflag",GRIDFLAG_}, {"gridflag",GRIDFLAG_}, {"crossingflag",CROSSINGFLAG_}, {"labelflag",LABELFLAG_}, {"ps_labelflag",LABELFLAG_}, {"hessian_normal_one",HESSIAN_NORMAL_ONE_}, {"interp_bdry_param",INTERP_BDRY_PARAM_}, {"hessian_double_normal",HESSIAN_DOUBLE_NORMAL_}, {"h_inverse_metric",H_INVERSE_METRIC_}, {"squared_gradient",SQUARED_GRADIENT_}, {"linear_metric",LINEAR_METRIC_}, {"metric_convert",METRIC_CONVERT_}, {"quantities_only",QUANTITIES_ONLY_}, {"ysmp",YSMP_}, {"bunch_kaufman",BUNCH_KAUFMAN_}, {"bunch_kauffman",BUNCH_KAUFMAN_}, {"hessian_normal",HESSIAN_NORMAL_}, {"jiggle",JIGGLE_TOGGLE_}, {"assume_oriented",ASSUME_ORIENTED_}, {"ribiere",RIBIERE_CG_}, {"area_normalization",MEAN_CURV_}, {"force_pos_def",FORCE_POS_DEF_}, {"autodisplay",AUTODISPLAY_}, {"lagrange",LAGRANGE_}, {"quadratic",QUADRATIC_}, {"linear",LINEAR_}, {"show_inner",SHOW_INNER_}, {"area_fixed",AREA_FIXED_}, {"fixed_area",AREA_FIXED_}, {"clipped",CLIPPED_CELLS_}, {"clipped_cells",CLIPPED_CELLS_}, {"raw_cells",RAW_CELLS_}, {"connected",CONNECTED_CELLS_}, {"connected_cells",CONNECTED_CELLS_}, {"show_outer",SHOW_OUTER_}, {"colormap",COLORMAP_}, {"thicken",THICKEN_}, {"hessian_diff",HESSIAN_DIFF_}, {"normal_motion",NORMAL_MOTION_}, {"runge_kutta",RUNGE_KUTTA_}, {"deturck",DETURCK_}, {"kusner",KUSNER_}, {"view_4d",VIEW_4D_}, {"conf_edge",CONF_EDGE_SQCURV_}, {"mean_curvature_integral",MEAN_CURV_INT_}, {"sqgauss",SQGAUSS_}, {"autopop",AUTOPOP_}, {"autopop_quartic",AUTOPOP_QUARTIC_}, {"old_area",OLD_AREA_}, {"approx_curv",APPROX_CURV_}, {"check_increase",CHECK_INCREASE_}, {"debug",DEBUG_}, {"memdebug",MEMDEBUG_}, {"itdebug",ITDEBUG_}, {"gravity",GRAVITY_}, {"effective_area",EFFECTIVE_AREA_}, {"estimate",ESTIMATE_}, {"post_project",POST_PROJECT_}, {"transforms",TRANSFORMS_}, {"quiet",QUIET_}, {"quietgo",QUIETGO_}, {"hessian_quiet",HESSIAN_QUIET_}, {"conj_grad",CONJ_GRAD_}, {"homothety",HOMOTHETY_}, {"facet_colors",FACET_COLORS_}, {"shading",SHADING_}, {"div_normal_curvature",DIV_NORMAL_CURVATURE_}, {"normal_curvature",NORMAL_CURVATURE_}, {"boundary_curvature",BOUNDARY_CURVATURE_}, {"self_similar",SELF_SIMILAR_}, {"gv_binary",GV_BINARY_}, {"metric_conversion",METRIC_CONVERSION_}, {"autorecalc",AUTORECALC_}, {"pinning",PINNING_} }; struct ckey colornames[] = { {"clear",CLEAR}, /* transparent */ {"transparent",CLEAR}, /* transparent */ {"black",BLACK}, /* dark colors */ {"blue",BLUE}, {"green",GREEN}, {"cyan",CYAN}, {"red",RED}, {"magenta",MAGENTA}, {"brown",BROWN}, {"lightgray",LIGHTGRAY}, {"lightgrey",LIGHTGRAY}, {"gray",LIGHTGRAY}, {"grey",LIGHTGRAY}, {"darkgray",DARKGRAY}, /* light colors */ {"darkgrey",DARKGRAY}, /* light colors */ {"lightblue",LIGHTBLUE}, {"lightgreen",LIGHTGREEN}, {"lightcyan",LIGHTCYAN}, {"lightred",LIGHTRED}, {"lightmagenta",LIGHTMAGENTA}, {"yellow",YELLOW}, {"white",WHITE} }; struct ckey internal_variables[] = { {"autochop_length",V_AUTOCHOP_LENGTH}, {"string_curve_tolerance",V_STRING_CURVE_TOLERANCE}, {"corona_state",V_CORONA_STATE}, {"mpi_maxtask",V_MPI_MAXTASK}, {"this_task",V_THIS_TASK}, /* MPI task number */ {"window_aspect_ratio",V_WINDOW_ASPECT_RATIO}, {"mindeg_debug_level",V_MINDEG_DEBUG_LEVEL}, {"mindeg_min_region_size",V_MINDEG_MIN_REGION_SIZE}, {"mindeg_margin",V_MINDEG_MARGIN}, {"fix_count",V_FIX_COUNT}, {"unfix_count",V_UNFIX_COUNT}, {"edge_delete_count",V_EDGE_DELETE_COUNT}, {"facet_delete_count",V_FACET_DELETE_COUNT}, {"vertex_dissolve_count",V_VERTEX_DISSOLVE_COUNT}, {"edge_dissolve_count",V_EDGE_DISSOLVE_COUNT}, {"facet_dissolve_count",V_FACET_DISSOLVE_COUNT}, {"body_dissolve_count",V_BODY_DISSOLVE_COUNT}, {"edge_refine_count",V_EDGE_REFINE_COUNT}, {"facet_refine_count",V_FACET_REFINE_COUNT}, {"vertex_pop_count",V_VERTEX_POP_COUNT}, {"edge_pop_count",V_EDGE_POP_COUNT}, {"pop_tri_to_edge_count",V_POP_TRI_TO_EDGE_COUNT}, {"pop_edge_to_tri_count",V_POP_EDGE_TO_TRI_COUNT}, {"pop_quad_to_quad_count",V_POP_QUAD_TO_QUAD_COUNT}, {"edgeswap_count",V_EDGESWAP_COUNT}, {"t1_edgeswap_count",V_T1_EDGESWAP_COUNT}, {"ps_labelsize",V_PS_LABELSIZE_}, {"ps_stringwidth",V_PS_STRINGWIDTH_}, {"ps_fixededgewidth",V_PS_FIXEDEDGEWIDTH_}, {"ps_tripleedgewidth",V_PS_TRIPLEEDGEWIDTH_}, {"ps_conedgewidth",V_PS_CONEDGEWIDTH_}, {"ps_bareedgewidth",V_PS_BAREEDGEWIDTH_}, {"ps_gridedgewidth",V_PS_GRIDEDGEWIDTH_}, {"scrollbuffersize",V_SCROLLBUFFERSIZE_}, {"visibility_debug",V_VISIBILITY_DEBUG_}, {"check_count",V_CHECK_COUNT_}, {"breakflag",V_BREAKFLAG_}, {"everything_quantities",EVERYTHING_QUANTITIES_}, {"gravity_constant",GRAV_CONST_}, {"hessian_slant_cutoff",V_HESSIAN_SLANT_CUTOFF}, {"ambient_pressure_value",V_AMBIENT_PRESSURE}, {"last_error",V_LAST_ERROR}, {"memory_arena",V_MEMARENA}, {"memory_used",V_MEMUSED}, {"background",V_BACKGROUND}, {"brightness",V_BRIGHTNESS}, {"diffusion_coeff",V_DIFFUSION}, {"transform_count",V_TRANSFORM_COUNT}, {"scale_limit",V_SCALE_LIMIT}, {"clock",V_CLOCK}, {"cpu_counter",V_CPU_COUNTER}, {"target_tolerance",V_TARGET_TOLERANCE}, {"thickness",V_THICKNESS}, {"spring_constant",V_GAP_CONSTANT}, {"gap_constant",V_GAP_CONSTANT}, {"lagrange_order",V_LAGRANGE_ORDER}, {"last_eigenvalue",V_LAST_EIGENVALUE}, {"last_hessian_scale",V_LAST_HESSIAN_SCALE}, {"integral_order",V_INTEGRAL_ORDER}, {"integral_order_1d",V_INTEGRAL_ORDER_1D}, {"integral_order_2d",V_INTEGRAL_ORDER_2D}, {"integration_order",V_INTEGRAL_ORDER}, {"integration_order_1d",V_INTEGRAL_ORDER_1D}, {"integration_order_2d",V_INTEGRAL_ORDER_2D}, {"random_seed",V_RANDOM_SEED}, {"random",V_RANDOM}, {"linear_metric_mix",V_LINEAR_METRIC_MIX}, {"quadratic_metric_mix",V_QUADRATIC_METRIC_MIX}, {"pickvnum",V_PICKVNUM}, {"pickenum",V_PICKENUM}, {"pickfnum",V_PICKFNUM}, {"eigen_pos",V_EIGENPOS}, {"eigen_neg",V_EIGENNEG}, {"eigen_zero",V_EIGENZERO}, {"eigenpos",V_EIGENPOS}, {"eigenneg",V_EIGENNEG}, {"eigenzero",V_EIGENZERO}, {"total_time",V_TIME}, {"jiggle_temperature",V_JIG_TEMP}, {"iteration_counter",V_ITER_COUNTER}, {"scale_scale",V_SCALE_SCALE}, {"vertex_count",V_VERTEXCOUNT}, {"edge_count",V_EDGECOUNT}, {"facet_count",V_FACETCOUNT}, {"body_count",V_BODYCOUNT}, {"facetedge_count",V_FACETEDGECOUNT}, {"total_energy",V_ENERGY}, {"total_area",V_AREA}, {"total_length",V_LENGTH}, {"scale",V_SCALE}, {"space_dimension",V_SPACE_DIMENSION}, {"surface_dimension",V_SURFACE_DIMENSION}, {"torus",V_TORUS}, {"symmetry_group",V_SYMMETRY_GROUP}, {"simplex_representation",V_SIMPLEX}, {"constraint_tolerance",V_TOLERANCE}, {"hessian_epsilon",V_HESS_EPSILON}, {"equi_count",V_EQUI_COUNT}, {"delete_count",V_DELETE_COUNT}, {"refine_count",V_REFINE_COUNT}, {"notch_count",V_NOTCH_COUNT}, {"dissolve_count",V_DISSOLVE_COUNT}, {"pop_count",V_POP_COUNT}, {"where_count",V_WHERE_COUNT} }; /* %option array */ /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int yywrap YY_PROTO(( void )); #else extern int yywrap YY_PROTO(( void )); #endif #endif #ifndef YY_NO_UNPUT static void yyunput YY_PROTO(( int c, char *buf_ptr )); #endif #ifndef yytext_ptr static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int )); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput YY_PROTO(( void )); #else static int input YY_PROTO(( void )); #endif #endif #if YY_STACK_USED static int yy_start_stack_ptr = 0; static int yy_start_stack_depth = 0; static int *yy_start_stack = 0; #ifndef YY_NO_PUSH_STATE static void yy_push_state YY_PROTO(( int new_state )); #endif #ifndef YY_NO_POP_STATE static void yy_pop_state YY_PROTO(( void )); #endif #ifndef YY_NO_TOP_STATE static int yy_top_state YY_PROTO(( void )); #endif #else #define YY_NO_PUSH_STATE 1 #define YY_NO_POP_STATE 1 #define YY_NO_TOP_STATE 1 #endif #ifdef YY_MALLOC_DECL YY_MALLOC_DECL #else #if __STDC__ #ifndef __cplusplus #include #endif #else /* Just try to get by without declaring the routines. This will fail * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int) * or sizeof(void*) != sizeof(int). */ #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #define YY_READ_BUF_SIZE 8192 #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( yy_current_buffer->yy_is_interactive ) \ { \ int c = '*', n; \ for ( n = 0; n < max_size && \ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \ && ferror( yyin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) #endif /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL int yylex YY_PROTO(( void )) #endif /* Code executed at the beginning of each rule, after yytext and yyleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK break; #endif #define YY_RULE_SETUP \ if ( yyleng > 0 ) \ yy_current_buffer->yy_at_bol = \ (yytext[yyleng - 1] == '\n'); \ YY_USER_ACTION YY_DECL { register yy_state_type yy_current_state; register char *yy_cp, *yy_bp; register int yy_act; if ( yy_init ) { yy_init = 0; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! yy_start ) yy_start = 1; /* first start state */ if ( ! yyin ) yyin = stdin; if ( ! yyout ) yyout = stdout; if ( ! yy_current_buffer ) yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); yy_load_buffer_state(); } while ( 1 ) /* loops until end-of-file is reached */ { yy_cp = yy_c_buf_p; /* Support of yytext. */ *yy_cp = yy_hold_char; /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = yy_start; yy_current_state += YY_AT_BOL(); yy_match: do { register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; if ( yy_accept[yy_current_state] ) { yy_last_accepting_state = yy_current_state; yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 227 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; ++yy_cp; } while ( yy_base[yy_current_state] != 825 ); yy_find_action: yy_act = yy_accept[yy_current_state]; if ( yy_act == 0 ) { /* have to back up */ yy_cp = yy_last_accepting_cpos; yy_current_state = yy_last_accepting_state; yy_act = yy_accept[yy_current_state]; } YY_DO_BEFORE_ACTION; do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = yy_hold_char; yy_cp = yy_last_accepting_cpos; yy_current_state = yy_last_accepting_state; goto yy_find_action; case 1: YY_RULE_SETUP { /* eat comment */ register int c; in_comment = 1; for ( ; ; ) { while ( (c = input()) != '*' && c != 0 ) if ( c == '\n' ) line_no++; /* eat up text of comment */ if ( c == '*' ) { while ( (c = input()) == '*' ) ; if ( c == '/' ) break; /* found the end */ if ( c == '\n' ) line_no++; } if ( c == 0 && yywrap() ) { kb_error(2325,"End-of-file in comment\n",RECOVERABLE ); break; } } in_comment = 0; } YY_BREAK case 2: YY_RULE_SETUP /* comment */ ; YY_BREAK case 3: YY_RULE_SETUP { line_no++; } YY_BREAK case 4: YY_RULE_SETUP { errspot -= 15; line_no--; } YY_BREAK case 5: YY_RULE_SETUP { char *c = strtok(yytext,whitespace); yylval.r = atof(strtok(yytext,whitespace)); yylval.i = atoi(strtok(yytext,whitespace)); if ( lists_flag==LISTS_FULL ) return(tok=LEAD_INTEGER_); else if ( uminus_flag || c[0] != '-' ) return(tok = (fabs(yylval.r) > MAXINT) ? REAL_ : INTEGER_); else { unput_string(c+1); c[1] = 0; return (tok = minus_type(c[0])); } } YY_BREAK case 6: YY_RULE_SETUP { char *c = strtok(yytext,whitespace); yylval.i = atoi(c); yylval.r = atof(strtok(c,whitespace)); yylval.qnum = 0; if ((lists_flag!=LISTS_OFF) && uminus_flag && (parens==0)) return(tok = (fabs(yylval.r) > MAXINT) ? REAL_ : INTEGER_); else { unput_string(c+1); c[1] = 0; return (tok = minus_type(c[0])); } } YY_BREAK case 7: YY_RULE_SETUP { yylval.i = atoi(yytext); yylval.r = atof(strtok(yytext,whitespace)); yylval.qnum = 0; return(tok = (fabs(yylval.r) > MAXINT) ? REAL_ : INTEGER_); } YY_BREAK case 8: YY_RULE_SETUP { yylval.i = atoi(yytext); yylval.qnum = atoi(strchr(yytext,'@')+1); return(tok = LEAD_INTEGER_AT_); } YY_BREAK case 9: YY_RULE_SETUP { yylval.i = atoi(yytext); yylval.qnum = atoi(strchr(yytext,'@')+1); return(tok = INTEGER_AT_); } YY_BREAK case 10: YY_RULE_SETUP { sscanf(yytext+2,"%x",&yylval.i); yylval.r = (REAL)(yylval.i); return(tok = INTEGER_); } /* hex */ YY_BREAK case 11: YY_RULE_SETUP { char *c = yytext; /* binary */ yylval.i = 0; while ( isdigit(*c) ) { yylval.i = 2*yylval.i + *c - '0'; c++;} yylval.r = (REAL)(yylval.i); return(tok = INTEGER_); } YY_BREAK case 12: YY_RULE_SETUP return VERSIONTOKEN_; YY_BREAK case 13: case 14: case 15: case 16: case 17: case 18: YY_RULE_SETUP { char *c = strtok(yytext,whitespace); yylval.r = atof(c); if ((lists_flag!=LISTS_OFF) && uminus_flag && (parens==0)) return(tok = REAL_); else { unput_string(c+1); c[1] = 0; verb_flag = 0; return (tok = minus_type(c[0])); } } YY_BREAK case 19: case 20: case 21: case 22: case 23: case 24: YY_RULE_SETUP { yylval.r = atof(yytext); return(tok = REAL_); } YY_BREAK case 25: YY_RULE_SETUP { /* read quoted string */ int n; in_quote = 1; for (n=0;;n++) { int c; c = input(); if ( c == '\n' ) line_no++; if ( c == '"' && (n == 0 || (yytext[n-1] != '\\')) ) break; if ( n < 198 ) yytext[n] = c; else if ( n==199 ) kb_error(2326,"Quoted string over 198 characters.",WARNING); } yytext[n] = 0; reduce_string(yytext); in_quote = 0; return(tok = QUOTATION_); } YY_BREAK case 26: YY_RULE_SETUP verb_flag = 0; return (tok = minus_type('-')); YY_BREAK case 27: *yy_cp = yy_hold_char; /* undo effects of setting up yytext */ yy_c_buf_p = yy_cp -= 1; YY_DO_BEFORE_ACTION; /* set up yytext again */ YY_RULE_SETUP verb_flag = 0; return (tok = minus_type('-')); YY_BREAK case 28: YY_RULE_SETUP { if (datafile_flag && uminus_flag && (parens == 0) ) return(tok = UMINUS_); else { verb_flag = 0; return ( tok = minus_type('-') );} } YY_BREAK case 29: YY_RULE_SETUP { verb_flag = 2; yylval.i = SUBASSIGN_; return (tok= ASSIGNOP_);} YY_BREAK case 30: YY_RULE_SETUP { return ( tok = minus_type('-')); } YY_BREAK case 31: YY_RULE_SETUP { if (datafile_flag && uminus_flag && (parens == 0) ) return ( tok = UMINUS_); else { verb_flag = 0; return ( tok = minus_type('-') ); } } YY_BREAK case 32: YY_RULE_SETUP { verb_flag = 2; return (tok= ASSIGN_);} YY_BREAK case 33: YY_RULE_SETUP { verb_flag = 2; kb_error(2327,"You mistyped ';=' for ':='?\n",WARNING); return (tok= ASSIGN_); } YY_BREAK case 34: YY_RULE_SETUP { verb_flag = 2; yylval.i = PLUSASSIGN_; return (tok= ASSIGNOP_);} YY_BREAK case 35: YY_RULE_SETUP { verb_flag = 2; yylval.i = SUBASSIGN_; return (tok= ASSIGNOP_);} YY_BREAK case 36: YY_RULE_SETUP { verb_flag = 2; yylval.i = MULTASSIGN_; return (tok= ASSIGNOP_);} YY_BREAK case 37: YY_RULE_SETUP { verb_flag = 2; yylval.i = DIVASSIGN_; return (tok= ASSIGNOP_);} YY_BREAK case 38: YY_RULE_SETUP { verb_flag = 2; return (tok = PERM_ASSIGN_);} YY_BREAK case 39: YY_RULE_SETUP { verb_flag = 2; return (tok = REDEFINE_); } YY_BREAK case 40: YY_RULE_SETUP { return (tok = minus_type('-')); } YY_BREAK case 41: YY_RULE_SETUP { verb_flag = 0 ; return(tok = yytext[0]); } YY_BREAK case 42: YY_RULE_SETUP { verb_flag = 1; return tok = ';'; } YY_BREAK case 43: YY_RULE_SETUP { verb_flag = 1; return(tok = yytext[0]); } YY_BREAK case 44: YY_RULE_SETUP { return(tok = yytext[0]); } YY_BREAK case 45: YY_RULE_SETUP { return tok = (datafile_flag ? '=' : EQ_); } YY_BREAK case 46: YY_RULE_SETUP { verb_flag = 0; return(tok = yytext[0]); } YY_BREAK case 47: YY_RULE_SETUP { return(tok = yytext[0]); } YY_BREAK case 48: YY_RULE_SETUP { return(tok = yytext[0]); } YY_BREAK case 49: YY_RULE_SETUP { return(tok = '^'); } YY_BREAK case 50: YY_RULE_SETUP { return(tok = EQ_); } YY_BREAK case 51: YY_RULE_SETUP { return(tok = NE_); } YY_BREAK case 52: YY_RULE_SETUP { return(tok = LE_); } YY_BREAK case 53: YY_RULE_SETUP { return(tok = GE_); } YY_BREAK case 54: YY_RULE_SETUP { return(tok = OR_); } YY_BREAK case 55: YY_RULE_SETUP { return(tok = AND_); } YY_BREAK case 56: YY_RULE_SETUP { return(tok = NOT_); } YY_BREAK case 57: YY_RULE_SETUP { return (tok = PIPE_); /* pipe followed by quoted string */ } YY_BREAK case 58: YY_RULE_SETUP { return (tok = REDIRECT_); /* for redirection */ } YY_BREAK case 59: YY_RULE_SETUP { return (tok = REDIRECTOVER_); /* for overwrite redirection */ } YY_BREAK case 60: YY_RULE_SETUP { return (tok = BACKQUOTE_COMMA_); } YY_BREAK case 61: YY_RULE_SETUP { if (!datafile_flag) kb_error(1880,"#define valid only in top of datafile.\n",WARNING); macro_flag = 1; } YY_BREAK case 62: YY_RULE_SETUP { /* nested include */ char *name = strchr(yytext,'"')+1;/* initial quote + 1 */ *strchr(name,'"') = 0; /* replace final quote by 0 */ push_commandfd(NULL,name); } YY_BREAK case 63: YY_RULE_SETUP { strncpy(yylval.lexeme,yytext,31); if ( !macro() ) return identcase(yytext); } YY_BREAK case 64: YY_RULE_SETUP { yylval.i = yytext[1]; return tok = SINGLE_LETTER_; } YY_BREAK case 65: YY_RULE_SETUP ; YY_BREAK case 66: YY_RULE_SETUP { if ( cond_expr_flag ) return tok = ':';} YY_BREAK case 67: YY_RULE_SETUP { if ( isprint(yytext[0]) ) sprintf(errmsg,"Illegal token: %c\n",yytext[0]); else sprintf(errmsg,"Illegal token: 0x%02X\n",yytext[0]); yyerror(errmsg); return tok=LEXERROR; } YY_BREAK case 68: YY_RULE_SETUP ECHO; YY_BREAK case YY_STATE_EOF(INITIAL): yyterminate(); case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = yy_hold_char; if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed yyin at a new source and called * yylex(). If so, then we have to assure * consistency between yy_current_buffer and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ yy_n_chars = yy_current_buffer->yy_n_chars; yy_current_buffer->yy_input_file = yyin; yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) { /* This was really a NUL. */ yy_state_type yy_next_state; yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state(); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state ); yy_bp = yytext_ptr + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++yy_c_buf_p; yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = yy_c_buf_p; goto yy_find_action; } } else switch ( yy_get_next_buffer() ) { case EOB_ACT_END_OF_FILE: { yy_did_buffer_switch_on_eof = 0; if ( yywrap() ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * yytext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! yy_did_buffer_switch_on_eof ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state(); yy_cp = yy_c_buf_p; yy_bp = yytext_ptr + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: yy_c_buf_p = &yy_current_buffer->yy_ch_buf[yy_n_chars]; yy_current_state = yy_get_previous_state(); yy_cp = yy_c_buf_p; yy_bp = yytext_ptr + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of yylex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer() { register char *dest = yy_current_buffer->yy_ch_buf; register char *source = yytext_ptr; register int number_to_move, i; int ret_val; if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( yy_current_buffer->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 ) { /* We matched a singled characater, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ yy_n_chars = 0; else { int num_to_read = yy_current_buffer->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ #ifdef YY_USES_REJECT YY_FATAL_ERROR( "input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); #else /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = yy_current_buffer; int yy_c_buf_p_offset = (int) (yy_c_buf_p - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ yy_flex_realloc( (void *) b->yy_ch_buf, b->yy_buf_size + 2 ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = 0; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = yy_current_buffer->yy_buf_size - number_to_move - 1; #endif } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), yy_n_chars, num_to_read ); } if ( yy_n_chars == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; yyrestart( yyin ); } else { ret_val = EOB_ACT_LAST_MATCH; yy_current_buffer->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; yy_n_chars += number_to_move; yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; yytext_ptr = &yy_current_buffer->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state() { register yy_state_type yy_current_state; register char *yy_cp; yy_current_state = yy_start; yy_current_state += YY_AT_BOL(); for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) { register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { yy_last_accepting_state = yy_current_state; yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 227 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ #ifdef YY_USE_PROTOS static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state ) #else static yy_state_type yy_try_NUL_trans( yy_current_state ) yy_state_type yy_current_state; #endif { register int yy_is_jam; register char *yy_cp = yy_c_buf_p; register YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { yy_last_accepting_state = yy_current_state; yy_last_accepting_cpos = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 227 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; yy_is_jam = (yy_current_state == 226); return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_UNPUT #ifdef YY_USE_PROTOS static void yyunput( int c, register char *yy_bp ) #else static void yyunput( c, yy_bp ) int c; register char *yy_bp; #endif { register char *yy_cp = yy_c_buf_p; /* undo effects of setting up yytext */ *yy_cp = yy_hold_char; if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) { /* need to shift things up to make room */ /* +2 for EOB chars. */ register int number_to_move = yy_n_chars + 2; register char *dest = &yy_current_buffer->yy_ch_buf[ yy_current_buffer->yy_buf_size + 2]; register char *source = &yy_current_buffer->yy_ch_buf[number_to_move]; while ( source > yy_current_buffer->yy_ch_buf ) *--dest = *--source; yy_cp += (int) (dest - source); yy_bp += (int) (dest - source); yy_n_chars = yy_current_buffer->yy_buf_size; if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) YY_FATAL_ERROR( "flex scanner push-back overflow" ); } *--yy_cp = (char) c; yytext_ptr = yy_bp; yy_hold_char = *yy_cp; yy_c_buf_p = yy_cp; } #endif /* ifndef YY_NO_UNPUT */ #ifdef __cplusplus static int yyinput() #else static int input() #endif { int c; *yy_c_buf_p = yy_hold_char; if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) /* This was really a NUL. */ *yy_c_buf_p = '\0'; else { /* need more input */ yytext_ptr = yy_c_buf_p; ++yy_c_buf_p; switch ( yy_get_next_buffer() ) { case EOB_ACT_END_OF_FILE: { if ( yywrap() ) { yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; return EOF; } if ( ! yy_did_buffer_switch_on_eof ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(); #else return input(); #endif } case EOB_ACT_CONTINUE_SCAN: yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; break; case EOB_ACT_LAST_MATCH: #ifdef __cplusplus YY_FATAL_ERROR( "unexpected last match in yyinput()" ); #else YY_FATAL_ERROR( "unexpected last match in input()" ); #endif } } } c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */ *yy_c_buf_p = '\0'; /* preserve yytext */ yy_hold_char = *++yy_c_buf_p; yy_current_buffer->yy_at_bol = (c == '\n'); return c; } #ifdef YY_USE_PROTOS void yyrestart( FILE *input_file ) #else void yyrestart( input_file ) FILE *input_file; #endif { if ( ! yy_current_buffer ) yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); yy_init_buffer( yy_current_buffer, input_file ); yy_load_buffer_state(); } #ifdef YY_USE_PROTOS void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) #else void yy_switch_to_buffer( new_buffer ) YY_BUFFER_STATE new_buffer; #endif { if ( yy_current_buffer == new_buffer ) return; if ( yy_current_buffer ) { /* Flush out information for old buffer. */ *yy_c_buf_p = yy_hold_char; yy_current_buffer->yy_buf_pos = yy_c_buf_p; yy_current_buffer->yy_n_chars = yy_n_chars; } yy_current_buffer = new_buffer; yy_load_buffer_state(); /* We don't actually know whether we did this switch during * EOF (yywrap()) processing, but the only time this flag * is looked at is after yywrap() is called, so it's safe * to go ahead and always set it. */ yy_did_buffer_switch_on_eof = 1; } #ifdef YY_USE_PROTOS void yy_load_buffer_state( void ) #else void yy_load_buffer_state() #endif { yy_n_chars = yy_current_buffer->yy_n_chars; yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos; yyin = yy_current_buffer->yy_input_file; yy_hold_char = *yy_c_buf_p; } #ifdef YY_USE_PROTOS YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) #else YY_BUFFER_STATE yy_create_buffer( file, size ) FILE *file; int size; #endif { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); b->yy_is_our_buffer = 1; yy_init_buffer( b, file ); return b; } #ifdef YY_USE_PROTOS void yy_delete_buffer( YY_BUFFER_STATE b ) #else void yy_delete_buffer( b ) YY_BUFFER_STATE b; #endif { if ( ! b ) return; if ( b == yy_current_buffer ) yy_current_buffer = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) yy_flex_free( (void *) b->yy_ch_buf ); yy_flex_free( (void *) b ); } #ifndef YY_ALWAYS_INTERACTIVE #ifndef YY_NEVER_INTERACTIVE extern int isatty YY_PROTO(( int )); #endif #endif #ifdef YY_USE_PROTOS void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) #else void yy_init_buffer( b, file ) YY_BUFFER_STATE b; FILE *file; #endif { yy_flush_buffer( b ); b->yy_input_file = file; b->yy_fill_buffer = 1; #if YY_ALWAYS_INTERACTIVE b->yy_is_interactive = 1; #else #if YY_NEVER_INTERACTIVE b->yy_is_interactive = 0; #else b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; #endif #endif } #ifdef YY_USE_PROTOS void yy_flush_buffer( YY_BUFFER_STATE b ) #else void yy_flush_buffer( b ) YY_BUFFER_STATE b; #endif { b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == yy_current_buffer ) yy_load_buffer_state(); } #ifndef YY_NO_SCAN_BUFFER #ifdef YY_USE_PROTOS YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size ) #else YY_BUFFER_STATE yy_scan_buffer( base, size ) char *base; yy_size_t size; #endif { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return 0; b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = 0; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; yy_switch_to_buffer( b ); return b; } #endif #ifndef YY_NO_SCAN_STRING #ifdef YY_USE_PROTOS YY_BUFFER_STATE yy_scan_string( yyconst char *str ) #else YY_BUFFER_STATE yy_scan_string( str ) yyconst char *str; #endif { int len; for ( len = 0; str[len]; ++len ) ; return yy_scan_bytes( str, len ); } #endif #ifndef YY_NO_SCAN_BYTES #ifdef YY_USE_PROTOS YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len ) #else YY_BUFFER_STATE yy_scan_bytes( bytes, len ) yyconst char *bytes; int len; #endif { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = len + 2; buf = (char *) yy_flex_alloc( n ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); for ( i = 0; i < len; ++i ) buf[i] = bytes[i]; buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; b = yy_scan_buffer( buf, n ); if ( ! b ) YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #endif #ifndef YY_NO_PUSH_STATE #ifdef YY_USE_PROTOS static void yy_push_state( int new_state ) #else static void yy_push_state( new_state ) int new_state; #endif { if ( yy_start_stack_ptr >= yy_start_stack_depth ) { yy_size_t new_size; yy_start_stack_depth += YY_START_STACK_INCR; new_size = yy_start_stack_depth * sizeof( int ); if ( ! yy_start_stack ) yy_start_stack = (int *) yy_flex_alloc( new_size ); else yy_start_stack = (int *) yy_flex_realloc( (void *) yy_start_stack, new_size ); if ( ! yy_start_stack ) YY_FATAL_ERROR( "out of memory expanding start-condition stack" ); } yy_start_stack[yy_start_stack_ptr++] = YY_START; BEGIN(new_state); } #endif #ifndef YY_NO_POP_STATE static void yy_pop_state() { if ( --yy_start_stack_ptr < 0 ) YY_FATAL_ERROR( "start-condition stack underflow" ); BEGIN(yy_start_stack[yy_start_stack_ptr]); } #endif #ifndef YY_NO_TOP_STATE static int yy_top_state() { return yy_start_stack[yy_start_stack_ptr - 1]; } #endif #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif #ifdef YY_USE_PROTOS static void yy_fatal_error( yyconst char msg[] ) #else static void yy_fatal_error( msg ) char msg[]; #endif { (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up yytext. */ \ yytext[yyleng] = yy_hold_char; \ yy_c_buf_p = yytext + n - YY_MORE_ADJ; \ yy_hold_char = *yy_c_buf_p; \ *yy_c_buf_p = '\0'; \ yyleng = n; \ } \ while ( 0 ) /* Internal utility routines. */ #ifndef yytext_ptr #ifdef YY_USE_PROTOS static void yy_flex_strncpy( char *s1, yyconst char *s2, int n ) #else static void yy_flex_strncpy( s1, s2, n ) char *s1; yyconst char *s2; int n; #endif { register int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_USE_PROTOS static void *yy_flex_alloc( yy_size_t size ) #else static void *yy_flex_alloc( size ) yy_size_t size; #endif { return (void *) malloc( size ); } #ifdef YY_USE_PROTOS static void *yy_flex_realloc( void *ptr, yy_size_t size ) #else static void *yy_flex_realloc( ptr, size ) void *ptr; yy_size_t size; #endif { /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return (void *) realloc( (char *) ptr, size ); } #ifdef YY_USE_PROTOS static void yy_flex_free( void *ptr ) #else static void yy_flex_free( ptr ) void *ptr; #endif { free( ptr ); } #if YY_MAIN int main() { yylex(); return 0; } #endif /* My own input() function to handle case insensitivity and macros */ /* and line counting */ /* for saving input for command definition */ int inputbuffersize = 0; int inputbufferspot = 0; /* where next character will go */ int inputsave_flag = 0; /* so only save when reading command */ char *inputbuffer; void savein(c) int c; { if ( !inputsave_flag ) return; if ( inputbufferspot >= inputbuffersize ) { int newsize = inputbuffersize?2*inputbuffersize:1000; inputbuffer = my_list_realloc(inputbuffer,newsize,ETERNAL_BLOCK); inputbuffersize = newsize; } inputbuffer[inputbufferspot++] = (char)c; } void reset_inputbuffer() { inputbufferspot = 0; inputsave_flag = 1; } #define MOREMAX 1000 char morebuff[MOREMAX+2]; int rawinput() { int c=0,retval; if ( cmdptr ) /* read from string */ { if ( *cmdptr == 0 ) /* newline at end of each line */ { if ( previous_char != '\n' ) { retval = previous_char = '\n'; errbuff[errspot++] = retval; savein(retval); } else retval = 0; goto rawreturn; } c = *(cmdptr++); if ( c == MOREIN ) /* for really long input lines */ { #ifdef USE_READLINE //CSL prompt(CONTPROMPT,morebuff,MOREMAX); #else getstring(morebuff,MOREMAX); #endif cmdptr = morebuff; if ( !*cmdptr ) { if (!in_quote && !in_comment) errbuff[errspot++] = '\n'; retval = '\n'; savein(retval); goto rawexit; } c = *(cmdptr++); } } else /* read from file */ { for(;;) { if ( commandfd == NULL ) /* in case read after close */ { retval = previous_char = 0; goto rawreturn; } c = getc(commandfd); if ( c != -1 ) break; pop_commandfd(); /* EOF, so pop #include stack */ if ( !datafile_flag ) /* EOF for a command file */ { retval = previous_char = 0; goto rawreturn; } } } /* taking care of various line-ending combinations */ if ( (c == '\n') && (previous_char == '\r') ) return rawinput(); previous_char = c; if ( c == '\r' ) c = '\n'; errbuff[errspot++] = (char)c; retval = c; savein(retval); rawexit: if ( errspot >= ERRBUFFSIZE ) /* need partial reset */ { memcpy(errbuff,errbuff+ERRBUFFSIZE/2,ERRBUFFSIZE/2); errspot = ERRBUFFSIZE/2; } rawreturn: return retval; } void get_more_input() {int n; #ifdef USE_READLINE //CSL prompt(MOREPROMPT,morebuff,MOREMAX); #else if ( !topflag && (!quiet_load_flag || (commandfd == stdin)) ) outstring("more> "); getstring(morebuff,MOREMAX); #endif n = strlen(morebuff); if ( (morebuff[n-1] != MOREIN) && (morebuff[n-1] != '\n') ) morebuff[n] = '\n'; morebuff[n+1] = 0; catfulltext(morebuff); cmdptr = morebuff; } int kb_input() { int c; if ( spot > 0 ) { c = buff[--spot]; goto input_exit; } retry: c = rawinput(); if ( (c == '\\') ) /* line splicing */ { /* have to do line splicing as input filter to remove special character of line start */ int cc = c; c = rawinput(); if ( c == 0 ) { get_more_input(); c = ' '; goto input_exit; } if ( c == '\n' ) { int ccc; line_no++; ccc = rawinput(); if ( ccc == 0 ) { get_more_input(); c = ' '; goto input_exit; } rawunput(ccc); c = ' '; } else { rawunput(c); c = cc; /* not linesplicing */ } } if ( c == 0 && in_comment ) { get_more_input(); goto retry; } if ( c == 0 && in_quote ) { get_more_input(); goto retry; } input_exit: return c; } void rawunput(c) int c; { if ( spot >= BUFFSIZE - 1 ) { buff = my_list_realloc(buff,BUFFSIZE+500,ETERNAL_BLOCK); BUFFSIZE += 500; } buff[spot++] = (char)c; } void unput_string(s) char *s; { char *c; c = s ; while ( *c ) c++; /* find end of token */ while ( c != s ) { --c; unput(*c); } } void dump_buff(str,size) /* for error reporting */ char *str; size_t size; { int place; strcpy(str," Input line so far: \n"); size -= strlen(str)+3; errbuff[errspot] = 0; for ( place = errspot-1 ; place >= 0 ; place-- ) { if (errbuff[place]==0 ) errbuff[place] = ' '; /* nulls creep in */ if (errbuff[place]=='\n' && place < errspot-2) break; } strncat(str,errbuff+place+1,size); if ( str[strlen(str)-1] != '\n' ) strcat(str,"\n"); } int yywrap() { /* Called at end of input. Return 1 if really done, 0 if not */ spot = 0; /* clean buffer */ if ( (parens > 0) || (brace_depth > 0) || in_quote || in_comment || in_function || in_control_structure /* || loopdepth */ ) { get_more_input(); return 0; } if ( !help_flag ) switch ( tok ) { case '+': case '-': case '*': /* might be wrap symbols */ if ( read_wrap_flag ) return 1; else { get_more_input(); return 0 ; } break; case UMINUS_: case ',': case '=': case '?': case ':': case OR_: case AND_: case NOT_: case EQ_: case '>': case '<': case LE_: case GE_: case NE_: case '/': case '%': case '^': case ON_CONSTRAINT_: case HIT_CONSTRAINT_: case ON_BOUNDARY_: case DO_ : case FOR_: case WHILE_ : case IF_ : case ELSE_: case THEN_ : case SET_: case DEFINE_: case WHERE_: case ASSIGN_ : case PERM_ASSIGN_: case REDEFINE_ : case FUNCTION_: case PROCEDURE_WORD_: get_more_input(); return 0; break; default: return 1; } return 1; /* done */ } void macro_init() { if ( macro_subs ) myfree((char *)macro_subs); if ( macros ) myfree((char *)macros); macro_subs = NULL; macros = NULL; macro_count = macro_subs_top = macro_max = macro_subs_max = 0; } void yylex_init() { unput_tok_count = 0; ubuff_spot = 0; spot = 0; errspot = 0; yylval.i = 0; yylval.r = 0.0; in_function = brace_depth = in_quote = in_comment = 0; #ifdef FLEX_SCANNER if ( yy_current_buffer) YY_FLUSH_BUFFER; #endif } /************************************************************* * * Function: record_macro() * * Purpose: Record macro definition. * */ void record_macro() { int len; char *mspot; int cont_flag; int backslash_spot; macro_flag = 0; if ( macro_count >= macro_max ) { if ( macro_max == 0 ) { macro_max = 10; macros = (struct macro *)mycalloc(macro_max,sizeof(struct macro)); } else { macros = (struct macro *)kb_realloc((char *)macros, 2*macro_max*sizeof(struct macro)); macro_max *= 2; } } strncpy(macros[macro_count].name,yytext,MACRONAMESIZE); if ( macro_subs_max == 0 ) { macro_subs_max = 2*SUBMAX; macro_subs = (char *)mycalloc(macro_subs_max,1); } /* read in macro string */ for ( len = 0, cont_flag = 0 ; ; len++ ) { int c = KB_INPUT(); if ( len == 0 && (c == ' ' || c == '\t') ) { len--; continue; /* skip leading whitespace */ } if ( c == '\\' ) { cont_flag = 1; backslash_spot = len; } else if ( c != ' ' && c != '\t' && c != '\n' && c != 0 ) cont_flag = 0; if ( (c == '\0') || (c == '\n') ) { line_no++; if ( !cont_flag ) break; cont_flag = 0; len = backslash_spot; macro_subs[macro_subs_top+len] = 0; len--; continue; } if ( macro_subs_top+len >= macro_subs_max-2 ) { macro_subs = (char *)kb_realloc(macro_subs,macro_subs_max+2*SUBMAX); macro_subs_max += 2*SUBMAX; } macro_subs[macro_subs_top+len] = (char)c; } mspot = macro_subs+macro_subs_top; /* strip terminal comment */ if ( strstr(mspot,"//") ) { *strstr(mspot,"//") = 0; len = strlen(mspot); } else mspot[len] = 0; /* strip trailing blanks */ while ( (len > 0) && (mspot[len-1] == ' ') ) mspot[--len] = 0; macros[macro_count].subsize = len; macros[macro_count++].offset = macro_subs_top; macro_subs_top += len+1; } /************************************************************* * * Function: macro() * * Purpose: See if yytext is a macro, and do substitution. * * Return: 1 if macro, 0 if not. * */ int macro() { int n,k; char *c; if ( macro_flag ) { record_macro(); return WAS_MACRO_DEF; } if ( strcmp(yytext,"_anti_line_no_") == 0 ) { line_no--; /* kludge line backup */ errspot -= 15; return 1; } if ( !keep_macros_flag && !datafile_flag ) return 0; /* no macros in commands */ /* find name, simple linear search */ for ( n = macro_count-1 ; n >= 0 ; n-- ) /* use latest definition */ if ( strcmp(yytext,macros[n].name) == 0 ) break; if ( n < 0 ) return 0; /* not found */ /* insert string into input in reverse order so reads ok */ c = macro_subs+macros[n].offset; for ( k = macros[n].subsize - 1 ; k >= 0 ; k-- ) KB_UNPUT(c[k]); KB_UNPUT(' '); /* substitution starts with whitespace; for proper preservation of unary minus leading in the macro */ return MACRO_EXPANDED; } /* for deciding type of minus sign or plus sign */ int minus_type(c) int c; /* '+' or '-' */ { int result; switch ( tok ) { case LEAD_INTEGER_: case SIGNED_NUMBER_: case INTEGER_: case REAL_: case PI_: case E_: case G_: case PARAM_: case ARRAYIDENT_: case COORD_: case VALUE_: case BARE_: case IDENT_: case QUANTITY_NAME_: case WRAP_: case PERM_IDENT_: case LENGTH_: case DIHEDRAL_: case VALENCE_: case AREA_: case VOLUME_: case DENSITY_: case TAG_: case ID_: case ORIGINAL_: case SQ_MEAN_CURV_: case OID_: case INTERNAL_VARIABLE_: case COLOR_: case TOGGLEVALUE_: case EXTRA_ATTRIBUTE_: case PRESSURE_: case ARRAY_ATTRIBUTE_: case FRONTCOLOR_: case BACKCOLOR_: case TARGET_: case NO_REFINE_: case VOLCONST_: case FRONTBODY_: case NONCONTENT_: case BACKBODY_: case TETRA_POINT_: case TRIPLE_POINT_: case MIDV_: case PHASE_: case MODULUS_: case METHOD_NAME_: case NODISPLAY_: case HIT_PARTNER_: case ']': case ')': result = c; break; default: result = (c=='-' ? UMINUS_ : UPLUS_) ; break; } return result; } /**************************************************************************** * * function identcase() * * purpose: handle case of identifier token, maybe macro, keyword, etc. */ int keyword_compare(a,b) struct ckey *a,*b; { return stricmp(a->name,b->name); } int identcase(lexeme) char *lexeme; { int k,i; int type; struct sym *s; static int sorted = 0; /* flag for sorting keywords */ struct ckey *keyptr; struct ckey key; /* for bsearch key */ char *c,*p; dll_func_type h; yylval.i = 0; if ( ! sorted ) { qsort((char*)datafile_keywords, sizeof(datafile_keywords)/ sizeof(struct dkey),sizeof(struct dkey), FCAST keyword_compare); qsort((char*)command_keywords, sizeof(command_keywords)/ sizeof(struct ckey),sizeof(struct ckey), FCAST keyword_compare); qsort((char*)togglenames, sizeof(togglenames)/ sizeof(struct ckey),sizeof(struct ckey), FCAST keyword_compare); qsort((char*)colornames, sizeof(colornames)/ sizeof(struct ckey),sizeof(struct ckey), FCAST keyword_compare); qsort((char*)internal_variables, sizeof(internal_variables)/ sizeof(struct ckey),sizeof(struct ckey), FCAST keyword_compare); qsort((char*)mathfunc_keywords, sizeof(mathfunc_keywords)/ sizeof(struct ckey),sizeof(struct ckey), FCAST keyword_compare); qsort((char*)mathfunc2_keywords, sizeof(mathfunc2_keywords)/ sizeof(struct ckey),sizeof(struct ckey), FCAST keyword_compare); sorted = 1; } if ( strcmp(lexeme,"_command_") == 0 ) { return tok = COMMAND_START_ ; } if ( strcmp(lexeme,"_expr_") == 0 ) { return tok = EXPRESSION_START_; } /* strip whitespace from front of lexeme */ c = strtok(lexeme,whitespace); if ( c != lexeme ) { p = lexeme; while ( *c ) *(p++) = *(c++); *p = 0;} if ( strlen(lexeme) == 1 ) { char ch = lexeme[0]; if ( verb_flag == 1 || tok==PRINT_ || (verb_flag == 2 && (toupper(ch) < 'W')) ) { yylval.i=lexeme[0]; if ( single_redefine[yylval.i].start ) return tok = SINGLE_REDEFD_; switch ( yylval.i ) { case 't': case 'l': case 'j': case 'P': case 'M': case 'w': case 'n': case 'm': case 'b': case 'k': case 'K': case 'p': case 'y': return ( tok = SINGLE_LETTER_ARG_ ); case 'G': if ( verb_flag ) return ( tok = SINGLE_LETTER_ARG_ ); else return tok=G_; default: return ( tok = SINGLE_LETTER_ ); } } else switch ( toupper(lexeme[0]) ) { case 'X': case 'Y': case 'Z': case 'W': yylval.i = ((toupper(lexeme[0]) - 'X' + 4) % 4) + 1; return COORD_; } } if ( toupper(lexeme[0]) == 'X' ) { char *c; for ( c = lexeme+1 ; isdigit(*c) ; c++ ) ; if ( *c == 0 ) { yylval.i = atoi(lexeme+1); if ( yylval.i != 0 ) return COORD_; } } if ( toupper(lexeme[0]) == 'P' && isdigit(lexeme[1]) && !lexeme[2] ) { yylval.i = atoi(lexeme+1); return PARAM_; } if ( lexeme[0] == 'g' ) { char *c; for ( c = lexeme+1 ; isdigit(*c) ; c++ ) ; if ( *c == 0 ) { yylval.i = atoi(lexeme+1); return GO_COUNT_; } } if ( strncmp(lexeme,"usr",3) == 0 ) { char *c; for ( c = lexeme+3 ; isdigit(*c) ; c++ ) ; if ( *c == 0 ) { yylval.i = atoi(lexeme+3); return USERFUNC_; } } key.name = lexeme; #define BSEARCH(namelist)\ (struct ckey*)bsearch((char*)&key,(char*)(namelist),\ sizeof(namelist)/sizeof(struct ckey),\ sizeof(struct ckey),FCAST keyword_compare); keyptr = BSEARCH(colornames); if ( keyptr ) { yylval.r = yylval.i = keyptr->token; return (tok = INTEGER_); } /* search math funcs */ keyptr = BSEARCH(mathfunc_keywords); if ( keyptr ) { yylval.i = keyptr->token; return (tok = MATHFUNC_); } keyptr = BSEARCH(mathfunc2_keywords); if ( keyptr ) { yylval.i = keyptr->token; return (tok = MATHFUNC2_); } if ( const_expr_flag ) { for ( k=0 ; ktoken;} keyptr = BSEARCH(command_keywords); if ( keyptr ) { sprintf(errmsg, "Use of the keyword '%s' as an identifier is very ill-advised!\n", lexeme); kb_error(2328,errmsg, WARNING); } } else /* search keywords and internal variables */ { keyptr = BSEARCH(command_keywords); if ( keyptr ) { if ( kb_stricmp(lexeme,"HELP") == 0 ) help_flag = 1; yylval.i = tok = keyptr->token; switch ( tok ) { /* set verb_flag when expecting a command */ case THEN_: case ELSE_: case DO_: verb_flag = 1; break; /* clear verb_flag when expecting an expression */ case PRINT_: case IF_: case WHILE_: case VIEW_TRANSFORMS_: case TRANSFORM_DEPTH_: case METIS_: case KMETIS_: case OMETIS_: case EDGEWEED_: case AREAWEED_: case EDGEDIVIDE_: case LANCZOS_: case RITZ_: case EIGENPROBE_: case MOVE_: case ZOOM_: case LAGRANGE_: case SET_: case PRINTF_: case LIST_: case DELETE_: case VERTEX_AVERAGE_: case BINARY_PRINTF_: case DISSOLVE_: case REFINE_: case EDGESWAP_: case FIX_: case UNFIX_: case SPRINTF_: case EPRINT_: case HISTOGRAM_: case LOGHISTOGRAM_: case FOREACH_: case SHOW_EXPR_: case UNSET_: case HESSIAN_SADDLE_: case HESSIAN_SEEK_: case NOTCH_: case AUTOCHOP_: case AUTOPOP_: case OPTIMIZE_: verb_flag = 0; break; case SHOW_: if ( verb_flag ) tok = SHOWVERB_; verb_flag = 0; break; case TRANSFORM_EXPR_: if ( verb_flag ) tok = TRANSFORM_EXPR_VERB_; verb_flag = 0; break; } return tok; } } for ( i = 0 ; i < NUMDATATYPES ; i++ ) if ( stricmp(lexeme,datatype_name[i]) == 0 ) { yylval.i = DATATYPE_; yylval.datatype = i; return tok = DATATYPE_; } keyptr = BSEARCH(togglenames); if ( keyptr ) { yylval.i = keyptr->token; if ( verb_flag != 0 ) tok = TOGGLENAME_; else tok = TOGGLEVALUE_; return tok; } keyptr = BSEARCH(internal_variables); if ( keyptr ) { yylval.i = keyptr->token; return tok = INTERNAL_VARIABLE_; } /* search local variables */ yylval.i = lookup_local_var(lexeme); if ( yylval.i ) { struct global *g; g = globals(yylval.i); if ( g->flags & SUBROUTINE ) return ( tok = PROCEDURE_ ); else if ( g->flags & ORDINARY_PARAM ) { switch ( g->type ) { case VERTEX_TYPE: yylval.etype = VERTEX; return ( tok = ELEMENT_IDENT_ ); case ELEMENTID_TYPE: yylval.etype = -1; return ( tok = ELEMENT_IDENT_ ); case EDGE_TYPE: yylval.etype = EDGE; return ( tok = ELEMENT_IDENT_ ); case FACET_TYPE: yylval.etype = FACET; return ( tok = ELEMENT_IDENT_ ); case BODY_TYPE: yylval.etype = BODY; return ( tok = ELEMENT_IDENT_ ); case FACETEDGE_TYPE: yylval.etype = FACETEDGE; return ( tok = ELEMENT_IDENT_ ); default: return ( tok = IDENT_ ); } } else if ( g->flags & FUNCTION_NAME ) return ( tok = FUNCTION_IDENT_ ); else if ( g->flags & PROCEDURE_NAME ) return ( tok = PROCEDURE_IDENT_ ); else if ( g->flags & STRINGVAL ) return ( tok = STRINGGLOBAL_ ); else if ( g->flags & QUANTITY_NAME ) /* can't happen */ return ( tok = QUANTITY_NAME_ ); else if ( g->flags & METHOD_NAME ) /* can't happen */ return ( tok = METHOD_NAME_ ); else if ( g->flags & CONSTRAINT_NAME ) /* can't happen */ return ( tok = CONSTRAINT_NAME_ ); else if ( g->flags & BOUNDARY_NAME ) /* can't happen */ return ( tok = BOUNDARY_NAME_ ); else if ( g->flags & DYNAMIC_LOAD_FUNC ) /* can't happen */ return ( tok = DYNAMIC_LOAD_FUNC_ ); else if ( g->flags & ARRAY_PARAM ) return ( tok = ARRAYIDENT_ ); else if ( g->flags & GLOB_LOCALVAR ) return IDENT_; else return (tok = NEWIDENT_); } /* search symbol table */ s = symbol_lookup(lexeme); if ( s ) { yysym = s; yylval.i = yysym-symtable; verb_flag = 0; return (tok = SYMBOL_ ); } /* search parameter names */ yylval.i = lookup_global_hash(lexeme,0,0,HASH_LOOK); if ( yylval.i != 0 ) { int nametype = yylval.i & NAMETYPEMASK; struct global *g; yylval.i &= INDEXMASK; /* get plain index */ if ( nametype == QUANTITYNAME ) return tok = QUANTITY_NAME_; else if ( nametype == METHODNAME ) return tok = METHOD_NAME_; else if ( nametype == PERM_NAME ) { yylval.i |= PERMGLOBAL; if ( perm_globals(yylval.i)->flags & SUBROUTINE ) return ( tok = PERM_PROCEDURE_ ); else if ( perm_globals(yylval.i)->flags & STRINGVAL ) return ( tok = PERM_STRINGGLOBAL_ ); else if ( perm_globals(yylval.i)->flags & ARRAY_PARAM ) return ( tok = ARRAYIDENT_ ); else return (tok = PERM_IDENT_); } yylval.i |= EPHGLOBAL; g = globals(yylval.i); if ( g->flags & SUBROUTINE ) return ( tok = PROCEDURE_ ); else if ( g->flags & FUNCTION_NAME ) return ( tok = FUNCTION_IDENT_ ); else if ( g->flags & PROCEDURE_NAME ) return ( tok = PROCEDURE_IDENT_ ); else if ( g->flags & STRINGVAL ) return ( tok = STRINGGLOBAL_ ); else if ( g->flags & QUANTITY_NAME ) return ( tok = QUANTITY_NAME_ ); else if ( g->flags & METHOD_NAME ) return ( tok = METHOD_NAME_ ); else if ( g->flags & CONSTRAINT_NAME ) return ( tok = CONSTRAINT_NAME_ ); else if ( g->flags & BOUNDARY_NAME ) return ( tok = BOUNDARY_NAME_ ); else if ( g->flags & DYNAMIC_LOAD_FUNC ) return ( tok = DYNAMIC_LOAD_FUNC_ ); else if ( g->flags & ARRAY_PARAM ) return ( tok = ARRAYIDENT_ ); else { switch ( g->type ) { case VERTEX_TYPE: yylval.etype = VERTEX; return ( tok = ELEMENT_IDENT_ ); case ELEMENTID_TYPE: yylval.etype = -1; return ( tok = ELEMENT_IDENT_ ); case EDGE_TYPE: yylval.etype = EDGE; return ( tok = ELEMENT_IDENT_ ); case FACET_TYPE: yylval.etype = FACET; return ( tok = ELEMENT_IDENT_ ); case BODY_TYPE: yylval.etype = BODY; return ( tok = ELEMENT_IDENT_ ); case FACETEDGE_TYPE: yylval.etype = FACETEDGE; return ( tok = ELEMENT_IDENT_ ); default: return ( tok = IDENT_ ); } } } /* search extra attributes */ for ( type = 0 ; type < NUMELEMENTS ; type++ ) { struct extra *ex; int i; for ( i = 0, ex = EXTRAS(type) ; i < web.skel[type].extra_count ; i++ , ex++ ) if ( stricmp(lexeme, ex->name) == 0 ) { strncpy(idname,lexeme,sizeof(idname)); /* save text */ yylval.qnum = i; yylval.etype = type; return tok = ex->array_spec.dim ? ARRAY_ATTRIBUTE_ : EXTRA_ATTRIBUTE_; } } /* search dynamic load libraries */ h = search_libraries(lexeme); if ( h ) { yylval.i = add_global(lexeme); globals(yylval.i)->flags |= DYNAMIC_LOAD_FUNC; globals(yylval.i)->value.funcptr = h; return ( tok = DYNAMIC_LOAD_FUNC_ ); } /* if here, then not keyword */ strncpy(idname,lexeme,sizeof(idname)); /* save text */ if ( strlen(lexeme) == 1 ) { sprintf(errmsg, "Use of single letter command '%c' is illegal here!\n", lexeme[0]); kb_error(2329,errmsg, WARNING); if ( !datafile_flag) return tok = lexeme[0]; } yylval.i = 0; return(tok = NEWIDENT_) ; } /* end identcase() */ /****************************************************************** * * function: keywordname() * * purpose: find keyword of given token number. * */ char *keywordname(toknum) int toknum; { int k,i,imax; for ( k = 0 ; k < sizeof(command_keywords)/sizeof(struct ckey) ; k++ ) if ( toknum == command_keywords[k].token ) { static char name[32]; strncpy(name,command_keywords[k].name,31); return name; } for ( k = 0 ; k < sizeof(togglenames)/sizeof(struct ckey) ; k++ ) if ( toknum == togglenames[k].token ) { static char name[32]; strncpy(name,togglenames[k].name,31); return name; } for ( k = 0 ; k < sizeof(internal_variables)/sizeof(struct ckey) ; k++ ) if ( toknum == internal_variables[k].token ) { static char name[32]; strncpy(name,internal_variables[k].name,31); return name; } imax = sizeof(colornames)/sizeof(struct ckey); for ( i = 0 ; i < imax ; i++ ) if ( colornames[i].token == toknum ) return colornames[i].name; imax = sizeof(datafile_keywords)/sizeof(struct dkey); for ( i = 0 ; i < imax ; i++ ) if ( datafile_keywords[i].token == toknum ) return datafile_keywords[i].name; imax = sizeof(mathfunc_keywords)/sizeof(struct ckey); for ( i = 0 ; i < imax ; i++ ) if ( mathfunc_keywords[i].token == toknum ) return mathfunc_keywords[i].name; imax = sizeof(mathfunc2_keywords)/sizeof(struct ckey); for ( i = 0 ; i < imax ; i++ ) if ( mathfunc2_keywords[i].token == toknum ) return mathfunc2_keywords[i].name; /* unfound */ return tokname(toknum); } /**************************************************************************** * * function identtype() * * purpose: find type of an identifier. This version used by * is_variable() etc. commands. */ int identtype(word) char *word; { int type; struct sym *s; struct ckey *keyptr; struct ckey key; /* for bsearch key */ if ( strlen(word) == 1 ) { if ( single_redefine[yylval.i].start ) return tok = SINGLE_REDEFD_; switch ( word[0] ) { case 't': case 'l': case 'j': case 'P': case 'M': case 'w': case 'n': case 'm': case 'b': case 'k': case 'K': case 'p': case 'y': return ( tok = SINGLE_LETTER_ARG_ ); case 'G': if ( verb_flag ) return tok = SINGLE_LETTER_ARG_; else return tok = G_; break; default: return ( tok = SINGLE_LETTER_ ); } } key.name = word; keyptr = BSEARCH(colornames); if ( keyptr ) return INTEGER_; /* search math funcs */ keyptr = BSEARCH(mathfunc_keywords); if ( keyptr ) { return MATHFUNC_; } keyptr = BSEARCH(mathfunc2_keywords); if ( keyptr ) { return MATHFUNC2_; } /* search keywords and internal variables */ { keyptr = BSEARCH(command_keywords); if ( keyptr ) { return tok; } keyptr = BSEARCH(togglenames); if ( keyptr ) return tok; keyptr = BSEARCH(internal_variables); if ( keyptr ) { return INTERNAL_VARIABLE_; } } /* search local variables */ yylval.i = lookup_local_var(word); if ( yylval.i ) { struct global *g; g = globals(yylval.i); if ( g->flags & SUBROUTINE ) return ( tok = PROCEDURE_ ); else if ( g->flags & ORDINARY_PARAM ) return ( tok = IDENT_ ); else if ( g->flags & STRINGVAL ) return ( tok = STRINGGLOBAL_ ); else if ( g->flags & QUANTITY_NAME ) /* can't happen */ return ( tok = QUANTITY_NAME_ ); else if ( g->flags & METHOD_NAME ) /* can't happen */ return ( tok = METHOD_NAME_ ); else if ( g->flags & CONSTRAINT_NAME ) /* can't happen */ return ( tok = CONSTRAINT_NAME_ ); else if ( g->flags & BOUNDARY_NAME ) /* can't happen */ return ( tok = BOUNDARY_NAME_ ); else if ( g->flags & DYNAMIC_LOAD_FUNC ) /* can't happen */ return ( tok = DYNAMIC_LOAD_FUNC_ ); else if ( g->flags & ARRAY_PARAM ) return ( tok = ARRAYIDENT_ ); else if ( g->flags & GLOB_LOCALVAR ) return IDENT_; else return (tok = NEWIDENT_); } /* search symbol table */ s = symbol_lookup(word); if ( s ) { yysym = s; yylval.i = yysym-symtable; verb_flag = 0; return (tok = SYMBOL_ ); } /* search parameter names */ yylval.i = lookup_perm_global(word); if ( yylval.i >= 0 ) { if ( perm_globals(yylval.i)->flags & SUBROUTINE ) return ( tok = PERM_PROCEDURE_ ); else if ( globals(yylval.i)->flags & STRINGVAL ) return ( tok = PERM_STRINGGLOBAL_ ); else return (tok = PERM_IDENT_); } yylval.i = lookup_global(word); if ( yylval.i >= 0 ) { if ( globals(yylval.i)->flags & SUBROUTINE ) return ( tok = PROCEDURE_ ); else if ( globals(yylval.i)->flags & STRINGVAL ) return ( tok = STRINGGLOBAL_ ); else if ( globals(yylval.i)->flags & QUANTITY_NAME ) return ( tok = QUANTITY_NAME_ ); else if ( globals(yylval.i)->flags & METHOD_NAME ) return ( tok = METHOD_NAME_ ); else if ( globals(yylval.i)->flags & DYNAMIC_LOAD_FUNC ) return ( tok = DYNAMIC_LOAD_FUNC_ ); else if ( globals(yylval.i)->flags & ARRAY_PARAM ) return ( tok = ARRAYIDENT_ ); else return (tok = IDENT_); } /* search extra attributes */ for ( type = 0 ; type <= BODY ; type++ ) { struct extra *ex; int i; for ( i = 0, ex = EXTRAS(type) ; i < web.skel[type].extra_count ; i++ , ex++ ) if ( stricmp(word, ex->name) == 0 ) { strncpy(idname,word,sizeof(idname)); /* save text */ yylval.qnum = i; yylval.etype = type; return tok = ex->array_spec.dim ? ARRAY_ATTRIBUTE_ : EXTRA_ATTRIBUTE_; } } /* if here, then not keyword */ yylval.i = 0; return(tok = NEWIDENT_) ; } /* end identtype() */ /********************************************************************* * * function: kblex() * * purpose: my own lexical analyzer, faster (for Evolver) and * more comprehensible. */ /* States */ #define L_START 1 #define L_SLASH 2 #define L_LINECOMMENT 3 #define L_DOT 4 #define L_DIGITS 5 #define L_DIGITS_DOT 6 #define L_WHITESPACE 7 #define L_WHITESIGN 8 #define L_LINESTART 9 #define L_LEAD_INTEGER 10 #define L_LEAD_INTEGER_AT 11 #define L_INTEGER_AT 12 #define L_DIGITS_E 13 #define L_ALPHANUM 14 #define L_COLON 15 #define L_SEMICOLON 16 #define L_PLUS 17 #define L_MINUS 18 #define L_STAR 19 #define L_BANG 20 #define L_PIPE 21 #define L_AND 22 #define L_GREATER 23 #define L_COLONCOLON 24 #define L_SINGLE_QUOTE 25 #define L_HEXNUMBER 26 #define L_ZERO 27 #define L_GRGR 28 #define L_BACKQUOTE 29 #define L_SHARP 30 #define L_COLONCOLONCOLON 31 #define L_QUOTE 32 #define L_EQUAL 33 #define L_LESS 34 #define L_DIGITS_E_DIGITS 35 #define L_LEAD_MINUS 36 #define L_BACKSLASH 37 /* Unput buffer */ int ubuff_max = 0; int ubuff_spot = 0; /* index of first vacant spot in unput_buff */ char *unput_buff; /* the token */ /*char *yytext;*/ int yytext_max; char kb_input_new() { char c; if ( ubuff_spot ) { c = unput_buff[--ubuff_spot]; unput_buff[ubuff_spot] = 0; return c; } c = rawinput(); while ( c == 0 ) { if ( yywrap() ) return 0; else c = rawinput(); } return c; } void kb_unput(c) char c; { if ( ubuff_spot >= ubuff_max ) { if ( unput_buff ) { ubuff_max *= 2; unput_buff = realloc(unput_buff,ubuff_max); } else { ubuff_max = 16000; unput_buff = calloc(ubuff_max,sizeof(char)); } } unput_buff[ubuff_spot++] = c; } int kblex() { int state; char nextchar; char *yyspot; /* first empty spot in yytext */ int retval; PROF_START(kblex); if ( !yytext ) { yytext_max = 200; yytext = calloc(yytext_max,sizeof(char)); } yyspot = yytext; memset(&yylval,0,sizeof(yylval)); state = tok ? L_START : L_LINESTART; for(;;) { int len = yyspot-yytext; if ( len >= yytext_max-2 ) { yytext_max *= 2; yytext = realloc(yytext,yytext_max); yyspot = yytext + len; } *(yyspot++) = nextchar = kb_input_new(); switch ( state ) { case L_START: if ( nextchar == '0' ) state = L_ZERO; else if ( isdigit(nextchar) ) state = L_DIGITS; else if ( isalpha(nextchar) ) state = L_ALPHANUM; else switch ( nextchar ) { case 0: tok=0; goto kblex_exit; case '\n' : line_no++; yyspot = yytext; state = L_LINESTART; break; case ' ': case '\t': yyspot--; state = L_WHITESPACE; break; case '/': state = L_SLASH; break; case '.': state = L_DOT; break; case '_': state = L_ALPHANUM; break; case ':': state = L_COLON; break; case ';': state = L_SEMICOLON; break; case '+': state = L_PLUS; break; case '-': state = L_MINUS; break; case '*': state = L_STAR; break; case '!': state = L_BANG; break; case '|': state = L_PIPE; break; case '&': state = L_AND; break; case '>': state = L_GREATER; break; case '\'': state = L_SINGLE_QUOTE; break; case '"': state = L_QUOTE; break; case '`': state = L_BACKQUOTE; break; case '=': state = L_EQUAL; break; case '<': state = L_LESS; break; case '\\': state = L_BACKSLASH; break; case '#': state = L_SHARP; break; case '(': verb_flag = 0; *yyspot = 0; tok = nextchar; goto kblex_exit; case ')': case '^': case ',': case '?': case '%': case '[': case ']': case '}': case '@': *yyspot = 0; tok = nextchar; goto kblex_exit; case '{': verb_flag = 1; *yyspot = 0; tok = nextchar; goto kblex_exit; default: sprintf(errmsg,"Illegal character '%c'\n",nextchar); kb_error(3658,errmsg,RECOVERABLE); } break; case L_BACKSLASH: /* allow trailing whitespace */ while ( nextchar == ' ' || nextchar == '\t' ) nextchar = kb_input_new(); if ( nextchar == '\n' ) { yyspot = yytext; state = L_START; line_no++; break; } kb_error(3659, "Illegal backslash. Backslash is line continuation character.\n", RECOVERABLE); break; case L_EQUAL: if ( nextchar == '=' ) { *yyspot = 0; tok = EQ_; goto kblex_exit; } kb_unput(nextchar); yyspot[-1] = 0; tok = (datafile_flag ? '=' : EQ_); goto kblex_exit; case L_QUOTE: { /* read quoted string */ int n; in_quote = 1; for (n=0;;n++) { if ( nextchar == '\n' ) line_no++; if ( nextchar == '"' && (n == 0 || (yytext[n-1] != '\\')) ) break; if ( n >= yytext_max - 2 ) { yytext_max *= 2; yytext = realloc(yytext,yytext_max); } yytext[n] = nextchar; nextchar = kb_input_new(); } yytext[n] = 0; reduce_string(yytext); in_quote = 0; tok = QUOTATION_; goto kblex_exit; } break; case L_LESS: if ( nextchar == '=' ) { *yyspot = 0; tok = LE_; goto kblex_exit; } else { kb_unput(nextchar); yyspot[-1] = 0; tok = '<'; goto kblex_exit; } break; case L_GREATER: if ( nextchar == '>' ) state = L_GRGR; else if ( nextchar == '=' ) { *yyspot = 0; tok = GE_; goto kblex_exit; } else { kb_unput(nextchar); yyspot[-1] = 0; tok = '>'; goto kblex_exit; } break; case L_GRGR: if ( nextchar == '>' ) { *yyspot = 0; tok = REDIRECTOVER_; goto kblex_exit; } else { kb_unput(nextchar); yyspot[-1] = 0; tok = REDIRECT_; goto kblex_exit; } break; case L_SINGLE_QUOTE: if ( nextchar == '\'' ) kb_error(3577,"Empty single quotes.\n",RECOVERABLE); if ( nextchar == 0 || nextchar == '\n' ) kb_error(3570,"Dangling single quote.\n",RECOVERABLE); yylval.i = nextchar; nextchar = kb_input_new(); if ( nextchar != '\'' ) kb_error(3583,"Single quote not closed after one character.\n", RECOVERABLE); yytext[1] = 0; tok = SINGLE_LETTER_; goto kblex_exit; break; case L_BACKQUOTE: while ( nextchar == ' ' ) yyspot[-1] = nextchar = kb_input_new(); if ( nextchar == ',' ) { *yyspot = 0; tok = BACKQUOTE_COMMA_; goto kblex_exit; } yyspot[-1] = 0; kb_unput(nextchar); tok = '`'; goto kblex_exit; break; case L_SHARP: while ( isalpha(nextchar) ) *(yyspot++) = nextchar = kb_input_new(); yyspot[-1] = 0; if ( strcmp(yytext,"#define") == 0 ) { if (!datafile_flag) kb_error(2853,"#define valid only in top of datafile.\n",WARNING); macro_flag = 1; state = L_START; yyspot = yytext; break; } if ( strcmp(yytext,"#include") == 0 ) { while ( nextchar != '"' ) nextchar = kb_input_new(); yyspot = yytext; do *(yyspot++) = nextchar = kb_input_new(); while ( nextchar != '"' ); yyspot[-1] = 0; push_commandfd(NULL,yytext); state = L_LINESTART; yyspot = yytext; break; } kb_error(3681,"Illegal # directive.\n",RECOVERABLE); break; case L_ZERO: if ( toupper(nextchar) == 'X' ) state = L_HEXNUMBER; else if ( nextchar == '.' ) state = L_DIGITS_DOT; else if ( isdigit(nextchar) ) state = L_DIGITS; else { kb_unput(nextchar); yyspot[-1] = 0; yylval.i = 0; verb_flag = 0; tok = INTEGER_; goto kblex_exit; } break; case L_HEXNUMBER: if ( isdigit(nextchar) || (toupper(nextchar) >= 'A' && toupper(nextchar) <= 'F') ) break; yyspot[-1] = 0; kb_unput(nextchar); sscanf(yytext+2,"%x",&yylval.i); yylval.r = (REAL)yylval.i; verb_flag = 0; tok = INTEGER_; goto kblex_exit; case L_WHITESPACE: if ( nextchar == '+' || nextchar == '-' ) { state = L_WHITESIGN; break; } if ( nextchar == ' ' || nextchar == '\t' ) { yyspot--; break; } else { state = L_START; kb_unput(nextchar); yyspot--; } break; case L_WHITESIGN: kb_unput(nextchar); if ( !isspace(nextchar) && (yytext[0] == '-') ) { if ( datafile_flag && uminus_flag && (parens == 0) ) { tok = UMINUS_; goto kblex_exit; } } /* else go back and treat as ordinary - */ yyspot--; state = yytext[0] == '+' ? L_PLUS : L_MINUS; break; case L_LINESTART: if ( nextchar == 0 ) { tok = 0; goto kblex_exit; } if ( nextchar == ' ' || nextchar == '\t' ) yyspot--; else if ( isdigit(nextchar) ) state = L_LEAD_INTEGER; else if ( nextchar == '-' ) state = L_LEAD_MINUS; else { kb_unput(nextchar); state = L_START; yyspot = yytext; } break; case L_LEAD_MINUS: if ( isdigit(nextchar) ) state = L_LEAD_INTEGER; else { kb_unput(nextchar); state = L_WHITESIGN; yyspot--; } break; case L_LEAD_INTEGER: if ( isdigit(nextchar) ) break; if ( nextchar == '.' ) { state = L_DIGITS_DOT; break; } if ( (toupper(nextchar) == 'E') || (toupper(nextchar) == 'D') ) { state = L_DIGITS_E; break; } if ( nextchar == '@' ) { state = L_LEAD_INTEGER_AT; break; } /* have LEAD_INTEGER_ */ kb_unput(nextchar); yyspot[-1] = 0; yylval.r = atof(yytext); yylval.i = atoi(yytext); verb_flag = 0; if ( lists_flag==LISTS_FULL ) { tok = LEAD_INTEGER_; goto kblex_exit; } else if ( uminus_flag || yytext[0] != '-' ) { tok = (fabs(yylval.r) > MAXINT) ? REAL_ : INTEGER_; goto kblex_exit; } else { for ( yyspot -= 2 ; yyspot != yytext ; yyspot-- ) kb_unput(*yyspot); yytext[1] = 0; tok = minus_type(yytext[0]); goto kblex_exit; } break; case L_DIGITS: if ( isdigit(nextchar) ) break; if ( nextchar == '.' ) { state = L_DIGITS_DOT; break; } if ( (toupper(nextchar) == 'E') || (toupper(nextchar) == 'D') ) { state = L_DIGITS_E; break; } if ( nextchar == '@' ) { state = L_INTEGER_AT; break; } if ( toupper(nextchar) == 'B' ) { char *c; yylval.i = 0; for ( c = yytext; *c=='0' || *c=='1' ; c++ ) yylval.i = 2*yylval.i + (*c - '0'); if ( c == yyspot-1 ) { yylval.r = (REAL)yylval.i; verb_flag = 0; tok = INTEGER_; goto kblex_exit; } /* else fall through to ordinary integer */ } /* have INTEGER_ */ kb_unput(nextchar); yyspot[-1] = 0; yylval.r = atof(yytext); yylval.i = atoi(yytext); verb_flag = 0; tok = (fabs(yylval.r) > MAXINT) ? REAL_ : INTEGER_; goto kblex_exit; case L_DIGITS_DOT: if ( (toupper(nextchar) == 'E') || (toupper(nextchar) == 'D') ) { state = L_DIGITS_E; break; } if ( isdigit(nextchar) ) break; if ( isalpha(nextchar) ) { *yyspot = 0; tok = VERSIONTOKEN_; goto kblex_exit; } /* have complete REAL */ kb_unput(nextchar); yyspot[-1] = 0; yylval.r = atof(yytext); verb_flag = 0; tok = REAL_; goto kblex_exit; case L_DOT: if ( isdigit(nextchar) ) { state = L_DIGITS_DOT; break; } /* else just a dot */ kb_unput(nextchar); yyspot[-1] = 0; tok = '.'; goto kblex_exit; case L_LEAD_INTEGER_AT: if ( isdigit(nextchar) ) break; /* have token */ kb_unput(nextchar); yyspot[-1] = 0; yylval.i = atoi(yytext); yylval.qnum = atoi(strchr(yytext,'@')+1); if ( yylval.i == 0 ) kb_error(3554,"Missing task number.",DATAFILE_ERROR); verb_flag = 0; tok = LEAD_INTEGER_AT_; goto kblex_exit; case L_INTEGER_AT: if ( isdigit(nextchar) ) break; /* have token */ kb_unput(nextchar); yyspot[-1] = 0; yylval.i = atoi(yytext); yylval.qnum = atoi(strchr(yytext,'@')+1); if ( yylval.i == 0 ) kb_error(3553,"Missing task number.",DATAFILE_ERROR); verb_flag = 0; tok = INTEGER_AT_; goto kblex_exit; case L_SLASH: switch ( nextchar ) { case '*': { /* eat comment */ register int c; in_comment = 1; state = L_START; for ( ; ; ) { while ( (c = kb_input_new()) != '*' && c != 0 ) if ( c == '\n' ) { line_no++; /* eat up text of comment */ state = L_LINESTART; } if ( c == '*' ) { while ( (c = kb_input_new()) == '*' ) ; if ( c == '/' ) break; /* found the end */ if ( c == '\n' ) { line_no++; state = L_LINESTART; } } if ( c == 0 && yywrap() ) { kb_error(2861,"End-of-file in comment\n",RECOVERABLE ); break; } } in_comment = 0; yyspot = yytext; } break; case '/': state = L_LINECOMMENT; break; case '=': *yyspot = 0; yylval.i = DIVASSIGN_; verb_flag = 0; tok=ASSIGNOP_; goto kblex_exit; default: kb_unput(nextchar); yyspot[-1] = 0; verb_flag = 0; tok = '/'; goto kblex_exit; } break; /* end L_SLASH */ case L_DIGITS_E: if ( isdigit(nextchar) || nextchar == '+' || nextchar == '-' ) { state = L_DIGITS_E_DIGITS; break; } kb_error(3664,"Missing exponent for scientific notation.\n", RECOVERABLE); break; case L_DIGITS_E_DIGITS: if ( isdigit(nextchar) ) break; /* have token */ kb_unput(nextchar); yyspot[-1] = 0; yylval.r = atof(yytext); verb_flag = 0; tok = REAL_; goto kblex_exit; case L_ALPHANUM: if ( isalnum(nextchar) || nextchar == '_' ) break; /* have token */ kb_unput(nextchar); yyspot[-1] = 0; strncpy(yylval.lexeme,yytext,31); retval = macro(); if ( retval == WAS_MACRO_DEF ) { /* macro() ate up rest of line */ state = L_LINESTART; yyspot = yytext; } else if ( retval == MACRO_EXPANDED ) { state = L_START; yyspot = yytext; } else { tok = identcase(yytext); goto kblex_exit; } break; case L_COLON: if ( nextchar == '=' ) { verb_flag = 2; yyspot[-1] = 0; tok = ASSIGN_; goto kblex_exit; } if ( nextchar == ':' ) { state = L_COLONCOLON; break; } kb_unput(nextchar); yyspot[-1] = 0; if ( cond_expr_flag ) { tok = ':'; goto kblex_exit; } /* else treat as whitespace */ yyspot = yytext; state = L_START; break; case L_COLONCOLON: if ( nextchar == '=' ) { verb_flag = 2; yyspot[-1] = 0; tok = PERM_ASSIGN_; goto kblex_exit; } if ( nextchar == ':' ) { state = L_COLONCOLONCOLON; break; } kb_error(3568,"Illegal token '::'\n",RECOVERABLE); break; case L_COLONCOLONCOLON: if ( nextchar == '=' ) { verb_flag = 2; yyspot[-1] = 0; tok = REDEFINE_; goto kblex_exit; } kb_error(3569,"Illegal token ':::'\n",RECOVERABLE); break; case L_SEMICOLON: if ( nextchar == '=' ) { verb_flag = 2; kb_error(2868,"You mistyped ';=' for ':='?\n",WARNING); tok= ASSIGN_; goto kblex_exit; } kb_unput(nextchar); yyspot[-1] = 0; verb_flag = 1; tok = ';'; goto kblex_exit; case L_PLUS: verb_flag = 0; if ( nextchar == '=' ) { verb_flag = 0; *yyspot = 0; yylval.i = PLUSASSIGN_; tok = ASSIGNOP_; goto kblex_exit; } kb_unput(nextchar); yyspot[-1] = 0; tok = '+'; goto kblex_exit; case L_MINUS: verb_flag = 0; if ( nextchar == '=' ) { verb_flag = 0; yylval.i = SUBASSIGN_; *yyspot = 0; tok= ASSIGNOP_; goto kblex_exit; } verb_flag = 0; kb_unput(nextchar); tok = minus_type('-'); goto kblex_exit; case L_STAR: verb_flag = 0; if ( nextchar == '=' ) { verb_flag = 0; *yyspot = 0; yylval.i = MULTASSIGN_; tok = ASSIGNOP_; goto kblex_exit; } if ( nextchar == '*' ) { verb_flag = 0; *yyspot = 0; tok = '^'; goto kblex_exit; } kb_unput(nextchar); yyspot[-1] = 0; tok = '*'; goto kblex_exit; case L_BANG: verb_flag = 0; if ( nextchar == '=' ) { *yyspot = 0; tok = NE_; goto kblex_exit; } kb_unput(nextchar); yyspot[-1] = 0; tok = NOT_; goto kblex_exit; case L_PIPE: if ( nextchar == '|' ) { *yyspot = 0; tok = OR_; goto kblex_exit; } kb_unput(nextchar); yyspot[-1] = 0; tok = PIPE_; goto kblex_exit; case L_AND: if ( nextchar == '&' ) { *yyspot = 0; tok = AND_; goto kblex_exit; } kb_error(3566,"Illegal token '&'\n",RECOVERABLE); break; case L_LINECOMMENT: if ( (nextchar == '\n') || (nextchar == 0) ) { line_no++; state = L_LINESTART; yyspot = yytext; break; } yyspot--; /* don't save comment token */ break; default: fprintf(stderr,"Unhandled state: %d\n",state); *yyspot = 0; tok = 1; goto kblex_exit; } } kblex_exit: PROF_FINISH(kblex); return tok; } evolver-2.30c.dfsg/src/wulff.c0000644000175300017530000001551111410765113016507 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /**************************************************************** * * File: wulff.c * * Purpose: Routines dealing with surface energy as function * of normal direction. */ /***************************************************************** * * Discussion: For a chunk of surface represented by its * normal vector N (length is area of chunk), its energy is * the inner product of N with the Wulff vector W, which is a * function of N. For a given set of Wulff vectors, W is the * one whose inner product with N is greatest. The Wulff vectors * are the vertices of the Wulff crystal. For simple area, * W is the normalization of N. * * Force is energy gradient, so for N depending on a parameter u, * * dE/du = + * * However, the fact that W has maximal product with N implies * = 0, so dE/du = . So we need only * the Wulff vector to calculate both energy and force. */ #include "include.h" /******************************************************* * * wulff_initialize() * * Purpose: Read in Wulff vectors from file and initialze * Wulff variables. If special name, redirects * Wulff functions to special functions in this * file. * * Input: Name of file with Wulff vectors. * * Output: Array of Wulff vectors, wulff_flag set */ void wulff_initialize(wulffname) char *wulffname; { FILE *wfd; REAL *row; /* current Wulff vector */ int k; /* vector number */ int j; /* dimension number */ if ( (web.representation != SOAPFILM ) || (SDIM != 3) ) kb_error(1385,"Can only do Wulff energy for 2D surface in 3D.\n",RECOVERABLE); if ( !wulffname || (wulffname[0] == 0) ) kb_error(2857,"Missing Wulff file name.\n",RECOVERABLE); web.wulff_flag = 1; /* save name */ strncpy(web.wulff_name,wulffname,sizeof(web.wulff_name)); /* test special names */ if ( (strncmp(wulffname,"hemisphere",10) == 0 ) || (strcmp(wulffname,"hemi") == 0) ) { get_wulff = hemi_wulff; return; } else if ( strncmp(wulffname,"lens",4) == 0 ) { get_wulff = lens_wulff; return; } /* default is from file */ get_wulff = file_wulff; wfd = path_open(wulffname,NOTDATAFILENAME); if ( wfd == NULL ) { sprintf(errmsg,"Cannot open Wulff vector file %s.\n",wulffname); kb_error(1386,errmsg,DATAFILE_ERROR); return; } for ( k = 0 ; k < MAXWULFF ; k++ ) { row = wulff_vector[k]; for ( j = 0 ; j < SDIM ; j++ ) #ifdef LONGDOUBLE if ( fscanf(wfd,"%Lf",row+j) != 1 ) break; #else if ( fscanf(wfd,"%lf",row+j) != 1 ) break; #endif } fclose(wfd); web.wulff_count = k; } /******************************************************************8 * * Function: file_wulff() * * Purpose: Finds Wulff vector that has maximum dot product * with given vector. * * Input: Pointer to given vector, * pointer to destination of Wulff vector. * * Output: Components of Wulff vector put in place. */ void file_wulff(norm,wulff) REAL *norm; REAL *wulff; { REAL maxw = -1e20; REAL *w; /* Wulff vector being tested */ int k; int best = 0; for ( k = 0 ; k < web.wulff_count ; k++ ) { w = wulff_vector[k]; if ( SDIM_dot(w,norm) > maxw ) { best = k; maxw = SDIM_dot(w,norm); } } memcpy((char *)wulff,(char *)wulff_vector[best],SDIM*sizeof(REAL)); } /****************************************************************** * * Function: hemi_wulff() * * Purpose: Provide Wulff vector for upper hemisphere Wulff shape. */ void hemi_wulff(normal,wulff) REAL *normal; REAL *wulff; { int i; REAL norm; wulff[0] = normal[0]; wulff[1] = normal[1]; if ( normal[2] < 0.0 ) wulff[2] = 0.0; else wulff[2] = normal[2]; norm = sqrt(SDIM_dot(wulff,wulff)); if ( norm > 0.0 ) for ( i = 0 ; i < SDIM ; i++ ) wulff[i] /= norm; } /******************************************************************** * * Function: lens_wulff * * Purpose: Provide Wulff vector for lens-shaped Wulff shape. */ void lens_wulff(normal,wulff) REAL *normal; REAL *wulff; { REAL norm; /* test whether interior or edge Wulff vector */ norm = dot(normal,normal,2); /* x and y only */ if ( norm < 3*normal[2]*normal[2] ) { /* interior */ norm = sqrt(norm + normal[2]*normal[2]); if ( norm > 0.0 ) { wulff[0] = normal[0]/norm; wulff[1] = normal[1]/norm; wulff[2] = normal[2]/norm - 0.5; } } else { /* edge */ norm = sqrt(norm/0.75); wulff[0] = normal[0]/norm; wulff[1] = normal[1]/norm; wulff[2] = 0.0; } } /************************************************************************ Wulff energy as method **************************************************************************/ void wulff_method_init(mode,mi) int mode; struct method_instance *mi; { char response[200]; if ( web.modeltype != LINEAR ) kb_error(2859,"Wulff energy can only be done in LINEAR model.\n", RECOVERABLE); if ( web.wulff_flag == 0 ) { prompt("Enter Wulff name (hemi,lens, or filename): ",response,sizeof(response)); wulff_initialize(response); } } REAL facet_wulff_value(f_info) struct qinfo *f_info; { REAL normal[MAXCOORD]; REAL wulff [MAXCOORD]; REAL density = get_facet_density(f_info->id); REAL energy; REAL side [MAXCOORD][MAXCOORD]; int i,j; for ( i = 0 ; i < FACET_EDGES ; i++ ) { int ii = (i+1)%FACET_EDGES; for ( j = 0 ; j < SDIM ; j++ ) side[i][j] = f_info->x[ii][j] - f_info->x[i][j]; } /* calculate normal */ cross_prod(side[0],side[1],normal); (*get_wulff)(normal,wulff); energy = SDIM_dot(wulff,normal)/2; return density*energy; } REAL facet_wulff_grad(f_info) struct qinfo *f_info; { REAL normal[MAXCOORD]; REAL wulff [MAXCOORD]; REAL temp [MAXCOORD]; REAL side [MAXCOORD][MAXCOORD]; REAL density = get_facet_density(f_info->id); REAL energy; int i,j; for ( i = 0 ; i < FACET_EDGES ; i++ ) { int ii = (i+1)%FACET_EDGES; for ( j = 0 ; j < SDIM ; j++ ) side[i][j] = f_info->x[ii][j] - f_info->x[i][j]; } /* calculate normal */ cross_prod(side[0],side[1],normal); (*get_wulff)(normal,wulff); /* force on each vertex */ for ( i = 0 ; i < FACET_VERTS ; i++ ) /* vertex loop */ { int k; j = (i+1)%FACET_EDGES; /* opposite side */ cross_prod(side[j],wulff,temp); for ( k = 0 ; k < SDIM ; k++ ) f_info->grad[i][k] -= density*temp[k]/2; } energy = SDIM_dot(wulff,normal)/2; return density*energy; } evolver-2.30c.dfsg/src/machine.c0000644000175300017530000000424011410765113016765 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /* machine.c */ /* missing routines on some systems; see include.h for defines */ #include "include.h" /**********************************************************************8 * * function: set_ctypes() * * purpose: implement toupper and tolower as arrays and set up * ctype flag bits. */ void set_ctypes() { int c; for ( c = 0xFF ; c > 0 ; c-- ) { kb_upper_array[c] = (char)c; kb_lower_array[c] = (char)c; } for ( c = 'a' ; c <= 'z' ; c++ ) kb_upper_array[c] = (char)(c + 'A' - 'a'); for ( c = 'A' ; c <= 'Z' ; c++ ) kb_lower_array[c] = (char)(c - 'A' + 'a'); } int kb_stricmp(a,b) char *a,*b; { register char aa,bb; /* lower case versions of characters */ for(;;a++,b++) { aa = tolower(*a); bb = tolower(*b); if ( aa < bb ) return -1; if ( aa > bb ) return 1; if ( !aa ) break; /* have reached both nulls */ } return 0; /* equal strings */ } int kb_strnicmp(a,b,n) char *a,*b; int n; /* maximum characters to compare */ { register char aa,bb; /* lower case versions of characters */ for(;n;n--,a++,b++) { aa = tolower(*a); bb = tolower(*b); if ( aa < bb ) return -1; if ( aa > bb ) return 1; if ( !aa ) break; /* have reached both nulls */ } return 0; /* equal strings */ } void kb_strupr(s) char *s; { while ( *s ) { *s = (char)toupper(*s); s++; } } /* finds string b in string a */ char *kb_strstr(a,b) char *a; char *b; { char *ptr,*ch; for ( ; *a ; a++ ) { for ( ptr = a, ch = b; *ch && (*ptr == *ch) ; ptr++,ch++ ) ; if ( *ch == '\0' ) return a; } return NULL; } void kb_memmove(dest,src,n) char *dest; char *src; size_t n; { /* crude bytewise move */ if ( (dest - src) > 0 ) /* move from top down */ { src += n; dest += n; for ( ; n ; n-- ) *(--dest) = *(--src); } else /* move from bottom up */ { for ( ; n ; n-- ) *(dest++) = *(src++); } } evolver-2.30c.dfsg/src/skeleton.c0000644000175300017530000014344711410765113017222 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /***************************************************************** * * File: skeleton.c * * Purpose: Variables and functions for skeleton element handling */ #include "include.h" /* non-inlined versions of inlinable functions */ #ifndef INLINE #define INLINE #include "inline.h" #endif /* extra attribute type sizes, matching order in skeleton.h */ int datatype_size[NUMDATATYPES] = {0,sizeof(REAL),sizeof(int),sizeof(unsigned long), sizeof(unsigned char), sizeof(unsigned short), sizeof(unsigned int), sizeof(long int),sizeof(char),sizeof(short),0,sizeof(char*),sizeof(char*), sizeof(vertex_id),sizeof(edge_id),sizeof(facet_id),sizeof(body_id), sizeof(facetedge_id),sizeof(element_id),sizeof(int),sizeof(int),sizeof(int),sizeof(int), sizeof(int) }; char *datatype_name[NUMDATATYPES] = { " ","real","integer","ulong_int","uchar","ushort_int","uint", "long_int","char","short_int"," ","string","pointer", "vertex_type", "edge_type","facet_type","body_type","facetedge_type", "element_id", "boundary_type","constraint_type","quantity_type","method_instance_type", "procedure" }; /* quadratic interpolation coefficients */ /* partials of interpolation polynomials at midpoints of patch edges */ /* control points are numbered 0 to 5 counterclockwise */ /* midpoints are numbered 0 to 2 counterclockwise after control pt 0 */ /* dip[midpoint][control point][partial] */ REAL dip[FACET_EDGES][FACET_CTRL][2] = { { { -0.5, -0.5 }, { 0.0, -1.0 }, { 0.5, 0.0 }, { 0.0, 1.0 }, { 0.0, -0.5 }, { 0.0, 1.0 } }, { { 0.5, 0.5 }, { -1.0, -1.0 }, { 0.5, 0.0 }, { 1.0, 1.0 }, { 0.0, 0.5 }, { -1.0, -1.0 } }, { { -0.5, -0.5 }, { 1.0, 0.0 }, { -0.5, 0.0 }, { 1.0, 0.0 }, { 0.0, 0.5 }, { -1.0, 0.0 } } }; int Ord(id) /* useful in debugger */ element_id id; { return loc_ordinal(id); } void vloop ARGS((vertex_id)); void vloop(v_id) /* prints vertex edge loop; useful in debugger */ element_id v_id; { edge_id e_id,ee_id; int n = 0; e_id = get_vertex_edge(v_id); if ( !valid_id(e_id) ) { puts("No valid edge on vertex."); return; } ee_id = e_id; do { printf("%d ",inverted(ee_id)?-(Ord(ee_id)+1): (Ord(ee_id)+1)); ee_id = get_next_tail_edge(ee_id); if ( ++n > web.skel[EDGE].count ) { puts("Unclosed loop."); break;} } while ( ee_id != e_id ); printf("\n"); } void set_facet_body(f_id,b_id) facet_id f_id; body_id b_id; /* may be invalid for unsetting */ { body_id bb_id; if ( web.skel[BODY].count == 0 ) return; if ( !valid_id(f_id) ) return; #ifdef MPI_EVOLVER if ( id_task(f_id) != this_task ) return; /* imported facets not part of local body chains */ #endif bb_id = get_facet_body(f_id); if ( equal_id(bb_id,b_id) ) return; /* check if old body of this facet links to this facet */ if ( valid_id(bb_id) ) { facet_id ff_id = get_body_facet(bb_id); facet_id next_f,prev_f; if ( valid_id(ff_id) ) { if ( equal_id(f_id,ff_id) ) /* need to give body new link */ { prev_f = get_prev_body_facet(f_id); if ( equal_id(prev_f,f_id) || !valid_id(prev_f) || !equal_id(bb_id,get_facet_body(prev_f)) ) set_body_facet(bb_id,NULLID); else set_body_facet(bb_id,prev_f); } /* remove from old body facet list */ next_f = get_next_body_facet(f_id); prev_f = get_prev_body_facet(f_id); set_next_body_facet(prev_f,next_f); set_prev_body_facet(next_f,prev_f); } } /* insert in new body list */ if ( valid_id(b_id) ) { facet_id nextf = get_body_facet(b_id); if ( valid_id(nextf) ) { facet_id prevf = get_prev_body_facet(nextf); set_next_body_facet(f_id,nextf); set_prev_body_facet(nextf,f_id); set_next_body_facet(prevf,f_id); set_prev_body_facet(f_id,prevf); } else /* first facet for body */ { set_next_body_facet(f_id,f_id); set_prev_body_facet(f_id,f_id); set_body_facet(b_id,f_id); } } if ( everything_quantities_flag ) { if ( inverted(f_id) ) /* back facet */ { body_id bb_id = F_ELID(f_id,F_BODY_LIST_ATTR)[1]; if ( equal_id(bb_id,b_id) ) return; if ( valid_id(bb_id) ) /* cancel out if already there */ apply_method_num(inverse_id(f_id),get_body_volmeth(bb_id)); F_ELID(f_id,F_BODY_LIST_ATTR)[1] = b_id; if ( valid_id(b_id) ) apply_method_num(f_id,get_body_volmeth(b_id)); } else /* front facet */ { body_id bb_id = F_ELID(f_id,F_BODY_LIST_ATTR)[0]; if ( equal_id(bb_id,b_id) ) return; if ( valid_id(bb_id) ) apply_method_num(inverse_id(f_id),get_body_volmeth(bb_id)); F_ELID(f_id,F_BODY_LIST_ATTR)[0] = b_id; if ( valid_id(b_id) ) apply_method_num(f_id,get_body_volmeth(b_id)); } /* and content integrands on edges */ if ( web.representation == SOAPFILM ) { facetedge_id fe = get_facet_fe(f_id); edge_id e_id; int i; unsigned int k; conmap_t *map; char name[100]; for ( i = 0 ; i < FACET_EDGES ; i++, fe = get_next_edge(fe)) { e_id = get_fe_edge(fe); if ( !get_eattr(e_id) & BDRY_CONTENT ) continue; map = get_e_constraint_map(e_id); for ( k = 1 ; k <= map[0] ; k++ ) { struct constraint *con = get_constraint(map[k]); int inst_num; if ( con->attr & CON_CONTENT ) { if ( valid_id(bb_id) ) { if ( con->attr & NAMED_THING ) sprintf(name,"body_%d_%s_meth",ordinal(bb_id)+1,con->name); else sprintf(name,"body_%d_con_%d_meth",ordinal(bb_id)+1,map[k]); inst_num = find_method_instance(name); if ( inst_num < 0 ) inst_num = create_body_constraint_content_method(bb_id,map[k]); apply_method_num(inverse_id(e_id),inst_num); /* cancel */ } if ( valid_id(b_id) ) { if ( con->attr & NAMED_THING ) sprintf(name,"body_%d_%s_meth",ordinal(b_id)+1,con->name); else sprintf(name,"body_%d_con_%d_meth",ordinal(b_id)+1,map[k]); inst_num = find_method_instance(name); if ( inst_num < 0 ) inst_num = create_body_constraint_content_method(b_id,map[k]); apply_method_num(e_id,inst_num); } } } } } } else { if ( inverted(f_id) ) F_ELID(f_id,F_BODY_LIST_ATTR)[1] = b_id; else F_ELID(f_id,F_BODY_LIST_ATTR)[0] = b_id; } top_timestamp = ++global_timestamp; } /************************************************************************** * * Function: set_facet_fe() * * Purpose: Link facet to facetedge. * */ void set_facet_fe(f_id,fe) facet_id f_id; facetedge_id fe; { if ( inverted(f_id) ) { invert(fe); invert(f_id); } fptr(f_id)->fe_id = fe; if ( web.representation == STRING ) { body_id b_id = get_facet_body(f_id); if ( valid_id(b_id) ) set_body_facet(b_id,f_id); b_id = get_facet_body(inverse_id(f_id)); if ( valid_id(b_id) ) set_body_facet(b_id,inverse_id(f_id)); } top_timestamp = ++global_timestamp; } REAL get_vertex_length_star(v_id) vertex_id v_id; { edge_id e_id = get_vertex_edge(v_id); edge_id firste = e_id; REAL star = 0.0; if ( get_vattr(v_id) & (Q_MIDPOINT|Q_MIDEDGE) ) return get_edge_length(e_id); if ( !valid_id(e_id) ) return 0.0; do { star += get_edge_length(e_id); e_id = get_next_tail_edge(e_id);} while ( !equal_element(e_id,firste) ); return star; } REAL get_vertex_area_star(v_id) vertex_id v_id; { REAL star = 0.0; if ( web.representation == STRING ) return get_vertex_length_star(v_id); else if ( web.representation == SOAPFILM ) { facetedge_id fe_id = get_vertex_first_facet(v_id); facetedge_id firstfe = fe_id; facet_id f_id; if ( !valid_id(fe_id) ) return 0.0; do { f_id = get_fe_facet(fe_id); star += get_facet_area(f_id); fe_id = get_next_vertex_facet(v_id,fe_id); } while ( !equal_element(fe_id,firstfe) ); } else /* SIMPLEX */ { facet_id f_id = get_vertex_first_facet(v_id); facet_id firstf = f_id; if ( !valid_id(f_id) ) return 0.0; do { star += get_facet_area(f_id); f_id = get_next_vertex_facet(v_id,f_id); } while ( !equal_element(f_id,firstf) ); } return star; } int get_vertex_fvalence(v_id) vertex_id v_id; { int valence = 0; facet_id f_id = get_vertex_first_facet(v_id); facet_id firstf = f_id; if ( !valid_id(f_id) ) return 0; do { valence++; f_id = get_next_vertex_facet(v_id,f_id);} while ( !equal_element(f_id,firstf) ); return valence; } void set_next_body_facet(f_id,ff_id) facet_id f_id,ff_id; { F_ELID(f_id,F_NEXT_BFACET_ATTR)[inverted(f_id)?1:0] = (ff_id) ; } void set_prev_body_facet(f_id,ff_id) facet_id f_id,ff_id; { F_ELID(f_id,F_NEXT_BFACET_ATTR)[inverted(f_id)?3:2] = (ff_id) ; } facet_id get_next_body_facet(f_id) facet_id f_id; { return F_ELID(f_id,F_NEXT_BFACET_ATTR)[inverted(f_id)?1:0]; } facet_id get_prev_body_facet(f_id) facet_id f_id; { return F_ELID(f_id,F_NEXT_BFACET_ATTR)[inverted(f_id)?3:2]; } void set_body_volume(b_id,v,mode) body_id b_id; REAL v; int mode; /* NOSETSTAMP or SETSTAMP */ { struct body *b = bptr(b_id); if ( !valid_id(b_id) ) return; b->volume = v; b->abstotal = fabs(v); if ( mode == SETSTAMP ) b->voltimestamp = global_timestamp; /* if ( web.representation == STRING) { facet_id f_id = get_body_facet(b_id)); if ( valid_id(f_id) ) set_facet_area(f_id,v); } */ if ( everything_quantities_flag ) { struct gen_quant *q = GEN_QUANT(b->volquant); q->value = v; } } void add_body_volume(b_id,v) /* uses binary tree add */ body_id b_id; REAL v; { struct body *b; if ( !valid_id(b_id) ) return; b = bptr(b_id); binary_tree_add(b->volume_addends,v); b->abstotal += fabs(v); } void add_body_volume_plain(b_id,v) /* with updates of related quantities */ body_id b_id; REAL v; { struct body *b; if ( !valid_id(b_id) ) return; b = bptr(b_id); b->volume += v; b->abstotal += fabs(v); b->voltimestamp = global_timestamp; /* if ( web.representation == STRING) { facet_id f_id = get_fe_facet(b->fe_id); if ( valid_id(f_id) ) set_facet_area(f_id,b->volume); } */ if ( everything_quantities_flag ) { struct gen_quant *q = GEN_QUANT(b->volquant); q->value = b->volume; } } void add_body_abstotal(b_id,v) body_id b_id; REAL v; { struct body *b; if ( !valid_id(b_id) ) return; b = bptr(b_id); b->abstotal += fabs(v); } void set_body_fixvol(b_id,v) body_id b_id; REAL v; { if ( valid_id(b_id) ) { bptr(b_id)->fixvol = (v); if ( everything_quantities_flag ) { struct gen_quant *q = GEN_QUANT(get_body_volquant(b_id)); q->target = v; if ( !(q->flags & Q_FIXED) ) { q->flags &= ~(Q_INFO|Q_ENERGY|Q_CONSERVED); q->flags |= Q_FIXED; } if ( web.pressure_flag ) { q = GEN_QUANT(get_body_ambquant(b_id)); q->flags &= ~(Q_INFO|Q_FIXED|Q_CONSERVED); q->flags |= Q_ENERGY; } } } else { sprintf(errmsg,"fix body volume: illegal body %s.\n",ELNAME(b_id)); kb_error(1307,errmsg,RECOVERABLE); } } vertex_id new_vertex(x,parent) REAL *x; element_id parent; /* for inherited stuff */ { int i; vertex_id v_id; REAL *y; v_id = new_element(VERTEX,parent,NULLID); if ( x ) { y = get_coord(v_id); for ( i = 0 ; i < SDIM ; i++ ) y[i] = x[i]; } set_vertex_edge(v_id,NULLID); set_vertex_facet(v_id,NULLID); set_v_global(v_id); /* interrupt conjugate gradient */ if ( cg_hvector ) { myfree((char *)cg_hvector); cg_hvector = NULL; } return v_id; } vertex_id dup_vertex(old_v) vertex_id old_v; { vertex_id v_id; struct vertex *v_id_p; v_id = new_element(VERTEX,NULLID,NULLID); v_id_p = vptr(v_id); memcpy((char *)&(v_id_p->attr),(char *)&(elptr(old_v)->attr), web.sizes[VERTEX] - ((char*)&(v_id_p->attr)-(char*)v_id_p)); v_id_p->self_id = v_id; /* restore new id */ v_id_p->e_id = NULLID; return v_id; } edge_id new_edge(tail_id,head_id,parent) vertex_id tail_id,head_id; element_id parent; /* for inherited stuff */ { edge_id e_id; vertex_id v_id; REAL *x,*h,*t; int i,k; e_id = new_element(EDGE,parent,NULLID); set_edge_fe(e_id,NULLFACETEDGE); set_edge_color(e_id,DEFAULT_EDGE_COLOR); if ( valid_id(tail_id) && valid_id(head_id) ) { set_edge_tailv(e_id,tail_id); set_edge_headv(e_id,head_id); if ( (web.modeltype == QUADRATIC) && valid_id(head_id) ) { /* quadratic version; linear interpolation of midpoint */ v_id = new_element(VERTEX,parent,NULLID); set_edge_midv(e_id,v_id); h = get_coord(head_id); t = get_coord(tail_id); x = get_coord(v_id); for ( i = 0 ; i < SDIM ; i++ ) x[i] = (h[i] + t[i])/2.0; } else if ( (web.modeltype == LAGRANGE) && valid_id(head_id) ) { /* Lagrange version; linear interpolation of points */ vertex_id *v = get_edge_vertices(e_id); h = get_coord(head_id); t = get_coord(tail_id); for ( k = 1 ; k < web.lagrange_order ; k++ ) { v[k] = new_element(VERTEX,parent,NULLID); set_attr(v[k],Q_MIDEDGE); set_vertex_edge(v[k],e_id); x = get_coord(v[k]); for ( i = 0 ; i < SDIM ; i++ ) x[i] = (k*h[i] + (web.lagrange_order-k)*t[i])/web.lagrange_order; } } } return e_id; } edge_id dup_edge(old_e) edge_id old_e; { edge_id e_id; struct edge *e_id_p,*old_e_p; vertex_id newmid; e_id = new_element(EDGE,NULLID,NULLID); e_id_p = eptr(e_id); old_e_p = eptr(old_e); memcpy((char *)&(e_id_p->attr),(char *)&(old_e_p->attr), web.sizes[EDGE] - ((char*)&(e_id_p->attr)-(char*)e_id_p)); e_id_p->self_id = e_id; /* restore new id */ e_id_p->next_vedge[0] = NULLID; e_id_p->next_vedge[1] = NULLID; if ( web.modeltype == QUADRATIC ) { newmid = new_vertex(NULL,old_e); set_edge_midv(e_id,newmid); set_attr(newmid,Q_MIDPOINT); set_vertex_edge(newmid,e_id); } else if ( web.modeltype == LAGRANGE ) { int i; vertex_id *v = get_edge_vertices(e_id); for ( i = 1 ; i < web.lagrange_order ; i++ ) v[i] = new_vertex(NULL,e_id); } if ( inverted(old_e) ) return inverse_id(e_id); return e_id; } void recalc_facet_area(f_id) facet_id f_id; { if ( everything_quantities_flag ) quantity_attribute(f_id,default_area_quant_num); else (*calc_facet_energy)(f_id,AREA_ONLY); } facet_id new_facet () { facet_id f_id; f_id = new_element(FACET,NULLID,NULLID); set_facet_color(f_id,DEFAULT_FACET_COLOR); set_facet_density(f_id,1.0); return f_id; } facet_id dup_facet(old_f) facet_id old_f; { facet_id f_id; struct facet *f_id_p; body_id b_id,*fb; int sign = inverted(old_f); old_f = positive_id(old_f); /* since new facet id will be positive */ f_id = new_element(FACET,NULLID,NULLID); f_id_p = fptr(f_id); memcpy((char *)&(f_id_p->attr),(char *)&(elptr(old_f)->attr), web.sizes[FACET] - ((char*)&(f_id_p->attr)-(char*)f_id_p)); f_id_p->self_id = f_id; /* restore new id */ set_attr(f_id,NEWELEMENT); /* update body facet lists */ if ( web.skel[BODY].count ) { fb = F_ELID(f_id,F_BODY_LIST_ATTR); fb[0] = fb[1] = NULLID; /* so set_facet_body works */ fb = F_ELID(f_id,F_NEXT_BFACET_ATTR); fb[0] = fb[1] = fb[2] = fb[3] = NULLID; b_id = get_facet_body(old_f); if ( valid_id(b_id) ) set_facet_body(f_id,b_id); b_id = get_facet_body(inverse_id(old_f)); if ( valid_id(b_id) ) set_facet_body(inverse_id(f_id),b_id); } return sign ? inverse_id(f_id) : f_id; } body_id new_body() { int two = 2; int four = 4; body_id b_id; expand_attribute(FACET,F_BODY_LIST_ATTR,&two); expand_attribute(FACET,F_NEXT_BFACET_ATTR,&four); b_id = new_element(BODY,NULLID,NULLID); set_body_facet(b_id,NULLID); if ( everything_quantities_flag ) convert_new_body_to_quantity(b_id); web.bodycount++; return b_id; } body_id dup_body(old_b) body_id old_b; { body_id b_id; struct body *b_id_p; b_id = new_element(BODY,NULLID,NULLID); b_id_p = bptr(b_id); memcpy((char *)&(b_id_p->attr),(char *)&(elptr(old_b)->attr), web.sizes[BODY] - ((char*)&(b_id_p->attr)-(char*)b_id_p)); b_id_p->self_id = b_id; /* restore new id */ set_body_facet(b_id,NULLID); if ( everything_quantities_flag ) convert_new_body_to_quantity(b_id); web.bodycount++; return b_id; } facetedge_id new_facetedge(f_id,e_id) facet_id f_id; edge_id e_id; { facetedge_id fe_id; fe_id = new_element(FACETEDGE,NULLID,NULLID); set_fe_edge(fe_id,e_id); set_fe_facet(fe_id,f_id); set_prev_edge(fe_id,NULLFACETEDGE); set_next_edge(fe_id,NULLFACETEDGE); set_prev_facet(fe_id,NULLFACETEDGE); set_prev_facet(fe_id,NULLFACETEDGE); #ifndef MPI_EVOLVER { vertex_id headv,tailv; tailv = get_edge_tailv(e_id); headv = get_edge_headv(e_id); if ( !valid_id(get_vertex_edge(tailv)) ) set_vertex_edge(tailv,e_id); if ( !valid_id(get_vertex_edge(headv)) ) set_vertex_edge(headv,e_id); } #endif if ( web.representation==STRING && everything_quantities_flag ) { /* attach volume quantities */ body_id b_id; b_id = get_facet_body(f_id); if ( valid_id(b_id) ) { if ( same_sign(f_id,e_id) ) apply_method_num(e_id,get_body_volmeth(b_id)); else apply_method_num(inverse_id(e_id),get_body_volmeth(b_id)); } b_id = get_facet_body(inverse_id(f_id)); if ( valid_id(b_id) ) { if ( same_sign(f_id,e_id) ) apply_method_num(inverse_id(e_id),get_body_volmeth(b_id)); else apply_method_num(e_id,get_body_volmeth(b_id)); } } return fe_id; } void set_fe_facet(fe_id,f_id) facetedge_id fe_id; facet_id f_id; { facet_id oldf = get_fe_facet(fe_id); feptr(fe_id)->fe_facet_id = inverted(fe_id) ? inverse_id(f_id) : f_id; if ( web.representation==STRING && everything_quantities_flag && !equal_id(oldf,f_id) ) { /* detach old volume quantity and attach new volume quantity */ body_id b_id, old_b_id; edge_id e_id = get_fe_edge(fe_id); b_id = get_facet_body(f_id); old_b_id = valid_id(oldf) ? get_facet_body(oldf) : NULLID; if ( valid_id(old_b_id) ) unapply_method(e_id,get_body_volmeth(old_b_id)); old_b_id = valid_id(oldf) ? get_facet_body(inverse_id(oldf)) : NULLID; if ( valid_id(old_b_id) ) unapply_method(e_id,get_body_volmeth(old_b_id)); if ( valid_id(b_id) ) { if ( same_sign(f_id,e_id) ) apply_method_num(e_id,get_body_volmeth(b_id)); else apply_method_num(inverse_id(e_id),get_body_volmeth(b_id)); } b_id = get_facet_body(inverse_id(f_id)); if ( valid_id(b_id) ) { if ( same_sign(f_id,e_id) ) apply_method_num(inverse_id(e_id),get_body_volmeth(b_id)); else apply_method_num(e_id,get_body_volmeth(b_id)); } } } REAL get_edge_length(e_id) edge_id e_id; { return (eptr(e_id)->length); } REAL get_facet_pressure(f_id) facet_id f_id; { return (get_body_pressure(get_facet_body(f_id)) - get_body_pressure(get_facet_body(facet_inverse(f_id)))); } /******************************************************************** * * Function: compare_vertex_attr(va,vb) * * Purpose: Compare constraints of vertex and vertex. * * Return value: 0 INCOMPARABLE Incomparable * 1 A_SUB_B Vertex proper subset of vertex * 2 A_EQ_B Vertex equal vertex * 3 A_SUPER_B Vertex superset of vertex */ int compare_vertex_attr(va,vb) vertex_id va,vb; { int i,j; conmap_t * con1,*con2; int same = 0; /* check constraints */ con1 = get_v_constraint_map(va); con2 = get_v_constraint_map(vb); for ( i = 1 ; i <= (int)con2[0] ; i++ ) { for ( j = 1 ; j <= (int)con1[0] ; j++ ) if ( (con1[j]&CONMASK) == (con2[i]&CONMASK) ) { same++; break; } } if ( (same==(int)con1[0]) && (same==(int)con2[0]) ) return A_EQ_B; if ( (same==(int)con1[0]) && (same < (int)con2[0]) ) return A_SUB_B; if ( (same < (int)con1[0]) && (same==(int)con2[0]) ) return A_SUPER_B; return INCOMPARABLE; } /******************************************************************** * * Function: compare_vertex_edge_attr(va,eb) * * Purpose: Compare constraints of vertex and edge. * * Return value: 0 INCOMPARABLE Incomparable * 1 A_SUB_B Vertex proper subset of edge * 2 A_EQ_B Vertex equal edge * 3 A_SUPER_B Vertex superset of edge */ int compare_vertex_edge_attr(va,eb) vertex_id va; edge_id eb; { int i,j; conmap_t * con1,*con2; int same = 0; /* check constraints */ con1 = get_v_constraint_map(va); con2 = get_e_constraint_map(eb); for ( i = 1 ; i <= (int)con2[0] ; i++ ) { for ( j = 1 ; j <= (int)con1[0] ; j++ ) if ( (con1[j]&CONMASK) == (con2[i]&CONMASK) ) { same++; break; } } if ( (same==(int)con1[0]) && (same==(int)con2[0]) ) return A_EQ_B; if ( (same==(int)con1[0]) && (same < (int)con2[0]) ) return A_SUB_B; if ( (same < (int)con1[0]) && (same==(int)con2[0]) ) return A_SUPER_B; return INCOMPARABLE; } /******************************************************************** * * Function: compare_edge_attr(ea,eb) * * Purpose: Compare constraints of edge and edge. * * Return value: 0 INCOMPARABLE Incomparable * 1 A_SUB_B edge proper subset of edge * 2 A_EQ_B edge equal edge * 3 A_SUPER_B edge superset of edge */ int compare_edge_attr(ea,eb) edge_id ea,eb; { int i,j; int fixa = get_eattr(ea) & FIXED; int fixb = get_eattr(eb) & FIXED; conmap_t * con1,*con2; int same = 0; /* check constraints */ con1 = get_e_constraint_map(ea); con2 = get_e_constraint_map(eb); for ( i = 1 ; i <= (int)con2[0] ; i++ ) { for ( j = 1 ; j <= (int)con1[0] ; j++ ) if ( (con1[j]&CONMASK) == (con2[i]&CONMASK) ) { same++; break; } } if ( (same==(int)con1[0]) && (same==(int)con2[0]) && !fixa && !fixb ) return A_EQ_B; if ( (same==(int)con1[0]) && (same < (int)con2[0]) && !fixa ) return A_SUB_B; if ( (same < (int)con1[0]) && (same==(int)con2[0]) && !fixb ) return A_SUPER_B; return INCOMPARABLE; } /******************************************************************** * * Function: compare_edge_facet_attr(ea,fb) * * Purpose: Compare constraints of edge and facet. * * Return value: 0 INCOMPARABLE Incomparable * 1 A_SUB_B Edge proper subset of facet * 2 A_EQ_B Edge equal facet * 3 A_SUPER_B Edge superset of facet */ int compare_edge_facet_attr(ea,fb) edge_id ea; facet_id fb; { int i,j; conmap_t * con1,*con2; int fixa = get_eattr(ea) & FIXED; int fixb = get_fattr(fb) & FIXED; int same = 0; /* check constraints */ con1 = get_e_constraint_map(ea); con2 = get_f_constraint_map(fb); for ( i = 1 ; i <= (int)con2[0] ; i++ ) { for ( j = 1 ; j <= (int)con1[0] ; j++ ) if ( (con1[j]&CONMASK) == (con2[i]&CONMASK) ) { same++; break; } } if ( (same==(int)con1[0]) && (same==(int)con2[0])&& !fixa && !fixb ) return A_EQ_B; if ( (same==(int)con1[0]) && (same < (int)con2[0]) && !fixa ) return A_SUB_B; if ( (same < (int)con1[0]) && (same==(int)con2[0]) && !fixb ) return A_SUPER_B; return INCOMPARABLE; } /************************************************************** * * Function: equal_constr() * * Purpose: See if two elements have the same set of constraints. * */ int equal_constr(id1,id2) element_id id1,id2; { int i,j; conmap_t * con1=NULL,*con2=NULL; switch ( id_type(id1) ) { case VERTEX: con1 = get_v_constraint_map(id1); break; case EDGE : con1 = get_e_constraint_map(id1); break; case FACET : con1 = get_f_constraint_map(id1); break; } switch ( id_type(id2) ) { case VERTEX: con2 = get_v_constraint_map(id2); break; case EDGE : con2 = get_e_constraint_map(id2); break; case FACET : con2 = get_f_constraint_map(id2); break; } if ( con2[0] != con1[0] ) return 0; for ( i = 1 ; i <= (int)con1[0] ; i++ ) { for ( j = 1 ; j <= (int)con2[0] ; j++ ) if ( (con1[i]&CONMASK) == (con2[j]&CONMASK) ) break; if ( j > (int)con2[0] ) return 0; } return 1; } /************************************************************************* * * function: add_attribute() * * purpose: add extra attribute to an element type * * return: index of attribute */ int add_attribute(e_type,name,attr_type,dim,dims,dumpflag,code) int e_type; /* VERTEX ,... */ char *name; int attr_type; /* REAL_TYPE or INTEGER_TYPE or ULONG_TYPE etc */ int dim; /* number of dimensions, 0 for scalar */ int *dims; /* sizes of dimensions, NULL for all sizes 0 */ /* Note: scalar still needs size of 0 or 1 */ int dumpflag; /* whether appears in dump file */ struct expnode *code; /* nonnull for function attribute */ { int newsize,newcount=0; struct extra *ex; int oldsize; int att_inx; att_inx = find_attribute(e_type,name); if ( att_inx >= 0 ) return att_inx; #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) { struct mpi_command message; int i; if ( code ) { kb_error(4525,"MPI Evolver doesn't do code attributes yet.\n", RECOVERABLE); } message.cmd = mpi_ADD_ATTRIBUTE; strncpy(message.name,name,MPI_NAME_SIZE); message.i = attr_type; message.type = e_type; message.mode = dumpflag; message.size = dims ? 1 : 0; /* just using as indicator */ message.count = dim; if ( dim ) for (i = 0 ; i < dim ; i++ ) message.data[i] = dims ? dims[i] : 0; else message.data[0] = dims ? dims[0] : 0; MPI_Bcast(&message,sizeof(struct mpi_command),MPI_BYTE,MASTER_TASK, MPI_COMM_WORLD); } #endif if ( web.skel[e_type].extra_count >= web.skel[e_type].maxextra-1 ) { web.skel[e_type].dy_extras = dy_realloc(web.skel[e_type].dy_extras, (web.skel[e_type].maxextra+10),sizeof(struct extra)); web.skel[e_type].maxextra += 10; } /* expand space */ /* get old used space, minus any padding, and pad to proper size */ if ( web.skel[e_type].extra_count > 0 ) { ex = EXTRAS(e_type) + web.skel[e_type].extra_count-1; oldsize = ex->offset + ex->array_spec.datacount*datatype_size[ex->type]; } else oldsize = web.sizes[e_type]; if (oldsize % datatype_size[attr_type]) oldsize += datatype_size[attr_type] - (oldsize % datatype_size[attr_type]); ex = EXTRAS(e_type) + web.skel[e_type].extra_count; if ( dim == 0 ) newcount = dims ? dims[0] : 0; else if ( dim > MAXARRAYDIMS ) { sprintf(errmsg,"Extra attribute \"%s\" has %d dimensions, exceeding limit of %d.\n", name,dim,MAXARRAYDIMS); kb_error(2510,errmsg,RECOVERABLE); } else if ( dims == NULL ) newcount = 0; else { int i; for ( i = 0, newcount = 1 ; i < dim ; i++ ) { newcount *= dims[i]; ex->array_spec.sizes[i] = dims[i]; } } if ( (newcount > 0) || (oldsize > web.sizes[e_type]) ) { newsize = newcount*datatype_size[attr_type]; expand(e_type,oldsize + newsize); } strncpy(ex->name,name,ATTR_NAME_SIZE); ex->type = attr_type; ex->array_spec.datatype = attr_type; ex->array_spec.itemsize = datatype_size[attr_type]; ex->offset = oldsize; ex->array_spec.datacount = newcount; ex->array_spec.dim = dim; if ( dim > 0 ) ex->flags |= DIMENSIONED_ATTR; if ( code ) ex->code = *code; if ( dumpflag ) ex->flags |= DUMP_ATTR; if ( stricmp(name,EXTRA_BDRY_NAME) == 0 ) { extra_bdry_attr = web.skel[e_type].extra_count; if ( ex->type != INTEGER_TYPE ) kb_error(2842,"Attribute extra_boundary must be of type integer.\n", RECOVERABLE); if ( e_type != VERTEX ) kb_error(2843,"Attribute extra_boundary should be vertex attribute.\n", RECOVERABLE); } else if ( stricmp(name,EXTRA_BDRY_PARAM_NAME) == 0 ) { extra_bdry_param_attr = web.skel[e_type].extra_count; if ( ex->type != REAL_TYPE ) kb_error(3369,"Attribute extra_boundary_param must be of type real.\n", RECOVERABLE); if ( e_type != VERTEX ) kb_error(2845, "Attribute extra_boundary_param should be vertex attribute.\n", RECOVERABLE); } web.skel[e_type].extra_count++; return web.skel[e_type].extra_count - 1; /* index */ } /************************************************************************* * * function: expand_attribute() * * purpose: enlarge space for attribute of an element type * or shrink it. */ void expand_attribute(e_type,attr_num,newsizes) int e_type; /* VERTEX ,... */ int attr_num; /* number of attribute */ int *newsizes; /* new numbers of components, even for scalar */ { int newsize; int diff; /* difference between old and new total sizes */ struct extra *ex; int chunksize,offset,available,needed; int pointercount=0; char *spot; element_id id; int k,d,n,dsize,dest,inx,blocksize; char *temp=NULL; /* for shuffling higher dim entries */ ex = EXTRAS(e_type) + attr_num; #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) { struct mpi_command message; int i; message.cmd = mpi_EXPAND_ATTRIBUTE; message.i = attr_num; message.type = e_type; message.count = ex->adim; if ( ex->adim ) for (i = 0 ; i < ex->adim ; i++ ) message.data[i] = newsizes[i]; else message.data[0] = newsizes[0]; MPI_Bcast(&message,sizeof(struct mpi_command),MPI_BYTE,MASTER_TASK, MPI_COMM_WORLD); } #endif dsize = ex->array_spec.itemsize; if ( ex->array_spec.dim == 0 ) newsize = newsizes[0]; else { pointercount = 1; for ( newsize = 1, k = 0 ; k < ex->array_spec.dim ; k++ ) { newsize *= newsizes[k]; if ( k < ex->array_spec.dim - 1 ) pointercount *= newsizes[k]; } } if ( (ex->array_spec.dim <= 1) && (newsize == ex->array_spec.datacount) ) return; /* expand or contract space */ /* see how much extra space is needed */ if ( attr_num < web.skel[e_type].extra_count-1 ) available = ex[1].offset - ex[0].offset; else available = web.sizes[e_type] - ex->offset; needed = newsize*datatype_size[ex->type]+pointercount*sizeof(REAL*); if ( ex->array_spec.dim >= 2 ) temp = (char*)temp_calloc(needed,1); if ( needed > available ) { /* expand */ /* check alignment of following fields */ diff = needed - available; for ( k = attr_num+1 ; k < web.skel[e_type].extra_count ; k++ ) while ( diff % datatype_size[EXTRAS(e_type)[k].type] ) diff++; expand(e_type,web.sizes[e_type] + diff); /* move stuff above */ if ( attr_num < web.skel[e_type].extra_count-1 ) offset = EXTRAS(e_type)[attr_num+1].offset; else offset = web.sizes[e_type] - diff; chunksize = web.sizes[e_type] - offset - diff; if ( chunksize || ( ex->array_spec.dim >= 2) ) { element_id sentinel; id = NULLID; if ( web.skel[e_type].count > 0 ) while ( generate_all(e_type,&id,&sentinel) ) { spot = (char*)elptr(id) + offset; kb_memmove(spot + diff,spot,chunksize); if ( ex->array_spec.dim >= 2 ) { /* entry shuffle via temp */ char *old = (char*)elptr(id) + EXTRAS(e_type)[attr_num].offset; for ( n = 0 ; n < ex->array_spec.datacount ; n++ ) { /* figure out indices and new spot */ int oldinx = n; for ( d = ex->array_spec.dim-1, dest = 0, blocksize = 1 ; d >= 0 ; d-- ) { inx = oldinx % ex->array_spec.sizes[d]; if ( inx >= newsizes[d] ) goto skipentry; dest += blocksize*inx; blocksize *= newsizes[d]; oldinx = oldinx/ex->array_spec.sizes[d]; } kb_memmove(temp+dest*dsize,old+n*dsize,dsize); skipentry: ; } kb_memmove(old,temp,needed); } else memset(spot,0,diff); } } for ( k = attr_num+1 ; k < web.skel[e_type].extra_count ; k++ ) EXTRAS(e_type)[k].offset += diff; } else if ( needed < available ) { /* maybe shrink */ /* check alignment of following fields */ diff = available - needed; for ( k = attr_num+1 ; k < web.skel[e_type].extra_count ; k++ ) while ( diff % datatype_size[EXTRAS(e_type)[k].type] ) diff--; /* move stuff above */ if ( attr_num < web.skel[e_type].extra_count-1 ) offset = EXTRAS(e_type)[attr_num+1].offset; else offset = web.sizes[e_type]; chunksize = web.sizes[e_type] - offset; if ( chunksize || (ex->array_spec.dim >= 2) ) { element_id sentinel; id = NULLID; if ( web.skel[e_type].count > 0 ) while ( generate_all(e_type,&id,&sentinel) ) { if ( ex->array_spec.dim >= 2 ) { /* entry shuffle via temp */ char *old = (char*)elptr(id)+EXTRAS(e_type)[attr_num].offset; for ( n = 0 ; n < ex->array_spec.datacount ; n++ ) { /* figure out indices and new spot */ int oldinx = n; for ( d = ex->array_spec.dim-1, dest = 0, blocksize = 1 ; d >= 0 ; d-- ) { inx = oldinx % ex->array_spec.sizes[d]; if ( inx >= newsizes[d] ) goto skipentry2; dest += blocksize*inx; blocksize *= newsizes[d]; oldinx = oldinx/ex->array_spec.sizes[d]; } kb_memmove(temp+dest*dsize,old+n*dsize,dsize); skipentry2: ; } kb_memmove(old,temp,needed); } spot = (char*)elptr(id) + offset; kb_memmove(spot - diff,spot,chunksize); } } for ( k = attr_num+1 ; k < web.skel[e_type].extra_count ; k++ ) EXTRAS(e_type)[k].offset -= diff; expand(e_type,web.sizes[e_type] - diff); } if ( ex->array_spec.dim >= 2 ) temp_free(temp); ex->array_spec.datacount = newsize; for ( n = 0 ; n < ex->array_spec.dim ; n++ ) ex->array_spec.sizes[n] = newsizes[n]; parallel_update_flag[e_type] = 1; #ifdef MPI_EVOLVER mpi_export_voffset = EXTRAS(VERTEX)[web.mpi_export_attr[VERTEX]].offset; mpi_export_eoffset = EXTRAS(EDGE)[web.mpi_export_attr[EDGE]].offset; mpi_export_foffset = EXTRAS(FACET)[web.mpi_export_attr[FACET]].offset; mpi_export_boffset = EXTRAS(BODY)[web.mpi_export_attr[BODY]].offset; mpi_export_feoffset = EXTRAS(FACETEDGE)[web.mpi_export_attr[FACETEDGE]].offset; #endif } /************************************************************************* * * function: find_attribute() * * purpose: find extra attribute by name, if it exists. * return: index number if found, -1 if not. */ int find_attribute(etype,name) int etype; char *name; { struct extra *ex; int n; ex = EXTRAS(etype); for ( n = 0 ; n < web.skel[etype].extra_count ; n++,ex++ ) if ( stricmp(ex->name,name) == 0 ) break; if ( n == web.skel[etype].extra_count ) return -1; return n; } /************************************************************************** * * function: find_extra() * * purpose: return index of named attribute, searching all element types. * return -1 if not found. */ int find_extra(name,etype) char *name; int *etype; /* for returning element type */ { int el_type,qnum,n; struct extra *ex; for ( el_type = VERTEX, qnum = -1 ; el_type <= FACETEDGE ; el_type++ ) { ex = EXTRAS(el_type); for ( n = 0 ; n < web.skel[el_type].extra_count ; n++,ex++ ) if ( stricmp(ex->name,name) == 0 ) {*etype = el_type;qnum = n;break;} } return qnum; } /*************************************************************************** Constraint handling routines ****************************************************************************/ /* Methodology: Constraint map is array of conmap_t. First entry is number of constraints. Then follow constraint numbers, with high bit CON_HIT_BIT set if constraint is hit. Allocated as an extra attribute if needed. */ conmap_t nullcon[2]; /* default empty list */ void set_v_global(v_id) vertex_id v_id; { int k; for ( k = 0 ; k < web.con_global_count ; k++ ) set_v_constraint_map(v_id,web.con_global_map[k]); } void set_v_conmap(v_id,map) vertex_id v_id; conmap_t *map; { int k, m=(int)map[0]; for ( k = 1 ; k <= m ; k++ ) set_v_constraint_map(v_id,map[k]); map = get_v_constraint_map(v_id); if ( map[0] == 0 ) unset_attr(v_id,CONSTRAINT); } void set_e_conmap(e_id,map) edge_id e_id; conmap_t *map; { int k, m=(int)map[0]; for ( k = 1 ; k <= m ; k++ ) set_e_constraint_map(e_id,map[k]); map = get_e_constraint_map(e_id); if ( map[0] == 0 ) unset_attr(e_id,CONSTRAINT); } void set_f_conmap(f_id,map) facet_id f_id; conmap_t *map; { int k, m=(int)map[0]; for ( k = 1 ; k <= m ; k++ ) set_f_constraint_map(f_id,map[k]); map = get_f_constraint_map(f_id); if ( map[0] == 0 ) unset_attr(f_id,CONSTRAINT); } void set_v_constraint_map(v_id,n) vertex_id v_id; int n; /* constraint number */ { conmap_t *map; int maxcon = EXTRAS(VERTEX)[V_CONSTR_LIST_ATTR].array_spec.datacount; struct constraint *constr; int k,j; int four = 4; n &= CONMASK; if ( maxcon == 0 ) expand_attribute(VERTEX,V_CONSTR_LIST_ATTR,&four); map = get_v_constraint_map(v_id); for ( k = 1; k <= (int)*map ; k++ ) if ( (map[k] & CONMASK) == ((conmap_t)n & CONMASK) ) return; if ( k >= maxcon ) { int newmax = maxcon+4; expand_attribute(VERTEX,V_CONSTR_LIST_ATTR,&newmax); map = get_v_constraint_map(v_id); } constr = get_constraint(n); map[k] = (conmap_t)n; if ( !(constr->attr & (NONPOSITIVE|NONNEGATIVE) )) map[k] |= CON_HIT_BIT; map[0]++; /* counter */ set_attr(v_id,CONSTRAINT); if ( (constr->attr & CON_ENERGY) && (web.representation == STRING) ) { set_attr(v_id, BDRY_ENERGY); if ( everything_quantities_flag ) apply_method_num(v_id,constr->energy_method); } if ( (constr->attr & CON_CONTENT) && (web.representation == STRING) ) { set_attr(v_id, BDRY_CONTENT); if ( everything_quantities_flag ) { edge_id e_id,first_e; int max_rank,min_rank; min_rank = MAXINT; max_rank = 0; for ( j = 1 ; j <= (int)map[0] ; j++ ) { struct constraint *c; if ( !(map[j] & CON_HIT_BIT) ) continue; c = get_constraint(map[j]); if ( c->content_rank < min_rank ) min_rank = c->content_rank; if ( c->content_rank > max_rank ) max_rank = c->content_rank; } first_e = e_id = get_vertex_edge(v_id); if ( valid_id(e_id) && !(get_eattr(e_id) & NONCONTENT) ) do { char name[100]; body_id b_id; facetedge_id first_fe,fe; first_fe = fe = get_edge_fe(e_id); if ( valid_id(fe) ) do { facet_id f_id = get_fe_facet(fe); fe = get_next_facet(fe); if ( !valid_id(f_id) ) continue; b_id = get_facet_body(f_id); if ( valid_id(b_id) && ( (!inverted(f_id) && constr->content_rank >= max_rank) || (inverted(f_id) && constr->content_rank <= min_rank))) { if ( constr->attr & NAMED_THING ) sprintf(name,"body_%d_%s_meth",ordinal(b_id)+1,constr->name); else sprintf(name,"body_%d_con_%d_meth",ordinal(b_id)+1,n); attach_method(get_body_volquant(b_id),name); apply_method(inverse_id(v_id),name); } b_id = get_facet_body(inverse_id(f_id)); if ( valid_id(b_id) && ( (!inverted(f_id) && constr->content_rank >= max_rank) || (inverted(f_id) && constr->content_rank <= min_rank)) ) { if ( constr->attr & NAMED_THING ) sprintf(name,"body_%d_%s_meth",ordinal(b_id)+1,constr->name); else sprintf(name,"body_%d_con_%d_meth",ordinal(b_id)+1,n); attach_method(get_body_volquant(b_id),name); apply_method(v_id,name); } } while ( !equal_id(fe,first_fe) ); e_id = get_next_tail_edge(e_id); } while ( !equal_id(first_e,e_id)); } } } void unset_v_constraint_map(v_id,n) vertex_id v_id; int n; { conmap_t *map; int maxcon = EXTRAS(VERTEX)[V_CONSTR_LIST_ATTR].array_spec.datacount; int k,j; n &= CONMASK; if ( maxcon == 0 ) return; map = get_v_constraint_map(v_id); for ( k = 1; k <= (int)*map ; k++ ) if ( (map[k] & CONMASK) == (conmap_t)n ) break; if ( k > (int)*map ) return; map[0]--; for ( j = k ; j <= (int)*map ; j++ ) map[j] = map[j+1]; map[j] = 0; if ( map[0] == 0 ) unset_attr(v_id,CONSTRAINT); if ( everything_quantities_flag ) { struct constraint *con = get_constraint(n); int i; if ( con->attr & CON_ENERGY ) unapply_method(v_id,con->energy_method); if ( con->attr & CON_CONTENT ) { int meth_offset = get_meth_offset(VERTEX); int *instlist = (int*)((char*)elptr(v_id) + meth_offset); int mcount = elptr(v_id)->method_count; for ( i = 0 ; i < mcount ; i++ ) if ( METH_INSTANCE(abs(instlist[i]))->connum == n ) unapply_method(v_id,instlist[i]); } } } int v_on_constraint(v_id,n) vertex_id v_id; int n; { conmap_t *map; int maxcon = EXTRAS(VERTEX)[V_CONSTR_LIST_ATTR].array_spec.datacount; int k; n &= CONMASK; if ( maxcon == 0 ) return 0; map = get_v_constraint_map(v_id); for ( k = 1; k <= (int)*map ; k++ ) { if ( (map[k] & CONMASK) == (conmap_t)n ) return 1; } return 0; } int v_hit_constraint_count(v_id) vertex_id v_id; { conmap_t *map; int maxcon = EXTRAS(VERTEX)[V_CONSTR_LIST_ATTR].array_spec.datacount; int count = 0; int k; if ( maxcon == 0 ) return 0; map = get_v_constraint_map(v_id); for ( k = 1; k <= (int)*map ; k++ ) { if ( (map[k] & CON_HIT_BIT) ) count++; } return count; } void get_v_common_conmap(v1,v2,conmap) vertex_id v1,v2; conmap_t *conmap; { conmap_t *map1 = get_v_constraint_map(v1); unsigned int k; conmap[0] = 0; for ( k = 1; k <= map1[0] ; k++ ) if ( v_on_constraint(v2,map1[k]) ) conmap[++conmap[0]] = map1[k] & (conmap_t)CONMASK; } int get_v_constraint_status(v_id,n) vertex_id v_id; int n; { conmap_t *map; int maxcon = EXTRAS(VERTEX)[V_CONSTR_LIST_ATTR].array_spec.datacount; int k; n &= CONMASK; if ( maxcon == 0 ) return 0; map = get_v_constraint_map(v_id); for ( k = 1; k <= (int)*map ; k++ ) { if ( (map[k] & CONMASK) == (conmap_t)n ) return (map[k] & CON_HIT_BIT) ? 1 : 0; } return 0; } void clear_v_conmap(v_id) vertex_id v_id; { conmap_t *map = get_v_constraint_map(v_id); unsigned int k; for ( k = 1 ; k <= map[0] ; k++ ) map[k] = 0; map[0] = 0; } void set_v_constraint_status(v_id,n) vertex_id v_id; int n; { conmap_t *map; int maxcon = EXTRAS(VERTEX)[V_CONSTR_LIST_ATTR].array_spec.datacount; int k; n &= CONMASK; if ( maxcon == 0 ) return; map = get_v_constraint_map(v_id); for ( k = 1; k <= (int)*map ; k++ ) { if ( (map[k] & CONMASK) == (conmap_t)n ) { map[k] |= CON_HIT_BIT; set_attr(v_id,HIT_WALL); return; } } } void unset_v_constraint_status(v_id,n) vertex_id v_id; int n; { conmap_t *map; int maxcon = EXTRAS(VERTEX)[V_CONSTR_LIST_ATTR].array_spec.datacount; int k; n &= CONMASK; if ( maxcon == 0 ) return; map = get_v_constraint_map(v_id); for ( k = 1; k <= (int)*map ; k++ ) { if ( (map[k] & CONMASK) == (conmap_t)n ) { map[k] &= ~CON_HIT_BIT; return; } } } void clear_v_constraint_status(v_id) vertex_id v_id; { conmap_t *map; int maxcon = EXTRAS(VERTEX)[V_CONSTR_LIST_ATTR].array_spec.datacount; int k; if ( maxcon == 0 ) return; map = get_v_constraint_map(v_id); for ( k = 1; k <= (int)*map ; k++ ) { map[k] &= ~CON_HIT_BIT; } } void set_e_constraint_map(e_id,n) edge_id e_id; int n; { conmap_t *map; int maxcon = EXTRAS(EDGE)[E_CONSTR_LIST_ATTR].array_spec.datacount; int k; struct constraint *constr; int four = 4; n &= CONMASK; if ( maxcon == 0 ) expand_attribute(EDGE,E_CONSTR_LIST_ATTR,&four); map = get_e_constraint_map(e_id); for ( k = 1; k <= (int)*map ; k++ ) if ( (map[k] & CONMASK) == (conmap_t)n ) return; if ( k >= maxcon ) { int newmax = maxcon+4; expand_attribute(EDGE,E_CONSTR_LIST_ATTR,&newmax); map = get_e_constraint_map(e_id); } map[k] = (conmap_t)n; map[0]++; /* counter */ set_attr(e_id,CONSTRAINT); constr = get_constraint(n); if ( constr->attr & CON_ENERGY ) { set_attr(e_id, BDRY_ENERGY); if ( everything_quantities_flag ) apply_method_num(positive_id(e_id),constr->energy_method); /* positive_id() to agree with old way */ } if ( constr->attr & CON_CONTENT ) { set_attr(e_id, BDRY_CONTENT); /* BIG PROBLEM HERE GETTING RIGHT BODY!!! */ if ( everything_quantities_flag ) { facetedge_id fe,first_fe = get_edge_fe(e_id); int min_rank, max_rank,j; min_rank = MAXINT; max_rank = 0; for ( j = 1 ; j <= (int)map[0] ; j++ ) { struct constraint *c = get_constraint(map[j]); if ( c->content_rank < min_rank ) min_rank = c->content_rank; if ( c->content_rank > max_rank ) max_rank = c->content_rank; } fe = first_fe; if ( valid_id(first_fe) ) do { char name[100]; body_id b_id; facet_id f_id; f_id = get_fe_facet(fe); fe = get_next_facet(fe); if ( !valid_id(f_id) ) continue; if ( get_fattr(f_id) & NONCONTENT ) continue; b_id = get_facet_body(f_id); if ( valid_id(b_id) && (constr->content_rank >= max_rank)) { if ( constr->attr & NAMED_THING ) sprintf(name,"body_%d_%s_meth",ordinal(b_id)+1,constr->name); else sprintf(name,"body_%d_con_%d_meth",ordinal(b_id)+1,n); attach_method(get_body_volquant(b_id),name); apply_method(e_id,name); } b_id = get_facet_body(inverse_id(f_id)); if ( valid_id(b_id) && (constr->content_rank <= min_rank)) { if ( constr->attr & NAMED_THING ) sprintf(name,"body_%d_%s_meth",ordinal(b_id)+1,constr->name); else sprintf(name,"body_%d_con_%d_meth",ordinal(b_id)+1,n); attach_method(get_body_volquant(b_id),name); apply_method(inverse_id(e_id),name); } } while ( !equal_id(fe,first_fe) ); } } } int e_on_constraint(e_id,n) edge_id e_id; int n; { conmap_t *map; int maxcon = EXTRAS(EDGE)[E_CONSTR_LIST_ATTR].array_spec.datacount; int k; n &= CONMASK; if ( maxcon == 0 ) return 0; map = get_e_constraint_map(e_id); for ( k = 1; k <= (int)*map ; k++ ) { if ( (map[k] & CONMASK) == (conmap_t)n ) return 1; } return 0; } void unset_e_constraint_map(e_id,n) edge_id e_id; int n; { conmap_t *map; int maxcon = EXTRAS(EDGE)[E_CONSTR_LIST_ATTR].array_spec.datacount; int j,k; n &= CONMASK; if ( maxcon == 0 ) return; map = get_e_constraint_map(e_id); for ( k = 1; k <= (int)*map ; k++ ) if ( (map[k] & CONMASK) == (conmap_t)n ) break; if ( k > (int)*map ) return; map[0]--; for ( j = k ; j <= (int)*map ; j++ ) map[j] = map[j+1]; map[j] = 0; if ( map[0] == 0 ) unset_attr(e_id,CONSTRAINT); if ( everything_quantities_flag ) { struct constraint *con = get_constraint(n); if ( con->attr & CON_ENERGY ) unapply_method(e_id,con->energy_method); if ( con->attr & CON_CONTENT ) { int meth_offset = get_meth_offset(EDGE); int i; int *instlist = (int*)((char*)elptr(e_id) + meth_offset); int mcount = elptr(e_id)->method_count; for ( i = 0 ; i < mcount ; i++ ) if ( METH_INSTANCE(abs(instlist[i]))->flags & BODY_INSTANCE ) unapply_method(e_id,instlist[i]); } } } void set_f_constraint_map(f_id,n) facet_id f_id; int n; { conmap_t *map; int maxcon = EXTRAS(FACET)[F_CONSTR_LIST_ATTR].array_spec.datacount; int k; int four = 4; n &= CONMASK; /* get rid of hit bit */ if ( maxcon == 0 ) expand_attribute(FACET,F_CONSTR_LIST_ATTR,&four); map = get_f_constraint_map(f_id); for ( k = 1; k <= (int)*map ; k++ ) if ( (map[k] & CONMASK) == (conmap_t)n ) return; if ( k >= maxcon ) { int newmax = maxcon+4; expand_attribute(FACET,F_CONSTR_LIST_ATTR,&newmax); map = get_f_constraint_map(f_id); } map[k] = (conmap_t)n; map[0]++; /* counter */ } int f_on_constraint(f_id,n) facet_id f_id; int n; { conmap_t *map; int maxcon = EXTRAS(FACET)[F_CONSTR_LIST_ATTR].array_spec.datacount; int k; n &= CONMASK; if ( maxcon == 0 ) return 0; map = get_f_constraint_map(f_id); for ( k = 1; k <= (int)*map ; k++ ) { if ( (map[k] & CONMASK) == (conmap_t)n ) return 1; } return 0; } void unset_f_constraint_map(f_id,n) facet_id f_id; int n; { conmap_t *map; int maxcon = EXTRAS(FACET)[F_CONSTR_LIST_ATTR].array_spec.datacount; int k,j; n &= CONMASK; if ( maxcon == 0 ) return; map = get_f_constraint_map(f_id); for ( k = 1; k <= (int)*map ; k++ ) if ( (map[k] & CONMASK) == (conmap_t)n ) break; if ( k > (int)*map ) return; map[0]--; for ( j = k ; j <= (int)*map ; j++ ) map[j] = map[j+1]; map[j] = 0; if ( map[0] == 0 ) unset_attr(f_id,CONSTRAINT); if ( everything_quantities_flag ) { struct constraint *con = get_constraint(n); if ( con->attr & CON_ENERGY ) unapply_method(f_id,con->energy_method); } } int get_tag(f_id) facet_id f_id; { if ( F_TAG_ATTR == 0 ) return 0; if ( EXTRAS(FACET)[F_TAG_ATTR].array_spec.datacount > 0 ) return (*FINT(f_id,F_TAG_ATTR)); else return 0; } void set_tag(f_id,t) facet_id f_id; int t; { int one=1; if ( EXTRAS(FACET)[F_TAG_ATTR].array_spec.datacount == 0 ) expand_attribute(FACET,F_TAG_ATTR,&one); *FINT(f_id,F_TAG_ATTR) = t; } evolver-2.30c.dfsg/src/method4.c0000644000175300017530000030460511410765113016735 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************* * * file: method4.c * * contents: miscellaneous named methods */ #include "include.h" /***************************************************************** * * function: johndust_energy() * * purpose: Energy request of John Sullivan, for point pairs on * sphere. E = (pi - asin(d/2))/d, where d is chord distance. * */ REAL johndust_energy(v_info) struct qinfo *v_info; { vertex_id v_id; int i; REAL energy = 0.0; REAL d,r[MAXCOORD]; REAL *x = get_coord(v_info->id); FOR_ALL_VERTICES(v_id) { REAL *y = get_coord(v_id); if ( v_id <= v_info->id ) continue; /* each pair once */ for ( i = 0 ; i < SDIM ; i++ ) r[i] = x[i] - y[i]; d = sqrt(SDIM_dot(r,r)); if ( d >= 2.0 ) energy += M_PI/4; /* antipodes */ else energy += (M_PI - asin(d/2))/d; } return energy; } /************************************************************** * * function: johndust_gradient() * * purpose: calculates energy of one vertex due to others * * input: info about vertex is in global qinfo structure. * */ REAL johndust_gradient(v_info) struct qinfo *v_info; { REAL *x = get_coord(v_info->id); REAL energy = 0.0; /* for this vertex */ vertex_id v_id; int i; REAL r[MAXCOORD]; /* difference vector between vertices */ REAL d,p,dp; for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] = 0.0; /* intialize gradient */ FOR_ALL_VERTICES(v_id) { REAL *y = get_coord(v_id); if ( equal_id(v_info->id,v_id) ) continue; for ( i = 0 ; i < SDIM ; i++ ) r[i] = x[i] - y[i]; d = sqrt(SDIM_dot(r,r)); if ( d >= 2.0 ) continue; /* antipodes have no force */ p = (M_PI - asin(d/2))/d; energy += p; dp = -p/d - 0.5/d/sqrt(1 - d*d/4); for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] += dp/d*r[i]; } return energy; } /****************************************************************************** String Gravity quantity stuff ******************************************************************************/ /*************************************************************** * * function: string_gravity_init() * * purpose: initialization for gravity method */ void string_gravity_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { /* method modulus is gravitation constant */ if ( gravity_quantity_num >= 0 ) GEN_QUANT(gravity_quantity_num)->modulus = web.gravflag ? web.grav_const : 0.0; } /************************************************************** * * function: string_gravity_all() * * purpose: calculates value, gradient, and hessian of one * facet due to gravitational potential. * * input: info about vertex is in qinfo structure. * */ REAL string_gravity_all_q ARGS((struct qinfo *,int)); REAL string_gravity_all_lagrange ARGS((struct qinfo *,int)); REAL get_edge_gdensity ARGS((edge_id)); REAL string_gravity_all ARGS((struct qinfo *,int)); REAL get_edge_gdensity(e_id) edge_id e_id; { REAL gdensity; facetedge_id fe,ffe; facet_id f_id; body_id b_id; gdensity = 0.0; fe = get_edge_fe(e_id); f_id = get_fe_facet(fe); b_id = get_facet_body(f_id); if ( valid_id(b_id) ) gdensity += get_body_density(b_id); b_id = get_facet_body(facet_inverse(f_id)); if ( valid_id(b_id) ) gdensity -= get_body_density(b_id); ffe = get_next_facet(fe); if ( !equal_id(fe,ffe) ) { f_id = get_fe_facet(ffe); b_id = get_facet_body(f_id); if ( valid_id(b_id) ) gdensity += get_body_density(b_id); b_id = get_facet_body(facet_inverse(f_id)); if ( valid_id(b_id) ) gdensity -= get_body_density(b_id); } return gdensity; } REAL string_gravity_all(e_info,mode) struct qinfo *e_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ { int i,j; REAL jac; /* jacobian */ REAL sum = 0.0; /* quadratic z sum of facet */ REAL djdx[MAXCOORD],dsdz[MAXCOORD]; REAL c = 1/6.; /* coefficient */ REAL gdensity; if ( web.modeltype == QUADRATIC ) return string_gravity_all_q(e_info,mode); if ( web.modeltype == LAGRANGE ) return string_gravity_all_lagrange(e_info,mode); gdensity = get_edge_gdensity(e_info->id); if ( gdensity == 0.0 ) { return 0.0; } c *= gdensity; jac = -e_info->sides[0][0][0]; for ( i = 0 ; i < e_info->vcount ; i++ ) for ( j = 0 ; j <= i ; j++ ) sum += e_info->x[i][1]*e_info->x[j][1]; if ( mode == METHOD_VALUE ) return jac*sum*c; djdx[0] = 1.0; djdx[1] = -1.0; dsdz[0] = 2*e_info->x[0][1]+e_info->x[1][1]; dsdz[1] = e_info->x[0][1]+2*e_info->x[1][1]; for ( i = 0 ; i < ctrl_num; i++ ) { e_info->grad[i][0] = djdx[i]*sum*c; e_info->grad[i][1] = dsdz[i]*jac*c; } if ( mode == METHOD_GRADIENT ) return jac*sum*c; /* second partials, self */ for ( i = 0 ; i < ctrl_num; i++ ) { e_info->hess[i][i][0][1] = e_info->hess[i][i][1][0] = djdx[i]*dsdz[i]*c; e_info->hess[i][i][1][1] = 2*jac*c; e_info->hess[i][i][0][0] = 0.0; } /* second partials, mixed */ for ( i = 0 ; i < ctrl_num ; i++ ) for ( j = 0 ; j < ctrl_num ; j++ ) { if ( j == i ) continue; e_info->hess[i][j][0][0] = 0.0; e_info->hess[i][j][0][1] = djdx[i]*dsdz[j]*c; e_info->hess[i][j][1][0] = djdx[j]*dsdz[i]*c; e_info->hess[i][j][1][1] = jac*c; } return jac*sum*c; } /************************************************************** * * function: string_gravity_all_q() * * purpose: calculates value, gradient, and hessian of one * facet due to gravitational potential. Quadratic model. * * input: info about vertex is in qinfo structure. * */ REAL string_gravity_all_q(e_info,mode) struct qinfo *e_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ { int i,k,kk,m; REAL **g=NULL,****h=NULL; REAL value = 0.0; REAL gdensity,w,z; REAL tang,sum; if ( mode == METHOD_GRADIENT ) { g = e_info->grad; } else if ( mode == METHOD_HESSIAN ) { g = e_info->grad; h = e_info->hess; } gdensity = get_edge_gdensity(e_info->id); if ( gdensity == 0.0 ) return 0.0; for ( m = 0 ; m < gauss1D_num ; m++ ) { z = e_info->gauss_pt[m][1]; w = gdensity*gauss1Dwt[m]; tang = 0.0; for ( k = 0 ; k < edge_ctrl ; k++ ) tang += gauss1polyd[k][m]*e_info->x[k][0]; value += w*tang*(-z*z/2); if ( mode == METHOD_VALUE ) continue; sum = (-z)*tang; for ( i = 0 ; i < edge_ctrl ; i++ ) { g[i][0] += w*gauss1polyd[i][m]*(-z*z/2); g[i][1] += w*gauss1poly[i][m]*sum; } if ( mode == METHOD_GRADIENT ) continue; sum = -tang; for ( k = 0 ; k < edge_ctrl ; k++ ) for ( kk = 0 ; kk < edge_ctrl ; kk++ ) { h[k][kk][1][1] += w*sum*gauss1poly[k][m]*gauss1poly[kk][m]; h[k][kk][0][1] += w*gauss1polyd[k][m]*(-z)*gauss1poly[kk][m]; h[k][kk][1][0] += w*gauss1polyd[kk][m]*(-z)*gauss1poly[k][m]; } } return value; } /************************************************************** * * function: string_gravity_all_lagrange() * * purpose: calculates value, gradient, and hessian of one * facet due to gravitational potential. Lagrange model. * * input: info about vertex is in qinfo structure. * */ REAL string_gravity_all_lagrange(e_info,mode) struct qinfo *e_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ { REAL gdensity; REAL sum,y; int m,i,k,kk; REAL value = 0.0; REAL val; REAL tang; REAL sign = (get_eattr(e_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; struct gauss_lag *gl = &gauss_lagrange[1][web.gauss1D_order]; gdensity = get_edge_gdensity(e_info->id); if ( gdensity == 0.0 ) return 0.0; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL weight = sign*gdensity*gl->gausswt[m]; tang = 0.0; for ( k = 0 ; k < gl->lagpts ; k++ ) tang += gl->gpolypart[m][0][k]*e_info->x[k][0]; y = e_info->gauss_pt[m][1]; val = -y*y/2; value += weight*val*tang; if ( mode == METHOD_VALUE ) continue; sum = -y*tang; for ( i = 0 ; i < gl->lagpts ; i++ ) { e_info->grad[i][0] += weight*gl->gpolypart[m][0][i]*val; e_info->grad[i][1] += weight*gl->gpoly[m][i]*sum; } if ( mode == METHOD_GRADIENT ) continue; sum = -tang; for ( k = 0 ; k < gl->lagpts ; k++ ) for ( kk = 0 ; kk < gl->lagpts ; kk++ ) { e_info->hess[k][kk][1][1] += weight* sum*gl->gpoly[m][k]*gl->gpoly[m][kk]; e_info->hess[k][kk][0][1] += weight* gl->gpolypart[m][0][k]*(-y)*gl->gpoly[m][kk]; e_info->hess[k][kk][1][0] += weight* gl->gpolypart[m][0][kk]*(-y)*gl->gpoly[m][k]; } } return value; } /************************************************************** * * function: string_gravity_energy() * * purpose: calculates energy of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL string_gravity_energy(e_info) struct qinfo *e_info; { return string_gravity_all(e_info,METHOD_VALUE); } /************************************************************** * * function: string_gravity_grads() * * purpose: calculates gradient of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL string_gravity_grads(e_info) struct qinfo *e_info; { return string_gravity_all(e_info,METHOD_GRADIENT); } /************************************************************** * * function: string_gravity_hessian() * * purpose: calculates hessian of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL string_gravity_hessian(e_info) struct qinfo *e_info; { return string_gravity_all(e_info,METHOD_HESSIAN); } /************************************************************************* Curvature rotated to binormal direction. Localized Induction Equation. For Ron Perline. ***************************************************************************/ void curvature_binormal_init(mode,mi) int mode; struct method_instance *mi; { } REAL curvature_binormal_energy(v_info) struct qinfo *v_info; { return 0.0; } REAL curvature_binormal_force(v_info) struct qinfo *v_info; { vertex_id v_id = v_info->id; edge_id e1,e2; REAL s1[MAXCOORD],s2[MAXCOORD]; REAL l1,l2; REAL denom; int i; int sign = 1; /* orientation correction */ e1 = get_vertex_edge(v_id); e2 = get_next_tail_edge(e1); if ( inverted(e1) ) { invert(e1); sign = -1; } if ( inverted(e2) ) invert(e2); get_edge_side(e1,s1); get_edge_side(e2,s2); cross_prod(s1,s2,v_info->grad[0]); l1 = sqrt(SDIM_dot(s1,s1)); l2 = sqrt(SDIM_dot(s2,s2)); denom = sign*l1*l2*(l1+l2)/2; for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] /= denom; return 0.0; /* energy */ } /************************************************************************* Various curve curvature quantities. For Ron Perline. ***************************************************************************/ /************************************************************************** quantity ddd_gamma_sq Third deriv of curve position as function of arclength, squared. ****************************************************************************/ void ddd_gamma_sq_init(mode,mi) int mode; struct method_instance *mi; { } REAL ddd_gamma_sq_energy(e_info) struct qinfo *e_info; { REAL *side1,*side2,*side3; REAL s1,s2,s3; REAL dddgamma[MAXCOORD]; int i; side1 = e_info->sides[0][1]; side2 = e_info->sides[0][0]; side3 = e_info->sides[0][2]; s1 = sqrt(SDIM_dot(side1,side1)); s2 = sqrt(SDIM_dot(side2,side2)); s3 = sqrt(SDIM_dot(side3,side3)); for ( i = 0 ; i < SDIM ; i++ ) dddgamma[i] = side1[i]/s1/s2/(s2+s3) - (side1[i]+side2[i])/s2/s3/(s1+s2) + (side1[i]+side2[i]+side3[i])/s3/(s2+s3)/(s1+s2+s3); return 36*SDIM_dot(dddgamma,dddgamma)*s2; } REAL ddd_gamma_sq_gradient(e_info) struct qinfo *e_info; { REAL *side1,*side2,*side3; REAL s1,s2,s3; REAL dddgamma[MAXCOORD]; REAL Ddddgamma[4][MAXCOORD][MAXCOORD]; REAL energy; int i,j,k; side1 = e_info->sides[0][1]; side2 = e_info->sides[0][0]; side3 = e_info->sides[0][2]; s1 = sqrt(SDIM_dot(side1,side1)); s2 = sqrt(SDIM_dot(side2,side2)); s3 = sqrt(SDIM_dot(side3,side3)); for ( i = 0 ; i < SDIM ; i++ ) dddgamma[i] = side1[i]/s1/s2/(s2+s3) - (side1[i]+side2[i])/s2/s3/(s1+s2) + (side1[i]+side2[i]+side3[i])/s3/(s2+s3)/(s1+s2+s3); energy = 36*SDIM_dot(dddgamma,dddgamma)*s2; for ( k = 0 ; k < 4 ; k++ ) for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) Ddddgamma[k][i][j] = 0.0; /* now the gradients on the four vertices */ /* recalling numbering order 2 0 1 3 */ for ( i = 0 ; i < SDIM ; i++ ) { Ddddgamma[2][i][i] -= 1.0/s1/s2/(s2+s3) - 1.0/s2/s3/(s1+s2) + 1.0/s3/(s2+s3)/(s1+s2+s3); Ddddgamma[0][i][i] += 1.0/s1/s2/(s2+s3) - 1.0/s2/s3/(s1+s2) + 1.0/s3/(s2+s3)/(s1+s2+s3); Ddddgamma[0][i][i] -= -1/s2/s3/(s1+s2) + 1/s3/(s2+s3)/(s1+s2+s3); Ddddgamma[1][i][i] += -1/s2/s3/(s1+s2) + 1/s3/(s2+s3)/(s1+s2+s3); Ddddgamma[1][i][i] -= 1.0/s3/(s2+s3)/(s1+s2+s3); Ddddgamma[3][i][i] += 1.0/s3/(s2+s3)/(s1+s2+s3); for ( j = 0 ; j < SDIM ; j++ ) { REAL d; d = side1[i]/s1/s2/(s2+s3)*(-side1[j]/s1/s1) - (side1[i]+side2[i])/s2/s3/(s1+s2)*(-side1[j]/s1/(s1+s2)) + (side1[i]+side2[i]+side3[i])/s3/(s2+s3)/(s1+s2+s3) *(-side1[j]/s1/(s1+s2+s3)); Ddddgamma[2][i][j] -= d; Ddddgamma[0][i][j] += d; d = side1[i]/s1/s2/(s2+s3)*(-side2[j]/s2/s2-side2[j]/s2/(s2+s3)) - (side1[i]+side2[i])/s2/s3/(s1+s2)*(-side2[j]/s2/s2-side2[j]/s2/(s1+s2)) + (side1[i]+side2[i]+side3[i])/s3/(s2+s3)/(s1+s2+s3)*(-side2[j]/s2/(s2+s3) - side2[j]/s2/(s1+s2+s3)); Ddddgamma[0][i][j] -= d; Ddddgamma[1][i][j] += d; d = side1[i]/s1/s2/(s2+s3)*(-side3[j]/s3/(s2+s3)) - (side1[i]+side2[i])/s2/s3/(s1+s2)*(-side3[j]/s3/s3) + (side1[i]+side2[i]+side3[i])/s3/(s2+s3)/(s1+s2+s3) *(-side3[j]/s3/s3 - side3[j]/s3/(s2+s3) -side3[j]/s3/(s1+s2+s3)); Ddddgamma[1][i][j] -= d; Ddddgamma[3][i][j] += d; } } for ( k = 0 ; k < 4; k++ ) { for ( j = 0 ; j < SDIM ; j++ ) { REAL g; for ( i = 0, g = 0.0 ; i < SDIM ; i++ ) g += 2*dddgamma[i]*Ddddgamma[k][i][j]; e_info->grad[k][j] = 36*g*s2; } } for ( j = 0 ; j < SDIM ; j++ ) { e_info->grad[0][j] -= energy*side2[j]/s2/s2; e_info->grad[1][j] += energy*side2[j]/s2/s2; } return energy; } /********************************************************************* Gap energy methods **********************************************************************/ /***************************************************************** * * Function: gap_energy() * * Purpose: Calculate energy of kludge constraint force. * Constant factor included to make it best approx * of true area. */ REAL gap_energy(e_info) struct qinfo *e_info; { edge_id e_id = e_info->id; REAL sprenergy = 0.0; REAL *s; REAL ss; struct constraint *constr[MAXCONPER]; int concount; conmap_t * conmap; int i,j; if ( get_eattr(e_id) & FIXED ) return 0.0; if ( !(get_eattr(e_id) & CONSTRAINT) ) return 0.0; /* find which constraints have CONVEX attribute */ conmap = get_e_constraint_map(e_id); for ( j = 1,i = 0 ; j <= (int)conmap[0] ; j++ ) { constr[i] = get_constraint(conmap[j]); if ( constr[i]->attr & B_CONVEX ) i++; /* keep this one */ } if ( i == 0 ) return 0.0; concount = i; /* now the calculation */ s = e_info->sides[0][0]; ss = SDIM_dot(s,s); for ( i = 0 ; i < concount ; i++ ) { REAL *coord,ff,fs; REAL fval,grad[MAXCOORD]; coord = e_info->x[0]; eval_all(constr[i]->formula,coord,SDIM,&fval,grad,e_id); ff = SDIM_dot(grad,grad); if ( ff <= 0.0 ) { sprintf(errmsg,"Edge %s is on a CONVEX constraint at zero gradient.\n", ELNAME(e_info->id)); kb_error(2148,errmsg,WARNING); ff = 1.0; } fs = SDIM_dot(s,grad); sprenergy += fabs(fs)*sqrt(ss/ff)/12; coord = e_info->x[1]; eval_all(constr[i]->formula,coord,SDIM,&fval,grad,e_id); ff = SDIM_dot(grad,grad); if ( ff <= 0.0 ) { sprintf(errmsg,"Edge %s is on a CONVEX constraint at zero gradient.\n", ELNAME(e_info->id)+1); kb_error(2149,errmsg,WARNING); ff = 1.0; } fs = SDIM_dot(s,grad); sprenergy += fabs(fs)*sqrt(ss/ff)/12; } sprenergy *= web.spring_constant; return sprenergy; } /**************************************************************** * * Function: gap_grads() * * Purpose: Since only vertices are actually confined to constraints, * edges and faces supposedly on constraints can pull * away from convex constraints, and in fact do, since * a long edge short-cuts the constraints. To prevent * this and encourage equal-length constraint edges, an * energy penalty is inflicted for an edge angling away * from its constraint. * The energy penalty is 2/3 of the area of the right * triangle whose base is half the side and whose hypoteneuse * lies on the constraint tangent. This is done for both * ends of the side. */ REAL gap_grads(e_info) struct qinfo *e_info; { edge_id e_id = e_info->id; REAL *s; REAL ss; /* square lengths */ struct constraint *constr[MAXCONPER]; int concount; conmap_t * conmap; int i,j; MAT2D(second,MAXCOORD,MAXCOORD); /* for second partials */ REAL sprenergy = 0.0; if ( get_eattr(e_id) & FIXED ) return 0.0; if ( !(get_eattr(e_id) & CONSTRAINT) ) return 0.0; /* find which constraints have CONVEX attribute */ conmap = get_e_constraint_map(e_id); for ( j = 1,i=0 ; j <= (int)conmap[0] ; j++ ) { constr[i] = get_constraint(conmap[j]); if ( constr[i]->attr & B_CONVEX ) i++; /* keep this one */ } if ( i == 0 ) return 0.0; concount = i; /* now the calculation */ s = e_info->sides[0][0]; ss = SDIM_dot(s,s); for ( i = 0 ; i < concount ; i++ ) { REAL *coord; REAL fval,grad[MAXCOORD]; REAL ff,fs,t; coord = e_info->x[0]; eval_second(constr[i]->formula,coord,SDIM,&fval,grad,second,e_info->id); ff = SDIM_dot(grad,grad); if ( ff <= 0.0 ) ff = 1.0; fs = SDIM_dot(s,grad); t = sqrt(ss/ff); sprenergy += fabs(fs)*sqrt(ss/ff)/12; if ( fs < 0.0 ) t = -t; /* to take care of fabs() */ for ( j = 0 ; j < SDIM ; j++ ) { REAL g; g = -t*grad[j] + t*SDIM_dot(s,second[j]) + fs/t*(-s[j]/ff - ss/ff/ff*SDIM_dot(grad,second[j])); e_info->grad[0][j] += web.spring_constant*g/12; g = t*grad[j] + fs/t/ff*s[j]; e_info->grad[1][j] += web.spring_constant*g/12; } coord = e_info->x[1]; eval_second(constr[i]->formula,coord,SDIM,&fval,grad,second,e_info->id); ff = SDIM_dot(grad,grad); if ( ff <= 0.0 ) ff = 1.0; fs = SDIM_dot(s,grad); sprenergy += fabs(fs)*sqrt(ss/ff)/12; t = sqrt(ss/ff); if ( fs < 0.0 ) t = -t; /* to take care of fabs() */ for ( j = 0 ; j < SDIM ; j++ ) { REAL g; g = t*grad[j] + t*SDIM_dot(s,second[j]) + fs/t*(s[j]/ff - ss/ff/ff*SDIM_dot(grad,second[j])); e_info->grad[1][j] += web.spring_constant*g/12; g = -t*grad[j] - fs/t/ff*s[j]; e_info->grad[0][j] += web.spring_constant*g/12; } } sprenergy *= web.spring_constant; return sprenergy; } /* end gap_grads() */ /***********************************************************************/ /************************************************************************ Named method: linear_elastic Linear elastic strain energy on facets. (for Frank Baginski's NASA balloon models) Let S be Gram matrix of unstrained facet (dots of sides). Let Q be the inverse of S. Let F be Gram matrix of strained facet. Let C = (FQ-I)/2, the Cauchy-Green strain tensor. Let v be Poisson ratio. Then energy density is (1/2/(1+v))(Tr(C^2) + v*(Tr C)^2/(1-(dim-1)*v)) Each facet has extra attribute poisson_ratio and extra attribute array form_factors[3] = {s11,s12,s22} where sij = dot(si,sj) and s1 = (v1-v0), s2 = (v2-v0). ************************************************************************/ #define POISSON_NAME "poisson_ratio" #define FORM_FACTORS_NAME "form_factors" int poisson_attr; /* number of poisson_ratio extra attribute */ int form_factors_attr; /* number of form_factors extra attribute */ /*************************************************************** * * function: linear_elastic_init() * * purpose: Make sure needed extra attributes are present. * * */ void linear_elastic_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { if ( web.modeltype != LINEAR ) kb_error(2529,"linear_elastic method only for LINEAR model.\n",RECOVERABLE); if ( web.dimension != 2 ) kb_error(2150,"linear_elastic method only for SOAPFILM model.\n", RECOVERABLE); /* extra edge atribute */ poisson_attr = find_attribute(FACET,POISSON_NAME); if ( poisson_attr < 0 ) /* not found */ kb_error(3200,"Facet extra attribute poisson_ratio missing. Needed by linear_elastic.\n",RECOVERABLE); form_factors_attr = find_attribute(FACET,FORM_FACTORS_NAME); if ( form_factors_attr < 0 ) /* not found */ kb_error(2152,"Facet extra attribute form_factors real[3] missing. Needed by linear_elastic.\n",RECOVERABLE); if ( EXTRAS(FACET)[form_factors_attr].array_spec.datacount != 3 ) kb_error(2153,"Facet extra attribute form_factors must have size 3.\n", RECOVERABLE); } /************************************************************************ * * function: linear_elastic_all() * * purpose: energy, gradient, and hessian for linear_elastic method. */ REAL linear_elastic_all ARGS((struct qinfo *,int)); REAL linear_elastic_all(f_info,mode) struct qinfo *f_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ { REAL *s; /* pointer to extra attributes */ REAL **side; REAL q11,q12,q22; /* Q entries */ REAL det; /* det S */ REAL area; /* reference area of facet */ REAL poisson; /* poisson ratio */ REAL f11,f12,f22; REAL c11,c12,c21,c22; REAL energy; REAL dc11dv[FACET_VERTS][MAXCOORD]; REAL dc12dv[FACET_VERTS][MAXCOORD]; REAL dc21dv[FACET_VERTS][MAXCOORD]; REAL dc22dv[FACET_VERTS][MAXCOORD]; REAL ddc11dv[FACET_VERTS][MAXCOORD][FACET_VERTS]; REAL ddc12dv[FACET_VERTS][MAXCOORD][FACET_VERTS]; REAL ddc21dv[FACET_VERTS][MAXCOORD][FACET_VERTS]; REAL ddc22dv[FACET_VERTS][MAXCOORD][FACET_VERTS]; int i,j,ii,jj; REAL coeff1,coeff2; poisson = *(REAL*)get_extra(f_info->id,poisson_attr); s = (REAL*)get_extra(f_info->id,form_factors_attr); det = s[0]*s[2] - s[1]*s[1]; if ( det <= 0.0 ) { if ( mode == METHOD_VALUE ) return 0.0; sprintf(errmsg,"linear_elastic: Facet %s has unstrained area <= 0.\n", ELNAME(f_info->id)); kb_error(2154,errmsg,RECOVERABLE); } area = sqrt(det)/2; coeff1 = area/8/(1 + poisson); coeff2 = coeff1*poisson/(1 - (web.dimension-1)*poisson); q11 = s[2]/det; q12 = -s[1]/det; q22 = s[0]/det; side = f_info->sides[0]; f11 = SDIM_dot(side[0],side[0]); f12 = SDIM_dot(side[0],side[1]); f22 = SDIM_dot(side[1],side[1]); c11 = f11*q11 + f12*q12 - 1; c12 = f11*q12 + f12*q22; c21 = f12*q11 + f22*q12; c22 = f12*q12 + f22*q22 - 1; energy = coeff1*(c11*c11+c12*c21+c12*c21+c22*c22) + coeff2*(c11+c22)*(c11+c22); if ( mode == METHOD_VALUE ) return energy; /* gradient */ for ( i = 0 ; i < SDIM ; i++ ) { dc11dv[1][i] = 2*side[0][i]*q11 + side[1][i]*q12; dc11dv[2][i] = side[0][i]*q12; dc12dv[1][i] = 2*side[0][i]*q12 + side[1][i]*q22; dc12dv[2][i] = side[0][i]*q22; dc21dv[1][i] = side[1][i]*q11; dc21dv[2][i] = side[0][i]*q11 + 2*side[1][i]*q12; dc22dv[1][i] = side[1][i]*q12; dc22dv[2][i] = side[0][i]*q12 + 2*side[1][i]*q22; dc11dv[0][i] = -(dc11dv[1][i] + dc11dv[2][i]); dc12dv[0][i] = -(dc12dv[1][i] + dc12dv[2][i]); dc21dv[0][i] = -(dc21dv[1][i] + dc21dv[2][i]); dc22dv[0][i] = -(dc22dv[1][i] + dc22dv[2][i]); } for ( j = 0 ; j < FACET_VERTS ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) { f_info->grad[j][i] = (coeff1*(2*c11*dc11dv[j][i] + 2*c12*dc21dv[j][i] + 2*c21*dc12dv[j][i] + 2*c22*dc22dv[j][i]) + 2*coeff2*(c11+c22)*(dc11dv[j][i] +dc22dv[j][i])); } if ( mode == METHOD_GRADIENT ) return energy; /* hessian */ for ( i = 0 ; i < SDIM ; i++ ) { ddc11dv[1][i][1] = 2*q11; ddc11dv[1][i][2] = q12; ddc11dv[2][i][1] = q12; ddc11dv[2][i][2] = 0.0; ddc12dv[1][i][1] = 2*q12; ddc12dv[1][i][2] = q22; ddc12dv[2][i][1] = q22; ddc12dv[2][i][2] = 0.0; ddc21dv[1][i][1] = 0.0; ddc21dv[1][i][2] = q11; ddc21dv[2][i][1] = q11; ddc21dv[2][i][2] = 2*q12; ddc22dv[1][i][1] = 0.0; ddc22dv[1][i][2] = q12; ddc22dv[2][i][1] = q12; ddc22dv[2][i][2] = 2*q22; for ( j = 1 ; j < FACET_VERTS; j++ ) { ddc11dv[0][i][j] = -(ddc11dv[1][i][j] + ddc11dv[2][i][j]); ddc12dv[0][i][j] = -(ddc12dv[1][i][j] + ddc12dv[2][i][j]); ddc21dv[0][i][j] = -(ddc21dv[1][i][j] + ddc21dv[2][i][j]); ddc22dv[0][i][j] = -(ddc22dv[1][i][j] + ddc22dv[2][i][j]); ddc11dv[j][i][0] = -(ddc11dv[j][i][1] + ddc11dv[j][i][2]); ddc12dv[j][i][0] = -(ddc12dv[j][i][1] + ddc12dv[j][i][2]); ddc21dv[j][i][0] = -(ddc21dv[j][i][1] + ddc21dv[j][i][2]); ddc22dv[j][i][0] = -(ddc22dv[j][i][1] + ddc22dv[j][i][2]); } ddc11dv[0][i][0] = -(ddc11dv[1][i][0] + ddc11dv[2][i][0]); ddc12dv[0][i][0] = -(ddc12dv[1][i][0] + ddc12dv[2][i][0]); ddc21dv[0][i][0] = -(ddc21dv[1][i][0] + ddc21dv[2][i][0]); ddc22dv[0][i][0] = -(ddc22dv[1][i][0] + ddc22dv[2][i][0]); } for ( j = 0 ; j < FACET_VERTS ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) for ( jj = 0 ; jj < FACET_VERTS ; jj++ ) { f_info->hess[j][jj][i][i] += (coeff1*( 2*c11*ddc11dv[j][i][jj] + 2*c12*ddc21dv[j][i][jj] + 2*c21*ddc12dv[j][i][jj] + 2*c22*ddc22dv[j][i][jj]) + 2*coeff2*(c11+c22)*(ddc11dv[j][i][jj]+ddc22dv[j][i][jj])); for ( ii = 0 ; ii < SDIM ; ii++ ) f_info->hess[j][jj][i][ii] += (coeff1*(2*dc11dv[jj][ii]*dc11dv[j][i] + 2*dc12dv[jj][ii]*dc21dv[j][i] + 2*dc21dv[jj][ii]*dc12dv[j][i] + 2*dc22dv[jj][ii]*dc22dv[j][i] ) + 2*coeff2*(dc11dv[jj][ii]+dc22dv[jj][ii]) *(dc11dv[j][i] +dc22dv[j][i])); } return energy; } /************************************************************** * * function: linear_elastic_energy() * * purpose: calculates energy of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL linear_elastic_energy(f_info) struct qinfo *f_info; { return linear_elastic_all(f_info,METHOD_VALUE); } /************************************************************** * * function: linear_elastic_gradient() * * purpose: calculates gradient of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL linear_elastic_gradient(f_info) struct qinfo *f_info; { return linear_elastic_all(f_info,METHOD_GRADIENT); } /************************************************************** * * function: linear_elastic_hessian() * * purpose: calculates hessian of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL linear_elastic_hessian(f_info) struct qinfo *f_info; { return linear_elastic_all(f_info,METHOD_HESSIAN); } /************************************************************************ Named method: linear_elastic_B Linear elastic strain energy on facets, with mobile reference coordinates as two extra dimensions at each vertex. Hence 3D balloon has to be set up as 5D surface. Does not use the form_factors attribute of linear_elastic. Does use same poisson_ratio. (for Frank Baginski's NASA balloon models) Let S be Gram matrix of unstrained facet (dots of sides). Let Q be the inverse of S. Let F be Gram matrix of strained facet. Let C = (FQ-I)/2, the Cauchy-Green strain tensor. Let v be Poisson ratio. Then energy density is (1/2/(1+v))(Tr(C^2) + v*(Tr C)^2/(1-(dim-1)*v)) Each facet has extra attribute poisson_ratio. ************************************************************************/ #define POISSON_NAME "poisson_ratio" #define FORM_FACTORS_NAME "form_factors" #define LEBWEIGHT_NAME "LEBweight" int LEBweight_attr; /* optional per-facet weight factor */ /************************************************************************ * * function: linear_elastic_B_init() * * purpose: Make sure needed extra attributes are present. * * */ void linear_elastic_B_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { if ( web.modeltype != LINEAR ) kb_error(2155,"linear_elastic_B method only for LINEAR model.\n",RECOVERABLE); /* extra facet attribute for poisson_ratio */ poisson_attr = find_attribute(FACET,POISSON_NAME); if ( poisson_attr < 0 ) /* not found */ kb_error(2156,"Facet extra attribute poisson_ratio missing. Needed by linear_elastic_B.\n",RECOVERABLE); LEBweight_attr = find_attribute(FACET,LEBWEIGHT_NAME); /* optional */ if ( web.dimension != 2 ) kb_error(2157,"linear_elastic_B method only for SOAPFILM model.\n",RECOVERABLE); } /************************************************************************ * * function: linear_elastic_B_all() * * purpose: energy, gradient, and hessian for linear_elastic_B method. */ REAL linear_elastic_B_all ARGS((struct qinfo *,int)); REAL linear_elastic_B_all(f_info,mode) struct qinfo *f_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ { REAL q11,q12,q22; /* Q entries */ REAL Q[2][2]; REAL det; /* det S */ REAL area; /* reference area of facet */ REAL areasign; /* for reference area orientation */ REAL poisson; /* poisson ratio */ REAL f11,f12,f22; REAL F[2][2]; REAL c11,c12,c21,c22; REAL energy; REAL stuff; REAL dadv[FACET_VERTS][MAXCOORD]; REAL dstuff[FACET_VERTS][MAXCOORD]; REAL dCdv[2][2][FACET_VERTS][MAXCOORD]; REAL dFdv[2][2][FACET_VERTS][MAXCOORD]; REAL dQdv[2][2][FACET_VERTS][MAXCOORD]; REAL dSdv[2][2][FACET_VERTS][MAXCOORD]; REAL ddFdv[2][2][2][2]; REAL ddSdv[2][2][2][2]; REAL ddCdv[2][2][FACET_VERTS][FACET_VERTS][MAXCOORD][MAXCOORD]; REAL ddQdv[2][2][FACET_VERTS][FACET_VERTS][MAXCOORD][MAXCOORD]; REAL ddstuff[FACET_VERTS][FACET_VERTS][MAXCOORD][MAXCOORD]; REAL ddadv[FACET_VERTS][FACET_VERTS][MAXCOORD][MAXCOORD]; int i,j,m,n,a,b,c,d,e; REAL coeff1,coeff2; REAL s[2][2]; /* form factors matrix */ int sdim = SDIM - web.dimension; /* true space dimension */ REAL combo; /* combination coeff for second edge orthogonality */ REAL weight; /* facet weighting factor */ REAL side[2][MAXCOORD]; poisson = *(REAL*)get_extra(f_info->id,poisson_attr); if ( LEBweight_attr >= 0 ) weight = *(REAL*)get_extra(f_info->id,LEBweight_attr); else weight = 1.0; /* compute orthogonal second side */ for ( j = 0 ; j < 2 ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) side[j][i] = f_info->sides[0][j][i]; s[0][0] = dot(side[0]+sdim,side[0]+sdim,web.dimension); /* in ref */ if ( s[0][0] == 0.0 ) return 0.0; s[0][1] = dot(side[0]+sdim,side[1]+sdim,web.dimension); /* in ref */ combo = s[0][1]/s[0][0]; for ( i = 0 ; i < SDIM ; i++ ) side[1][i] -= combo*side[0][i]; /* compute form factors */ for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < web.dimension ; j++ ) s[i][j] = dot(side[i]+sdim,side[j]+sdim,web.dimension); det = s[0][0]*s[1][1] - s[0][1]*s[1][0]; if ( det <= 0.0 ) { if ( mode == METHOD_VALUE ) return 0.0; sprintf(errmsg,"linear_elastic_B: Facet %s has unstrained area 0.\n", ELNAME(f_info->id)); kb_error(2158,errmsg,RECOVERABLE); } area = side[0][sdim]*side[1][sdim+1] - side[0][sdim+1]*side[1][sdim]; areasign = (area < 0.0) ? -0.5 : 0.5; /* triangle factor and orientation */ area *= areasign; coeff1 = 1.0/8/(1 + poisson); coeff2 = coeff1*poisson/(1 - (web.dimension-1)*poisson); Q[0][0] = q11 = s[1][1]/det; Q[0][1] = Q[1][0] = q12 = -s[0][1]/det; Q[1][1] = q22 = s[0][0]/det; F[0][0] = f11 = dot(side[0],side[0],sdim); F[0][1] = F[1][0] = f12 = dot(side[0],side[1],sdim); F[1][1] = f22 = dot(side[1],side[1],sdim); c11 = f11*q11 + f12*q12 - 1; c12 = f11*q12 + f12*q22; c21 = f12*q11 + f22*q12; c22 = f12*q12 + f22*q22 - 1; stuff = coeff1*(c11*c11+c12*c21+c12*c21+c22*c22) + coeff2*(c11+c22)*(c11+c22); energy = weight*area*stuff; if ( mode == METHOD_VALUE ) return energy; /* gradient */ for ( i = 0 ; i < sdim ; i++ ) /* with respect to space coord */ { for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) for ( m = 0 ; m < web.dimension ; m++ ) dFdv[a][b][m][i] = (m==a ? side[b][i] : 0.0) + (m==b ? side[a][i] : 0.0); } for ( i = sdim ; i < SDIM ; i++ ) /* with respect to ref coord */ { for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) for ( m = 0 ; m < web.dimension ; m++ ) dSdv[a][b][m][i] = (m==a ? side[b][i] : 0.0) + (m==b ? side[a][i] : 0.0); } for ( i = sdim ; i < SDIM ; i++ ) /* with respect to ref coord */ { for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) for ( m = 0 ; m < web.dimension ; m++ ) { REAL sum = 0.0; for ( c = 0 ; c < web.dimension ; c++ ) for ( d = 0 ; d < web.dimension ; d++ ) sum -= Q[a][c]*dSdv[c][d][m][i]*Q[d][b]; dQdv[a][b][m][i] = sum; } } for ( i = 0 ; i < sdim ; i++ ) /* with respect to space coord */ { for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) for ( m = 0 ; m < web.dimension ; m++ ) { REAL sum = 0.0; for ( c = 0 ; c < web.dimension ; c++ ) sum += dFdv[a][c][m][i]*Q[c][b]; dCdv[a][b][m][i] = sum; } } for ( i = sdim ; i < SDIM ; i++ ) /* with respect to ref coord */ { for ( a = 0 ; a < web.dimension ; a++ ) for ( e = 0 ; e < web.dimension ; e++ ) for ( m = 0 ; m < web.dimension ; m++ ) { REAL sum = 0.0; for ( b = 0 ; b < web.dimension ; b++ ) sum += F[a][b]*dQdv[b][e][m][i]; dCdv[a][e][m][i] = sum; } } for ( j = 0 ; j < web.dimension ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) { dstuff[j][i] = (coeff1*(2*c11*dCdv[0][0][j][i] + 2*c12*dCdv[1][0][j][i] + 2*c21*dCdv[0][1][j][i] + 2*c22*dCdv[1][1][j][i]) + 2*coeff2*(c11+c22)*(dCdv[0][0][j][i] +dCdv[1][1][j][i])); } /* reference area change */ for ( i = 0 ; i < SDIM ; i++ ) for ( a = 0 ; a < FACET_VERTS ; a++ ) dadv[a][i] = 0.0; dadv[0][sdim] = side[1][sdim+1]*areasign; dadv[1][sdim+1] = side[0][sdim]*areasign; dadv[0][sdim+1] = -side[1][sdim]*areasign; dadv[1][sdim] = -side[0][sdim+1]*areasign; /* grand finale for gradient */ for ( a = 0 ; a < web.dimension ; a++ ) for ( i = 0 ; i < SDIM ; i++ ) { f_info->grad[a+1][i] = weight*(dadv[a][i]*stuff + area*dstuff[a][i]); f_info->grad[0][i] -= weight*(dadv[a][i]*stuff + area*dstuff[a][i]); } /* combo correction */ for ( i = 0 ; i < SDIM ; i++ ) { f_info->grad[1][i] -= combo*f_info->grad[2][i]; f_info->grad[0][i] += combo*f_info->grad[2][i]; } if ( mode == METHOD_GRADIENT ) return energy; /* hessian */ /* calculate ddFdv and ddSdv, which are same and nonzero only for like coordinates, so coordinate index not used */ for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) for ( m = 0 ; m < web.dimension ; m++ ) for ( n = 0 ; n < web.dimension ; n++ ) { ddFdv[a][b][m][n] = (a==m && b==n ? 1.0 : 0.0) + (a==n && b==m ? 1.0 : 0.0); ddSdv[a][b][m][n] = (a==m && b==n ? 1.0 : 0.0) + (a==n && b==m ? 1.0 : 0.0); } /* calculate ddQdv */ for ( i = sdim ; i < SDIM ; i++ ) for ( j = sdim ; j < SDIM ; j++ ) for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) for ( m = 0 ; m < web.dimension ; m++ ) for ( n = 0 ; n < web.dimension ; n++ ) { REAL sum = 0.0; for ( c = 0 ; c < web.dimension ; c++ ) for ( d = 0 ; d < web.dimension ; d++ ) sum += - dQdv[a][c][m][i]*dSdv[c][d][n][j]*Q[d][b] - ( i==j ? Q[a][c]*ddSdv[c][d][m][n]*Q[d][b] : 0.0 ) - Q[a][c]*dSdv[c][d][m][i]*dQdv[d][b][n][j]; ddQdv[a][b][m][n][i][j] = sum; } /* calculate ddCdv */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) { for ( m = 0 ; m < web.dimension ; m++ ) for ( n = 0 ; n < web.dimension ; n++ ) { REAL sum = 0.0; for ( c = 0 ; c < web.dimension ; c++ ) sum += ( (i==j && i < sdim) ? ddFdv[a][c][m][n]*Q[c][b] : 0.0 ) + ( (i < sdim && j >= sdim) ? dFdv[a][c][m][i]*dQdv[c][b][n][j] : 0.0) + ( (j < sdim && i >= sdim) ? dFdv[a][c][n][j]*dQdv[c][b][m][i] : 0.0) + ( (i >= sdim && j >= sdim) ? F[a][c]*ddQdv[c][b][m][n][i][j] : 0.0); ddCdv[a][b][m][n][i][j] = sum; } } /* calculate ddstuff */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) { REAL term; term = (coeff1*( 2*c11*ddCdv[0][0][a][b][i][j] + 2*c12*ddCdv[1][0][a][b][i][j] + 2*c21*ddCdv[0][1][a][b][i][j] + 2*c22*ddCdv[1][1][a][b][i][j]) + 2*coeff2*(c11+c22)*(ddCdv[0][0][a][b][i][j]+ddCdv[1][1][a][b][i][j])) + (coeff1*(2*dCdv[0][0][a][i]*dCdv[0][0][b][j] + 2*dCdv[0][1][a][i]*dCdv[1][0][b][j] + 2*dCdv[1][0][a][i]*dCdv[0][1][b][j] + 2*dCdv[1][1][a][i]*dCdv[1][1][b][j] ) + 2*coeff2*(dCdv[0][0][a][i]+dCdv[1][1][a][i]) *(dCdv[0][0][b][j] +dCdv[1][1][b][j])); ddstuff[a][b][i][j] = term; } /* area hessian */ for ( i = sdim ; i < SDIM ; i++ ) for ( j = sdim ; j < SDIM ; j++ ) for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) ddadv[a][b][i][j] = 0.0; ddadv[0][1][sdim][sdim+1] = areasign; ddadv[1][0][sdim+1][sdim] = areasign; ddadv[0][1][sdim+1][sdim] = -areasign; ddadv[1][0][sdim][sdim+1] = -areasign; /* big Hessian finale */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) { REAL term; term = ( (i >= sdim && j >= sdim) ? ddadv[a][b][i][j]*stuff : 0.0) + dadv[a][i]*dstuff[b][j] + dadv[b][j]*dstuff[a][i] + area*ddstuff[a][b][i][j]; term *= weight; f_info->hess[a+1][b+1][i][j] = term; f_info->hess[a+1][0][i][j] -= term; f_info->hess[0][b+1][i][j] -= term; f_info->hess[0][0][i][j] += term; } /* combo correction */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { REAL term; term = combo*(combo*f_info->hess[2][2][i][j]-f_info->hess[1][2][i][j] -f_info->hess[2][1][i][j]); f_info->hess[1][1][i][j] += term; f_info->hess[0][1][i][j] -= term; f_info->hess[1][0][i][j] -= term; f_info->hess[0][0][i][j] += term; term = combo*f_info->hess[2][2][i][j]; f_info->hess[1][2][i][j] -= term; f_info->hess[0][2][i][j] += term; f_info->hess[1][0][i][j] += term; f_info->hess[0][0][i][j] -= term; term = combo*f_info->hess[2][2][i][j]; f_info->hess[2][1][i][j] -= term; f_info->hess[0][1][i][j] += term; f_info->hess[2][0][i][j] += term; f_info->hess[0][0][i][j] -= term; } return energy; } /************************************************************** * * function: linear_elastic_B_energy() * * purpose: calculates energy of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL linear_elastic_B_energy(f_info) struct qinfo *f_info; { return linear_elastic_B_all(f_info,METHOD_VALUE); } /************************************************************** * * function: linear_elastic_B_gradient() * * purpose: calculates gradient of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL linear_elastic_B_gradient(f_info) struct qinfo *f_info; { return linear_elastic_B_all(f_info,METHOD_GRADIENT); } /************************************************************** * * function: linear_elastic_B_hessian() * * purpose: calculates hessian of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL linear_elastic_B_hessian(f_info) struct qinfo *f_info; { return linear_elastic_B_all(f_info,METHOD_HESSIAN); } /************************************************************************ ************************************************************************* Named method: relaxed_elastic Linear elastic strain energy on facets, with mobile reference coordinates as two extra dimensions at each vertex. Hence 3D balloon has to be set up as 5D surface. Does not use the form_factors attribute of linear_elastic. Does use same poisson_ratio. Revised from linear_elastic_B to implement Bill Collier's relaxed energy model. Basically, stress is set to 0 when stress would be negative, to model wrinkling. (for Frank Baginski's NASA balloon models) Let S be Gram matrix of unstrained facet (dots of sides). Let Q be the inverse of S. Let F be Gram matrix of strained facet. Let C = (FQ-I)/2, the Cauchy-Green strain tensor. Let v be Poisson ratio. Let eps1 and eps2 be the eigenvalues of C. Let sigma1 = eps1+v*eps2 and sigma2 = eps2+v*eps1 be the stress eigenvalues. Then energy density is (1/2/(1+v))(Tr(C^2) + v*(Tr C)^2/(1-(dim-1)*v)) sigma1,sigma2 >= 0 eps1^2/2 sigma2 < 0, eps1 > 0 eps2^2/2 sigma1 < 0, eps2 > 0 0 eps2 < 0, eps1 < 0. Each facet has extra attribute poisson_ratio for v. ************************************************************************/ /************************************************************************ * * function: relaxed_elastic_init() * * purpose: Make sure needed extra attributes are present. * * */ void relaxed_elastic_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { if ( web.modeltype != LINEAR ) kb_error(2159,"relaxed_elastic method only for LINEAR model.\n",RECOVERABLE); if ( web.dimension != 2 ) kb_error(2160,"relaxed_elastic method only for SOAPFILM model.\n",RECOVERABLE); /* extra facet attribute for poisson_ratio */ poisson_attr = find_attribute(FACET,POISSON_NAME); if ( poisson_attr < 0 ) /* not found */ kb_error(2161,"Facet extra attribute poisson_ratio missing. Needed by relaxed_elastic.\n",RECOVERABLE); LEBweight_attr = find_attribute(FACET,LEBWEIGHT_NAME); /* optional */ } /************************************************************************ * * function: relaxed_elastic_all() * * purpose: energy, gradient, and hessian for relaxed_elastic_method. */ REAL relaxed_elastic_all ARGS((struct qinfo *,int,int)); #define ONE_STRESS 1 #define TWO_STRESS 2 REAL relaxed_elastic_all(f_info,mode,part) struct qinfo *f_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ int part; /* flag for one stress, two stress, or both parts */ { REAL q11,q12,q22; /* Q entries */ REAL Q[2][2]; REAL det; /* det S */ REAL area; /* reference area of facet */ REAL areasign; /* for reference area orientation */ REAL poisson; /* poisson ratio */ REAL f11,f12,f22; REAL F[2][2]; REAL c11,c12,c21,c22; REAL energy; REAL stuff; REAL dadv[FACET_VERTS][MAXCOORD]; REAL dstuff[FACET_VERTS][MAXCOORD]; REAL dCdv[2][2][FACET_VERTS][MAXCOORD]; REAL dFdv[2][2][FACET_VERTS][MAXCOORD]; REAL dQdv[2][2][FACET_VERTS][MAXCOORD]; REAL dSdv[2][2][FACET_VERTS][MAXCOORD]; REAL ddFdv[2][2][2][2]; REAL ddSdv[2][2][2][2]; REAL ddCdv[2][2][FACET_VERTS][FACET_VERTS][MAXCOORD][MAXCOORD]; REAL ddQdv[2][2][FACET_VERTS][FACET_VERTS][MAXCOORD][MAXCOORD]; REAL ddstuff[FACET_VERTS][FACET_VERTS][MAXCOORD][MAXCOORD]; REAL ddadv[FACET_VERTS][FACET_VERTS][MAXCOORD][MAXCOORD]; int i,j,m,n,a,b,c,d,e; REAL coeff1,coeff2; REAL s[2][2]; /* form factors matrix */ int sdim = SDIM - web.dimension; /* true space dimension */ REAL combo; /* combination coeff for second edge orthogonality */ REAL weight; /* facet weighting factor */ REAL side[2][MAXCOORD]; REAL eps1,eps2; /* strain eigenvalues */ REAL TrC,DetC; /* trace and determinant of C */ poisson = *(REAL*)get_extra(f_info->id,poisson_attr); if ( LEBweight_attr >= 0 ) weight = *(REAL*)get_extra(f_info->id,LEBweight_attr); else weight = 1.0; /* compute orthogonal second side */ for ( j = 0 ; j < 2 ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) side[j][i] = f_info->sides[0][j][i]; s[0][0] = dot(side[0]+sdim,side[0]+sdim,web.dimension); /* in ref */ if ( s[0][0] == 0.0 ) return 0.0; s[0][1] = dot(side[0]+sdim,side[1]+sdim,web.dimension); /* in ref */ combo = s[0][1]/s[0][0]; for ( i = 0 ; i < SDIM ; i++ ) side[1][i] -= combo*side[0][i]; /* compute form factors */ for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < web.dimension ; j++ ) s[i][j] = dot(side[i]+sdim,side[j]+sdim,web.dimension); det = s[0][0]*s[1][1] - s[0][1]*s[1][0]; if ( det <= 0.0 ) { if ( mode == METHOD_VALUE ) return 0.0; sprintf(errmsg,"relaxed_elastic_ Facet %s has unstrained area 0.\n", ELNAME(f_info->id)); kb_error(2162,errmsg,RECOVERABLE); } area = side[0][sdim]*side[1][sdim+1] - side[0][sdim+1]*side[1][sdim]; areasign = (area < 0.0) ? -0.5 : 0.5; /* triangle factor and orientation */ area *= areasign; coeff1 = 1.0/8/(1 + poisson); coeff2 = coeff1*poisson/(1 - (web.dimension-1)*poisson); Q[0][0] = q11 = s[1][1]/det; Q[0][1] = Q[1][0] = q12 = -s[0][1]/det; Q[1][1] = q22 = s[0][0]/det; F[0][0] = f11 = dot(side[0],side[0],sdim); F[0][1] = F[1][0] = f12 = dot(side[0],side[1],sdim); F[1][1] = f22 = dot(side[1],side[1],sdim); c11 = f11*q11 + f12*q12 - 1; c12 = f11*q12 + f12*q22; c21 = f12*q11 + f22*q12; c22 = f12*q12 + f22*q22 - 1; TrC = c11 + c22; DetC = c11*c22 - c12*c21; eps1 = (TrC + sqrt(TrC*TrC - 4*DetC))/2; eps2 = (TrC - sqrt(TrC*TrC - 4*DetC))/2; if ( eps1 <= 0.0 ) return 0.0; /* relaxed in both directions */ stuff = 0.0; if ( eps2 + poisson*eps1 <= 0.0 ) { /* relaxed in one dimension */ if ( part & ONE_STRESS ) stuff = eps1*eps1/2/4; /* 1/4 fudge factor to agree with other */ } else { /* stressed in both directions */ if ( part & TWO_STRESS ) stuff = coeff1*(c11*c11+c12*c21+c12*c21+c22*c22) + coeff2*(c11+c22)*(c11+c22); } energy = weight*area*stuff; if ( mode == METHOD_VALUE ) return energy; /* gradient */ for ( i = 0 ; i < sdim ; i++ ) /* with respect to space coord */ { for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) for ( m = 0 ; m < web.dimension ; m++ ) dFdv[a][b][m][i] = (m==a ? side[b][i] : 0.0) + (m==b ? side[a][i] : 0.0); } for ( i = sdim ; i < SDIM ; i++ ) /* with respect to ref coord */ { for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) for ( m = 0 ; m < web.dimension ; m++ ) dSdv[a][b][m][i] = (m==a ? side[b][i] : 0.0) + (m==b ? side[a][i] : 0.0); } for ( i = sdim ; i < SDIM ; i++ ) /* with respect to ref coord */ { for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) for ( m = 0 ; m < web.dimension ; m++ ) { REAL sum = 0.0; for ( c = 0 ; c < web.dimension ; c++ ) for ( d = 0 ; d < web.dimension ; d++ ) sum -= Q[a][c]*dSdv[c][d][m][i]*Q[d][b]; dQdv[a][b][m][i] = sum; } } for ( i = 0 ; i < sdim ; i++ ) /* with respect to space coord */ { for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) for ( m = 0 ; m < web.dimension ; m++ ) { REAL sum = 0.0; for ( c = 0 ; c < web.dimension ; c++ ) sum += dFdv[a][c][m][i]*Q[c][b]; dCdv[a][b][m][i] = sum; } } for ( i = sdim ; i < SDIM ; i++ ) /* with respect to ref coord */ { for ( a = 0 ; a < web.dimension ; a++ ) for ( e = 0 ; e < web.dimension ; e++ ) for ( m = 0 ; m < web.dimension ; m++ ) { REAL sum = 0.0; for ( b = 0 ; b < web.dimension ; b++ ) sum += F[a][b]*dQdv[b][e][m][i]; dCdv[a][e][m][i] = sum; } } for ( j = 0 ; j < web.dimension ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) dstuff[j][i] = 0.0; if ( eps2 + poisson*eps1 <= 0.0 ) { /* relaxed in one dimension */ if ( part & ONE_STRESS ) for ( j = 0 ; j < web.dimension ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) { dstuff[j][i] = eps1*0.5/4*(dCdv[0][0][j][i]+dCdv[1][1][j][i] + 0.5/sqrt(TrC*TrC - 4*DetC)*(2*TrC*(dCdv[0][0][j][i]+dCdv[1][1][j][i]) - 4*(c11*dCdv[1][1][j][i] + dCdv[0][0][j][i]*c22 - c12*dCdv[1][0][j][i] - c21*dCdv[0][1][j][i]))); } } else /* stressed in both directions */ { if ( part & TWO_STRESS ) for ( j = 0 ; j < web.dimension ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) { dstuff[j][i] = (coeff1*(2*c11*dCdv[0][0][j][i] + 2*c12*dCdv[1][0][j][i] + 2*c21*dCdv[0][1][j][i] + 2*c22*dCdv[1][1][j][i]) + 2*coeff2*(c11+c22)*(dCdv[0][0][j][i] +dCdv[1][1][j][i])); } } /* reference area change */ for ( i = 0 ; i < SDIM ; i++ ) for ( a = 0 ; a < FACET_VERTS ; a++ ) dadv[a][i] = 0.0; dadv[0][sdim] = side[1][sdim+1]*areasign; dadv[1][sdim+1] = side[0][sdim]*areasign; dadv[0][sdim+1] = -side[1][sdim]*areasign; dadv[1][sdim] = -side[0][sdim+1]*areasign; /* grand finale for gradient */ for ( a = 0 ; a < web.dimension ; a++ ) for ( i = 0 ; i < SDIM ; i++ ) { f_info->grad[a+1][i] = weight*(dadv[a][i]*stuff + area*dstuff[a][i]); f_info->grad[0][i] -= weight*(dadv[a][i]*stuff + area*dstuff[a][i]); } /* combo correction */ for ( i = 0 ; i < SDIM ; i++ ) { f_info->grad[1][i] -= combo*f_info->grad[2][i]; f_info->grad[0][i] += combo*f_info->grad[2][i]; } if ( mode == METHOD_GRADIENT ) return energy; /* hessian */ /* calculate ddFdv and ddSdv, which are same and nonzero only for like coordinates, so coordinate index not used */ for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) for ( m = 0 ; m < web.dimension ; m++ ) for ( n = 0 ; n < web.dimension ; n++ ) { ddFdv[a][b][m][n] = (a==m && b==n ? 1.0 : 0.0) + (a==n && b==m ? 1.0 : 0.0); ddSdv[a][b][m][n] = (a==m && b==n ? 1.0 : 0.0) + (a==n && b==m ? 1.0 : 0.0); } /* calculate ddQdv */ for ( i = sdim ; i < SDIM ; i++ ) for ( j = sdim ; j < SDIM ; j++ ) for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) for ( m = 0 ; m < web.dimension ; m++ ) for ( n = 0 ; n < web.dimension ; n++ ) { REAL sum = 0.0; for ( c = 0 ; c < web.dimension ; c++ ) for ( d = 0 ; d < web.dimension ; d++ ) sum += - dQdv[a][c][m][i]*dSdv[c][d][n][j]*Q[d][b] - ( i==j ? Q[a][c]*ddSdv[c][d][m][n]*Q[d][b] : 0.0 ) - Q[a][c]*dSdv[c][d][m][i]*dQdv[d][b][n][j]; ddQdv[a][b][m][n][i][j] = sum; } /* calculate ddCdv */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) { for ( m = 0 ; m < web.dimension ; m++ ) for ( n = 0 ; n < web.dimension ; n++ ) { REAL sum = 0.0; for ( c = 0 ; c < web.dimension ; c++ ) sum += ( (i==j && i < sdim) ? ddFdv[a][c][m][n]*Q[c][b] : 0.0 ) + ( (i < sdim && j >= sdim) ? dFdv[a][c][m][i]*dQdv[c][b][n][j] : 0.0) + ( (j < sdim && i >= sdim) ? dFdv[a][c][n][j]*dQdv[c][b][m][i] : 0.0) + ( (i >= sdim && j >= sdim) ? F[a][c]*ddQdv[c][b][m][n][i][j] : 0.0); ddCdv[a][b][m][n][i][j] = sum; } } /* calculate ddstuff */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) ddstuff[a][b][i][j] = 0.0; if ( eps2 + poisson*eps1 <= 0.0 ) { /* relaxed in one dimension */ if ( part & ONE_STRESS ) for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) { REAL deps1a,deps1b,disc,ddisca,ddiscb,dddisc,ddeps; REAL dTrCa,dTrCb,dDetCa,dDetCb; dTrCa = dCdv[0][0][a][i]+dCdv[1][1][a][i]; dTrCb = dCdv[0][0][b][j]+dCdv[1][1][b][j]; dDetCa = c11*dCdv[1][1][a][i] + dCdv[0][0][a][i]*c22 - c12*dCdv[1][0][a][i] - c21*dCdv[0][1][a][i]; dDetCb = c11*dCdv[1][1][b][j] + dCdv[0][0][b][j]*c22 - c12*dCdv[1][0][b][j] - c21*dCdv[0][1][b][j]; disc = TrC*TrC - 4*DetC; ddisca = 2*TrC*dTrCa - 4*dDetCa; ddiscb = 2*TrC*dTrCb - 4*dDetCb; deps1a = (dTrCa + 0.5/sqrt(disc)*ddisca)/2; deps1b = (dTrCb + 0.5/sqrt(disc)*ddiscb)/2; dddisc = 2*dTrCa*dTrCb + + 2*TrC*(ddCdv[0][0][a][b][i][j]+ddCdv[1][1][a][b][i][j]) - 4*(dCdv[0][0][b][j]*dCdv[1][1][a][i] + dCdv[0][0][a][i]*dCdv[1][1][b][j] - dCdv[0][1][b][j]*dCdv[1][0][a][i] - dCdv[1][0][b][j]*dCdv[0][1][a][i]) - 4*(c11*ddCdv[1][1][a][b][i][j] + ddCdv[0][0][a][b][i][j]*c22 - c12*ddCdv[1][0][a][b][i][j] - c21*ddCdv[0][1][a][b][i][j]); ddeps = 0.5*(ddCdv[0][0][a][b][i][j]+ddCdv[1][1][a][b][i][j] -.25/sqrt(disc)/disc*ddisca*ddiscb + 0.5/sqrt(disc)*dddisc); ddstuff[a][b][i][j] = (deps1a*deps1b + eps1*ddeps)/4; } } else { if ( part & TWO_STRESS ) for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) { REAL term; term = (coeff1*( 2*c11*ddCdv[0][0][a][b][i][j] + 2*c12*ddCdv[1][0][a][b][i][j] + 2*c21*ddCdv[0][1][a][b][i][j] + 2*c22*ddCdv[1][1][a][b][i][j]) + 2*coeff2*(c11+c22)*(ddCdv[0][0][a][b][i][j]+ddCdv[1][1][a][b][i][j])) + (coeff1*(2*dCdv[0][0][a][i]*dCdv[0][0][b][j] + 2*dCdv[0][1][a][i]*dCdv[1][0][b][j] + 2*dCdv[1][0][a][i]*dCdv[0][1][b][j] + 2*dCdv[1][1][a][i]*dCdv[1][1][b][j] ) + 2*coeff2*(dCdv[0][0][a][i]+dCdv[1][1][a][i]) *(dCdv[0][0][b][j] +dCdv[1][1][b][j])); ddstuff[a][b][i][j] = term; } } /* area hessian */ for ( i = sdim ; i < SDIM ; i++ ) for ( j = sdim ; j < SDIM ; j++ ) for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) ddadv[a][b][i][j] = 0.0; ddadv[0][1][sdim][sdim+1] = areasign; ddadv[1][0][sdim+1][sdim] = areasign; ddadv[0][1][sdim+1][sdim] = -areasign; ddadv[1][0][sdim][sdim+1] = -areasign; /* big Hessian finale */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) { REAL term; term = ( (i >= sdim && j >= sdim) ? ddadv[a][b][i][j]*stuff : 0.0) + dadv[a][i]*dstuff[b][j] + dadv[b][j]*dstuff[a][i] + area*ddstuff[a][b][i][j]; term *= weight; f_info->hess[a+1][b+1][i][j] = term; f_info->hess[a+1][0][i][j] -= term; f_info->hess[0][b+1][i][j] -= term; f_info->hess[0][0][i][j] += term; } /* combo correction */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { REAL term; term = combo*(combo*f_info->hess[2][2][i][j]-f_info->hess[1][2][i][j] -f_info->hess[2][1][i][j]); f_info->hess[1][1][i][j] += term; f_info->hess[0][1][i][j] -= term; f_info->hess[1][0][i][j] -= term; f_info->hess[0][0][i][j] += term; term = combo*f_info->hess[2][2][i][j]; f_info->hess[1][2][i][j] -= term; f_info->hess[0][2][i][j] += term; f_info->hess[1][0][i][j] += term; f_info->hess[0][0][i][j] -= term; term = combo*f_info->hess[2][2][i][j]; f_info->hess[2][1][i][j] -= term; f_info->hess[0][1][i][j] += term; f_info->hess[2][0][i][j] += term; f_info->hess[0][0][i][j] -= term; } return energy; } /************************************************************** * * function: relaxed_elastic_energy() * * purpose: calculates energy of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL relaxed_elastic_energy(f_info) struct qinfo *f_info; { return relaxed_elastic_all(f_info,METHOD_VALUE,ONE_STRESS|TWO_STRESS); } /************************************************************** * * function: relaxed_elastic_gradient() * * purpose: calculates gradient of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL relaxed_elastic_gradient(f_info) struct qinfo *f_info; { return relaxed_elastic_all(f_info,METHOD_GRADIENT,ONE_STRESS|TWO_STRESS); } /************************************************************** * * function: relaxed_elastic_hessian() * * purpose: calculates hessian of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL relaxed_elastic_hessian(f_info) struct qinfo *f_info; { return relaxed_elastic_all(f_info,METHOD_HESSIAN,ONE_STRESS|TWO_STRESS); } /* Partial relaxed energy methods */ REAL relaxed_elastic1_energy(f_info) struct qinfo *f_info; { return relaxed_elastic_all(f_info,METHOD_VALUE,ONE_STRESS); } REAL relaxed_elastic1_gradient(f_info) struct qinfo *f_info; { return relaxed_elastic_all(f_info,METHOD_GRADIENT,ONE_STRESS); } REAL relaxed_elastic1_hessian(f_info) struct qinfo *f_info; { return relaxed_elastic_all(f_info,METHOD_HESSIAN,ONE_STRESS); } REAL relaxed_elastic2_energy(f_info) struct qinfo *f_info; { return relaxed_elastic_all(f_info,METHOD_VALUE,TWO_STRESS); } REAL relaxed_elastic2_gradient(f_info) struct qinfo *f_info; { return relaxed_elastic_all(f_info,METHOD_GRADIENT,TWO_STRESS); } REAL relaxed_elastic2_hessian(f_info) struct qinfo *f_info; { return relaxed_elastic_all(f_info,METHOD_HESSIAN,TWO_STRESS); } /************************************************************************ ************************************************************************* Named method: relaxed_elastic_A Linear elastic strain energy on facets, with fixed reference coordinates manifested as form_factors for each facet. This allows more flexibility in reference coordinates assigned to vertices that are taped together. Revised from linear_elastic to implement Bill Collier's relaxed energy model. Basically, stress is set to 0 when stress would be negative, to model wrinkling. (for Frank Baginski's NASA balloon models) Let S be Gram matrix of unstrained facet (form_factors, dots of sides). Let Q be the inverse of S. Let F be Gram matrix of strained facet. Let C = (FQ-I)/2, the Cauchy-Green strain tensor. Let v be Poisson ratio. Let eps1 and eps2 be the eigenvalues of C. Let sigma1 = eps1+v*eps2 and sigma2 = eps2+v*eps1 be the stress eigenvalues. Then energy density is (1/2/(1+v))(Tr(C^2) + v*(Tr C)^2/(1-(dim-1)*v)) sigma1,sigma2 >= 0 eps1^2/2 sigma2 < 0, eps1 > 0 eps2^2/2 sigma1 < 0, eps2 > 0 0 eps2 < 0, eps1 < 0. Each facet has extra attribute poisson_ratio for v. ************************************************************************/ /************************************************************************ * * function: relaxed_elastic_A_init() * * purpose: Make sure needed extra attributes are present. * * */ void relaxed_elastic_A_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { if ( web.modeltype != LINEAR ) kb_error(2163,"relaxed_elastic_A method only for LINEAR model.\n",RECOVERABLE); if ( web.dimension != 2 ) kb_error(2164,"relaxed_elastic_A method only for SOAPFILM model.\n",RECOVERABLE); /* extra facet attribute for poisson_ratio */ poisson_attr = find_attribute(FACET,POISSON_NAME); if ( poisson_attr < 0 ) /* not found */ kb_error(2165,"Facet extra attribute poisson_ratio missing. Needed by relaxed_elastic_A.\n",RECOVERABLE); form_factors_attr = find_attribute(FACET,FORM_FACTORS_NAME); if ( form_factors_attr < 0 ) /* not found */ kb_error(2166,"Facet extra attribute form_factors real[3] missing. Needed by relaxed_elastic_A.\n",RECOVERABLE); if ( EXTRAS(FACET)[form_factors_attr].array_spec.datacount != 3 ) kb_error(2167,"Facet extra attribute form_factors must have size 3.\n", RECOVERABLE); LEBweight_attr = find_attribute(FACET,LEBWEIGHT_NAME); /* optional */ } /************************************************************************ * * function: relaxed_elastic_A_all() * * purpose: energy, gradient, and hessian for relaxed_elastic_A_method. */ REAL relaxed_elastic_A_all ARGS((struct qinfo *,int,int)); #define ONE_STRESS 1 #define TWO_STRESS 2 REAL relaxed_elastic_A_all(f_info,mode,part) struct qinfo *f_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ int part; /* flag for one stress, two stress, or both parts */ { REAL q11,q12,q22; /* Q entries */ REAL Q[2][2]; REAL det; /* det S */ REAL area; /* reference area of facet */ REAL poisson; /* poisson ratio */ REAL f11,f12,f22; REAL c11,c12,c21,c22; REAL energy; REAL stuff; REAL dstuff[FACET_VERTS][MAXCOORD]; REAL dCdv[2][2][FACET_VERTS][MAXCOORD]; REAL dFdv[2][2][FACET_VERTS][MAXCOORD]; REAL ddFdv[2][2][2][2]; REAL ddCdv[2][2][FACET_VERTS][FACET_VERTS][MAXCOORD][MAXCOORD]; REAL ddstuff[FACET_VERTS][FACET_VERTS][MAXCOORD][MAXCOORD]; int i,j,m,n,a,b,c; REAL coeff1,coeff2; REAL *s; /* form factors */ int sdim = SDIM; /* true space dimension */ REAL weight; /* facet weighting factor */ REAL **side; /* sides of facet */ REAL eps1,eps2; /* strain eigenvalues */ REAL TrC,DetC; /* trace and determinant of C */ poisson = *(REAL*)get_extra(f_info->id,poisson_attr); if ( LEBweight_attr >= 0 ) weight = *(REAL*)get_extra(f_info->id,LEBweight_attr); else weight = 1.0; s = (REAL*)get_extra(f_info->id,form_factors_attr); det = s[0]*s[2] - s[1]*s[1]; if ( det <= 0.0 ) { if ( mode == METHOD_VALUE ) return 0.0; sprintf(errmsg,"relaxed_elastic_A: Facet %s has unstrained area 0.\n", ELNAME(f_info->id)); kb_error(2168,errmsg,RECOVERABLE); } area = sqrt(det)/2; coeff1 = 1.0/8/(1 + poisson); coeff2 = coeff1*poisson/(1 - (web.dimension-1)*poisson); Q[0][0] = q11 = s[2]/det; Q[0][1] = Q[1][0] = q12 = -s[1]/det; Q[1][1] = q22 = s[0]/det; side = f_info->sides[0]; f11 = dot(side[0],side[0],sdim); f12 = dot(side[0],side[1],sdim); f22 = dot(side[1],side[1],sdim); c11 = f11*q11 + f12*q12 - 1; c12 = f11*q12 + f12*q22; c21 = f12*q11 + f22*q12; c22 = f12*q12 + f22*q22 - 1; TrC = c11 + c22; DetC = c11*c22 - c12*c21; eps1 = (TrC + sqrt(TrC*TrC - 4*DetC))/2; eps2 = (TrC - sqrt(TrC*TrC - 4*DetC))/2; if ( eps1 <= 0.0 ) return 0.0; /* relaxed in both directions */ stuff = 0.0; if ( eps2 + poisson*eps1 <= 0.0 ) { /* relaxed in one dimension */ if ( part & ONE_STRESS ) stuff = eps1*eps1/2/4; /* 1/4 fudge factor to agree with other */ } else { /* stressed in both directions */ if ( part & TWO_STRESS ) stuff = coeff1*(c11*c11+c12*c21+c12*c21+c22*c22) + coeff2*(c11+c22)*(c11+c22); } energy = weight*area*stuff; if ( mode == METHOD_VALUE ) return energy; /* gradient */ for ( i = 0 ; i < sdim ; i++ ) /* with respect to space coord */ { for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) for ( m = 0 ; m < web.dimension ; m++ ) dFdv[a][b][m][i] = (m==a ? side[b][i] : 0.0) + (m==b ? side[a][i] : 0.0); } for ( i = 0 ; i < sdim ; i++ ) /* with respect to space coord */ { for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) for ( m = 0 ; m < web.dimension ; m++ ) { REAL sum = 0.0; for ( c = 0 ; c < web.dimension ; c++ ) sum += dFdv[a][c][m][i]*Q[c][b]; dCdv[a][b][m][i] = sum; } } for ( j = 0 ; j < web.dimension ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) dstuff[j][i] = 0.0; if ( eps2 + poisson*eps1 <= 0.0 ) { /* relaxed in one dimension */ if ( part & ONE_STRESS ) for ( j = 0 ; j < web.dimension ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) { dstuff[j][i] = eps1*0.5/4*(dCdv[0][0][j][i]+dCdv[1][1][j][i] + 0.5/sqrt(TrC*TrC - 4*DetC)*(2*TrC*(dCdv[0][0][j][i]+dCdv[1][1][j][i]) - 4*(c11*dCdv[1][1][j][i] + dCdv[0][0][j][i]*c22 - c12*dCdv[1][0][j][i] - c21*dCdv[0][1][j][i]))); } } else /* stressed in both directions */ { if ( part & TWO_STRESS ) for ( j = 0 ; j < web.dimension ; j++ ) for ( i = 0 ; i < SDIM ; i++ ) { dstuff[j][i] = (coeff1*(2*c11*dCdv[0][0][j][i] + 2*c12*dCdv[1][0][j][i] + 2*c21*dCdv[0][1][j][i] + 2*c22*dCdv[1][1][j][i]) + 2*coeff2*(c11+c22)*(dCdv[0][0][j][i] +dCdv[1][1][j][i])); } } /* grand finale for gradient */ for ( a = 0 ; a < web.dimension ; a++ ) for ( i = 0 ; i < SDIM ; i++ ) { f_info->grad[a+1][i] = weight*area*dstuff[a][i]; f_info->grad[0][i] -= weight*area*dstuff[a][i]; } if ( mode == METHOD_GRADIENT ) return energy; /* hessian */ /* calculate ddFdv */ for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) for ( m = 0 ; m < web.dimension ; m++ ) for ( n = 0 ; n < web.dimension ; n++ ) { ddFdv[a][b][m][n] = (a==m && b==n ? 1.0 : 0.0) + (a==n && b==m ? 1.0 : 0.0); } /* calculate ddCdv */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) { for ( m = 0 ; m < web.dimension ; m++ ) for ( n = 0 ; n < web.dimension ; n++ ) { REAL sum = 0.0; for ( c = 0 ; c < web.dimension ; c++ ) sum += ( i==j ? ddFdv[a][c][m][n]*Q[c][b] : 0.0 ); ddCdv[a][b][m][n][i][j] = sum; } } /* calculate ddstuff */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) ddstuff[a][b][i][j] = 0.0; if ( eps2 + poisson*eps1 <= 0.0 ) { /* relaxed in one dimension */ if ( part & ONE_STRESS ) for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) { REAL deps1a,deps1b,disc,ddisca,ddiscb,dddisc,ddeps; REAL dTrCa,dTrCb,dDetCa,dDetCb; dTrCa = dCdv[0][0][a][i]+dCdv[1][1][a][i]; dTrCb = dCdv[0][0][b][j]+dCdv[1][1][b][j]; dDetCa = c11*dCdv[1][1][a][i] + dCdv[0][0][a][i]*c22 - c12*dCdv[1][0][a][i] - c21*dCdv[0][1][a][i]; dDetCb = c11*dCdv[1][1][b][j] + dCdv[0][0][b][j]*c22 - c12*dCdv[1][0][b][j] - c21*dCdv[0][1][b][j]; disc = TrC*TrC - 4*DetC; ddisca = 2*TrC*dTrCa - 4*dDetCa; ddiscb = 2*TrC*dTrCb - 4*dDetCb; deps1a = (dTrCa + 0.5/sqrt(disc)*ddisca)/2; deps1b = (dTrCb + 0.5/sqrt(disc)*ddiscb)/2; dddisc = 2*dTrCa*dTrCb + + 2*TrC*(ddCdv[0][0][a][b][i][j]+ddCdv[1][1][a][b][i][j]) - 4*(dCdv[0][0][b][j]*dCdv[1][1][a][i] + dCdv[0][0][a][i]*dCdv[1][1][b][j] - dCdv[0][1][b][j]*dCdv[1][0][a][i] - dCdv[1][0][b][j]*dCdv[0][1][a][i]) - 4*(c11*ddCdv[1][1][a][b][i][j] + ddCdv[0][0][a][b][i][j]*c22 - c12*ddCdv[1][0][a][b][i][j] - c21*ddCdv[0][1][a][b][i][j]); ddeps = 0.5*(ddCdv[0][0][a][b][i][j]+ddCdv[1][1][a][b][i][j] -.25/sqrt(disc)/disc*ddisca*ddiscb + 0.5/sqrt(disc)*dddisc); ddstuff[a][b][i][j] = (deps1a*deps1b + eps1*ddeps)/4; } } else { if ( part & TWO_STRESS ) for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) { REAL term; term = (coeff1*( 2*c11*ddCdv[0][0][a][b][i][j] + 2*c12*ddCdv[1][0][a][b][i][j] + 2*c21*ddCdv[0][1][a][b][i][j] + 2*c22*ddCdv[1][1][a][b][i][j]) + 2*coeff2*(c11+c22)*(ddCdv[0][0][a][b][i][j]+ddCdv[1][1][a][b][i][j])) + (coeff1*(2*dCdv[0][0][a][i]*dCdv[0][0][b][j] + 2*dCdv[0][1][a][i]*dCdv[1][0][b][j] + 2*dCdv[1][0][a][i]*dCdv[0][1][b][j] + 2*dCdv[1][1][a][i]*dCdv[1][1][b][j] ) + 2*coeff2*(dCdv[0][0][a][i]+dCdv[1][1][a][i]) *(dCdv[0][0][b][j] +dCdv[1][1][b][j])); ddstuff[a][b][i][j] = term; } } /* big Hessian finale */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) { REAL term; term = area*ddstuff[a][b][i][j]; term *= weight; f_info->hess[a+1][b+1][i][j] = term; f_info->hess[a+1][0][i][j] -= term; f_info->hess[0][b+1][i][j] -= term; f_info->hess[0][0][i][j] += term; } return energy; } /************************************************************** * * function: relaxed_elastic_A_energy() * * purpose: calculates energy of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL relaxed_elastic_A_energy(f_info) struct qinfo *f_info; { return relaxed_elastic_A_all(f_info,METHOD_VALUE,ONE_STRESS|TWO_STRESS); } /************************************************************** * * function: relaxed_elastic_A_gradient() * * purpose: calculates gradient of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL relaxed_elastic_A_gradient(f_info) struct qinfo *f_info; { return relaxed_elastic_A_all(f_info,METHOD_GRADIENT,ONE_STRESS|TWO_STRESS); } /************************************************************** * * function: relaxed_elastic_A_hessian() * * purpose: calculates hessian of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL relaxed_elastic_A_hessian(f_info) struct qinfo *f_info; { return relaxed_elastic_A_all(f_info,METHOD_HESSIAN,ONE_STRESS|TWO_STRESS); } /* Partial relaxed energy methods */ REAL relaxed_elastic1_A_energy(f_info) struct qinfo *f_info; { return relaxed_elastic_A_all(f_info,METHOD_VALUE,ONE_STRESS); } REAL relaxed_elastic1_A_gradient(f_info) struct qinfo *f_info; { return relaxed_elastic_A_all(f_info,METHOD_GRADIENT,ONE_STRESS); } REAL relaxed_elastic1_A_hessian(f_info) struct qinfo *f_info; { return relaxed_elastic_A_all(f_info,METHOD_HESSIAN,ONE_STRESS); } REAL relaxed_elastic2_A_energy(f_info) struct qinfo *f_info; { return relaxed_elastic_A_all(f_info,METHOD_VALUE,TWO_STRESS); } REAL relaxed_elastic2_A_gradient(f_info) struct qinfo *f_info; { return relaxed_elastic_A_all(f_info,METHOD_GRADIENT,TWO_STRESS); } REAL relaxed_elastic2_A_hessian(f_info) struct qinfo *f_info; { return relaxed_elastic_A_all(f_info,METHOD_HESSIAN,TWO_STRESS); } /*************************************************************************** **************************************************************************** Named Method dihedral_hooke Energy is edge length times square of angle between normals of adjacent facets. Actually, e = (1 - cos(angle))*length. ***************************************************************************/ /******************************************************************** * * Function: dihedral_hooke_energy() * * Purpose: Does energy calculation for an edge. * */ REAL dihedral_hooke_energy(e_info) struct qinfo *e_info; { REAL s1[MAXCOORD],s2[MAXCOORD],t2[MAXCOORD]; REAL s1s1,s1s2,s1t2,s2s2,t2t2,s2t2; REAL a1,a2; REAL det; facetedge_id fe_s1,fe_s2,fe_t2; edge_id e_id = e_info->id; REAL cos_th; /* cosine of angle between facets */ /* get edge vectors away from tail vertex */ fe_s1 = get_edge_fe(e_id); fe_s2 = get_prev_edge(get_next_facet(fe_s1)); fe_s2 = inverse_id(fe_s2); fe_t2 = get_prev_edge(fe_s1); fe_t2 = inverse_id(fe_t2); get_fe_side(fe_s1,s1); get_fe_side(fe_s2,s2); get_fe_side(fe_t2,t2); s1s1 = SDIM_dot(s1,s1); s1s2 = SDIM_dot(s1,s2); s1t2 = SDIM_dot(s1,t2); t2t2 = SDIM_dot(t2,t2); s2s2 = SDIM_dot(s2,s2); s2t2 = SDIM_dot(s2,t2); det = s1s1*t2t2 - s1t2*s1t2; a1 = sqrt(det); det = s1s1*s2s2 - s1s2*s1s2; a2 = sqrt(det); cos_th = (s1s2*s1t2 - s2t2*s1s1)/a1/a2; return sqrt(s1s1)*(1 - cos_th); } /************************************************************************ * * Function: dihedral_hooke_grad() * * Purpose: Does gradient calculation. * */ REAL dihedral_hooke_grad(e_info) struct qinfo *e_info; { REAL s1[MAXCOORD],s2[MAXCOORD],t2[MAXCOORD]; REAL s1s1,s1s2,s1t2,s2s2,t2t2,s2t2; REAL a1,a2; REAL det; facetedge_id fe_s1,fe_s2,fe_t2; edge_id e_id = e_info->id; REAL cos_th; /* cosine of angle between facets */ REAL dcosds1[MAXCOORD],dcosds2[MAXCOORD],dcosdt2[MAXCOORD]; REAL da1ds1[MAXCOORD], da1dt2[MAXCOORD]; REAL da2ds1[MAXCOORD], da2ds2[MAXCOORD]; int i; /* get edge vectors away from tail vertex */ fe_s1 = get_edge_fe(e_id); fe_s2 = get_prev_edge(get_next_facet(fe_s1)); fe_s2 = inverse_id(fe_s2); fe_t2 = get_prev_edge(fe_s1); fe_t2 = inverse_id(fe_t2); get_fe_side(fe_s1,s1); get_fe_side(fe_s2,s2); get_fe_side(fe_t2,t2); s1s1 = SDIM_dot(s1,s1); s1s2 = SDIM_dot(s1,s2); s1t2 = SDIM_dot(s1,t2); t2t2 = SDIM_dot(t2,t2); s2s2 = SDIM_dot(s2,s2); s2t2 = SDIM_dot(s2,t2); det = s1s1*t2t2 - s1t2*s1t2; a1 = sqrt(det); det = s1s1*s2s2 - s1s2*s1s2; a2 = sqrt(det); cos_th = (s1s2*s1t2 - s2t2*s1s1)/a1/a2; /* gradients of various terms */ for ( i = 0 ; i < SDIM ; i++ ) { da1ds1[i] = 0.5/a1*(2*s1[i]*t2t2 - 2*s1t2*t2[i]); da1dt2[i] = 0.5/a1*(2*s1s1*t2[i] - 2*s1t2*s1[i]); da2ds1[i] = 0.5/a2*(2*s1[i]*s2s2 - 2*s1s2*s2[i]); da2ds2[i] = 0.5/a2*(2*s1s1*s2[i] - 2*s1s2*s1[i]); dcosds1[i] = (s2[i]*s1t2 + s1s2*t2[i] - 2*s2t2*s1[i])/a1/a2 - cos_th/a1*da1ds1[i] - cos_th/a2*da2ds1[i]; dcosdt2[i] = (s1s2*s1[i] - s2[i]*s1s1)/a1/a2 - cos_th/a1*da1dt2[i]; dcosds2[i] = (s1[i]*s1t2 - t2[i]*s1s1)/a1/a2 - cos_th/a2*da2ds2[i]; } for ( i = 0 ; i < SDIM ; i++ ) { REAL f; /* part of force */ f = s1[i]*(1 - cos_th)/sqrt(s1s1); e_info->grad[1][i] += f; e_info->grad[0][i] -= f; f = -sqrt(s1s1)*dcosds1[i]; e_info->grad[1][i] += f; e_info->grad[0][i] -= f; f = -sqrt(s1s1)*dcosds2[i]; e_info->grad[3][i] += f; e_info->grad[0][i] -= f; f = -sqrt(s1s1)*dcosdt2[i]; e_info->grad[2][i] += f; e_info->grad[0][i] -= f; } return sqrt(s1s1)*(1 - cos_th); } /************************************************************************ * * Function: dihedral_hooke_hess() * * Purpose: Does gradient and hessian calculation. * */ static REAL Gtemp[6]; static REAL Ltemp[6][6]; static REAL Htemp[6][6]; static int Lcount; void gradList ARGS((REAL,REAL,REAL,REAL,REAL,REAL)); void LList ARGS((REAL*,REAL*,REAL*,REAL*,REAL*,REAL*)); REAL *List ARGS((REAL,REAL,REAL,REAL,REAL,REAL)); /* utility functions for deciphering Mathematica output */ void gradList(a,b,c,d,e,f) REAL a,b,c,d,e,f; { Gtemp[0] = a; Gtemp[1] = b; Gtemp[2] = c; Gtemp[3] = d; Gtemp[4] = e; Gtemp[5] = f; } void LList(a,b,c,d,e,f) REAL *a,*b,*c,*d,*e,*f; { int i; for ( i = 0 ; i < 6 ; i++ ) { Htemp[0][i] = a[i]; Htemp[1][i] = b[i]; Htemp[2][i] = c[i]; Htemp[3][i] = d[i]; Htemp[4][i] = e[i]; Htemp[5][i] = f[i]; } } REAL * List(a,b,c,d,e,f) REAL a,b,c,d,e,f; { Ltemp[Lcount][0] = a; Ltemp[Lcount][1] = b; Ltemp[Lcount][2] = c; Ltemp[Lcount][3] = d; Ltemp[Lcount][4] = e; Ltemp[Lcount][5] = f; return Ltemp[Lcount++]; } /* This has a really long expression in it, which some compilers may not be able to handle. If so, just use gcc instead of cc, or #ifdef out the bulk of the function, since you're probably not going to use it anyway. */ REAL dihedral_hooke_hess(e_info) struct qinfo *e_info; { #ifndef MAC_CW int i,j,k,n,jj,nn; REAL s1[MAXCOORD],s2[MAXCOORD],t2[MAXCOORD]; REAL s1s1,s1s2,s1t2,s2s2,t2t2,s2t2; REAL a1,a2; REAL det; facetedge_id fe_s1,fe_s2,fe_t2; edge_id e_id = e_info->id; REAL cos_th; /* cosine of angle between facets */ REAL dcosds1[MAXCOORD],dcosds2[MAXCOORD],dcosdt2[MAXCOORD]; REAL da1ds1[MAXCOORD], da1dt2[MAXCOORD]; REAL da2ds1[MAXCOORD], da2ds2[MAXCOORD]; REAL dqdx[6][3][MAXCOORD]; REAL ddqdxx[6][3][3]; /* get edge vectors away from tail vertex */ fe_s1 = get_edge_fe(e_id); fe_s2 = get_prev_edge(get_next_facet(fe_s1)); fe_s2 = inverse_id(fe_s2); fe_t2 = get_prev_edge(fe_s1); fe_t2 = inverse_id(fe_t2); get_fe_side(fe_s1,s1); get_fe_side(fe_s2,s2); get_fe_side(fe_t2,t2); s1s1 = SDIM_dot(s1,s1); s1s2 = SDIM_dot(s1,s2); s1t2 = SDIM_dot(s1,t2); t2t2 = SDIM_dot(t2,t2); s2s2 = SDIM_dot(s2,s2); s2t2 = SDIM_dot(s2,t2); det = s1s1*t2t2 - s1t2*s1t2; a1 = sqrt(det); det = s1s1*s2s2 - s1s2*s1s2; a2 = sqrt(det); cos_th = (s1s2*s1t2 - s2t2*s1s1)/a1/a2; /* gradients of various terms */ for ( i = 0 ; i < SDIM ; i++ ) { da1ds1[i] = 0.5/a1*(2*s1[i]*t2t2 - 2*s1t2*t2[i]); da1dt2[i] = 0.5/a1*(2*s1s1*t2[i] - 2*s1t2*s1[i]); da2ds1[i] = 0.5/a2*(2*s1[i]*s2s2 - 2*s1s2*s2[i]); da2ds2[i] = 0.5/a2*(2*s1s1*s2[i] - 2*s1s2*s1[i]); dcosds1[i] = (s2[i]*s1t2 + s1s2*t2[i] - 2*s2t2*s1[i])/a1/a2 - cos_th/a1*da1ds1[i] - cos_th/a2*da2ds1[i]; dcosdt2[i] = (s1s2*s1[i] - s2[i]*s1s1)/a1/a2 - cos_th/a1*da1dt2[i]; dcosds2[i] = (s1[i]*s1t2 - t2[i]*s1s1)/a1/a2 - cos_th/a2*da2ds2[i]; } /* gradients */ for ( i = 0 ; i < SDIM ; i++ ) { REAL f; /* part of force */ f = s1[i]*(1 - cos_th)/sqrt(s1s1); e_info->grad[1][i] += f; e_info->grad[0][i] -= f; f = -sqrt(s1s1)*dcosds1[i]; e_info->grad[1][i] += f; e_info->grad[0][i] -= f; f = -sqrt(s1s1)*dcosds2[i]; e_info->grad[3][i] += f; e_info->grad[0][i] -= f; f = -sqrt(s1s1)*dcosdt2[i]; e_info->grad[2][i] += f; e_info->grad[0][i] -= f; } /* hessian */ /* from Mathematica */ #define Sqrt(a) sqrt(a) #define Power(a,b) pow(a,(REAL)(b)) gradList(Sqrt(s1s1)*(((s1s2*s1t2 - s1s1*s2t2)*t2t2)/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) + s2t2/(Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)) + (s2s2*(s1s2*s1t2 - s1s1*s2t2))/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2))) + (1 - (s1s2*s1t2 - s1s1*s2t2)/ (Sqrt(-Power(s1s2,2) + s1s1*s2s2)*Sqrt(-Power(s1t2,2) + s1s1*t2t2)))/ (2.*Sqrt(s1s1)),Sqrt(s1s1)* (-(s1t2/(Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Sqrt(-Power(s1t2,2) + s1s1*t2t2))) - (s1s2*(s1s2*s1t2 - s1s1*s2t2))/ (Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2))), Sqrt(s1s1)*(-((s1t2*(s1s2*s1t2 - s1s1*s2t2))/ (Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5))) - s1s2/(Sqrt(-Power(s1s2,2) + s1s1*s2s2)*Sqrt(-Power(s1t2,2) + s1s1*t2t2)) ),(Power(s1s1,1.5)*(s1s2*s1t2 - s1s1*s2t2))/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)), Power(s1s1,1.5)/ (Sqrt(-Power(s1s2,2) + s1s1*s2s2)*Sqrt(-Power(s1t2,2) + s1s1*t2t2)), (Power(s1s1,1.5)*(s1s2*s1t2 - s1s1*s2t2))/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5))); Lcount = 0; LList(List(Sqrt(s1s1)*((-3*(s1s2*s1t2 - s1s1*s2t2)*Power(t2t2,2))/ (4.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,2.5)) - (s2t2*t2t2)/ (Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) - (s2s2*(s1s2*s1t2 - s1s1*s2t2)*t2t2)/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) - (s2s2*s2t2)/ (Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)) - (3*Power(s2s2,2)*(s1s2*s1t2 - s1s1*s2t2))/ (4.*Power(-Power(s1s2,2) + s1s1*s2s2,2.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2))) + (((s1s2*s1t2 - s1s1*s2t2)*t2t2)/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) + s2t2/(Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)) + (s2s2*(s1s2*s1t2 - s1s1*s2t2))/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)))/Sqrt(s1s1) - (1 - (s1s2*s1t2 - s1s1*s2t2)/ (Sqrt(-Power(s1s2,2) + s1s1*s2s2)*Sqrt(-Power(s1t2,2) + s1s1*t2t2)))/ (4.*Power(s1s1,1.5)),Sqrt(s1s1)* ((s1t2*t2t2)/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) + (s1s2*(s1s2*s1t2 - s1s1*s2t2)*t2t2)/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) + (s1t2*s2s2)/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)) + (s1s2*s2t2)/ (Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)) + (3*s1s2*s2s2*(s1s2*s1t2 - s1s1*s2t2))/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,2.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2))) + (-(s1t2/(Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Sqrt(-Power(s1t2,2) + s1s1*t2t2))) - (s1s2*(s1s2*s1t2 - s1s1*s2t2))/ (Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)))/(2.*Sqrt(s1s1)), Sqrt(s1s1)*((3*s1t2*(s1s2*s1t2 - s1s1*s2t2)*t2t2)/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,2.5)) + (s1t2*s2t2)/ (Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) + (s1t2*s2s2*(s1s2*s1t2 - s1s1*s2t2))/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) + (s1s2*t2t2)/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) + (s1s2*s2s2)/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2))) + (-((s1t2*(s1s2*s1t2 - s1s1*s2t2))/ (Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5))) - s1s2/(Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)))/(2.*Sqrt(s1s1)), -(Power(s1s1,1.5)*(s1s2*s1t2 - s1s1*s2t2)*t2t2)/ (4.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) - (Power(s1s1,1.5)*s2t2)/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)) - (3*Power(s1s1,1.5)*s2s2*(s1s2*s1t2 - s1s1*s2t2))/ (4.*Power(-Power(s1s2,2) + s1s1*s2s2,2.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)) + (3*Sqrt(s1s1)*(s1s2*s1t2 - s1s1*s2t2))/ (4.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)), -(Power(s1s1,1.5)*t2t2)/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) - (Power(s1s1,1.5)*s2s2)/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)) + (3*Sqrt(s1s1))/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)*Sqrt(-Power(s1t2,2) + s1s1*t2t2)), (-3*Power(s1s1,1.5)*(s1s2*s1t2 - s1s1*s2t2)*t2t2)/ (4.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,2.5)) - (Power(s1s1,1.5)*s2t2)/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) - (Power(s1s1,1.5)*s2s2*(s1s2*s1t2 - s1s1*s2t2))/ (4.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) + (3*Sqrt(s1s1)*(s1s2*s1t2 - s1s1*s2t2))/ (4.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5))), List(Sqrt(s1s1)*((s1t2*t2t2)/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) + (s1s2*(s1s2*s1t2 - s1s1*s2t2)*t2t2)/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) + (s1t2*s2s2)/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)) + (s1s2*s2t2)/ (Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)) + (3*s1s2*s2s2*(s1s2*s1t2 - s1s1*s2t2))/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,2.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2))) + (-(s1t2/(Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Sqrt(-Power(s1t2,2) + s1s1*t2t2))) - (s1s2*(s1s2*s1t2 - s1s1*s2t2))/ (Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)))/(2.*Sqrt(s1s1)), Sqrt(s1s1)*((-2*s1s2*s1t2)/ (Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)) - (3*Power(s1s2,2)*(s1s2*s1t2 - s1s1*s2t2))/ (Power(-Power(s1s2,2) + s1s1*s2s2,2.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)) - (s1s2*s1t2 - s1s1*s2t2)/ (Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2))), Sqrt(s1s1)*(-(Power(s1t2,2)/ (Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5))) - (s1s2*s1t2*(s1s2*s1t2 - s1s1*s2t2))/ (Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) - Power(s1s2,2)/ (Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)) - 1/(Sqrt(-Power(s1s2,2) + s1s1*s2s2)*Sqrt(-Power(s1t2,2) + s1s1*t2t2))), (Power(s1s1,1.5)*s1t2)/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)) + (3*Power(s1s1,1.5)*s1s2*(s1s2*s1t2 - s1s1*s2t2))/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,2.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)), (Power(s1s1,1.5)*s1s2)/ (Power(-Power(s1s2,2) + s1s1*s2s2,1.5)*Sqrt(-Power(s1t2,2) + s1s1*t2t2)), (Power(s1s1,1.5)*s1t2)/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) + (Power(s1s1,1.5)*s1s2*(s1s2*s1t2 - s1s1*s2t2))/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5))), List(Sqrt(s1s1)*((3*s1t2*(s1s2*s1t2 - s1s1*s2t2)*t2t2)/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,2.5)) + (s1t2*s2t2)/ (Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) + (s1t2*s2s2*(s1s2*s1t2 - s1s1*s2t2))/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) + (s1s2*t2t2)/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) + (s1s2*s2s2)/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2))) + (-((s1t2*(s1s2*s1t2 - s1s1*s2t2))/ (Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5))) - s1s2/(Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)))/(2.*Sqrt(s1s1)), Sqrt(s1s1)*(-(Power(s1t2,2)/ (Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5))) - (s1s2*s1t2*(s1s2*s1t2 - s1s1*s2t2))/ (Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) - Power(s1s2,2)/ (Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)) - 1/(Sqrt(-Power(s1s2,2) + s1s1*s2s2)*Sqrt(-Power(s1t2,2) + s1s1*t2t2))), Sqrt(s1s1)*((-3*Power(s1t2,2)*(s1s2*s1t2 - s1s1*s2t2))/ (Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,2.5)) - (2*s1s2*s1t2)/ (Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) - (s1s2*s1t2 - s1s1*s2t2)/ (Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5))), (Power(s1s1,1.5)*s1t2*(s1s2*s1t2 - s1s1*s2t2))/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) + (Power(s1s1,1.5)*s1s2)/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)), (Power(s1s1,1.5)*s1t2)/ (Sqrt(-Power(s1s2,2) + s1s1*s2s2)*Power(-Power(s1t2,2) + s1s1*t2t2,1.5)), (3*Power(s1s1,1.5)*s1t2*(s1s2*s1t2 - s1s1*s2t2))/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,2.5)) + (Power(s1s1,1.5)*s1s2)/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5))), List((Sqrt(s1s1)*(s1s2*s1t2 - s1s1*s2t2))/ (4.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)) + Sqrt(s1s1)*(-(s1s1*(s1s2*s1t2 - s1s1*s2t2)*t2t2)/ (4.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) - (s1s1*s2t2)/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)) - (3*s1s1*s2s2*(s1s2*s1t2 - s1s1*s2t2))/ (4.*Power(-Power(s1s2,2) + s1s1*s2s2,2.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)) + (s1s2*s1t2 - s1s1*s2t2)/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2))), Sqrt(s1s1)*((s1s1*s1t2)/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)) + (3*s1s1*s1s2*(s1s2*s1t2 - s1s1*s2t2))/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,2.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2))), Sqrt(s1s1)*((s1s1*s1t2*(s1s2*s1t2 - s1s1*s2t2))/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) + (s1s1*s1s2)/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2))), (-3*Power(s1s1,2.5)*(s1s2*s1t2 - s1s1*s2t2))/ (4.*Power(-Power(s1s2,2) + s1s1*s2s2,2.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)), -Power(s1s1,2.5)/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)), -(Power(s1s1,2.5)*(s1s2*s1t2 - s1s1*s2t2))/ (4.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5))), List(Sqrt(s1s1)/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)*Sqrt(-Power(s1t2,2) + s1s1*t2t2)) + Sqrt(s1s1)*(-(s1s1*t2t2)/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) - (s1s1*s2s2)/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)) + 1/(Sqrt(-Power(s1s2,2) + s1s1*s2s2)*Sqrt(-Power(s1t2,2) + s1s1*t2t2))) ,(Power(s1s1,1.5)*s1s2)/ (Power(-Power(s1s2,2) + s1s1*s2s2,1.5)*Sqrt(-Power(s1t2,2) + s1s1*t2t2)), (Power(s1s1,1.5)*s1t2)/ (Sqrt(-Power(s1s2,2) + s1s1*s2s2)*Power(-Power(s1t2,2) + s1s1*t2t2,1.5)), -Power(s1s1,2.5)/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Sqrt(-Power(s1t2,2) + s1s1*t2t2)),0, -Power(s1s1,2.5)/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5))), List((Sqrt(s1s1)*(s1s2*s1t2 - s1s1*s2t2))/ (4.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) + Sqrt(s1s1)*((-3*s1s1*(s1s2*s1t2 - s1s1*s2t2)*t2t2)/ (4.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,2.5)) - (s1s1*s2t2)/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) - (s1s1*s2s2*(s1s2*s1t2 - s1s1*s2t2))/ (4.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) + (s1s2*s1t2 - s1s1*s2t2)/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5))), Sqrt(s1s1)*((s1s1*s1t2)/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)) + (s1s1*s1s2*(s1s2*s1t2 - s1s1*s2t2))/ (2.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5))), Sqrt(s1s1)*((3*s1s1*s1t2*(s1s2*s1t2 - s1s1*s2t2))/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,2.5)) + (s1s1*s1s2)/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5))), -(Power(s1s1,2.5)*(s1s2*s1t2 - s1s1*s2t2))/ (4.*Power(-Power(s1s2,2) + s1s1*s2s2,1.5)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)), -Power(s1s1,2.5)/ (2.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,1.5)), (-3*Power(s1s1,2.5)*(s1s2*s1t2 - s1s1*s2t2))/ (4.*Sqrt(-Power(s1s2,2) + s1s1*s2s2)* Power(-Power(s1t2,2) + s1s1*t2t2,2.5)))); #define S1S1 0 #define S1S2 1 #define S1T2 2 #define S2S2 3 #define S2T2 4 #define T2T2 5 #define S1 0 #define T2 1 #define S2 2 /* chain rule intermediate partials */ for ( i = 0 ; i < 6 ; i++ ) for ( j = 0 ; j < 3 ; j++ ) for ( k = 0 ; k < SDIM ; k++ ) dqdx[i][j][k] = 0.0; for ( i = 0 ; i < SDIM ; i++ ) { dqdx[S1S1][S1][i] = 2*s1[i]; dqdx[S1S2][S1][i] = s2[i]; dqdx[S1S2][S2][i] = s1[i]; dqdx[S1T2][S1][i] = t2[i]; dqdx[S1T2][T2][i] = s1[i]; dqdx[S2S2][S2][i] = 2*s2[i]; dqdx[S2T2][S2][i] = t2[i]; dqdx[S2T2][T2][i] = s2[i]; dqdx[T2T2][T2][i] = 2*t2[i]; } for ( i = 0 ; i < 6 ; i++ ) for ( j = 0 ; j < 3 ; j++ ) for ( k = 0 ; k < 3 ; k++ ) ddqdxx[i][j][k] = 0.0; ddqdxx[S1S1][S1][S1] = 2.0; ddqdxx[S1S2][S1][S2] = 1.0; ddqdxx[S1S2][S2][S1] = 1.0; ddqdxx[S1T2][S1][T2] = 1.0; ddqdxx[S1T2][T2][S1] = 1.0; ddqdxx[S2S2][S2][S2] = 2.0; ddqdxx[S2T2][S2][T2] = 1.0; ddqdxx[S2T2][T2][S2] = 1.0; ddqdxx[T2T2][T2][T2] = 2.0; /* now assemble */ for ( n = 0 ; n < 3 ; n++ ) /* which edge fan vertex */ for ( i = 0 ; i < SDIM ; i++ ) /* dimension on first */ for ( nn = 0 ; nn < 3 ; nn++ ) /* which edge fan vertex */ for ( k = 0 ; k < SDIM ; k++ ) /* dimension on second */ { REAL sum = 0.0; for ( j = 0 ; j < 6 ; j++ ) { for ( jj = 0 ; jj < 6; jj++ ) sum += Htemp[j][jj]*dqdx[j][n][i]*dqdx[jj][nn][k]; if ( i == k ) sum += Gtemp[j]*ddqdxx[j][n][nn]; } e_info->hess[n+1][nn+1][i][k] += sum; e_info->hess[n+1][0][i][k] -= sum; e_info->hess[0][nn+1][i][k] -= sum; e_info->hess[0][0][i][k] += sum; } return sqrt(s1s1)*(1 - cos_th); #else kb_error(2169,"dihedral_hooke hessian not implemented on Mac 68K.\n",WARNING); return 0.0; #endif } evolver-2.30c.dfsg/src/evalmore.c0000644000175300017530000061454311410765113017210 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /***************************************************************** * * File: evalmore.c * * Purpose: Overflow from eval() in evaltree.c. */ #include "include.h" #include "lex.h" #include "ytab.h" #ifdef MAC_APP #define S_IFIFO 0x100 #elif !defined(MAC_CW) #include /* #include */ #ifndef S_IFIFO #ifdef __S_IFIFO #define S_IFIFO __S_IFIFO #elif defined(_S_IFIFO) #define S_IFIFO _S_IFIFO #else #define S_IFIFO 0010000 #endif #endif #endif /* int wait ARGS((int*)); int fork ARGS((void)); int execlp(); int stat(); */ void flip_toggle ARGS((int*,int,char*)); void set_body ARGS((element_id,body_id)); /************************************************************************ * * Function: other_stuff() * * Purpose: execute command nodes, overflow from eval(). * * Return: stacktop pointer */ void other_stuff(node,recalc_flag,update_display_flag,q_id,localstack, localbase) struct treenode * node; int *recalc_flag; int *update_display_flag; element_id q_id; REAL *localstack; /* stack base */ struct locallist_t *localbase; { int i,j,n,k; char *s,*h; REAL scale; int old; /* old state of toggle */ int oldquiet; /* old state of quiet_flag */ element_id id,fe,f_id,b_id; struct thread_data *td = GET_THREAD_DATA; #define newstack (td->eval_stack) #define stackmax (td->eval_stack_size) #define stacktop (td->stack_top) #define this_frame ((struct eval_frame*)(newstack + td->frame_spot)) switch ( node->type ) { case SIMPLEX_TO_FE_: simplex_to_fe(); break; case REORDER_STORAGE_: reorder_storage(); break; case RENUMBER_ALL_: renumber_all(); break; case DUMP_MEMLIST_: mem_list_dump(); break; case VIEW_MATRIX_: i = (int)(*(stacktop--)); k = (int)(*(stacktop--)); if ( (k < 1) || (k > SDIM+1) || (i < 1) || (i > SDIM+1) ) { sprintf(errmsg, "Illegal index: view_matrix[%d][%d] (must be 1 to %d)\n",k,i,SDIM); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2008,errmsg,RECOVERABLE); } *(++stacktop) = view[k-1][i-1]; break; case RESET_COUNTS_: reset_counts(); break; case FLUSH_COUNTS_: flush_counts(); break; case PRINT_PROFILING_: print_profiling(); break; case RESET_PROFILING_: reset_profiling(); break; case PAUSE_: outstring("Paused; hit RETURN to continue, b to break: "); my_fgets(msg,msgmax,stdin); if ( msg[0] == 'b' ) breakflag = BREAKFULL; break; case HELP_KEYWORD: keyword_help(node->op1.string); break; case CREATE_VERTEX_: { vertex_id v_id; REAL x[MAXCOORD]; for ( i = SDIM-1 ; i >= 0 ; i-- ) x[i] = *(stacktop--); v_id = new_vertex(x,NULLID); new_vertex_id = ordinal(v_id) + 1; set_original(v_id,new_vertex_id); *(++stacktop) = (REAL)new_vertex_id; *recalc_flag = 1; break; } case CREATE_EDGE_: { vertex_id v_id2; vertex_id v_id1; int v2 = (int)(*stacktop--); int v1 = (int)(*stacktop--); edge_id e_id; v_id2 = get_ordinal_id(VERTEX,v2-1); v_id1 = get_ordinal_id(VERTEX,v1-1); if ( !valid_id(v_id1) ) { sprintf(errmsg,"Invalid tail vertex %d in new_edge.\n",v1); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2009,errmsg,RECOVERABLE); } if ( !valid_id(v_id2) ) { sprintf(errmsg,"Invalid head vertex %d in new_edge.\n",v2); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2010,errmsg, RECOVERABLE); } e_id = new_edge(v_id1,v_id2,NULLID); new_edge_id = ordinal(e_id)+1; set_original(e_id,new_edge_id); *(++stacktop) = (REAL)new_edge_id; *recalc_flag = 1; } break; case CREATE_FACET_: { facetedge_id old_fe = NULLID; edge_id e_id; vertex_id tv,hv; #define ORD_ID(id) ((id)<0 ? inverse_id(get_ordinal_id(EDGE,-1-(id))) : \ get_ordinal_id(EDGE,(id)-1)) f_id = NULLID; fe = NULLID; if ( (web.representation == STRING) || (web.representation == SOAPFILM) ) { n = node[node->left].op1.argcount; /* size of edge list */ for ( i = 0 ; i < n ; i++ ) if ( !valid_id(ORD_ID((int)(stacktop[-i]))) ) { sprintf(msg,"Invalid edge %g in new_facet.\n",(double)stacktop[-i]); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2011,msg,RECOVERABLE); } /* check continuity */ i = 1-n; e_id = ORD_ID((int)(stacktop[i])); hv = get_edge_headv(e_id); for ( i++ ; i <= 0 ; i++ ) { e_id = ORD_ID((int)(stacktop[i])); tv = get_edge_tailv(e_id); if ( !equal_id(hv,tv) ) { sprintf(errmsg, "Inconsistency: edge %s tail vertex %s disagrees with previous head %s.\n", ELNAME(e_id),ELNAME1(tv),ELNAME2(hv)); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2012,errmsg,RECOVERABLE); } hv = get_edge_headv(e_id); } if ( web.representation == SOAPFILM) { e_id = ORD_ID((int)(stacktop[0])); hv = get_edge_headv(e_id); e_id = ORD_ID((int)(stacktop[1-n])); tv = get_edge_tailv(e_id); if ( !equal_id(hv,tv) ) { sprintf(errmsg, "Inconsistency in first edge tail vertex and last edge head.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2013,errmsg,RECOVERABLE); } } f_id = new_facet(); set_original(f_id,f_id); for ( i = 0 ; i < n ; i++ ) { facetedge_id edge_fe; e_id = ORD_ID((int)(stacktop[i-n+1])); fe = new_facetedge(f_id,e_id); if ( !valid_id(get_facet_fe(f_id)) ) set_facet_fe(f_id,fe); else { set_next_edge(old_fe,fe); set_prev_edge(fe,old_fe); } old_fe = fe; /* add to edge facet list, and get in geometric order */ edge_fe = get_edge_fe(e_id); if ( valid_id(edge_fe) ) { /* insert in chain */ set_next_facet(fe,get_next_facet(edge_fe)); set_prev_facet(fe,edge_fe); set_prev_facet(get_next_facet(fe),fe); set_next_facet(edge_fe,fe); } else { set_next_facet(fe,fe); set_prev_facet(fe,fe); set_edge_fe(e_id,fe); /* link edge to rest of world */ } } if ( equal_id(get_fe_tailv(get_facet_fe(f_id)),get_fe_headv(fe)) ) { set_next_edge(fe,get_facet_fe(f_id)); /* close up ring */ set_prev_edge(get_facet_fe(f_id),fe); } if ( web.representation == SOAPFILM ) { fe = get_facet_fe(f_id); for ( i = 0 ; i < n ; i++,fe = get_next_edge(fe) ) fe_reorder(get_fe_edge(fe)); if ( n > 3 ) face_triangulate(f_id,n); recalc_facet_area(f_id); } if ( web.representation == SOAPFILM ) need_fe_reorder_flag = 1; stacktop -= n; /* pop id's */ } else if ( web.representation == SIMPLEX ) { vertex_id *v; n = node[node->left].op1.argcount; /* size of edge list */ for ( i = 0 ; i < n ; i++ ) if ( !valid_id(get_ordinal_id(VERTEX,(int)stacktop[-i]-1) ) ) { sprintf(msg,"Invalid vertex %d in new_facet.\n",(int)stacktop[-i]); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2014,msg,RECOVERABLE); } f_id = new_facet(); v = get_facet_vertices(f_id); for ( i = 0 ; i < n ; i++ ) v[i] = get_ordinal_id(VERTEX,(int)stacktop[-n+i+1]-1); stacktop -= n; /* pop id's */ } } new_facet_id = ordinal(f_id) + 1; *(++stacktop) = (REAL)new_facet_id; *recalc_flag = 1; break; case CREATE_BODY_: { body_id b_id = new_body(); new_body_id = ordinal(b_id) + 1; set_original(b_id,new_body_id); *(++stacktop) = (REAL)new_body_id; } *recalc_flag = 1; break; case MERGE_VERTEX_: { vertex_id v_id2; vertex_id v_id1; int v2 = (int)(*stacktop--); int v1 = (int)(*stacktop--); v_id2 = get_ordinal_id(VERTEX,v2-1); v_id1 = get_ordinal_id(VERTEX,v1-1); if ( (v1==0) || !valid_id(v_id1) ) { sprintf(errmsg,"Invalid first vertex %d in vertex_merge.\n",v1); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3886,errmsg,RECOVERABLE); } if ( (v2==0) || !valid_id(v_id2) ) { sprintf(errmsg,"Invalid second vertex %d in vertex_merge.\n",v2); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3916,errmsg, RECOVERABLE); } merge_vertex(v_id1,v_id2); *recalc_flag = 1; } break; case MERGE_EDGE_: { edge_id e_id2; edge_id e_id1; int e2 = (int)(*stacktop--); int e1 = (int)(*stacktop--); e_id2 = get_ordinal_id(EDGE,abs(e2)-1); if ( e2 < 0 ) invert(e_id2); e_id1 = get_ordinal_id(EDGE,abs(e1)-1); if ( e1 < 0 ) invert(e_id1); if ( (e1==0) ||!valid_id(e_id1) ) { sprintf(errmsg,"Invalid first edge %d in edge_merge.\n",e1); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3878,errmsg,RECOVERABLE); } if ( (e2==0) || !valid_id(e_id2) ) { sprintf(errmsg,"Invalid second edge %d in edge_merge.\n",e2); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3912,errmsg, RECOVERABLE); } merge_edge(e_id1,e_id2); *recalc_flag = 1; } break; case MERGE_FACET_: { facet_id f_id2; facet_id f_id1; int f2 = (int)(*stacktop--); int f1 = (int)(*stacktop--); f_id2 = get_ordinal_id(FACET,abs(f2)-1); if ( f2 < 0 ) invert(f_id2); f_id1 = get_ordinal_id(FACET,abs(f1)-1); if ( f1 < 0 ) invert(f_id1); if ( (f1==0) || !valid_id(f_id1) ) { sprintf(errmsg,"Invalid first facet %d in facet_merge.\n",f1); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3880,errmsg,RECOVERABLE); } if ( (f2==0) || !valid_id(f_id2) ) { sprintf(errmsg,"Invalid second facet %d in facet_merge.\n",f2); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3879,errmsg, RECOVERABLE); } merge_facet(f_id1,f_id2); *recalc_flag = 1; } break; case GET_TORUS_PERIODS_: j = (int)(*stacktop--); i = (int)(*stacktop--); if ( (j<1) || (j > SDIM)) { sprintf(errmsg, "Torus_periods index %d; must be between 1 and %d.\n",j,SDIM); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2015,errmsg,RECOVERABLE); } if ((i<1) || (i>SDIM) ) { sprintf(errmsg, "Torus_periods index %d; must be between 1 and %d.\n",i,SDIM); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2016,errmsg,RECOVERABLE); } *++stacktop = web.torus_period[i-1][j-1]; /* 1-based indexing */ break; case GET_INVERSE_PERIODS_: j = (int)(*stacktop--); i = (int)(*stacktop--); if ( (j<1) || (j > SDIM) ) { sprintf(errmsg, "Inverse_periods index %d; must be between 1 and %d.\n",j,SDIM); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2017,errmsg,RECOVERABLE); } if ( (i<1) || (i>SDIM) ) { sprintf(errmsg, "Inverse_periods index %d; must be between 1 and %d.\n",i,SDIM); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2018,errmsg,RECOVERABLE); } *++stacktop = web.inverse_periods[i-1][j-1]; /* 1-based indexing */ break; case SET_GAP_CONSTANT_: web.spring_constant = *(stacktop--); if ( everything_quantities_flag ) GEN_QUANT(gap_quantity_num)->modulus = web.spring_constant; if ( web.spring_constant == 0.0 ) web.convex_flag = 0; else web.convex_flag = 1; *recalc_flag = 1; break; case GET_INTERNAL_: *++stacktop = get_internal_variable(node->op1.name_id); break; case SET_INTERNAL_: { REAL oldvalue=0.0; REAL val; /* get old value */ switch(node->op1.name_id) { #ifdef MPI_EVOLVER case V_CORONA_STATE : oldvalue = mpi_corona_state; break; #endif case V_PS_LABELSIZE_ : oldvalue = ps_labelsize; break; case V_PS_STRINGWIDTH_: oldvalue = ps_stringwidth; break; case V_PS_FIXEDEDGEWIDTH_: oldvalue = ps_fixededgewidth; break; case V_PS_TRIPLEEDGEWIDTH_: oldvalue = ps_tripleedgewidth; break; case V_PS_CONEDGEWIDTH_: oldvalue = ps_conedgewidth; break; case V_PS_BAREEDGEWIDTH_: oldvalue = ps_bareedgewidth; break; case V_PS_GRIDEDGEWIDTH_: oldvalue = ps_gridedgewidth; break; case V_TOLERANCE: oldvalue = web.tolerance; break; case V_LAST_ERROR: oldvalue = last_error; break; case V_BRIGHTNESS: oldvalue = brightness; break; case V_BACKGROUND: oldvalue = background_color; break; case V_TARGET_TOLERANCE: oldvalue = web.target_tolerance; break; case V_HESS_EPSILON: oldvalue = hessian_epsilon; break; case GRAV_CONST_: oldvalue = web.grav_const; break; case V_THICKNESS: oldvalue = thickness; break; case V_HESSIAN_SLANT_CUTOFF: oldvalue = hessian_slant_cutoff; break; case V_AMBIENT_PRESSURE: oldvalue = web.pressure; break; case V_DIFFUSION: oldvalue = web.diffusion_const; web.diffusion_flag = 1; break; case V_SCALE_SCALE: oldvalue = web.scale_scale; break; case V_SCALE: oldvalue = web.scale; break; case V_SCALE_LIMIT: oldvalue = web.maxscale; break; case V_TIME: oldvalue = total_time; break; case V_CHECK_COUNT_: oldvalue = check_count; break; case V_VISIBILITY_DEBUG_: oldvalue = visdebuglevel; break; case V_SCROLLBUFFERSIZE_: oldvalue = scrollbuffersize; break; case V_BREAKFLAG_: oldvalue = breakflag; break; case V_STRING_CURVE_TOLERANCE: oldvalue = string_curve_tolerance; break; case V_MINDEG_DEBUG_LEVEL: oldvalue = mindeg_debug_level; break; case V_MINDEG_MARGIN: oldvalue = mindeg_margin; break; case V_MINDEG_MIN_REGION_SIZE: oldvalue = mindeg_min_region_size; break; case V_WINDOW_ASPECT_RATIO: oldvalue = window_aspect_ratio; break; case V_PICKVNUM: oldvalue = pickvnum; break; case V_PICKENUM: oldvalue = pickenum; break; case V_PICKFNUM: oldvalue = pickfnum; break; case V_JIG_TEMP: oldvalue = web.temperature; break; case V_LINEAR_METRIC_MIX: oldvalue = linear_metric_mix; break; case V_GAP_CONSTANT: oldvalue = web.spring_constant; break; case V_QUADRATIC_METRIC_MIX: oldvalue = quadratic_metric_mix; break; case V_RANDOM_SEED: oldvalue = random_seed;break; case V_RANDOM: sprintf(errmsg,"Cannot set random. Set random_seed instead.\n"); stacktop--; sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2019,errmsg, WARNING); break; case V_INTEGRAL_ORDER: oldvalue = web.gauss2D_order; break; case V_INTEGRAL_ORDER_1D: oldvalue = web.gauss1D_order; break; case V_INTEGRAL_ORDER_2D: oldvalue = web.gauss2D_order; break; case V_AUTOCHOP_LENGTH: oldvalue = autochop_length; break; default: sprintf(errmsg,"Internal: illegal variable number %d.\n", node->op1.name_id); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1209,errmsg, RECOVERABLE); } val = *(stacktop--); switch ( node->op2.assigntype ) { case ASSIGN_: break; case PLUSASSIGN_: val += oldvalue; break; case SUBASSIGN_: val = oldvalue - val; break; case MULTASSIGN_: val = oldvalue * val; break; case DIVASSIGN_: val = oldvalue / val; break; } switch(node->op1.name_id) { case V_TOLERANCE: web.tolerance = val; break; case V_PS_LABELSIZE_: ps_labelsize = val; break; case V_PS_STRINGWIDTH_: ps_stringwidth = val; break; case V_PS_FIXEDEDGEWIDTH_: ps_fixededgewidth = val; break; case V_PS_TRIPLEEDGEWIDTH_: ps_tripleedgewidth = val; break; case V_PS_CONEDGEWIDTH_: ps_conedgewidth = val; break; case V_PS_BAREEDGEWIDTH_: ps_bareedgewidth = val; break; case V_PS_GRIDEDGEWIDTH_: ps_gridedgewidth = val; break; #ifdef MPI_EVOLVER case V_CORONA_STATE: mpi_set_corona((int)val); break; #else case V_CORONA_STATE: ; break; #endif case V_BRIGHTNESS: if ( (val < 0.0) || (val > 1.0) ) { sprintf(errmsg,"Brightness is %f; must be between 0 and 1.\n",val); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2020,errmsg, RECOVERABLE); } else { brightness = val; update_display(); } break; case V_BACKGROUND: background_color = (int)val; update_display(); break; case V_TARGET_TOLERANCE: web.target_tolerance = val; break; case V_HESS_EPSILON: hessian_epsilon = val; if ( hessian_epsilon < 0.0 ) { sprintf(errmsg,"hessian_epsilon is negative!\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2558,errmsg, WARNING); } break; case GRAV_CONST_: web.grav_const = val; if ( web.grav_const != 0.0 ) web.gravflag = 1; else web.gravflag = 0; if (gravity_quantity_num >= 0 ) GEN_QUANT(gravity_quantity_num)->modulus = web.gravflag ? web.grav_const : 0.0; break; case V_DIFFUSION: web.diffusion_const = val; break; case V_AMBIENT_PRESSURE: web.pressure = val; break; case V_HESSIAN_SLANT_CUTOFF: hessian_slant_cutoff = val; break; case V_THICKNESS: thickness = val; user_thickness_flag = 1; update_display();break; case V_SCALE_SCALE: web.scale_scale = val; break; case V_SCALE: web.scale = val; break; case V_SCALE_LIMIT: web.maxscale = val; break; case V_LAST_ERROR: last_error = (int)val; break; case V_TIME: total_time = val; break; case V_CHECK_COUNT_: check_count = (int)val; break; case V_VISIBILITY_DEBUG_: visdebuglevel = (int)val; break; case V_SCROLLBUFFERSIZE_: scrollbuffersize = (int)val; set_scroll_size(scrollbuffersize); break; case V_BREAKFLAG_: breakflag = (int)val; break; case V_STRING_CURVE_TOLERANCE: string_curve_tolerance = val; update_display();break; case V_MINDEG_DEBUG_LEVEL: mindeg_debug_level = (int)val; break; case V_MINDEG_MARGIN: mindeg_margin = (int)val; break; case V_MINDEG_MIN_REGION_SIZE: mindeg_min_region_size = (int)val; break; case V_WINDOW_ASPECT_RATIO: window_aspect_ratio = (REAL)val; update_display(); break; case V_PICKVNUM: pickvnum = (int)val; break; case V_PICKENUM: pickenum = (int)val; break; case V_PICKFNUM: pickfnum = (int)val; break; case V_JIG_TEMP: web.temperature = val; break; case V_LINEAR_METRIC_MIX: linear_metric_mix=val; break; case V_GAP_CONSTANT: web.spring_constant = val; break; case V_AUTOCHOP_LENGTH: autochop_length = val; break; case V_QUADRATIC_METRIC_MIX: quadratic_metric_mix=val; break; case V_RANDOM_SEED: random_seed = (int)val; srand(random_seed); srand48(random_seed); kb_initr(random_seed); break; case V_RANDOM: sprintf(errmsg, "Cannot set random. Set random_seed instead.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2021,errmsg,WARNING); stacktop--; break; case V_INTEGRAL_ORDER: web.gauss1D_order = (int)val; set_by_user_gauss_1D = web.gauss1D_order; web.gauss2D_order = (int)val; set_by_user_gauss_2D = web.gauss2D_order; gauss_setup(); if ( web.modeltype == LAGRANGE ) { gauss_lagrange_setup( (web.dimension==1)?1:web.dimension-1, web.lagrange_order,web.gauss1D_order); gauss_lagrange_setup(web.dimension, web.lagrange_order,web.gauss2D_order); } *recalc_flag = 1; break; case V_INTEGRAL_ORDER_1D: web.gauss1D_order = (int)val; set_by_user_gauss_1D = web.gauss1D_order; if ( web.modeltype == LAGRANGE ) gauss_lagrange_setup( (web.dimension==1)?1:web.dimension-1, web.lagrange_order,web.gauss1D_order); gauss_setup(); *recalc_flag = 1; break; case V_INTEGRAL_ORDER_2D: web.gauss2D_order = (int)val; set_by_user_gauss_2D = web.gauss2D_order; gauss_setup(); if ( web.modeltype == LAGRANGE ) gauss_lagrange_setup(web.dimension, web.lagrange_order,web.gauss2D_order); *recalc_flag = 1; break; default: sprintf(errmsg,"Internal: illegal internal variable number %d.\n", node->op1.name_id); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1114,errmsg,RECOVERABLE); } break; } /* end SET_INTERNAL_ */ case SIZEOF_ATTR_: *++stacktop = (REAL)EXTRAS(node->op2.eltype)[node->op1.extranum].array_spec.datacount; break; case SIZEOF_ARRAY_: *++stacktop = (REAL)get_name_arrayptr(node->op1.name_id,localstack,localbase)->datacount; break; case SIZEOF_STRING_: { char *s = *(char**)stacktop; *stacktop = s ? (REAL)strlen(s) : 0; if ( node->left && (node[node->left].flags & DEALLOCATE_POINTER) ) myfree(s); break; } case TORDUP_: i = (int)*(stacktop--); if ( ! web.torus_flag ) { outstring("Torus model not in effect.\n"); break; } if ( i < 1 || i > SDIM ) outstring("Improper period number.\n"); else tordup(i-1); recalc(); break; case SKINNY_: sprintf(msg,"New edges: %d\n",web.facet_refine_count = skinny(*(stacktop--))); outstring(msg); recalc(); break; case PUSHQPRESSURE_: *++stacktop = GEN_QUANT(node->op1.quant_id)->pressure; break; case PUSHQTARGET_: *++stacktop = GEN_QUANT(node->op1.quant_id)->target; break; case PUSHQMODULUS_: *++stacktop = GEN_QUANT(node->op1.quant_id)->modulus; break; case PUSHQTOLERANCE_: *++stacktop = GEN_QUANT(node->op1.quant_id)->tolerance; break; case PUSHMMODULUS_: *++stacktop = METH_INSTANCE(node->op1.meth_id)->modulus; break; case PUSHQVALUE_: { struct gen_quant *q = GEN_QUANT(node->op1.quant_id); if ( q->timestampflags&(Q_INFO|Q_ENERGY|Q_FIXED)); *++stacktop = q->value; } break; case PUSHQFIXED_: *++stacktop = GEN_QUANT(node->op1.quant_id)->flags & Q_FIXED ? 1:0 ; break; case PUSHQENERGY_: *++stacktop = GEN_QUANT(node->op1.quant_id)->flags & Q_ENERGY ? 1:0 ; break; case PUSHQINFO_ONLY_: *++stacktop = GEN_QUANT(node->op1.quant_id)->flags & Q_INFO ? 1:0 ; break; case PUSHQCONSERVED_: *++stacktop = GEN_QUANT(node->op1.quant_id)->flags & Q_CONSERVED ? 1:0 ; break; case PUSHMVALUE_: { struct method_instance *q = METH_INSTANCE(node->op1.meth_id); if ((q->timestamptimestampvalue; } break; case PUSHQVOLCONST_: *++stacktop = GEN_QUANT(node->op1.quant_id)->volconst; break; case PUSH_NAMED_QUANTITY: case PUSH_METHOD_INSTANCE_: *++stacktop = (REAL)node->op1.quant_id; break; case FIX_QUANTITY_: case SET_Q_FIXED_: { struct gen_quant *q = GEN_QUANT(node->op1.quant_id); q->flags &= ~(Q_ENERGY|Q_INFO|Q_CONSERVED); q->flags |= Q_FIXED; if ( everything_quantities_flag ) if ( valid_id(q->b_id) ) set_attr(q->b_id,FIXEDVOL); } break; case UNFIX_QUANTITY_: { struct gen_quant *q = GEN_QUANT(node->op1.quant_id); q->flags &= ~(Q_ENERGY|Q_FIXED|Q_CONSERVED); q->flags |= Q_INFO; if ( everything_quantities_flag ) if ( valid_id(q->b_id) ) unset_attr(q->b_id,FIXEDVOL); } break; case SET_Q_INFO_: { struct gen_quant *q = GEN_QUANT(node->op1.quant_id); q->flags &= ~(Q_ENERGY|Q_FIXED|Q_CONSERVED); q->flags |= Q_INFO; if ( everything_quantities_flag ) if ( valid_id(q->b_id) ) unset_attr(q->b_id,FIXEDVOL); } break; case SET_Q_ENERGY_: { struct gen_quant *q = GEN_QUANT(node->op1.quant_id); if ( everything_quantities_flag ) if ( valid_id(q->b_id) ) { sprintf(errmsg,"Cannot set body volume to ENERGY.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2416,errmsg,RECOVERABLE); } q->flags &= ~(Q_FIXED|Q_INFO|Q_CONSERVED); q->flags |= Q_ENERGY; } break; case SET_Q_CONSERVED_: { struct gen_quant *q = GEN_QUANT(node->op1.quant_id); if ( everything_quantities_flag ) if ( valid_id(q->b_id) ) { sprintf(errmsg,"Cannot set body volume to CONSERVED.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2546,errmsg,RECOVERABLE); } q->flags &= ~(Q_FIXED|Q_INFO|Q_ENERGY); q->flags |= Q_CONSERVED; } break; case UNFIX_PARAMETER_: { struct global *p = globals(node->op1.name_id); int old = p->flags & OPTIMIZING_PARAMETER; if ( !old ) { if ( p->type != REAL_TYPE ) { sprintf(errmsg,"Cannot unfix \"%s\" since it is not type REAL.\n", p->name); kb_error(4124,errmsg,RECOVERABLE); } if ( optparamcount >= MAXOPTPARAM-1 ) { sprintf(errmsg,"Too many optimizing parameters. Increase MAXOPTPARAM in extern.h and recompile.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2022,errmsg,RECOVERABLE); } p->flags |= OPTIMIZING_PARAMETER; memset((char*)&optparam[optparamcount],0,sizeof(struct optparam_t)); optparam[optparamcount++].pnum = node->op1.name_id; if ( !everything_quantities_flag ) convert_to_quantities(); sprintf(msg,"%s now optimizing parameter. (was not)\n",p->name); } else sprintf(msg,"%s now optimizing parameter. (already was)\n",p->name); break; } case FIX_PARAMETER_: { struct global *p = globals(node->op1.name_id); int old = p->flags & OPTIMIZING_PARAMETER; int spot; if ( old ) { for ( spot = 0 ; spot < optparamcount ; spot++ ) if ( optparam[spot].pnum == node->op1.name_id ) break; if ( spot == optparamcount ) { sprintf(errmsg,"`%s` not found in optimizing parameter list.\n",p->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2023,errmsg,WARNING); break; } p->flags &= ~OPTIMIZING_PARAMETER; optparam[spot] = optparam[--optparamcount]; sprintf(msg,"%s now nonoptimizing parameter. (was not)\n",p->name); } else sprintf(msg,"%s now nonoptimizing parameter. (already was)\n",p->name); break; } /* attribute setting in set statements */ case SET_ATTRIBUTE_: case SET_ATTRIBUTE_L: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; parallel_update_flag[id_type(id)] = 1; /* set when element info changed */ switch ( node->op2.attr_kind ) { case SET_FIXED_: set_attr(id,FIXED); break; case SET_TRIPLE_PT_: set_attr(id,TRIPLE_PT); --stacktop; break; case SET_TETRA_PT_: set_attr(id,TETRA_PT); --stacktop; break; case SET_AXIAL_POINT_: set_attr(id,AXIAL_POINT); --stacktop; break; case SET_EXTRA_ATTR_: { struct extra *ext; int spot; /* index */ n = node->op3.extra_info & ((1<op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ext = EXTRAS(id_type(id)) + n; /* get index */ spot = 0; for ( k = 0 ; k < ext->array_spec.dim ; k++ ) { int j = (int)(stacktop[-ext->array_spec.dim+k+1]); spot *= ext->array_spec.sizes[k]; if ( j > ext->array_spec.sizes[k] ) { sprintf(errmsg, "Attribute %s index %d is %d; maximum is %d (in %s).\n", ext->name,k+1,j,ext->array_spec.sizes[k],ext->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1210,errmsg,RECOVERABLE); } if ( j < 1 ) { sprintf(errmsg, "Attribute %s index %d is %d; must be positive.\n", ext->name,k,j); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1129,errmsg,RECOVERABLE); } spot += (int)(stacktop[-ext->array_spec.dim+k+1]) - 1; } stacktop -= ext->array_spec.dim; switch ( ext->type ) { case REAL_TYPE: ((REAL*)get_extra(id,n))[spot] = *(stacktop--); break; case INTEGER_TYPE: ((int*)get_extra(id,n))[spot] = (int)*(stacktop--); break; case ULONG_TYPE: ((unsigned long*)get_extra(id,n))[spot] = (unsigned long)*(stacktop--); break; case USHORT_TYPE: ((unsigned short*)get_extra(id,n))[spot] = (unsigned short)*(stacktop--); break; case UINT_TYPE: ((unsigned int*)get_extra(id,n))[spot] = (unsigned int)*(stacktop--); break; case UCHAR_TYPE: ((unsigned char*)get_extra(id,n))[spot] = (unsigned char)*(stacktop--); break; case LONG_TYPE: ((long int*)get_extra(id,n))[spot] = (long int)*(stacktop--); break; case SHORT_TYPE: ((short*)get_extra(id,n))[spot] = (short)*(stacktop--); break; case CHAR_TYPE: ((char*)get_extra(id,n))[spot] = (char)*(stacktop--); break; case ELEMENTID_TYPE: case VERTEX_TYPE: case EDGE_TYPE: case FACET_TYPE: case BODY_TYPE: case FACETEDGE_TYPE: ((element_id*)get_extra(id,n))[spot] = *(element_id*)(stacktop--); break; case PTR_TYPE: ((char**)get_extra(id,n))[spot] = *(char**)(stacktop--); break; case BOUNDARY_TYPE: case CONSTRAINT_TYPE: case QUANTITY_TYPE: case INSTANCE_TYPE: case PROCEDURE_TYPE: ((int*)get_extra(id,n))[spot] = (int)*(stacktop--); break; } } break; case SET_DENSITY_: { REAL density = *(stacktop--); if ( density != 1.0 ) set_attr(id,DENSITY); switch ( id_type(id) ) { case EDGE: set_edge_density(id,density); pressure_set_flag = 0; break; case FACET: set_facet_density(id,density); pressure_set_flag = 0; break; case BODY: set_body_density(id,density); web.gravflag = 1; if (gravity_quantity_num >= 0 ) GEN_QUANT(gravity_quantity_num)->modulus = web.grav_const; break; } *recalc_flag = 1; } break; case SET_PHASE_: { int phase = (int)*(stacktop--); switch ( id_type(id) ) { case BODY: set_b_phase(id,phase); break; case FACET: set_f_phase(id,phase); break; } *recalc_flag = 1; } break; case SET_VOLCONST_: { REAL v = *(stacktop--); REAL oldvcon = get_body_volconst(id); set_body_volconst(id,v); set_body_volume(id,get_body_volume(id)+v-oldvcon,SETSTAMP); set_body_oldvolume(id,get_body_oldvolume(id)+v-oldvcon); if ( everything_quantities_flag ) GEN_QUANT(get_body_volquant(id))->volconst = v; *recalc_flag = 1; break; } case SET_TARGET_: { REAL v = *(stacktop--); if ( get_attr(id) & PRESSURE ) { sprintf(errmsg,"Must unset body pressure before fixing target.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2024,errmsg, RECOVERABLE); } set_attr(id,FIXEDVOL); set_body_fixvol(id,v); /* takes care of quantity */ reset_conj_grad(); *recalc_flag = 1; break; } case SET_PRESSURE_: { REAL p = *(stacktop--); unset_attr(id,FIXEDVOL); set_attr(id,PRESSURE); web.pressflag = 1; set_body_pressure(id,p); reset_conj_grad(); if ( everything_quantities_flag ) { struct gen_quant *q = GEN_QUANT(get_body_volquant(id)); if ( p == 0.0 ) { q->modulus = 1.0; if ( !(q->flags & Q_INFO) ) { q->flags &= ~(Q_ENERGY|Q_FIXED|Q_CONSERVED); q->flags |= Q_INFO; } } else { q->modulus = -p; if ( !(q->flags & Q_ENERGY ) ) { q->flags &= ~(Q_INFO|Q_FIXED|Q_CONSERVED); q->flags |= Q_ENERGY; } } } *recalc_flag = 1; break; } case SET_OPACITY_: facet_alpha = *(stacktop)--; break; case SET_CONSTRAINT_: { int con = (int)*(stacktop--); struct constraint *constr; constr = get_constraint(con); if ( (con<0) || (con>=web.maxcon) || !(constr->attr & IN_USE)) { sprintf(errmsg,"Illegal constraint number: %d.\n",con); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1212,errmsg,RECOVERABLE); } if ( get_attr(id) & BOUNDARY ) { sprintf(errmsg, "Cannot set constraint on %s %s since it is on a boundary.\n", typenames[id_type(id)],ELNAME(id)); kb_error(4111,errmsg,RECOVERABLE); } set_attr(id,CONSTRAINT); if ( constr->attr & CON_ENERGY ) set_attr(id, BDRY_ENERGY); if ( constr->attr & CON_CONTENT ) set_attr(id, BDRY_CONTENT); switch ( id_type(id) ) { case VERTEX: set_v_constraint_map(id,con); project_v_constr(id,ACTUAL_MOVE,RESET_ONESIDEDNESS); break; case EDGE: set_e_constraint_map(id,con); break; case FACET: set_f_constraint_map(id,con); break; } *recalc_flag = 1; } break; case SET_BOUNDARY_: { struct boundary *bdry; k = (int)*(stacktop--); /* boundary number */ bdry = web.boundaries+abs(k); if ( (abs(k) >= web.bdrymax) || !(bdry->attr&IN_USE) ) { sprintf(errmsg,"Boundary %d is not valid.\n",k); kb_error(3374,errmsg,RECOVERABLE); } if ( get_attr(q_id) & BOUNDARY ) { struct boundary *qbdry; switch ( id_type(q_id) ) { case VERTEX: qbdry = get_boundary(q_id); break; case EDGE: qbdry = get_edge_boundary(q_id); break; case FACET: qbdry = get_facet_boundary(q_id); break; default: qbdry = NULL; /* error message below */ } if ( qbdry == bdry ) break; sprintf(errmsg,"%s %s already on a different boundary.\n", typenames[id_type(q_id)],ELNAME(q_id)); kb_error(3375,errmsg,RECOVERABLE); } set_attr(q_id,BOUNDARY); if ( k < 0 ) set_attr(q_id,NEGBOUNDARY); switch(id_type(q_id)) { case VERTEX: { REAL *x = get_coord(q_id); int n; set_boundary_num(q_id,abs(k)); for ( n = 0 ; n < SDIM ; n++ ) if ( bdry->coordf[n]->root != NULL ) x[n] = eval(bdry->coordf[n],get_param(q_id),q_id,NULL); break; } case EDGE: set_edge_boundary_num(q_id,abs(k)); break; case FACET: set_facet_boundary_num(q_id,abs(k)); break; default: sprintf(errmsg,"Bad element type for boundary.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3376,errmsg, RECOVERABLE); } if ( bdry->attr & CON_ENERGY ) apply_method_num(q_id,bdry->energy_method); if ( bdry->attr & CON_CONTENT ) { if ( (web.representation == STRING) && (id_type(q_id) == VERTEX) ) fixup_vertex_content_meths(q_id); else if ( (web.representation == SOAPFILM) && (id_type(q_id) == EDGE) ) fixup_edge_content_meths(q_id); } /* go back to next element generator */ node += node->op1.skipsize - 1; /* back to start of loop */ break; } case SET_ORIGINAL_: set_original(id,(int)*(stacktop--) | ((element_id)id_type(id)<right ) /* indexed */ { int dim = (int)*(stacktop--)-1; if ( (dim >= SDIM) || (dim < 0) ) { sprintf(errmsg, "Trying to set coordinate %d, in space dimension %d.\n", dim+1,SDIM); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2026,errmsg,RECOVERABLE); } get_coord(id)[dim] = *(stacktop--); *recalc_flag = 1; break; } /* else fall through */ case SET_COORD_2: case SET_COORD_3: case SET_COORD_4: case SET_COORD_5: case SET_COORD_6: case SET_COORD_7: case SET_COORD_8: get_coord(id)[node->op2.attr_kind-SET_COORD_1] = *(stacktop--); if ( node->op2.attr_kind-SET_COORD_1 <= SDIM ) *recalc_flag = 1; break; case SET_PARAM_: case SET_PARAM_1: case SET_PARAM_2: case SET_PARAM_3: case SET_PARAM_4: if ( get_vattr(id) & BOUNDARY ) { struct boundary *boundary = get_boundary(id); REAL *param = get_param(id); REAL *xx = get_coord(id); int pnum = node->op2.attr_kind-SET_PARAM_1; if ( node->right ) pnum += (int)*(stacktop--); if ( pnum > boundary->pcount ) { sprintf(errmsg,"Parameter number is %d; maximum is %d.\n", pnum,boundary->pcount); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1149,errmsg,RECOVERABLE); } param[pnum] = *(stacktop--); for ( k = 0 ; k < SDIM ; k++ ) xx[k] = eval(boundary->coordf[k],param,id,NULL); *recalc_flag = 1; } else /* just parking a value probably */ { REAL *param = get_param(id); int pnum = node->op2.attr_kind-SET_PARAM_1; if ( node->right ) pnum += (int)*(stacktop--); param[pnum] = *(stacktop--); } break; case SET_FRONTBODY_: case SET_BACKBODY_: b_id = get_ordinal_id(BODY,(int)*(stacktop--)-1); if ( b_id && !valid_id(b_id) ) { sprintf(errmsg,"Invalid body in SET FRONTBODY or BACKBODY.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2027,errmsg,RECOVERABLE); } set_body((node->op2.attr_kind==SET_FRONTBODY_?id:inverse_id(id)),b_id); *recalc_flag = 1; break; default: sprintf(errmsg,"Unhandled SET attribute %d\n",node->op2.attr_kind); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2028,errmsg,RECOVERABLE); break; } /* end SET_ATTRIBUTE */ break; /***************************** * assignop attribute values * * for single element assigns * *****************************/ case SET_ATTRIBUTE_A: { int assign_type = node[1].op1.assigntype; if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; parallel_update_flag[id_type(id)] = 1; /* set when element info changed */ switch ( node->op2.attr_kind ) { /* Those attributes not fitted for arithmetic assignment have error test for that in yexparse() */ case SET_ORIENTATION_: if ( *(stacktop--) >= 0.0 ) unset_attr(id,NEGBOUNDARY); else set_attr(id,NEGBOUNDARY); break; case SET_EXTRA_ATTR_: { struct extra *ext; int spot; /* index */ n = node->op3.extra_info & ((1<op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ext = EXTRAS(id_type(id)) + n; /* get index */ spot = 0; for ( k = 0 ; k < ext->array_spec.dim ; k++ ) { int j = (int)(stacktop[-ext->array_spec.dim+k+1]); spot *= ext->array_spec.sizes[k]; if ( j > ext->array_spec.sizes[k] ) { sprintf(errmsg, "Attribute %s index %d is %d; maximum is %d (in %s).\n", ext->name,k+1,j,ext->array_spec.sizes[k],ext->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2524,errmsg,RECOVERABLE); } if ( j < 1 ) { sprintf(errmsg,"Attribute %s index %d is %d; must be positive.\n", ext->name,k,j); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3365,errmsg,RECOVERABLE); } spot += (int)(stacktop[-ext->array_spec.dim+k+1]) - 1; } stacktop -= ext->array_spec.dim; #define ATTRCASE(type) \ switch ( assign_type ) \ { case ASSIGN_: \ ((type*)get_extra(id,n))[spot] = (type)*(stacktop--); break; \ case PLUSASSIGN_: \ ((type*)get_extra(id,n))[spot] += (type)*(stacktop--); break; \ case SUBASSIGN_: \ ((type*)get_extra(id,n))[spot] -= (type)*(stacktop--); break; \ case MULTASSIGN_: \ ((type*)get_extra(id,n))[spot] *= (type)*(stacktop--); break; \ case DIVASSIGN_: \ ((type*)get_extra(id,n))[spot] /= (type)*(stacktop--); break; \ } switch ( ext->type ) { case REAL_TYPE: switch ( assign_type ) { case ASSIGN_: ((REAL*)get_extra(id,n))[spot] = *(stacktop--); break; case PLUSASSIGN_: ((REAL*)get_extra(id,n))[spot] += *(stacktop--); break; case SUBASSIGN_: ((REAL*)get_extra(id,n))[spot] -= *(stacktop--); break; case MULTASSIGN_: ((REAL*)get_extra(id,n))[spot] *= *(stacktop--); break; case DIVASSIGN_: ((REAL*)get_extra(id,n))[spot] /= *(stacktop--); break; } break; case INTEGER_TYPE: switch ( assign_type ) { case ASSIGN_: ((int*)get_extra(id,n))[spot] = (int)*(stacktop--); break; case PLUSASSIGN_: ((int*)get_extra(id,n))[spot] += (int)*(stacktop--); break; case SUBASSIGN_: ((int*)get_extra(id,n))[spot] -= (int)*(stacktop--); break; case MULTASSIGN_: ((int*)get_extra(id,n))[spot] = (int)(((int*)get_extra(id,n))[spot] * *(stacktop--));break; case DIVASSIGN_: ((int*)get_extra(id,n))[spot] = (int)(((int*)get_extra(id,n))[spot] / *(stacktop--));break; } break; case ULONG_TYPE: ATTRCASE(unsigned long int); break; case LONG_TYPE: ATTRCASE(long int); break; case UINT_TYPE: ATTRCASE(unsigned int); break; case UCHAR_TYPE: ATTRCASE(unsigned char); break; case CHAR_TYPE: ATTRCASE(char); break; case SHORT_TYPE: ATTRCASE(short int); break; case USHORT_TYPE: ATTRCASE(unsigned short int); break; case ELEMENTID_TYPE: case VERTEX_TYPE: case EDGE_TYPE: case FACET_TYPE: case BODY_TYPE: case FACETEDGE_TYPE: switch ( assign_type ) { case ASSIGN_: ((element_id*)get_extra(id,n))[spot] = *(element_id*)(stacktop--); break; default: kb_error(3453, "Illegal assignment operation on element id attribute.\n", RECOVERABLE); } case PTR_TYPE: switch ( assign_type ) { case ASSIGN_: ((char**)get_extra(id,n))[spot] = *(char**)(stacktop--); break; default: kb_error(3454, "Illegal assignment operation on pointer attribute.\n", RECOVERABLE); } case BOUNDARY_TYPE: case CONSTRAINT_TYPE: case QUANTITY_TYPE: case INSTANCE_TYPE: case PROCEDURE_TYPE: switch ( assign_type ) { case ASSIGN_: ((int*)get_extra(id,n))[spot] = *(int*)(stacktop--); break; default: kb_error(3682, "Illegal assignment operation on attribute.\n", RECOVERABLE); } } } break; case SET_DENSITY_: { REAL density = *(stacktop--); REAL oldvalue = 0.0; if ( density != 1.0 ) set_attr(id,DENSITY); switch ( id_type(id) ) { case EDGE: oldvalue = get_edge_density(id); break; case FACET: oldvalue = get_facet_density(id); break; case BODY: oldvalue = get_body_density(id); break; } switch ( assign_type ) { case ASSIGN_: break; case PLUSASSIGN_: density += oldvalue; break; case SUBASSIGN_: density = oldvalue - density; break; case MULTASSIGN_: density *= oldvalue; break; case DIVASSIGN_: density = oldvalue / density; break; } switch ( id_type(id) ) { case EDGE: set_edge_density(id,density); pressure_set_flag = 0; break; case FACET: set_facet_density(id,density); pressure_set_flag = 0; break; case BODY: set_body_density(id,density); web.gravflag = 1; if (gravity_quantity_num >= 0 ) GEN_QUANT(gravity_quantity_num)->modulus = web.grav_const; break; } *recalc_flag = 1; } break; case SET_PHASE_: { int phase = (int)*(stacktop--); switch ( id_type(id) ) { case BODY: set_b_phase(id,phase); break; case FACET: set_f_phase(id,phase); break; } *recalc_flag = 1; } break; case SET_VOLCONST_: { REAL v = *(stacktop--); REAL oldvalue = get_body_volconst(id); switch ( assign_type ) { case ASSIGN_: break; case PLUSASSIGN_: v += oldvalue; break; case SUBASSIGN_: v = oldvalue - v; break; case MULTASSIGN_: v = oldvalue * v; break; case DIVASSIGN_: v = oldvalue / v; break; } set_body_volconst(id,v); set_body_volume(id,get_body_volume(id)+v-oldvalue,SETSTAMP); set_body_oldvolume(id,get_body_oldvolume(id)+v-oldvalue); if ( everything_quantities_flag ) GEN_QUANT(get_body_volquant(id))->volconst = v; *recalc_flag = 1; break; } case SET_TARGET_: { REAL v = *(stacktop--); REAL oldvalue = get_body_fixvol(id); switch ( assign_type ) { case ASSIGN_: break; case PLUSASSIGN_: v += oldvalue; break; case SUBASSIGN_: v = oldvalue - v; break; case MULTASSIGN_: v = oldvalue * v; break; case DIVASSIGN_: v = oldvalue / v; break; } if ( get_attr(id) & PRESSURE ) { sprintf(errmsg,"Must unset body pressure before fixing target.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2029,errmsg,RECOVERABLE); } set_attr(id,FIXEDVOL); set_body_fixvol(id,v); *recalc_flag = 1; break; } case SET_PRESSURE_: { REAL p = *(stacktop--); REAL oldvalue = get_body_pressure(id); switch ( assign_type ) { case ASSIGN_: break; case PLUSASSIGN_: p += oldvalue; break; case SUBASSIGN_: p = oldvalue - p; break; case MULTASSIGN_: p = oldvalue * p; break; case DIVASSIGN_: p = oldvalue / p; break; } if ( get_attr(id) & FIXEDVOL ) { sprintf(errmsg,"Must unset body target before fixing pressure.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2030,errmsg,RECOVERABLE); } set_attr(id,PRESSURE); set_body_pressure(id,p); web.pressflag = 1; if ( everything_quantities_flag ) { struct gen_quant *q = GEN_QUANT(get_body_volquant(id)); if ( p == 0.0 ) { q->modulus = 1.0; if ( !(q->flags & Q_INFO) ) { q->flags &= ~(Q_ENERGY|Q_FIXED|Q_CONSERVED); q->flags |= Q_INFO; } } else { q->modulus = -p; if ( !(q->flags & Q_ENERGY ) ) { q->flags &= ~(Q_INFO|Q_FIXED|Q_CONSERVED); q->flags |= Q_ENERGY; } } } *recalc_flag = 1; break; } case SET_OPACITY_: facet_alpha = *(stacktop)--; break; case SET_CONSTRAINT_: { int con = (int)*(stacktop--); struct constraint *constr = get_constraint(con); if ( (con<0) || (con>=web.maxcon) || !(constr->attr & IN_USE)) { sprintf(errmsg,"Illegal constraint number: %d.\n",con); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1146,errmsg,RECOVERABLE); } set_attr(id,CONSTRAINT); if ( constr->attr & CON_ENERGY ) set_attr(id, BDRY_ENERGY); if ( constr->attr & CON_CONTENT ) set_attr(id, BDRY_CONTENT); switch ( id_type(id) ) { case VERTEX: set_v_constraint_map(id,con); project_v_constr(id,ACTUAL_MOVE,RESET_ONESIDEDNESS); break; case EDGE: set_e_constraint_map(id,con); break; case FACET: set_f_constraint_map(id,con); break; } *recalc_flag = 1; } break; case SET_ORIGINAL_: set_original(id,(int)*(stacktop--) | ((element_id)id_type(id)<right ) /* indexed */ { int dim = (int)*(stacktop--)-1; REAL oldvalue = get_coord(id)[dim]; REAL v; if ( (dim >= SDIM) || (dim < 0) ) { sprintf(errmsg, "Trying to set coordinate %d, in space dimension %d.\n", dim,SDIM); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2031,errmsg,RECOVERABLE); } v = *(stacktop--); switch ( assign_type ) { case ASSIGN_: break; case PLUSASSIGN_: v += oldvalue; break; case SUBASSIGN_: v = oldvalue - v; break; case MULTASSIGN_: v = oldvalue * v; break; case DIVASSIGN_: v = oldvalue / v; break; } get_coord(id)[dim] = v; *recalc_flag = 1; break; } /* else fall through */ case SET_COORD_2: case SET_COORD_3: case SET_COORD_4: case SET_COORD_5: case SET_COORD_6: case SET_COORD_7: case SET_COORD_8: { REAL v = *(stacktop--); REAL oldvalue = get_coord(id)[node->op2.attr_kind-SET_COORD_1]; switch ( assign_type ) { case ASSIGN_: break; case PLUSASSIGN_: v += oldvalue; break; case SUBASSIGN_: v = oldvalue - v; break; case MULTASSIGN_: v = oldvalue * v; break; case DIVASSIGN_: v = oldvalue / v; break; } get_coord(id)[node->op2.attr_kind-SET_COORD_1] = v; if ( node->op2.attr_kind-SET_COORD_1 <= SDIM ) *recalc_flag = 1; } break; case SET_PARAM_: case SET_PARAM_1: case SET_PARAM_2: case SET_PARAM_3: case SET_PARAM_4: if ( get_vattr(id) & BOUNDARY ) { struct boundary *boundary = get_boundary(id); REAL *param = get_param(id); REAL *xx = get_coord(id); REAL v; int pnum = node->op2.attr_kind-SET_PARAM_1; REAL oldvalue = param[pnum]; if ( node->right ) pnum += (int)*(stacktop--); if ( pnum > boundary->pcount ) { sprintf(errmsg,"Parameter number is %d; maximum is %d.\n", pnum,boundary->pcount); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1213,errmsg,RECOVERABLE); } v = *(stacktop--); switch ( assign_type ) { case ASSIGN_: break; case PLUSASSIGN_: v += oldvalue; break; case SUBASSIGN_: v = oldvalue - v; break; case MULTASSIGN_: v = oldvalue * v; break; case DIVASSIGN_: v = oldvalue / v; break; } param[pnum] = v; for ( k = 0 ; k < SDIM ; k++ ) xx[k] = eval(boundary->coordf[k],param,id,NULL); *recalc_flag = 1; } else /* just parking a value probably */ { REAL *param = get_param(id); int pnum = node->op2.attr_kind-SET_PARAM_1; REAL oldvalue = param[pnum]; REAL v; if ( node->right ) pnum += (int)*(stacktop--); v = *(stacktop--); switch ( assign_type ) { case ASSIGN_: break; case PLUSASSIGN_: v += oldvalue; break; case SUBASSIGN_: v = oldvalue - v; break; case MULTASSIGN_: v = oldvalue * v; break; case DIVASSIGN_: v = oldvalue / v; break; } param[pnum] = v; } break; case SET_FRONTBODY_: case SET_BACKBODY_: b_id = get_ordinal_id(BODY,(int)*(stacktop--)-1); if ( !valid_id(b_id) ) { sprintf(errmsg,"Invalid body in SET FRONTBODY or BACKBODY.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2032,errmsg,RECOVERABLE); } set_body((node->op2.attr_kind==SET_FRONTBODY_?id:inverse_id(id)),b_id); *recalc_flag = 1; break; default: sprintf(errmsg,"Unhandled SET attribute %d\n",node->op2.attr_kind); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2033,errmsg,RECOVERABLE); break; } /* end assigned SET_ATTRIBUTE_A */ } break; /******************************/ /* initializing element loops */ /******************************/ case INIT_VERTEX_: /* all vertices */ *(element_id *)++stacktop = q_id; /* save old element */ *(vertex_id*)++stacktop=web.skel[VERTEX].last; /* sentinel */ *(element_id *)++stacktop = web.skel[VERTEX].used; break; case INIT_EDGE_VERTEX_: /* edge endpoints */ if ( node->op2.localnum ) id = *(element_id*)get_localp(node->op2.localnum); else id = q_id; *(element_id *)++stacktop = id; /* save old element */ *(int *)++stacktop = 0; /* element number */ *(element_id **)++stacktop = get_edge_vertices(id); break; case INIT_FACET_VERTEX_: /* facet vertices */ if ( node->op2.localnum ) id = *(element_id*)get_localp(node->op2.localnum); else id = q_id; *(element_id *)++stacktop = id; /* save old element */ if ( (web.representation == SIMPLEX) || (web.modeltype==LAGRANGE) || ((web.modeltype==QUADRATIC) && (web.representation==SOAPFILM))) { *(element_id *)++stacktop = 0 ; /* counter */ *(element_id *)++stacktop = id ; /* facet */ } else { facetedge_id fe = get_facet_fe(id); if ( inverted(id) && web.representation==STRING && valid_id(fe) ) { facetedge_id nextfe=fe,startfe=fe; /* go back round to find start */ do { fe = nextfe; nextfe = get_prev_edge(fe); } while ( valid_id(nextfe) && !equal_id(nextfe,startfe) ); } *(element_id *)++stacktop = fe; /* start */ *(element_id *)++stacktop = fe; /* current */ } break; case INIT_BODY_VERTEX_: /* vertices on body */ sprintf(errmsg,"Can't do body vertices yet.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1214,errmsg,RECOVERABLE); break; case INIT_EDGE_: /* all edges */ *(element_id *)++stacktop = q_id; /* save old element */ *(edge_id *)++stacktop = web.skel[EDGE].last; /* sentinel */ *(element_id *)++stacktop = web.skel[EDGE].used; break; case INIT_VERTEX_EDGE_: if ( node->op2.localnum ) id = *(element_id*)get_localp(node->op2.localnum); else id = q_id; *(element_id *)++stacktop = id; /* save old element */ if ( get_vattr(id) & Q_MIDFACET ) { *(element_id *)++stacktop = NULLID; *(element_id *)++stacktop = NULLID; } else { *(element_id *)++stacktop = get_vertex_edge(id); *(element_id *)++stacktop = get_vertex_edge(id); } break; case INIT_FACET_EDGE_: if ( node->op2.localnum ) id = *(element_id*)get_localp(node->op2.localnum); else id = q_id; *(element_id *)++stacktop = id; /* save old element */ if ( web.representation == STRING ) { facetedge_id fe = get_facet_fe(id); if ( inverted(id) && valid_id(fe) ) { facetedge_id nextfe=fe,startfe=fe; /* go back round to find start */ do { fe = nextfe; nextfe = get_prev_edge(fe); } while ( valid_id(nextfe) && !equal_id(nextfe,startfe) ); } *(element_id *)++stacktop = fe; /* start */ *(element_id *)++stacktop = fe; } else { *(int *)++stacktop = 0; /* counter */ *(element_id *)++stacktop = get_facet_fe(id); } break; case INIT_BODY_EDGE_: sprintf(errmsg,"Can't do body edges yet.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1215,errmsg,RECOVERABLE); break; case INIT_FACET_: /* all facets */ *(element_id *)++stacktop = q_id; /* save old element */ *(facet_id *)++stacktop = web.skel[FACET].last; /* sentinel */ *(element_id *)++stacktop = web.skel[FACET].used; break; case INIT_VERTEX_FACET_: if ( node->op2.localnum ) id = *(element_id*)get_localp(node->op2.localnum); else id = q_id; *(element_id *)++stacktop = id; /* save old element */ *(element_id *)++stacktop = f_id = get_vertex_first_facet(id); *(element_id *)++stacktop = f_id; break; case INIT_FACETEDGE_EDGE_: if ( node->op2.localnum ) id = *(element_id*)get_localp(node->op2.localnum); else id = q_id; *(element_id *)++stacktop = id; /* save old element */ *(element_id *)++stacktop = get_fe_edge(id); /* start */ *(element_id *)++stacktop = get_fe_edge(id); break; case INIT_FACETEDGE_FACET_: if ( node->op2.localnum ) id = *(element_id*)get_localp(node->op2.localnum); else id = q_id; *(element_id *)++stacktop = id; /* save old element */ *(element_id *)++stacktop = get_fe_facet(id); /* start */ *(element_id *)++stacktop = get_fe_facet(id); break; case INIT_EDGE_FACET_: if ( node->op2.localnum ) id = *(element_id*)get_localp(node->op2.localnum); else id = q_id; *(element_id *)++stacktop = id; /* save old element */ *(element_id *)++stacktop = get_edge_fe(id); /* start */ *(element_id *)++stacktop = get_edge_fe(id); break; case INIT_EDGE_FACETEDGE_: if ( node->op2.localnum ) id = *(element_id*)get_localp(node->op2.localnum); else id = q_id; *(element_id *)++stacktop = id; /* save old element */ *(element_id *)++stacktop = get_edge_fe(id); /* start */ *(element_id *)++stacktop = get_edge_fe(id); break; case INIT_BODY_FACET_: if ( node->op2.localnum ) id = *(element_id*)get_localp(node->op2.localnum); else id = q_id; *(element_id *)++stacktop = q_id; /* save old element */ f_id = get_body_facet(id); *(element_id *)++stacktop = f_id; *(element_id *)++stacktop = f_id; /* kludge to make things work in the face of arbitrary deletions. */ if ( valid_id(f_id) ) { facet_id start_f = f_id; int counter = 0; int maxcount = 2*web.skel[FACET].count; do { f_id = get_next_body_facet(f_id); unset_attr(f_id,DID_BODYFRONTFACET|DID_BODYBACKFACET); if ( counter++ > maxcount ) { sprintf(errmsg,"Internal error: Body %s facet list is not closed.\n", ELNAME(q_id)); kb_error(4376,errmsg,RECOVERABLE); } } while ( !equal_id(f_id,start_f) ); set_attr(f_id,(inverted(f_id)?DID_BODYBACKFACET:DID_BODYFRONTFACET)); } break; case INIT_BODY_: /* all bodies */ *(element_id *)++stacktop = q_id; /* save old element */ *(body_id *)++stacktop = web.skel[BODY].last; /* sentinel */ *(element_id *)++stacktop = web.skel[BODY].used; break; case INIT_FACET_BODY_: if ( node->op2.localnum ) id = *(element_id*)get_localp(node->op2.localnum); else id = q_id; *(element_id *)++stacktop = id; /* save old element */ *(int *)++stacktop = 0; /* element number */ *(element_id *)++stacktop = NULLID; break; case INIT_VERTEX_BODY_: sprintf(errmsg,"Can't do vertex bodies yet.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1216,errmsg,RECOVERABLE); break; case INIT_EDGE_BODY_: sprintf(errmsg,"Can't do edge bodies yet.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1217,errmsg,RECOVERABLE); break; case INIT_FACETEDGE_: /* all edges */ *(element_id *)++stacktop = q_id; /* save old element */ *(edge_id *)++stacktop = web.skel[FACETEDGE].last; /* sentinel */ *(element_id *)++stacktop = web.skel[FACETEDGE].used; break; case SINGLE_ELEMENT_INIT_: *(element_id *)++stacktop = q_id; /* save old element */ *(++stacktop) = 0.0 ; /* iteration count */ stacktop++; /* dummy local to pop */ break; /*******************/ /* simple commands */ /*******************/ case NOP_: case NULLBLOCK_: case NULLCMD_: break; case DEFINE_EXTRA_: { int one = 1; struct extra *ex = EXTRAS(node->op2.eltype) + node->op1.extranum; if ( ex->array_spec.dim == 0 ) { expand_attribute(node->op2.eltype,node->op1.extranum,&one); ex->array_spec.datacount = 1; ex->flags &= ~DIMENSIONED_ATTR; } break; } case DEFINE_EXTRA_INDEX_: /* have indexes tacked onto DEFINE_EXTRA_ */ { int newsizes[MAXARRAYDIMS]; struct extra *ex = EXTRAS(node->op2.eltype) + node->op1.extranum; for ( k = ex->array_spec.dim-1 ; k >= 0 ; k-- ) newsizes[k] = (int)*(stacktop--); expand_attribute(node->op2.eltype,node->op1.extranum,newsizes); break; } case HISTORY_: if ( history_space == NULL ) { outstring("No history.\n"); break;} for ( k = history_count-1 ; k >= 0 ; k-- ) { h = history_space+history_offsets[k]; sprintf(msg,"%d) ",history_number-k); outstring(msg); outstring(h); } break; case REDIRECT_: case REDIRECTOVER_: { struct stat st; int retval; if ( node->left ) s = *(char**)(stacktop--); else s = node->op1.string; *(FILE **)(++stacktop) = outfd; retval = stat(s,&st); outfd = NULL; if ( ((retval == 0) && (st.st_mode & S_IFIFO)) || (node->type==REDIRECTOVER_) ) outfd = fopen(s,"w"); else outfd = fopen(s,"a"); if ( outfd == NULL ) { #ifdef MAC_APP sprintf(errmsg,"Cannot open file %s. \n",s); #else perror(s); sprintf(errmsg,"Cannot open redirection file %s.\n",s); #endif sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1218,errmsg,RECOVERABLE); } if ( node->left && (node[node->left].flags & DEALLOCATE_POINTER) ) myfree(s); } break; case REDIRECT_END_: fclose(outfd); /* outfd = stdout; */ outfd = *(FILE**)(stacktop--); break; case PIPE_: if ( node->left ) s = *(char**)(stacktop--); else s = node->op1.string; *(FILE **)(++stacktop) = outfd; if ( s[0] == '+' ) /* kludge to permit appending to file */ outfd = fopen(s+1,"a+"); #ifdef NOPIPE else outfd = fopen(s,"w"); #else outfd = popen(s,"w"); #endif if ( outfd == NULL ) { outfd = *(FILE**)(stacktop--); #ifdef MAC_APP sprintf(errmsg,"Cannot open pipe %s. \n",s); #else perror(s); sprintf(errmsg,"Cannot open pipe %s.\n",s); #endif sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1219,errmsg,RECOVERABLE); } if ( node->left && (node[node->left].flags & DEALLOCATE_POINTER) ) myfree(s); broken_pipe_flag = 0; /* reset */ break; case PIPE_END_: #ifdef NOPIPE fclose(outfd); #else pclose(outfd); #endif /* outfd = stdout; */ outfd = *(FILE**)(stacktop--); break; case DEBUG_: outstring(yydebug?"YACC debugging was ON.": "YACC debugging was OFF.\n"); yydebug = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(yydebug?"Now ON.\n":"Now OFF.\n"); break; case ITDEBUG_: outstring(itdebug?"Iteration debugging was ON.": "Iteration debugging was OFF.\n"); itdebug = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(itdebug?"Now ON.\n":"Now OFF.\n"); break; case MEMDEBUG_: old = memdebug; memdebug = (node->op1.toggle_state==ON_) ? 1 : 0; #ifdef M_DEBUG mallopt(M_DEBUG,memdebug); #endif outstring(memdebug?"Memory debugging ON.": "Memory debugging OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case RECALC_: recalc(); break; case CLOSE_SHOW_: close_graphics(); break; case TOPINFO_: oldquiet = quiet_flag; quiet_flag = 0; top_dump(outfd); quiet_flag = oldquiet; break; case BOTTOMINFO_: oldquiet = quiet_flag; quiet_flag = 0; bottom_dump(outfd); quiet_flag = oldquiet; break; case LIST_QUANTITY_: list_quantity(node->op1.quant_id); break; case LIST_METHOD_INSTANCE_: list_method_instance(node->op1.meth_id); break; case LIST_CONSTRAINT_: k = (int)(*(stacktop--)); if ( k < 0 || k >= web.maxcon || !(get_constraint(k)->attr & IN_USE) ) kb_error(3614,"Invalid constraint for list_constraint.", RECOVERABLE); list_constraint(k); break; case LIST_BOUNDARY_: k = (int)(*(stacktop--)); if ( k < 0 || k >= web.bdrymax || !(web.boundaries[k].attr & IN_USE) ) kb_error(3615,"Invalid boundary for list_boundary.", RECOVERABLE); list_boundary(k); break; case LIST_PROCS_: oldquiet = quiet_flag; quiet_flag = 0; list_procedures(LIST_PROTO); quiet_flag = oldquiet; break; case LIST_ATTRIBUTES_: oldquiet = quiet_flag; quiet_flag = 0; outstring("//Element attributes: \n"); list_attributes(); quiet_flag = oldquiet; break; case PRINT_ARRAY_: oldquiet = quiet_flag; quiet_flag = 0; print_array(globals(node->op1.name_id)->attr.arrayptr,NULL); quiet_flag = oldquiet; break; case PRINT_ARRAY_LVALUE_: { struct array *a,kludge; oldquiet = quiet_flag; quiet_flag = 0; a = get_name_arrayptr(node->op2.name_id,localstack,localbase); if ( node[node->left].flags & IS_VIRTUAL_ATTR ) { kludge = *a; kludge.datacount = SDIM; kludge.sizes[0] = SDIM; a = &kludge; } print_array(a,*(char**)(stacktop--)); quiet_flag = oldquiet; break; } case PRINT_ARRAYPART_: { struct array *part; struct global *g = globals(node[node->left].op1.name_id); int offset = 0; struct array *a; if ( g->flags & GLOB_LOCALVAR ) a = *(struct array **)(localstack+g->value.offset); else a = g->attr.arrayptr; part = (struct array *)temp_calloc(1,sizeof(struct array)+ a->dim*sizeof(int)); part->dim = a->dim - node->op1.indexcount; part->datatype = a->datatype; part->itemsize = a->itemsize; part->datacount = a->datacount; part->datastart = a->datastart + ((char*)(a) - (char*)part); for ( n = 0; n < node->op1.indexcount ; n++ ) { int size = a->sizes[n]; int inx = (int)*(stacktop - node->op1.indexcount + n + 1) - 1; if ( inx >= size ) { sprintf(errmsg, "Index %d of array %s is %d; exceeds declared size, %d\n", n+1,g->name,inx+1,size); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2610,errmsg,RECOVERABLE); } if ( size ) part->datacount /= size; offset *= size; offset += inx; } stacktop -= n; for ( k = 0 ; n < a->dim ; n++,k++ ) { int size = a->sizes[n]; part->sizes[k] = size; offset *= size; } part->datastart += offset*part->itemsize; msg[0] = 0; oldquiet = quiet_flag; quiet_flag = 0; print_array(part,NULL); quiet_flag = oldquiet; temp_free((char*)part); break; } case PRINT_ATTR_ARRAY_: { struct array *part; struct extra *ext; int offset = 0; n = node->op3.extranum; /* attribute number */ id = *(element_id*)get_localp((node+node->left)->op2.localnum); ext = EXTRAS(node->op2.eltype) + n; part = (struct array *)temp_calloc(1,sizeof(struct array)+ ext->array_spec.dim*sizeof(int)); part->dim = ext->array_spec.dim - node->op1.indexcount; part->datatype = ext->type; part->itemsize = ext->array_spec.itemsize; part->datacount = ext->array_spec.datacount; part->datastart = (char*)get_extra(id,n) - (char*)part; for ( n = 0; n < node->op1.indexcount ; n++ ) { int size = ext->array_spec.sizes[n]; int inx = (int)*(stacktop - node->op1.indexcount + n + 1) - 1; if ( inx >= size ) { sprintf(errmsg, "Index %d of attribute %s is %d; exceeds declared size, %d\n", n+1,ext->name,inx+1,size); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2645,errmsg,RECOVERABLE); } if ( size ) part->datacount /= size; offset *= size; offset += inx; } stacktop -= n; for ( k = 0 ; n < ext->array_spec.dim ; n++,k++ ) { int size = ext->array_spec.sizes[n]; part->sizes[k] = size; offset *= size; } part->datastart += offset*part->itemsize; msg[0] = 0; oldquiet = quiet_flag; quiet_flag = 0; print_array(part,NULL); quiet_flag = oldquiet; temp_free((char*)part); break; } case DIRICHLET_: case DIRICHLET_SEEK_: scale = dirichlet(node->type==DIRICHLET_?0:1); recalc(); #ifdef LONGDOUBLE sprintf(msg,"%3d. energy: %#*.*Lg scale: %#g\n",gocount, DWIDTH,DPREC, web.total_energy,(DOUBLE)scale); #else sprintf(msg,"%3d. %s: %#17.15g energy: %#17.15g scale: %#g\n",gocount, areaname,web.total_area,web.total_energy,scale); #endif outstring(msg); break; case SOBOLEV_: case SOBOLEV_SEEK_: scale = sobolev(node->type==SOBOLEV_?0:1); recalc(); #ifdef LONGDOUBLE sprintf(msg,"%3d. energy: %#*.*Lg scale: %#g\n",gocount, DWIDTH,DPREC,web.total_energy,(DOUBLE)scale); #else sprintf(msg,"%3d. %s: %#17.15g energy: %#17.15g scale: %#g\n",gocount, areaname,web.total_area,web.total_energy,scale); #endif outstring(msg); break; case HESSIAN_SEEK_: { REAL maxscale; if ( node->left ) maxscale = *(stacktop--); else maxscale = 10.; scale = hessian_seek(maxscale); recalc(); #ifdef LONGDOUBLE sprintf(msg,"%3d. energy: %#*.*Lg scale: %#g\n",gocount, DWIDTH,DPREC,web.total_energy,(DOUBLE)scale); #else sprintf(msg,"%3d. %s: %#17.15g energy: %#17.15g scale: %#g\n", gocount, areaname,web.total_area,web.total_energy,scale); #endif outstring(msg); } break; case HESSIAN_SADDLE_: { REAL maxscale; if ( node->left ) maxscale = *(stacktop--); else maxscale = 100.; hessian_saddle(maxscale); } break; case HESSIAN_MENU_: if ( hessian_subshell_flag ) { kb_error(3633,"Can't do hessian_menu in a hessian subshell.\n",WARNING); break; } hessian_menu(); recalc(); #ifdef LONGDOUBLE sprintf(msg,"%3d. energy: %#*.*Lg \n",1, DWIDTH,DPREC,web.total_energy); #else sprintf(msg,"%3d. %s: %#17.15g energy: %#17.15g \n",1, areaname,web.total_area,web.total_energy); #endif outstring(msg); break; case HESSIAN_ : hessian_auto(); recalc(); #ifdef LONGDOUBLE sprintf(msg,"%3d. energy: %#*.*Lg \n",gocount, DWIDTH, DPREC,web.total_energy); #else sprintf(msg,"%3d. %s: %#17.15g energy: %#17.15g\n",gocount, areaname,web.total_area,web.total_energy); #endif outstring(msg); break; case SHELL_: /* execute subshell */ #if defined(__WIN32__) || defined(_WIN32) spawnlp(P_WAIT,"command",NULL); #else #if defined(MAC_APP) || defined(MAC_CW) kb_error(1221,"No subshell on Mac.\n",RECOVERABLE); #else if ( fork() == 0 ) { execlp("sh","sh",NULL); perror("sh"); } else wait(NULL); #endif #endif break; case SHOW_TRANS_: view_transform(*(char**)(stacktop--)); update_display(); break; case CHECK_: run_checks(); break; case CONVERT_TO_QUANTS_: if ( everything_quantities_flag ) outstring("Everything already quantities.\n"); else { convert_to_quantities(); recalc(); } break; case SHOW_VOL_: show_volumes(); break; case LOGFILE_: start_logfile(*(char**)(stacktop--)); outstring("Logfile ON.\n"); break; case KEYLOGFILE_: start_keylogfile(*(char**)(stacktop--)); outstring("Keylogfile ON.\n"); break; case GEOMVIEW_: geomview_command(*(char**)(stacktop--)); break; case IS_DEFINED_: *stacktop = (identcase(*(char**)stacktop) != NEWIDENT_); break; case REPARTITION_: #ifdef MPI_EVOLVER mpi_repartition(); update_display(); #else kb_error(5005,"'Repartition' command only implemented in MPI Evolver.\n", WARNING); #endif break; case EXEC_: command(*(char**)(stacktop--),NO_HISTORY); break; case PARALLEL_EXEC_: #ifdef MPI_EVOLVER mpi_parallel_exec(*(char**)(stacktop--)); #else command(*(char**)(stacktop--),NO_HISTORY); #endif break; case TASK_EXEC_: { int task = (int)(stacktop[-1]); #ifdef MPI_EVOLVER if ( task == 0 ) command(*(char**)(stacktop--),NO_HISTORY); else if ( task >= mpi_nprocs ) { sprintf(errmsg,"Node number %d exceeds maximum node number, %d.\n", task,mpi_nprocs-1); kb_error(3167,errmsg,RECOVERABLE); } else if ( task < 0 ) { sprintf(errmsg,"Node number %d is negative!\n", task); kb_error(3168,errmsg,RECOVERABLE); } else mpi_task_exec(task,*(char**)(stacktop--)); #else if ( task != 0 ) kb_error(3169,"Node_exec node number must be 0 for non-MPI Evolver.\n",RECOVERABLE); command(*(char**)(stacktop--),NO_HISTORY); #endif stacktop--; /* pop node number */ } break; case SYSTEM_: system(*(char**)(stacktop--)); break; case CHDIR_: #ifdef MSC k = _chdir(*(char**)(stacktop--)); if ( k < 0 ) { sprintf(errmsg,"Unable to change directory. \n"); if ( !strchr(*(char**)(stacktop+1),'\\') ) strcat(errmsg,"Try using \\\\ or / instead of \\.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2034,errmsg,RECOVERABLE); } #else k = chdir(*(char**)(stacktop--)); if ( k < 0 ) { sprintf(errmsg,"Unable to change to directory \"%s\".\n", *(char**)(stacktop+1)); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1116,errmsg,RECOVERABLE); } #endif break; case READ_: if ( read_depth <= 1 ) warning_messages_new = 0; exec_file(NULL,*(char**)(stacktop--)); break; case VIEW_TRANSFORM_SWAP_COLORS_: { int k = (int)(*stacktop--); if ( (k < 1) || (k > transform_count) ) { sprintf(errmsg, "Invalid index %d to view_transform_swap_colors.\n",k); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2518,errmsg, RECOVERABLE); } if ( !transform_colors ) *(++stacktop) = 0; else *(++stacktop) = (transform_colors[k-1] == SWAP_COLORS); break; } case VIEW_TRANSFORM_PARITY_: { int k = (int)(*stacktop--); if ( !transform_parity ) *(++stacktop) = 1; else *(++stacktop) = transform_parity[k-1]; break; } case VIEW_TRANSFORMS_: read_transforms((int)(*stacktop--)); break; case VIEW_TRANSFORMS_NOP_: break; /* just to get first two indices on stack */ case VIEW_TRANSFORMS_ELEMENT_: { int k = (int)(*stacktop--); int j = (int)(*stacktop--); int i = (int)(*stacktop--); if ( i < 1 ) { sprintf(errmsg, "View_transforms index 1 is %d, must be at least 1.\n",i); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2036,errmsg,RECOVERABLE); } if ( j < 1 ) { sprintf(errmsg, "View_transforms index 2 is %d, must be at least 1.\n",j); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2037,errmsg,RECOVERABLE); } if ( k < 1 ) { sprintf(errmsg, "View_transforms index 3 is %d, must be at least 1.\n",k); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2038,errmsg,RECOVERABLE); } if ( (view_transforms == 0) && (i == 1)) { *(++stacktop) = (j==k) ? 1 : 0; break; } if ( i > transform_count ) { sprintf(errmsg, "View_transforms index 1 is %d, must be at most transform_count %d.\n",i,transform_count); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2039,errmsg,RECOVERABLE); } if ( j > 4 ) { sprintf(errmsg, "View_transforms index 2 is %d, must be at most 4.\n",j); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2040,errmsg,RECOVERABLE); } if ( k > 4 ) { sprintf(errmsg, "View_transforms index 3 is %d, must be at most 4.\n",k); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2041,errmsg,RECOVERABLE); } *(++stacktop) = view_transforms[i-1][j-1][k-1]; break; } case TRANSFORM_DEPTH_: generate_transforms((int)(*stacktop--)); update_display(); break; case TRANSFORM_EXPR_: calc_view_transform_gens(); transform_gen_expr(*(char**)(stacktop--)); update_display(); break; case DEFINE_METHOD_INSTANCE_: case DEFINE_QUANTITY_: case DEFINE_CONSTRAINT_: case DEFINE_BOUNDARY_: /* was parse-time action */ break; case GRAVITY_: old = web.gravflag; web.gravflag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(web.gravflag ? "Gravity ON." : "Gravity OFF."); outstring(old?" (was on)\n":" (was off)\n"); recalc(); break; case TOGGLEVALUE: *++stacktop = (REAL)get_toggle_value(node->op1.toggle_id); break; case FACET_COLORS_: old = color_flag; color_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(color_flag ? "Facet colors ON.": "Facet colors OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case SHADING_: old = shading_flag; shading_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(shading_flag ? "Facet shading ON.": "Facet shading OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case PREPRINTF_: case PRESPRINTF_: break; case GO_: iterate(); break; default: more_other_stuff(node,recalc_flag, update_display_flag,q_id,localstack,localbase); break; } } /* end other_stuff() */ /****************************************************************** * * Function: flip_toggle() * * Purpose: Utility function to flip a toggle. Put here so Mac 68K * can make a short jump to it within this file. */ void flip_toggle(flag,newstate,phrase) int *flag; /* toggle variable */ int newstate; char *phrase; /* to print */ { int old; old = *flag; *flag = (newstate==ON_) ? 1 : 0; outstring(phrase); outstring(*flag ? " ON." : " OFF."); if ( old < 0 ) outstring(" (was unset)\n"); else if ( old == 0 ) outstring(" (was off)\n"); else outstring(" (was on)\n"); } /************************************************************************ * * Function: more_other_stuff() * * Purpose: execute command nodes, overflow from eval() and other_stuff(). * * Return: stacktop pointer * */ void more_other_stuff(node,recalc_flag,update_display_flag,q_id, localstack,localbase) struct treenode * node; int *recalc_flag; int *update_display_flag; element_id q_id; REAL *localstack; struct locallist_t *localbase; { char response[100]; int old; /* old state of toggle */ int n,i; char *s; int oldquiet; /* old state of quiet_flag */ struct thread_data *td = GET_THREAD_DATA; #define newstack (td->eval_stack) #define stackmax (td->eval_stack_size) #define stacktop (td->stack_top) #define this_frame ((struct eval_frame*)(newstack + td->frame_spot)) switch ( node->type ) { case WRAP_VERTEX_: { vertex_id v_id = get_ordinal_id(VERTEX,(int)(stacktop[-1])-1); int wrap = (int)(stacktop[0]); stacktop -= 2; if ( !web.symmetry_flag || !wrap ) break; wrap_vertex(v_id,wrap); *update_display_flag = 1; } break; case NORMAL_MOTION_: old = normal_motion_flag; normal_motion_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(normal_motion_flag ? "Normal motion ON.": "Normal motion OFF."); outstring(old?" (was on)\n":" (was off)\n"); if ( normal_motion_flag ) begin_normal_motion(); else end_normal_motion(); break; case VIEW_4D_: old = view_4D_flag; view_4D_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(view_4D_flag ? "4D graphics ON." : "4D graphics OFF."); outstring(old?" (was on)\n":" (was off)\n"); update_display(); break; case PINNING_: old = check_pinning_flag; check_pinning_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(check_pinning_flag ? "Constraint pinning ON." : "Constraint pinning OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case METRIC_CONVERSION_: old = metric_convert_flag; metric_convert_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(metric_convert_flag ? "Metric conversion ON." : "Metric conversion OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case SELF_SIMILAR_: old = self_similar_flag; self_similar_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(self_similar_flag ? "Self similarity mode ON." : "Self similarity mode OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case GV_BINARY_: old = gv_binary_flag; gv_binary_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(gv_binary_flag ? "Geomview binary mode ON." : "Geomview binary mode OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case KUSNER_: old = kusner_flag; kusner_flag = (node->op1.toggle_state==ON_) ? 1 : 0; conf_edge_curv_flag = 0; outstring(kusner_flag ? "Edge square curvature ON." : "Edge square curvature OFF."); outstring(old?" (was on)\n":" (was off)\n"); recalc(); break; case BOUNDARY_CURVATURE_: old = boundary_curvature_flag; boundary_curvature_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(boundary_curvature_flag ? "Boundary curvature ON." : "Boundary curvature OFF."); outstring(old?" (was on)\n":" (was off)\n"); recalc(); break; case CONF_EDGE_SQCURV_: old = conf_edge_curv_flag; conf_edge_curv_flag = (node->op1.toggle_state==ON_) ? 1 : 0; kusner_flag = 0; outstring(conf_edge_curv_flag ? "Conformal edge square curvature ON." : "Conformal edge square curvature OFF."); outstring(old?" (was on)\n":" (was off)\n"); recalc(); break; case POST_PROJECT_: old = post_project_flag; post_project_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(post_project_flag ? "Post-projection ON." : "Post-projection OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case MEAN_CURV_INT_: old = mean_curv_int_flag; mean_curv_int_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(mean_curv_int_flag ? "Mean curvature integral ON." : "Mean curvature integral OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case AUTORECALC_: old = autorecalc_flag; autorecalc_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(autorecalc_flag ? "Autorecalc ON." : "Autorecalc OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case FORCE_POS_DEF_: old = make_pos_def_flag; make_pos_def_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(make_pos_def_flag ? "Force positive definite ON." : "Force positive definite OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case ESTIMATE_: old = estimate_flag; estimate_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(estimate_flag ? "Estimation ON." : "Estimation OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case TRANSFORMS_: old = transforms_flag; transforms_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(transforms_flag ? "Transform showing ON." : "Transform showing OFF."); outstring(old?" (was on)\n":" (was off)\n"); update_display(); break; case DETURCK_: old = unit_normal_flag; unit_normal_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(sqgauss_flag ? "Unit normal motion ON." : "Unit normal motion OFF."); if ( unit_normal_flag ) { sprintf(msg,"Enter unit normal weight factor(%g): ", (DOUBLE)deturck_factor); prompt(msg,response,sizeof(response)); const_expr(response,&deturck_factor); } outstring(old?" (was on)\n":" (was off)\n"); break; case SQGAUSS_: old = sqgauss_flag; sqgauss_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(sqgauss_flag ? "Squared Gaussian curvature ON." : "Squared Gaussian curvature OFF."); outstring(old?" (was on)\n":" (was off)\n"); recalc(); break; case STABILITY_TEST_: stability_test(); break; case AUTOPOP_: old = autopop_flag; autopop_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(autopop_flag ? "Autopopping ON.\n" : "Autopopping OFF.\n"); outstring(old?" (was on)\n":" (was off)\n"); if ( autopop_flag ) { n = 0; if ( web.representation == STRING ) sprintf(msg,"Number of vertices popped: %d\n", web.vertex_pop_count = n=verpop_str()); else sprintf(msg,"Number of vertices popped: %d\n", web.vertex_pop_count = n = edgepop_film()); outstring(msg); if ( n > 0 ) recalc(); } break; case AUTOPOP_QUARTIC_: old = autopop_quartic_flag; autopop_quartic_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(autopop_quartic_flag ? "Autopop quartic mode ON.\n" : "Autopop quartic mode OFF.\n"); outstring(old?" (was on)\n":" (was off)\n"); break; case IMMEDIATE_AUTOPOP_: old = immediate_autopop_flag; immediate_autopop_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(immediate_autopop_flag ? "Immediate autopop mode ON.\n" : "Immediate autopop mode OFF.\n"); outstring(old?" (was on)\n":" (was off)\n"); break; case AUTOCHOP_: old = autochop_flag; autochop_flag = (node->op1.toggle_state==ON_) ? 1 : 0; if ( autochop_flag ) { sprintf(msg,"Autochopping ON. Chop length %g ",(DOUBLE)autochop_length); outstring(msg); outstring(old?" (was on)\n":" (was off)\n"); outstring("Set autochop length with AUTOCHOP := value\n"); } else { outstring("Autochopping OFF."); outstring(old?" (was on)\n":" (was off)\n"); } break; case UTEST_: simplex_delaunay_test(); break; case OLD_AREA_: old = old_area_flag; old_area_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(old_area_flag ? "old_area ON." : "old_area OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case APPROX_CURV_: old = approx_curve_flag; approx_curve_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(approx_curve_flag ? "approx_curvature ON." : "approx_curvature OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case H_INVERSE_METRIC_: old = web.h_inverse_metric_flag; web.h_inverse_metric_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(web.h_inverse_metric_flag ? "h_inverse_metric ON." : "h_inverse_metric OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case ASSUME_ORIENTED_: old = assume_oriented_flag; assume_oriented_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(assume_oriented_flag ? "assume_oriented ON." : "assume_oriented OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case OOGLFILE_: strncpy(pix_file_name,*(char**)(stacktop--),sizeof(pix_file_name)); do_gfile('2',pix_file_name); break; case BINARY_OFF_FILE_: strncpy(pix_file_name,*(char**)(stacktop--),sizeof(pix_file_name)); do_gfile('7',pix_file_name); break; case POSTSCRIPT_: if ( ps_colorflag < 0 ) ps_colorflag = 0 ; if ( gridflag < 0 ) gridflag = 0; if ( crossingflag < 0 ) crossingflag = 0; if ( labelflag < 0 ) labelflag = 0; if ( torus_display_mode == TORUS_DEFAULT_MODE ) { torus_display_mode = TORUS_RAW_MODE; web.torus_body_flag = 0; web.torus_clip_flag = 0; } strncpy(ps_file_name,*(char**)(stacktop--),sizeof(ps_file_name)); do_gfile('3',ps_file_name); break; case SET_CONSTRAINT_GLOBAL: { int connum = (int)(*stacktop--); struct constraint *con = get_constraint(connum); if ( !(con->attr & GLOBAL) ) { vertex_id v_id; con->attr |= GLOBAL; web.con_global_map[web.con_global_count++] = (conmap_t)connum; FOR_ALL_VERTICES(v_id) set_v_constraint_map(v_id,connum); } } break; case UNSET_CONSTRAINT_GLOBAL: { int connum = (int)(*stacktop--); struct constraint *con = get_constraint(connum); con->attr &= ~GLOBAL; for ( i = 0 ; i < web.con_global_count ; i++ ) if ( web.con_global_map[i] == connum ) { web.con_global_map[i] = web.con_global_map[--web.con_global_count]; break; } } break; case SET_CONSTRAINT_NAME_GLOBAL: { int connum = node->op3.connum; struct constraint *con = get_constraint(connum); if ( !(con->attr & GLOBAL) ) { vertex_id v_id; con->attr |= GLOBAL; web.con_global_map[web.con_global_count++] = (conmap_t)connum; FOR_ALL_VERTICES(v_id) set_v_constraint_map(v_id,connum); } } break; case UNSET_CONSTRAINT_NAME_GLOBAL: { int connum = node->op3.connum; struct constraint *con = get_constraint(connum); con->attr &= ~GLOBAL; for ( i = 0 ; i < web.con_global_count ; i++ ) if ( web.con_global_map[i] == connum ) { web.con_global_map[i] = web.con_global_map[--web.con_global_count]; break; } } break; case GEOMPIPE_: /* to command */ old = geompipe_flag; do_gfile('C',*(char**)(stacktop--)); break; case GEOMPIPE_TOGGLE_: old = geompipe_flag; do_gfile((node->op1.toggle_state==ON_) ? 'A' : 'B',NULL); outstring(geompipe_flag ? "geompipe ON." : "geompipe OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case GEOMVIEW_TOGGLE_: old = geomview_flag; if ( !geomview_flag && (node->op1.toggle_state==ON_) ) do_gfile( '8',NULL); else if ( geomview_flag && !(node->op1.toggle_state==ON_) ) do_gfile( '9',NULL); outstring(geomview_flag ? "geomview ON." : "geomview OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case LOGFILE_TOGGLE_: old = logfile_flag; if ( !logfile_flag && (node->op1.toggle_state==ON_) ) start_logfile(NULL); else if ( logfile_flag && (node->op1.toggle_state!=ON_) ) stop_logfile(); outstring(logfile_flag ? "Logfile ON." : "Logfile OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case KEYLOGFILE_TOGGLE_: old = keylogfile_flag; if ( !keylogfile_flag && (node->op1.toggle_state==ON_) ) start_keylogfile(NULL); else if ( keylogfile_flag && (node->op1.toggle_state!=ON_) ) stop_keylogfile(); outstring(keylogfile_flag ? "Keylogfile ON." : "Keylogfile OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case JIGGLE_TOGGLE_: old = web.jiggle_flag; web.jiggle_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(web.jiggle_flag ? "jiggling ON." : "jiggling OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case RIBIERE_CG_: old = ribiere_flag; ribiere_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(ribiere_flag ? "Polak-Ribiere conjugate gradient ON." : "Polak-Ribiere conjugate gradient OFF."); outstring(old?" (was on)\n":" (was off)\n"); reset_conj_grad(); break; case CONJ_GRAD_: old = conj_grad_flag; conj_grad_flag = (node->op1.toggle_state==ON_) ? 1 : 0; reset_conj_grad(); if ( conj_grad_flag ) { outstring("Conjugate gradient now ON."); outstring(old?" (was on)\n":" (was off)\n"); if ( web.motion_flag ) { sprintf(errmsg, "Fixed scale is ON! Not a good idea with conjugate gradient.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1223,errmsg,WARNING); } } else { outstring("Conjugate gradient now OFF."); outstring(old?" (was on)\n":" (was off)\n"); } break; case MEAN_CURV_: web.norm_check_flag = 0; /* default OFF */ old = web.area_norm_flag; web.area_norm_flag = (node->op1.toggle_state==ON_) ? 1 : 0; if ( web.area_norm_flag ) { outstring("Area normalization ON."); } else { outstring("Area normalization OFF."); } outstring(old?" (was on)\n":" (was off)\n"); calc_energy(); /* to make sure vertex areas set */ break; case SHOW_ALL_QUANTITIES_: flip_toggle(&show_all_quantities,node->op1.toggle_state,"show_all_quantities"); break; case PSCOLORFLAG_: flip_toggle(&ps_colorflag,node->op1.toggle_state,"pscolorflag"); break; case GRIDFLAG_: flip_toggle(&gridflag,node->op1.toggle_state,"gridflag"); break; case CROSSINGFLAG_: flip_toggle(&crossingflag,node->op1.toggle_state,"crossingflag"); break; case LABELFLAG_: flip_toggle(&labelflag,node->op1.toggle_state,"labelflag"); break; case TORUS_FILLED_: if ( !web.torus_flag ) { sprintf(errmsg, "torus_filled is invalid toggle because not in torus model.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2042,errmsg, WARNING); break; } flip_toggle(&web.full_flag,node->op1.toggle_state,"torus_filled"); break; case VERBOSE_: old = verbose_flag; verbose_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(verbose_flag ? "Verbose ON." : "Verbose OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case QUIET_: old = quiet_flag; quiet_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(quiet_flag ? "Quiet ON." : "Quiet OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case QUIETLOAD_: old = quiet_load_flag; quiet_load_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(quiet_load_flag ? "QuietLoad ON." : "QuietLoad OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case FUNCTION_QUANTITY_SPARSE_: old = quantity_function_sparse_flag; quantity_function_sparse_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(quantity_function_sparse_flag ? "function_quantity_sparse ON." : "function_quantity_sparse OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case FORCE_DELETION_: old = force_deletion; force_deletion = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(force_deletion ? "force_deletion ON." : "force_deletion OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case STAR_FINAGLING_: old = star_finagling; star_finagling = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(star_finagling ? "star_finagling ON." : "star_finagling OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case SLICE_VIEW_: old = slice_view_flag; slice_view_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(slice_view_flag ? "slice_view ON." : "slice_view OFF."); outstring(old?" (was on)\n":" (was off)\n"); update_display(); break; case CLIP_VIEW_: old = clip_view_flag; clip_view_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(clip_view_flag ? "clip_view ON." : "clip_view OFF."); outstring(old?" (was on)\n":" (was off)\n"); update_display(); break; case BACKCULL_: old = backcull_flag; backcull_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(backcull_flag ? "backcull ON." : "backcull OFF."); outstring(old?" (was on)\n":" (was off)\n"); update_display(); break; case VOLGRADS_EVERY_: old = volgrads_every_flag; volgrads_every_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(volgrads_every_flag ? "Volgrads_every ON." : "Volgrads_every OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case ZENER_DRAG_: old = zener_drag_flag; zener_drag_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(zener_drag_flag ? "Zener_drag ON." : "Zener_drag OFF."); outstring(old?" (was on)":" (was off)"); { int zd = lookup_global(ZENER_COEFF_NAME); if ( zd >= 0 ) sprintf (msg," (%s: %g)\n",ZENER_COEFF_NAME,(double)globals(zd)->value.real); else sprintf(msg," (%s not set)\n",ZENER_COEFF_NAME); outstring(msg); } break; case QUIETGO_: old = quiet_go_flag; quiet_go_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(quiet_go_flag ? "QuietGo ON." : "QuietGo OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case DIRICHLET_MODE_: if ( !everything_quantities_flag ) { convert_to_quantities(); recalc(); } flip_toggle(&dirichlet_flag,node->op1.toggle_state,"Dirichlet area mode"); sobolev_flag = 0; break; case RGB_COLORS_FLAG_: flip_toggle(&rgb_colors_flag,node->op1.toggle_state,"RGB colors"); update_display(); break; case BREAK_AFTER_WARNING_: flip_toggle(&break_after_warning,node->op1.toggle_state, "Break after warning"); break; case BLAS_FLAG_: #ifdef BLAS flip_toggle(&blas_flag,node->op1.toggle_state,"using BLAS"); #else sprintf(errmsg,"This Evolver not compiled with BLAS.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2472,errmsg,WARNING); #endif break; case AUGMENTED_HESSIAN_: flip_toggle(&augmented_hessian_flag,node->op1.toggle_state, "augmented Hessian"); if ( augmented_hessian_flag ) { if ( !sparse_constraints_flag ) flip_toggle(&sparse_constraints_flag,node->op1.toggle_state, "sparse constraints"); } break; case SPARSE_CONSTRAINTS_: flip_toggle(&sparse_constraints_flag,node->op1.toggle_state, "sparse constraints"); if ( !sparse_constraints_flag && augmented_hessian_flag ) flip_toggle(&augmented_hessian_flag,node->op1.toggle_state, "augmented Hessian"); break; case VISIBILITY_TEST_: flip_toggle(&visibility_test,node->op1.toggle_state,"visibility test"); update_display(); break; case CIRCULAR_ARC_DRAW_: flip_toggle(&circular_arc_flag,node->op1.toggle_state,"Circular arc drawing"); break; case KRAYNIKPOPVERTEX_FLAG_: flip_toggle(&kraynikpopvertex_flag,node->op1.toggle_state,"Kraynik pop vertex mode"); break; case KRAYNIKPOPEDGE_FLAG_: flip_toggle(&kraynikpopedge_flag,node->op1.toggle_state,"Kraynik pop edge mode"); break; case SMOOTH_GRAPH_: flip_toggle(&smooth_graph_flag,node->op1.toggle_state,"smooth graph"); update_display(); break; case FULL_BOUNDING_BOX_: flip_toggle(&full_bounding_box_flag,node->op1.toggle_state, "full_bounding_box"); update_display(); break; case POP_TO_EDGE_: flip_toggle(&pop_to_edge_flag,node->op1.toggle_state,"pop_to_edge"); if ( pop_to_edge_flag ) pop_to_face_flag = 0; break; case POP_TO_FACE_: flip_toggle(&pop_to_face_flag,node->op1.toggle_state,"pop_to_face"); if ( pop_to_face_flag ) pop_to_edge_flag = 0; break; case POP_DISJOIN_: flip_toggle(&pop_disjoin_flag,node->op1.toggle_state,"pop_disjoin"); if ( pop_disjoin_flag ) pop_enjoin_flag = 0; break; case POP_ENJOIN_: flip_toggle(&pop_enjoin_flag,node->op1.toggle_state,"pop_enjoin"); if ( pop_enjoin_flag ) pop_disjoin_flag = 0; break; case BIG_ENDIAN_: flip_toggle(&big_endian_flag,node->op1.toggle_state,"big_endian"); if ( big_endian_flag ) little_endian_flag = 0; break; case LITTLE_ENDIAN_: flip_toggle(&little_endian_flag,node->op1.toggle_state, "little_endian"); if ( little_endian_flag ) big_endian_flag = 0; break; case MPI_DEBUG_: flip_toggle(&mpi_debug,node->op1.toggle_state,"mpi_debug"); update_display(); #ifdef MPI_EVOLVER mpi_synch_mpi_debug(); #endif break; case BEZIER_BASIS_: { int dim,k; old = bezier_flag; bezier_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(bezier_flag ? "bezier_basis ON." : "bezier_basis OFF."); outstring(old?" (was on)\n":" (was off)\n"); /* wipe old polynomials and reinitialize */ for ( dim = 1 ; dim < MAXCOORD ; dim++ ) for ( k = 1 ; k <= maxgaussorder[dim] ; k++ ) { struct gauss_lag *gl = &gauss_lagrange[dim][k]; if ( gl == NULL ) continue; gauss_lagrange_setup(dim,web.lagrange_order,k); } if ( web.modeltype == LAGRANGE ) { if ( !old && bezier_flag ) lagrange_to_bezier(); if ( old && !bezier_flag ) bezier_to_lagrange(); } recalc(); break; } case SOBOLEV_MODE_: if ( !everything_quantities_flag ) { convert_to_quantities(); recalc(); } flip_toggle(&dirichlet_flag,node->op1.toggle_state,"Sobolev area mode"); dirichlet_flag = 0; break; case HESSIAN_NORMAL_: old = hessian_normal_flag; hessian_normal_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(hessian_normal_flag ? "hessian_normal ON." : "hessian_normal OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case HESSIAN_SPECIAL_NORMAL_: old = hessian_special_normal_flag; if ( node->op1.toggle_state == ON_ && hessian_special_normal_expr[0].start == NULL ) kb_error(3835,"hessian_special_normal_vector not set.\n", RECOVERABLE); hessian_special_normal_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(hessian_special_normal_flag ? "hessian_special_normal ON." : "hessian_special_normal OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case HESSIAN_NORMAL_PERP_: old = hessian_normal_perp_flag; hessian_normal_perp_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(hessian_normal_perp_flag ? "hessian_normal_perp ON." : "hessian_normal_perp OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case HESSIAN_DOUBLE_NORMAL_: old = hessian_double_normal_flag; hessian_double_normal_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(hessian_double_normal_flag ? "hessian_double_normal ON." : "hessian_double_normal OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case HESSIAN_NORMAL_ONE_: if ( node->op1.toggle_state==ON_ && ((web.representation == SIMPLEX) || (SDIM - web.dimension > 1))) { sprintf(errmsg, "HESSIAN_NORMAL_ONE only for STRING or SOAPFILM hypersurfaces.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2043,errmsg,RECOVERABLE); } old = hessian_normal_one_flag; hessian_normal_one_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(hessian_normal_one_flag ? "hessian_normal_one ON." : "hessian_normal_one OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case HESSIAN_QUIET_: old = hessian_quiet_flag; hessian_quiet_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(hessian_quiet_flag ? "hessian_quiet ON." : "hessian_quiet OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case HESSIAN_DIFF_: old = hessian_by_diff_flag; if ( !web.pressure_flag && count_fixed_vol() ) { sprintf(errmsg, "Hessian_diff not implemented for constrained quants.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3902,errmsg,RECOVERABLE); } hessian_by_diff_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(hessian_by_diff_flag ? "Hessian by differences ON." : "Hessian by differences OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case INTERP_BDRY_PARAM_: old = interp_bdry_param; interp_bdry_param = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(interp_bdry_param ? "interpolation of boundary parameters ON." : "interpolation of boundary parameters OFF (using extrapolation)."); outstring(old?" (was on)\n":" (was off)\n"); break; case RITZ_: { int krydim; /* Krylov subspace dimension */ krydim = (int)*(stacktop--); ritz_command(*(stacktop--),krydim); } break; case LANCZOS_: { int krydim; /* Krylov subspace dimension */ if ( node->right ) krydim = (int)*(stacktop--); else krydim = 100; lanczos_command(*(stacktop--),krydim); } break; case MOVE_: calc_all_grads(CALC_VOLGRADS); move_vertices(ACTUAL_MOVE,*(stacktop--)); vgrad_end(); #ifdef LONGDOUBLE sprintf(msg,"1. energy: %*.*Lg stepsize: %g\n",DWIDTH,DPREC, web.total_energy,(DOUBLE)stacktop[1]); #else sprintf(msg,"1. %s: %17.15g energy: %17.15g stepsize: %g\n", areaname,web.total_area,web.total_energy,stacktop[1]); #endif outstring(msg); update_display(); break; case EIGENPROBE_: { int iters; if ( node->right ) iters = (int)*(stacktop--); else iters = 0; eigenprobe_command(*(stacktop--),iters); } break; case EXPRLIST_: break; /* leave expression on stack */ case GET_TRANSFORM_EXPR_: /* put ptr in both halves */ { int pp = (sizeof(REAL))/sizeof(char*); int nn; ++stacktop; for ( nn = 0 ; nn < pp ; nn++ ) ((char **)stacktop)[nn] = transform_expr; } break; case DATAFILENAME_: /* put ptr in both halves */ { int pp = (sizeof(REAL))/sizeof(char*); int nn; ++stacktop; for ( nn = 0 ; nn < pp ; nn++ ) ((char **)stacktop)[nn] = datafilename; } break; case WARNING_MESSAGES_: /* put ptr in both halves */ { int pp = (sizeof(REAL))/sizeof(char*); int nn; ++stacktop; for ( nn = 0 ; nn < pp ; nn++ ) ((char **)stacktop)[nn] = warning_messages; } break; case QUOTATION_: /* put ptr in both halves */ { int pp = (sizeof(REAL))/sizeof(char*); int nn; *++stacktop = 0.0; for ( nn = 0 ; nn < pp ; nn++ ) ((char **)stacktop)[nn] = node->op1.string; } break; case DATE_AND_TIME_: { time_t ltime; time(<ime); *(char**)(++stacktop) = ctime(<ime); if ( strchr(*(char**)stacktop,'\n') ) *strchr(*(char**)stacktop,'\n') = 0; break; } case PRINTFHEAD_: case BINARY_PRINTFHEAD_: if ( node->op1.string ) s = node->op1.string; else s = *(char**)(stacktop--); oldquiet = quiet_flag; quiet_flag = 0; outstring(s); quiet_flag = oldquiet; break; case ERRPRINTFHEAD_: if ( node->op1.string ) s = node->op1.string; else s = *(char**)(stacktop--); erroutstring(s); break; case SPRINTFHEAD_: if ( node->op1.string ) *(char **)(++stacktop) = node->op1.string; /* else already on stack */ break; case PRINTF_: case ERRPRINTF_: case SPRINTF_: { char format[1000]; char *newmsg; int newmsgsize; int formatcount = 0; newmsgsize = 1000; newmsg = temp_calloc(1,newmsgsize); n = node[node->right].op1.argcount; if ( node[node->left].op1.string ) s = node[node->left].op1.string; else s = *(char**)(stacktop-n); /* remove old string munges */ { char *p,*c; /* strip %0.0s from format string */ while ( (c = strstr(s,"%0.0s")) != NULL ) { for ( p = c+5 ; *p ; c++,p++ ) *c = *p; *c = 0; } while ( (c = strstr(s,"%.0s")) != NULL ) { for ( p = c+4 ; *p ; c++,p++ ) *c = *p; *c = 0; } } /* new way, parse through format string */ { char *msgspot = newmsg; char *sp = s; while ( *sp ) { char *f = format; /* for one format */ int nn; struct treenode *nnode; char *ss; if ( (msgspot-newmsg) > (newmsgsize-500) ) { size_t len = msgspot-newmsg; newmsg = temp_realloc(newmsg,2*newmsgsize); newmsgsize *= 2; msgspot = newmsg + len; } if ( *sp != '%' ) { *(msgspot++) = *(sp++); continue; } /* now have % */ /* check for %%, which reduces to % */ if ( sp[1] == '%' ) { *(msgspot++) = *(sp++); (sp++); continue; } /* find format character */ while ( *sp && !isalpha(*sp) ) *(f++) = *(sp++); *(f++) = *(sp++); /* copy format character */ *f = 0; /* null terminator */ switch ( f[-1] ) { case 's': /* check that really have a string argument */ nnode = node; for ( nn = 0 ; nn <= formatcount ; nn++ ) nnode += nnode->right; nnode += nnode->left; if ( !(nnode->flags & HAS_STRING) && !(nnode->type == STRINGGLOBAL_) && !(nnode->type == PERM_STRINGGLOBAL_) && !(nnode->type == SPRINTFHEAD_) && !(nnode->type == DATE_AND_TIME_) && !(nnode->type == DATAFILENAME_)) { sprintf(errmsg, "Argument %d: String format does not have string argument.\n", formatcount+1); strcat(errmsg,"Possibly % not followed by legal format?\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d; nnode->type %d)\n", file_names[node->file_no],node->line_no,nnode->type); kb_error(3111,errmsg,RECOVERABLE); } ss = *(char**)(stacktop-n+1+formatcount); if ( ss == NULL ) ss = "(NULL)"; while ( (int)(msgspot-newmsg+strlen(ss)) > (int)(newmsgsize-10) ) { size_t len = msgspot-newmsg; newmsg = temp_realloc(newmsg,2*newmsgsize); newmsgsize *= 2; msgspot = newmsg + len; } sprintf(msgspot,format,ss); ++formatcount; break; case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': sprintf(msgspot,format,(int)(*(stacktop-n+ ++formatcount))); break; case 'f': case 'g': case 'e': case 'E': case 'G': if ( fabs(*(stacktop-n+ (formatcount+1))) > 1e100 ) f[-1] = 'g'; /* prevent buffer overflow */ #ifdef LONGDOUBLE f[0] = f[-1]; f[-1] = 'L'; f[1] = 0; #endif sprintf(msgspot,format,(*(stacktop-n+ ++formatcount))); break; default: sprintf(msgspot,format,0,0,0,0); /* unrecognized */ } msgspot += strlen(msgspot); } if ( formatcount > n ) { sprintf(errmsg, "Format string has %d formats, but there are only %d arguments.\n", formatcount,n); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2044,errmsg,RECOVERABLE); } *msgspot = 0; /* null terminator */ stacktop -= n; /* pop args */ } if ( node[node->left].op1.string == NULL ) stacktop--; if ( node->type == PRINTF_ ) { int old_flag = quiet_flag; quiet_flag = 0; outstring(newmsg); quiet_flag = old_flag; } else if ( node->type == ERRPRINTF_ ) erroutstring(newmsg); else /* SPRINTF_ */ { char *str = mycalloc(strlen(newmsg)+2,1); strcpy(str,newmsg); *(char **)(++stacktop) = str; } temp_free(newmsg); } break; case BINARY_PRINTF_: { char format[1000]; int formatcount = 0; int byte_reverse = 0; int test_int = 0x0124567; if ( !big_endian_flag && !little_endian_flag ) { kb_error(5387, "binary_printf: you must set 'big_endian' or 'little_endian' toggles.",RECOVERABLE); } if ( big_endian_flag && (*(char*)&test_int)==0x67) byte_reverse = 1; if ( little_endian_flag && (*(char*)&test_int)==0x01) byte_reverse = 1; #ifdef WIN32 fflush(outfd); _setmode(_fileno(outfd),_O_BINARY); #endif n = node[node->right].op1.argcount; if ( node[node->left].op1.string ) s = node[node->left].op1.string; else s = *(char**)(stacktop-n); /* parse through format string */ { char *msgspot=NULL; char *sp = s; while ( *sp ) { char *f = format; /* for one format */ int nn; struct treenode *nnode; if ( *sp != '%' ) { fwrite(sp,1,1,outfd); sp++; continue; } while ( *sp && (!isalpha(*sp) || *sp=='l') ) *(f++) = *(sp++); *(f++) = *(sp++); /* copy format character */ *f = 0; /* null terminator */ switch ( f[-1] ) { case 's': /* check that really have a string argument */ nnode = node; for ( nn = 0 ; nn <= formatcount ; nn++ ) nnode += nnode->right; nnode += nnode->left; if ( !(nnode->flags & HAS_STRING) && !(nnode->type == STRINGGLOBAL_) && !(nnode->type == PERM_STRINGGLOBAL_) && !(nnode->type == SPRINTFHEAD_) && !(nnode->type == DATE_AND_TIME_) && !(nnode->type == DATAFILENAME_)) { sprintf(errmsg, "Argument %d: String format does not have string argument.\n", formatcount+1); strcat(errmsg,"Possibly % not followed by legal format?\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d; nnode->type %d)\n", file_names[node->file_no],node->line_no,nnode->type); kb_error(3212,errmsg,RECOVERABLE); } msgspot = *(char**)(stacktop-n+ ++formatcount); fwrite(msgspot,1,strlen(msgspot),outfd); break; case 'c': /* one byte */ { int c = (int)*(stacktop-n+ ++formatcount); fwrite(&c,1,1,outfd); } break; case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': if ( f[-2] == 'l' ) { int m = (int)*(stacktop-n+ ++formatcount); if ( byte_reverse ) { char buf[4]; for ( i = 0 ; i < 4 ; i++ ) buf[i] = ((char*)&m)[4-1-i]; fwrite(buf,4,1,outfd); } else fwrite(&m,4,1,outfd); } else /* 2-byte short */ { short m = (short)*(stacktop-n+ ++formatcount); if ( byte_reverse ) { char buf[2]; for ( i = 0 ; i < 2 ; i++ ) buf[i] = ((char*)&m)[2-1-i]; fwrite(buf,2,1,outfd); } else fwrite(&m,2,1,outfd); } break; case 'f': case 'g': case 'e': case 'E': case 'G': if ( f[-2] == 'l' ) { /* 8-byte double */ double x = *(stacktop-n+ ++formatcount); if ( byte_reverse ) { char buf[8]; int i; for ( i = 0 ; i < 8 ; i++ ) buf[i] = ((char*)&x)[7-i]; fwrite(buf,8,1,outfd); } else fwrite(&x,8,1,outfd); } else /* 4-byte float */ { float x = (float)*(stacktop-n+ ++formatcount); if ( byte_reverse ) { char buf[4]; int i; for ( i = 0 ; i < 4 ; i++ ) buf[i] = ((char*)&x)[4-i]; fwrite(buf,4,1,outfd); } else fwrite(&x,4,1,outfd); } break; default: sprintf(errmsg,"binary_printf format string has unrecognized format character '%c'\n",f[-1]); break; /* unrecognized */ } } if ( formatcount > n ) { sprintf(errmsg, "Format string has %d formats, but there are only %d arguments.\n", formatcount,n); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(4023,errmsg,RECOVERABLE); } stacktop -= n; /* pop args */ } if ( node[node->left].op1.string == NULL ) stacktop--; #ifdef WIN32 fflush(outfd); _setmode(_fileno(outfd),_O_TEXT); #endif } break; case PRINT_: /* verb */ #ifdef LONGDOUBLE sprintf(msg,"%*.*Lg\n",DWIDTH,DPREC,*(stacktop--)); #else sprintf(msg,"%20.15g\n",*(stacktop--)); #endif oldquiet = quiet_flag; quiet_flag = 0; outstring(msg); quiet_flag = oldquiet; break; case STRPRINT_: /* verb */ oldquiet = quiet_flag; quiet_flag = 0; s = *(char**)(stacktop--); outstring(s); outstring("\n"); quiet_flag = oldquiet; if ( node->left && (node[node->left].flags & DEALLOCATE_POINTER) ) myfree(s); break; case PRINT_LETTER_: oldquiet = quiet_flag; quiet_flag = 0; if ( single_redefine[node->op1.letter].start ) outstring(print_express(&single_redefine[node->op1.name_id],'X')); else { sprintf(msg,"%c",node->op1.letter); outstring(msg);} outstring("\n\n"); quiet_flag = oldquiet; break; case PRINT_PROCEDURE_: oldquiet = quiet_flag; quiet_flag = 0; outstring(print_express(&globals(node->op1.name_id)->value.proc,'X')); outstring("\n\n"); quiet_flag = oldquiet; break; case PRINT_PERM_PROCEDURE_: oldquiet = quiet_flag; quiet_flag = 0; outstring(print_express(&perm_globals(node->op1.name_id)->value.proc,'X')); outstring("\n\n"); quiet_flag = oldquiet; break; case EXPRINT_PROCEDURE_: oldquiet = quiet_flag; quiet_flag = 0; outstring(globals(node->op1.name_id)->attr.procstuff.proc_text); outstring("\n\n"); quiet_flag = oldquiet; break; case EPRINT_: /* print and pass on value inside expression */ #ifdef LONGDOUBLE sprintf(msg,"%*.*Lg\n",DWIDTH,DPREC,*stacktop); #else sprintf(msg,"%20.15g\n",*stacktop); #endif oldquiet = quiet_flag; quiet_flag = 0; outstring(msg); quiet_flag = oldquiet; break; case SHOWQ_: display(); break; case SET_THICKEN_ : thickness = *(stacktop--); break; case JIGGLE_: web.temperature = *(stacktop--); jiggle(); recalc(); break; case NOTCH_: if ( web.representation == SIMPLEX ) { sprintf(errmsg,"Notching not implemented for simplex representation.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1224,errmsg,RECOVERABLE); } web.max_angle = *(stacktop--); if ( web.max_angle <= 0.0 ) { sprintf(errmsg,"Notching angle not positive.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1225,errmsg,RECOVERABLE); } if ( web.representation == STRING ) { sprintf(msg,"refine edge ee where max(ee.vertex,dihedral) > %f", (DOUBLE)web.max_angle); command(msg,NO_HISTORY); web.notch_count = web.edge_refine_count; } else { web.notch_count = ridge_notcher(web.max_angle); sprintf(msg,"Number of edges notched: %d\n",web.notch_count); outstring(msg); recalc(); } break; case SET_AUTOCHOP_: autochop_length = *(stacktop--); break; case SET_AMBIENT_PRESSURE_: web.pressure = *(stacktop--); if ( web.pressure > 0.00000001 ) { body_id b_id; if ( !web.full_flag && !valid_id(web.outside_body) ) add_outside(); web.projection_flag = 0; web.pressure_flag = 1; if ( everything_quantities_flag ) FOR_ALL_BODIES(b_id) create_pressure_quant(b_id); } else { web.projection_flag = 1; web.pressure_flag = 0; } recalc(); break; case SET_DIFFUSION_: old = web.diffusion_flag; web.diffusion_const = *(stacktop--); web.diffusion_flag = 1; outstring(web.diffusion_flag ? "Diffusion ON." : "Diffusion OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case AREAWEED_: sprintf(msg,"Skinny triangles weeded: %d\n", web.facet_delete_count = areaweed(*(stacktop--))); outstring(msg); energy_init = 0; recalc(); break; case METIS_: case KMETIS_: /* metis_partition_dual((int)(*(stacktop--)),node->type); */ metis_partition_plain((int)(*(stacktop--)),node->type); break; case METIS_READJUST_: metis_partition_dual((int)(*(stacktop--)),node->type); /* metis_partition_plain((int)(*(stacktop--)),node->type); */ break; case BODY_METIS_: metis_partition_body((int)(*(stacktop--)),METIS_); break; case OMETIS_: if ( node->left ) metis_vertex_order((int)(*(stacktop--))); else metis_vertex_order(100); break; case EDGEWEED_: sprintf(msg,"Deleted edges: %d\n", web.edge_delete_count = edgeweed(*(stacktop--))); outstring(msg); recalc(); break; case OPTIMIZE_: web.motion_flag = (node->op1.toggle_state==ON_) ? 0 : 1; if ( web.motion_flag ) sprintf(msg,"Scale fixed at %g.\n",(DOUBLE)web.scale); else sprintf(msg,"Scale optimizing with bound %g.\n",(DOUBLE)web.maxscale); outstring(msg); break; case SET_OPTIMIZE_: web.maxscale = *(stacktop--); web.motion_flag = 0; sprintf(msg,"Scale optimizing with bound %g.\n",(DOUBLE)web.maxscale); outstring(msg); break; case SET_SCALE_: web.scale = *(stacktop--); web.motion_flag = 1; sprintf(msg,"Scale fixed at %g.\n",(DOUBLE)web.scale); outstring(msg); break; case SET_GRAVITY_: switch ( node->op1.assigntype ) { case ASSIGN_: web.grav_const = *(stacktop--); break; case PLUSASSIGN_: web.grav_const += *(stacktop--); break; case SUBASSIGN_: web.grav_const -= *(stacktop--); break; case MULTASSIGN_: web.grav_const *= *(stacktop--); break; case DIVASSIGN_: web.grav_const /= *(stacktop--); break; } old = web.gravflag; if ( web.grav_const != 0.0 ) { web.gravflag = 1; sprintf(msg,"Gravity is now ON with gravitational constant %g.", (DOUBLE)web.grav_const); } else { web.gravflag = 0; sprintf(msg,"Gravity is now OFF."); } if (gravity_quantity_num >= 0 ) GEN_QUANT(gravity_quantity_num)->modulus = web.gravflag ? web.grav_const : 0.0; outstring(msg); outstring(old?" (was on)\n":" (was off)\n"); recalc(); break; case SET_MODEL_: switch ( (int)*(stacktop--) ) { case LINEAR: if ( web.modeltype == QUADRATIC ) { outstring("Changing to LINEAR model. (was QUADRATIC)\n"); quad_to_linear(); recalc(); } else if ( web.modeltype == LAGRANGE ) { outstring("Changing to LINEAR model. (was LAGRANGE)\n"); lagrange_to_linear(); recalc(); } else outstring("Model already LINEAR.\n"); break; case QUADRATIC: if ( web.modeltype == LINEAR ) { outstring("Changing to QUADRATIC model. (was LINEAR)\n"); linear_to_quad(); recalc(); } else if ( web.modeltype == LAGRANGE ) { outstring("Changing to QUADRATIC model. (was LAGRANGE)\n"); lagrange_to_quad(); recalc(); } else outstring("Model already QUADRATIC.\n"); break; default: if ( stacktop[1] > 2. ) { if ( web.modeltype == LINEAR ) { outstring("Changing to LAGRANGE model. (was LINEAR)\n"); linear_to_lagrange((int)stacktop[1]); recalc(); } else if ( web.modeltype == QUADRATIC ) { outstring("Changing to LAGRANGE model. (was QUADRATIC)\n"); quad_to_lagrange((int)stacktop[1]); recalc(); } else lagrange_to_lagrange((int)stacktop[1]); } else { sprintf(errmsg, "Bad model choice. Legal: 1 (linear), 2 (quadratic), > 2 (Lagrange)\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1230,errmsg,RECOVERABLE); } } recalc(); break; case INVOKE_P_MENU_: display_file((int)*(stacktop--)); break; case EDGEDIVIDE_: sprintf(msg,"New edges: %d\n", web.edge_refine_count = articulate(*(stacktop--))); outstring(msg); recalc(); break; case SET_SGLOBAL_: { struct global *g = globals(node->op1.name_id); char *s,**ss; if ( g->flags & GLOB_LOCALVAR ) ss = (char**)(localstack+g->value.offset); else ss = &(g->value.string); if ( *ss ) myfree(*ss); s = *(char**)(stacktop--); if ( s ) { *ss = mycalloc(strlen(s)+1,sizeof(char)); strcpy(*ss,s); } else { *ss = mycalloc(1,sizeof(char)); /* empty */ } g->flags |= STRINGVAL; if ( node->left && (node[node->left].flags & DEALLOCATE_POINTER) ) myfree(s); } break; case SET_PERM_SGLOBAL_: { struct global *g = perm_globals(node->op1.name_id); char *s; if ( g->value.string ) free(g->value.string); s = *(char**)(stacktop--); if ( s ) { g->value.string = calloc(strlen(s)+1,sizeof(char)); strcpy(g->value.string,s); } else { g->value.string = calloc(1,sizeof(char)); /* empty */ } if ( node->left && (node[node->left].flags & DEALLOCATE_POINTER) ) myfree(s); g->flags |= STRINGVAL; } break; case SET_PARAM_SCALE: { struct global *g = globals(node->op1.name_id); REAL value = *(stacktop--); switch ( node->op2.assigntype ) { case ASSIGN_: g->attr.varstuff.pscale = value; break; case PLUSASSIGN_: g->attr.varstuff.pscale += value; break; case SUBASSIGN_: g->attr.varstuff.pscale -= value; break; case MULTASSIGN_: g->attr.varstuff.pscale *= value; break; case DIVASSIGN_: g->attr.varstuff.pscale /= value; break; } break; } case SET_DELTA_: { struct global *g = globals(node->op1.name_id); REAL value = *(stacktop--); switch ( node->op2.assigntype ) { case ASSIGN_: g->attr.varstuff.delta = value; break; case PLUSASSIGN_: g->attr.varstuff.delta += value; break; case SUBASSIGN_: g->attr.varstuff.delta -= value; break; case MULTASSIGN_: g->attr.varstuff.delta *= value; break; case DIVASSIGN_: g->attr.varstuff.delta /= value; break; } break; } case DEFINE_IDENT_: break; case DEFINE_ARRAY_: { int dim,size,i; struct treenode *nnode = node; struct global *g = globals(node->op1.name_id); int itemsize; int pointercount; struct array *oldarray; struct array **array_info; if ( g->flags & GLOB_LOCALVAR ) { localbase->flags |= LL_HAS_ARRAY; array_info = (struct array **)(localstack+g->value.offset); } else array_info = &(g->attr.arrayptr); oldarray = *array_info; itemsize = datatype_size[node->op2.valtype]; pointercount = 1; for ( size = 1, dim=0 ; dim < g->attr.arrayptr->dim ; dim++ ) { size *= (int)stacktop[-dim]; if ( dim > 0 ) pointercount *= (int)stacktop[-dim]; } if ( g->flags & GLOB_LOCALVAR ) *array_info = (struct array*)temp_calloc( sizeof(struct array)+dim*sizeof(int) + (size+1)*itemsize+pointercount*sizeof(REAL*),1); /* extra for alignment */ else /* nonlocal */ *array_info = (struct array*)mycalloc( sizeof(struct array)+dim*sizeof(int) + (size+1)*itemsize+pointercount*sizeof(REAL*),1); /* extra for alignment */ (*array_info)->dim = dim; (*array_info)->itemsize = itemsize; (*array_info)->datatype = node->op2.valtype; (*array_info)->datacount = size; for ( i=0, nnode = node ; i < dim ; nnode += nnode->left, i++ ) (*array_info)->sizes[i] = (int)stacktop[1-dim+i]; stacktop -= dim; (*array_info)->datastart = sizeof(struct array) + dim*sizeof(int); /* guarantee bus alignment */ if ( (*array_info)->datastart % itemsize ) (*array_info)->datastart = (*array_info)->datastart + itemsize - ((*array_info)->datastart % itemsize ); /* copy old data */ if ( oldarray ) { int oldoff,n; for ( oldoff = 0 ; oldoff < oldarray->datacount ; oldoff ++ ) { int newoff = 0; int prod = 1; int p,pp; p = oldoff; for ( n = dim-1 ; n >= 0 ; n-- ) { int nd = (*array_info)->sizes[n]; int od = oldarray->sizes[n]; pp = p % od; p = p / od; if ( pp >= nd ) goto skip; newoff += prod*pp; prod *= nd; } memcpy((char*)(*array_info)+(*array_info)->datastart + newoff*(*array_info)->itemsize, (char*)oldarray+oldarray->datastart + oldoff*oldarray->itemsize, oldarray->itemsize); skip: ; } } if ( g->flags & GLOB_LOCALVAR ) temp_free((char*)oldarray); else myfree((char*)oldarray); } break; case SET_ELEMENT_GLOBAL_: { struct global *g = globals(node->op1.name_id); g->value.id = *(element_id*)(stacktop--); break; } case ZOOM_: if ( node->left ) /* have vertex number */ { vertex_id v_id; int vnum; int found = 0; web.zoom_radius = *(stacktop--); vnum = (int)*(stacktop--); FOR_ALL_VERTICES(v_id) { if ( vnum == (ordinal(v_id)+1) ) { web.zoom_v = v_id; found = 1; break; } } if ( !found ) { sprintf(errmsg,"Zoom vertex %d not found.\n",vnum); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1231,errmsg,RECOVERABLE); } } else if ( !valid_element(web.zoom_v) ) { sprintf(errmsg,"Zoom vertex not found.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1232,errmsg,RECOVERABLE); } zoom_vertex(web.zoom_v,web.zoom_radius); /* resize(); */ recalc(); break; case VIEW_MATRIX_LVALUE_: break; /* just to hold indices */ case SET_VIEW_MATRIX_: { int i = (int)(stacktop[-2]); int k = (int)(stacktop[-1]); REAL value = *stacktop; stacktop -= 3; if ( (k < 1) || (k > SDIM+1) || (i < 1) || (i > SDIM+1) ) { sprintf(errmsg, "Illegal index: view_matrix[%d][%d] (must be 1 to %d)\n",i,k,SDIM); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2049,errmsg,RECOVERABLE); } i--;k--; /* convert to 0 based indexing */ switch ( node->op2.assigntype ) { case ASSIGN_: view[i][k] = value; break; case PLUSASSIGN_: view[i][k] += value; break; case SUBASSIGN_: view[i][k] -= value; break; case MULTASSIGN_: view[i][k] *= value; break; case DIVASSIGN_: if ( value == 0.0 ) { sprintf(errmsg,"Division by zero.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2565,errmsg,RECOVERABLE); } view[i][k] /= value; break; } *update_display_flag = 1; } break; case SET_QTARGET_: { struct gen_quant *q = GEN_QUANT(node->op1.quant_id); switch ( node->op2.assigntype ) { case ASSIGN_: q->target = *(stacktop--); break; case PLUSASSIGN_: q->target += *(stacktop--); break; case SUBASSIGN_: q->target -= *(stacktop--); break; case MULTASSIGN_: q->target *= *(stacktop--); break; case DIVASSIGN_: if ( *stacktop == 0.0 ) { sprintf(errmsg,"Division by zero.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2588,errmsg,RECOVERABLE); } q->target /= *(stacktop--); break; } if ( valid_id(q->b_id) ) set_body_fixvol(q->b_id,q->target); } break; case SET_QMODULUS_: { REAL v = GEN_QUANT(node->op1.quant_id)->modulus; switch ( node->op2.assigntype ) { case ASSIGN_: v = *(stacktop--); break; case PLUSASSIGN_: v += *(stacktop--); break; case SUBASSIGN_: v -= *(stacktop--); break; case MULTASSIGN_: v *= *(stacktop--); break; case DIVASSIGN_: if ( *stacktop == 0.0 ) { sprintf(errmsg,"Division by zero.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2589,errmsg,RECOVERABLE); } v /= *(stacktop--); break; } GEN_QUANT(node->op1.quant_id)->modulus = v; } break; case SET_QTOLERANCE_: if ( *stacktop <= 0.0 ) kb_error(2050,"Tolerance must be positive.\n",RECOVERABLE); { REAL v = GEN_QUANT(node->op1.quant_id)->tolerance; switch ( node->op2.assigntype ) { case ASSIGN_: v = *(stacktop--); break; case PLUSASSIGN_: v += *(stacktop--); break; case SUBASSIGN_: v -= *(stacktop--); break; case MULTASSIGN_: v *= *(stacktop--); break; case DIVASSIGN_: if ( *stacktop == 0.0 ) { sprintf(errmsg,"Division by zero.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2487,errmsg,RECOVERABLE); } v /= *(stacktop--); break; } GEN_QUANT(node->op1.quant_id)->tolerance = v; } break; case SET_MMODULUS_: { REAL v = METH_INSTANCE(node->op1.meth_id)->modulus; switch ( node->op2.assigntype ) { case ASSIGN_: v = *(stacktop--); break; case PLUSASSIGN_: v += *(stacktop--); break; case SUBASSIGN_: v -= *(stacktop--); break; case MULTASSIGN_: v *= *(stacktop--); break; case DIVASSIGN_: if ( *stacktop == 0.0 ) { sprintf(errmsg,"Division by zero.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2488,errmsg,RECOVERABLE); } v /= *(stacktop--); break; } METH_INSTANCE(node->op1.meth_id)->modulus = v; } break; case SET_QVOLCONST_: { struct gen_quant *q = GEN_QUANT(node->op1.quant_id); switch ( node->op2.assigntype ) { case ASSIGN_: q->volconst = *(stacktop--); break; case PLUSASSIGN_: q->volconst += *(stacktop--); break; case SUBASSIGN_: q->volconst -= *(stacktop--); break; case MULTASSIGN_: q->volconst *= *(stacktop--); break; case DIVASSIGN_: if ( *stacktop == 0.0 ) { sprintf(errmsg,"Division by zero.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2489,errmsg,RECOVERABLE); } q->volconst /= *(stacktop--); break; } if ( valid_id(q->b_id) ) set_body_volconst(q->b_id,q->volconst); } break; case RUNGE_KUTTA_: old = runge_kutta_flag; runge_kutta_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(runge_kutta_flag ? "Runge-Kutta ON." : "Runge-Kutta OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case CHECK_INCREASE_: old = check_increase_flag; check_increase_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(check_increase_flag?"Increase check ON.": "Increase check OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case HOMOTHETY_: old = web.homothety; web.homothety = (node->op1.toggle_state==ON_) ? 1 : 0; if ( web.homothety && (web.skel[BODY].count == 0) ) { web.homothety = 0; sprintf(errmsg,"Cannot do homothety without bodies.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1233,errmsg,RECOVERABLE); } sprintf(msg,"Homothety adjustment is %s.", web.homothety ? "ON" : "OFF"); outstring(msg); outstring(old?" (was on)\n":" (was off)\n"); if ( web.homothety ) { sprintf(msg,"Enter target size (%g): ",(DOUBLE)homothety_target); prompt(msg,response,sizeof(response)); const_expr(response,&homothety_target); } break; case COUNTS_: /* report count of elements and status */ memory_report(); break; case EXTRAPOLATE_: extrapolate(); break; case DIFFUSION_: /* Set diffusion */ old = web.diffusion_flag; web.diffusion_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(web.diffusion_flag ? "Diffusion ON." : "Diffusion OFF."); outstring(old?" (was on)\n":" (was off)\n"); break; case AUTODISPLAY_: old = go_display_flag; go_display_flag = (node->op1.toggle_state == ON_) ? 1 : 0; outstring(go_display_flag ? "Autodisplay ON." : "Autodisplay OFF."); outstring(old?" (was on)\n":" (was off)\n"); if ( go_display_flag ) update_display(); break; case SHOW_INNER_: old = innerflag; innerflag = (node->op1.toggle_state == ON_) ? 1 : 0; outstring(innerflag ? "show inner ON." : "show inner OFF."); outstring(old?" (was on)\n":" (was off)\n"); update_display(); break; case SHOW_OUTER_: old = outerflag; outerflag = (node->op1.toggle_state == ON_) ? 1 : 0; outstring(outerflag ? "show outer ON." : "show outer OFF."); outstring(old?" (was on)\n":" (was off)\n"); update_display(); break; case INTERP_NORMALS_: normflag = (node->op1.toggle_state == ON_) ? 1 : 0; break; case COLORMAP_: colorflag = (node->op1.toggle_state == ON_) ? 1 : 0; break; case AMBIENT_PRESSURE_: web.pressure_flag = (node->op1.toggle_state == ON_) ? 1 : 0; if ( web.pressure_flag) { if ( !web.full_flag && !valid_id(web.outside_body) ) add_outside(); web.projection_flag = 0; sprintf(msg,"Ambient pressure ON; ambient pressure = %2.15g\n", (double)web.pressure); } else { web.projection_flag = 1; sprintf(msg,"Ambient pressure OFF.\n"); } calc_energy(); outstring(msg); break; case EFFECTIVE_AREA_: if ( SDIM > 3 ) { sprintf(errmsg,"effective_area only for dimension 3.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2051,errmsg,RECOVERABLE); } old = effective_area_flag; effective_area_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(effective_area_flag ? "effective_area ON." : "effective_area OFF."); outstring(old?" (was on)\n":" (was off)\n"); if ( effective_area_flag ) web.area_norm_flag = 1; if ( square_curvature_flag ) calc_energy(); break; case NORMAL_CURVATURE_: if ( SDIM != 3 ) { sprintf(errmsg,"Normal_curvature only for dimension 3.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2052,errmsg,RECOVERABLE); } old = normal_curvature_flag; normal_curvature_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(normal_curvature_flag ? "normal_curvature ON." : "normal_curvature OFF."); outstring(old?" (was on)\n":" (was off)\n"); if ( square_curvature_flag ) calc_energy(); break; case DIV_NORMAL_CURVATURE_: if ( SDIM != 3 ) { sprintf(errmsg,"Div_normal_curvature only for dimension 3.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2053,errmsg,RECOVERABLE); } old = div_normal_curvature_flag; div_normal_curvature_flag = (node->op1.toggle_state==ON_) ? 1 : 0; outstring(div_normal_curvature_flag ? "div_normal_curvature ON." : "div_normal_curvature OFF."); outstring(old?" (was on)\n":" (was off)\n"); if ( square_curvature_flag ) calc_energy(); break; case LONG_JIGGLE_: long_jiggle(); break; case RAW_VERAVG_: vertex_average(NOVOLKEEP); recalc(); outstring("Vertex averaging done.\n"); break; case RAWEST_VERAVG_: vertex_average(RAWEST); recalc(); outstring("Vertex averaging done.\n"); break; case TEXT_SPOT_: /* just accumulate arguments */ break; case DISPLAY_TEXT_: { char *text = *(char**)(stacktop--); REAL yspot = *(stacktop--); REAL xspot = *(stacktop--); int length = strlen(text); int text_id; for ( text_id = 0 ; text_id < MAXTEXTS ; text_id++ ) { if ( text_chunks[text_id].text == NULL ) { text_chunks[text_id].text = (char*)mycalloc(length+1,1); break; } } if ( text_id >= MAXTEXTS-1 ) { kb_error(4998,"Too many display texts.\n",WARNING); break; } strcpy(text_chunks[text_id].text,text); text_chunks[text_id].start_x = xspot; text_chunks[text_id].start_y = yspot; display_text_count++; *(++stacktop) = text_id+1; *recalc_flag = 1; break; } case DELETE_TEXT_: { int text_id = (int)*(stacktop--) - 1; if ( (text_id < 0) || (text_id >= MAXTEXTS) ) { sprintf(errmsg,"Text id must be between 1 and %d.\n",MAXTEXTS); kb_error(7543,errmsg,RECOVERABLE); } if ( text_chunks[text_id].text ) { myfree(text_chunks[text_id].text); text_chunks[text_id].text = NULL; display_text_count--; *recalc_flag = 1; } break; } case REBODY_: { body_id b_id; sprintf(msg,"New bodies: %d\n",rebody()); outstring(msg); sprintf(msg,"Merged bodies: %d\n",merge_bodies()); outstring(msg); calc_content(Q_FIXED|Q_INFO); FOR_ALL_BODIES(b_id) if ( get_battr(b_id) & FIXEDVOL ) set_body_fixvol(b_id,get_body_volume(b_id)); break; } case SUPPRESS_WARNING_: { int wnum = (int)*(stacktop--); if ( warnings_suppressed_count < MAXSUPPRESS ) warnings_suppressed[warnings_suppressed_count++] = wnum; else kb_error(4535,"Too many warnings suppressed.\n",WARNING); } break; case UNSUPPRESS_WARNING_: { int wnum = (int)*(stacktop--); for ( i = 0 ; i < warnings_suppressed_count ; i++ ) if ( warnings_suppressed[i] == wnum ) { warnings_suppressed[i] = warnings_suppressed[--warnings_suppressed_count]; break; } } break; case ALICE_: alice(); break; case BURCHARD_: burchard(node->op1.maxsteps); break; case DUMP_: if ( node->left ) do_dump(*(char**)(stacktop--)); else do_dump(NULL); break; case SET_COLORMAP_: strncpy(cmapname,*(char**)(stacktop--),sizeof(cmapname)); break; case RAW_CELLS_: web.torus_body_flag = 0; web.torus_clip_flag = 0; torus_display_mode = TORUS_RAW_MODE; update_display(); break; case CONNECTED_CELLS_: if ( web.skel[BODY].count == 0 ) { sprintf(errmsg,"There are no bodies to display connectedly.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1234,errmsg,WARNING); } else { web.torus_body_flag = 1; web.torus_clip_flag = 0; torus_display_mode = TORUS_CONNECTED_MODE; update_display(); } break; case CLIPPED_CELLS_: if ( !web.torus_flag && !web.torus_display_period ) { sprintf(errmsg, "CLIPPED mode has no effect since torus model not used.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1254,errmsg, WARNING); } torus_display_mode = TORUS_CLIPPED_MODE; web.torus_body_flag = 0; web.torus_clip_flag = 1; update_display(); break; case THICKEN_: thickenflag = (node->op1.toggle_state == ON_) ? 1 : 0; update_display(); break; case METRIC_CONVERT_: metric_convert_flag = (node->op1.toggle_state == ON_) ? 1 : 0; if ( metric_convert_flag ) outstring("Converting form to vector using metric.\n"); else outstring("Not using metric to convert form to vector.\n"); break; case QUANTITIES_ONLY_: old = quantities_only_flag; quantities_only_flag = (node->op1.toggle_state == ON_) ? 1 : 0; outstring("Using quantities only "); outstring(quantities_only_flag ? "ON.": "OFF."); outstring(old ? "(was ON)\n" : "(was OFF)\n"); break; case SQUARED_GRADIENT_: old = min_square_grad_flag; min_square_grad_flag = (node->op1.toggle_state == ON_) ? 1 : 0; outstring("Squared gradient minimization with Hessian "); outstring(min_square_grad_flag ? "ON.": "OFF."); outstring(old ? "(was ON)\n" : "(was OFF)\n"); break; case LINEAR_METRIC_: old = hessian_linear_metric_flag; hessian_linear_metric_flag = (node->op1.toggle_state == ON_) ? 1 : 0; outstring("Linear interpolation metric with Hessian "); outstring(hessian_linear_metric_flag ? "ON.": "OFF."); outstring(old ? "(was ON)\n" : "(was OFF)\n"); break; case YSMP_: old = ysmp_flag; ysmp_flag = (node->op1.toggle_state == ON_) ? YSMP_FACTORING : MINDEG_FACTORING; change_hessian_functions(old,ysmp_flag); break; case METIS_FACTOR_: old = ysmp_flag; #ifndef METIS kb_error(2055,"This Evolver not compiled with METIS.\n",RECOVERABLE); #else ysmp_flag = METIS_FACTORING; change_hessian_functions(old,ysmp_flag); #endif break; case BUNCH_KAUFMAN_: flip_toggle(&BK_flag,node->op1.toggle_state, "Bunch-Kaufman version of minimal degree"); break; case CONTINUE_: case SET_PROC_END_: case SET_FUNC_END_: case SET_PERM_PROC_END_: case SHOW_END_: case ELSE_: case COND_ELSE_: /* just a continue node */ break; default: sprintf(errmsg,"Bad expression eval node type: %s.", tokname(node->type)); if ( node->file_no >= 0 && node->file_no <= file_no_used ) sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); else sprintf(errmsg+strlen(errmsg),"(appears to be corrupt node)\n"); kb_error(1235,errmsg,RECOVERABLE); break; } } /* end more_other_stuff() */ /******************************************************************** * * function: get_toggle_value() * * purpose: get boolean value of toggle for expression */ int get_toggle_value(tog) int tog; /* type of toggle */ { switch (tog) { case GEOMVIEW_: return geomview_flag; case BEZIER_BASIS_: return bezier_flag; case SMOOTH_GRAPH_: return smooth_graph_flag; case FULL_BOUNDING_BOX_: return full_bounding_box_flag; case POP_DISJOIN_: return pop_disjoin_flag; case POP_ENJOIN_: return pop_enjoin_flag; case POP_TO_EDGE_: return pop_to_edge_flag; case POP_TO_FACE_: return pop_to_face_flag; case MPI_DEBUG_: return mpi_debug; case AMBIENT_PRESSURE_: return web.pressure_flag; case ZENER_DRAG_: return zener_drag_flag; case BACKCULL_: return backcull_flag; case TORUS_FILLED_: return web.full_flag; case VOLGRADS_EVERY_: return volgrads_every_flag; case LOGFILE_: return logfile_flag; case KEYLOGFILE_: return logfile_flag; case LINEAR_: return (web.modeltype == LINEAR); case QUADRATIC_: return (web.modeltype == QUADRATIC); case LAGRANGE_: return (web.modeltype == LAGRANGE); case KUSNER_: return kusner_flag; case ESTIMATE_: return estimate_flag; case DETURCK_: return unit_normal_flag; case HOMOTHETY_: return web.homothety; case SLICE_VIEW_: return slice_view_flag; case CLIP_VIEW_: return clip_view_flag; case SQGAUSS_: return sqgauss_flag; case AUTOPOP_: return autopop_flag; case IMMEDIATE_AUTOPOP_: return immediate_autopop_flag; case AUTOPOP_QUARTIC_: return autopop_quartic_flag; case AUTOCHOP_: return autochop_flag; case QUIET_: return quiet_flag; case HESSIAN_QUIET_: return hessian_quiet_flag; case QUIETGO_: return quiet_go_flag; case QUIETLOAD_: return quiet_load_flag; case OLD_AREA_: return old_area_flag; case APPROX_CURV_: return approx_curve_flag; case RUNGE_KUTTA_: return runge_kutta_flag; case CHECK_INCREASE_: return check_increase_flag; case DEBUG_: return yydebug; case MEAN_CURV_: return web.area_norm_flag; case DIFFUSION_: return web.diffusion_flag; case GRAVITY_: return web.gravflag; case CONJ_GRAD_: return conj_grad_flag; case TRANSFORMS_: return transforms_flag; case CONF_EDGE_SQCURV_: return conf_edge_curv_flag; case EFFECTIVE_AREA_: return effective_area_flag; case RAW_CELLS_: return !web.torus_body_flag && !web.torus_clip_flag; case CONNECTED_CELLS_: return web.torus_body_flag; case CLIPPED_CELLS_: return web.torus_clip_flag; case THICKEN_: return thickenflag; case YSMP_: return ysmp_flag; case LINEAR_METRIC_: return hessian_linear_metric_flag; case METRIC_CONVERT_: return metric_convert_flag; case BUNCH_KAUFMAN_: return BK_flag; case SHOW_INNER_: return innerflag; case SHOW_OUTER_: return outerflag; case COLORMAP_: return colorflag; case HESSIAN_DIFF_: return hessian_by_diff_flag; case POST_PROJECT_: return post_project_flag; case MEAN_CURV_INT_: return mean_curv_int_flag; case OPTIMIZE_: return !web.motion_flag; case NORMAL_CURVATURE_: return normal_curvature_flag; case DIV_NORMAL_CURVATURE_: return div_normal_curvature_flag; case SHADING_: return shading_flag; case FACET_COLORS_: return color_flag; case BOUNDARY_CURVATURE_: return boundary_curvature_flag; case NORMAL_MOTION_: return normal_motion_flag; case PINNING_: return check_pinning_flag; case VIEW_4D_: return view_4D_flag; case MEMDEBUG_: return memdebug; case ITDEBUG_: return itdebug; case METRIC_CONVERSION_: return metric_convert_flag; case AUTORECALC_: return autorecalc_flag; case FORCE_POS_DEF_: return make_pos_def_flag; case GV_BINARY_: return gv_binary_flag; case SELF_SIMILAR_: return self_similar_flag; case AUTODISPLAY_: return go_display_flag; case RIBIERE_CG_: return ribiere_flag; case ASSUME_ORIENTED_: return assume_oriented_flag; case DIRICHLET_MODE_: return dirichlet_flag; case SOBOLEV_MODE_: return sobolev_flag; case KRAYNIKPOPVERTEX_FLAG_: return kraynikpopvertex_flag; case KRAYNIKPOPEDGE_FLAG_: return kraynikpopedge_flag; case HESSIAN_NORMAL_: return hessian_normal_flag; case HESSIAN_SPECIAL_NORMAL_: return hessian_special_normal_flag; case HESSIAN_NORMAL_PERP_: return hessian_normal_perp_flag; case HESSIAN_NORMAL_ONE_: return hessian_normal_one_flag; case HESSIAN_DOUBLE_NORMAL_: return hessian_double_normal_flag; case INTERP_BDRY_PARAM_: return interp_bdry_param; case H_INVERSE_METRIC_: return web.h_inverse_metric_flag; case PSCOLORFLAG_: return ps_colorflag; case GRIDFLAG_: return gridflag; case METIS_FACTOR_: return ysmp_flag == METIS_FACTORING; case CROSSINGFLAG_: return crossingflag; case LABELFLAG_: return labelflag; case SHOW_ALL_QUANTITIES_: return show_all_quantities; case QUANTITIES_ONLY_: return quantities_only_flag; case VISIBILITY_TEST_: return visibility_test; case SPARSE_CONSTRAINTS_: return sparse_constraints_flag; case BLAS_FLAG_: return blas_flag; case BREAK_AFTER_WARNING_ : return break_after_warning; case AUGMENTED_HESSIAN_: return augmented_hessian_flag; case VERBOSE_: return verbose_flag; case FUNCTION_QUANTITY_SPARSE_: return quantity_function_sparse_flag; case BIG_ENDIAN_: return big_endian_flag; case LITTLE_ENDIAN_ : return little_endian_flag; default: sprintf(errmsg,"Internal error: Toggle value omitted for toggle %d.\n",tog); kb_error(1236,errmsg,WARNING); } return 0; } /* end get_toggle_value */ /************************************************************************* * * function: get_internal_variable() * * purpose: get value of various variables visible to user. */ REAL get_internal_variable(vartok) int vartok; /* token number of variable */ { switch(vartok) { case V_HIGH_BOUNDARY: return web.highbdry; case V_HIGH_CONSTRAINT: return web.highcon; case V_RANDOM: return kb_drand(); case V_MPI_MAXTASK: #ifdef MPI_EVOLVER return mpi_nprocs-1; #else return 1; #endif #ifdef MPI_EVOLVER case V_CORONA_STATE: return (REAL)mpi_corona_state; #endif case V_VERTEXCOUNT: return (REAL)web.skel[VERTEX].count; case V_EDGECOUNT: return (REAL)web.skel[EDGE].count; case V_FACETCOUNT: return (REAL)web.skel[FACET].count; case V_BODYCOUNT: return (REAL)web.skel[BODY].count; case V_FACETEDGECOUNT: return (REAL)web.skel[FACETEDGE].count; case V_ENERGY: return web.total_energy; case V_GAP_CONSTANT: return web.spring_constant; case V_TIME: return total_time; case V_JIG_TEMP: return web.temperature; case V_AREA: case V_LENGTH: return web.total_area; case V_ITER_COUNTER: return (REAL)gocount; case V_AMBIENT_PRESSURE: return web.pressure; case V_DIFFUSION: return web.diffusion_const; case V_SCALE: return web.scale; case V_SCALE_SCALE: return web.scale_scale; case V_SPACE_DIMENSION: return (REAL)SDIM; case V_SURFACE_DIMENSION: return (REAL)web.dimension; case V_TORUS: return (REAL)web.torus_flag; case V_SYMMETRY_GROUP: return (REAL)web.symmetry_flag; case V_SIMPLEX: return (REAL)(web.representation == SIMPLEX); case V_INTEGRAL_ORDER: return ((REAL)web.gauss1D_order+2)/2; case V_INTEGRAL_ORDER_1D: return (REAL)web.gauss1D_order; case V_INTEGRAL_ORDER_2D: return (REAL)web.gauss2D_order; case V_TOLERANCE: return web.tolerance; case V_TARGET_TOLERANCE: return web.target_tolerance; case V_THICKNESS: return thickness; case V_HESSIAN_SLANT_CUTOFF: return hessian_slant_cutoff; case V_HESS_EPSILON: return hessian_epsilon; case GRAV_CONST_: return web.grav_const; case V_EQUI_COUNT: return (REAL)web.equi_count; case V_EDGESWAP_COUNT: return (REAL)web.edgeswap_count; case V_T1_EDGESWAP_COUNT: return (REAL)web.t1_edgeswap_count; case V_DELETE_COUNT: return (REAL)web.edge_delete_count + web.facet_delete_count; case V_EDGE_DELETE_COUNT: return (REAL)web.edge_delete_count; case V_FACET_DELETE_COUNT: return (REAL)web.facet_delete_count; case V_REFINE_COUNT: return (REAL)web.edge_refine_count + web.facet_refine_count;; case V_EDGE_REFINE_COUNT: return (REAL)web.edge_refine_count; case V_FACET_REFINE_COUNT: return (REAL)web.facet_refine_count; case V_NOTCH_COUNT: return (REAL)web.notch_count; case V_DISSOLVE_COUNT: return (REAL)web.vertex_dissolve_count + web.edge_dissolve_count + web.facet_dissolve_count + web.body_dissolve_count; case V_VERTEX_DISSOLVE_COUNT: return (REAL)web.vertex_dissolve_count; case V_EDGE_DISSOLVE_COUNT: return (REAL)web.edge_dissolve_count; case V_FACET_DISSOLVE_COUNT: return (REAL)web.facet_dissolve_count; case V_BODY_DISSOLVE_COUNT: return (REAL)web.body_dissolve_count; case V_EDGE_REVERSE_COUNT: return (REAL)web.edge_reverse_count; case V_FACET_REVERSE_COUNT: return (REAL)web.facet_reverse_count; case V_FIX_COUNT: return web.fix_count; case V_THIS_TASK: return this_task; case V_WINDOW_ASPECT_RATIO: return window_aspect_ratio; case V_STRING_CURVE_TOLERANCE: return string_curve_tolerance; case V_MINDEG_DEBUG_LEVEL: return mindeg_debug_level; case V_MINDEG_MARGIN: return mindeg_margin; case V_MINDEG_MIN_REGION_SIZE: return mindeg_min_region_size; case V_UNFIX_COUNT: return web.unfix_count; case V_POP_COUNT: return web.vertex_pop_count + web.edge_pop_count; case V_VERTEX_POP_COUNT: return web.vertex_pop_count; case V_EDGE_POP_COUNT: return web.edge_pop_count; case V_POP_TRI_TO_EDGE_COUNT: return web.pop_tri_to_edge_count; case V_POP_EDGE_TO_TRI_COUNT: return web.pop_edge_to_tri_count; case V_POP_QUAD_TO_QUAD_COUNT: return web.pop_quad_to_quad_count; case V_WHERE_COUNT: return (REAL)web.where_count; case V_EIGENPOS: return (REAL)eigen_pos; case V_EIGENNEG: return (REAL)eigen_neg; case V_EIGENZERO: return (REAL)eigen_zero; case V_CHECK_COUNT_: return (REAL)check_count; case V_VISIBILITY_DEBUG_: return (REAL)visdebuglevel; case V_SCROLLBUFFERSIZE_: return (REAL)scrollbuffersize; case V_BREAKFLAG_: return (REAL)breakflag; case V_PICKVNUM: return (REAL)pickvnum; case V_PICKENUM: return (REAL)pickenum; case V_PICKFNUM: return (REAL)pickfnum; case V_LINEAR_METRIC_MIX: return linear_metric_mix; case V_QUADRATIC_METRIC_MIX: return quadratic_metric_mix; case V_RANDOM_SEED: return (REAL)random_seed; case V_LAST_EIGENVALUE: return last_eigenvalue; case V_LAST_HESSIAN_SCALE: return last_hessian_scale; case V_LAGRANGE_ORDER: return (REAL)web.lagrange_order; case V_SCALE_LIMIT: return web.maxscale; case V_BRIGHTNESS: return brightness; case V_LAST_ERROR: return last_error; case V_BACKGROUND: return background_color; case V_PS_LABELSIZE_: return ps_labelsize; case V_PS_STRINGWIDTH_: return ps_stringwidth; case V_PS_FIXEDEDGEWIDTH_: return ps_fixededgewidth; case V_PS_TRIPLEEDGEWIDTH_: return ps_tripleedgewidth; case V_PS_CONEDGEWIDTH_: return ps_conedgewidth; case V_PS_BAREEDGEWIDTH_: return ps_bareedgewidth; case V_PS_GRIDEDGEWIDTH_: return ps_gridedgewidth; case EVERYTHING_QUANTITIES_: return everything_quantities_flag; case V_AUTOCHOP_LENGTH: return autochop_length; case V_TRANSFORM_COUNT: return transform_count == 0 ? 1 : transform_count; case V_CLOCK: #if defined (__SYS_TIMES_H__) && defined(HZ) { struct tms t; times(&t); return t.tms_utime/(REAL)HZ; } #else #if defined (_SYS_TIMES_H) && defined(CLK_TCK) { struct tms t; /* SGI, anyway */ times(&t); return t.tms_utime/(REAL)CLK_TCK; } #else #ifdef CLOCKS_PER_SEC return clock()/(REAL)CLOCKS_PER_SEC; #else return 0.0; #endif #endif #endif break; case V_CPU_COUNTER: { int cycles[2]; PROF_NOW(cycles); return PROF_CYCLES(cycles); } case V_MEMARENA: #if defined(M_MXFAST) && defined(IRIS) { struct mallinfo m = mallinfo(); return (REAL)m.arena; } #else #if defined(_UNISTD_H) /* do this only on unix systems with unistd.h */ return (char*)sbrk(0)-(char*)&evolver_version; #else #ifdef MSC { struct _heapinfo hinfo; long mem_use=0,mem_free=0; hinfo._pentry = NULL; while ( _heapwalk(&hinfo) == _HEAPOK ) if (hinfo._useflag) { mem_use+= hinfo._size; } else { mem_free += hinfo._size; } return (REAL)(mem_use+mem_free); } #else #ifdef TC { struct heapinfo hinfo; long mem_use=0,mem_free=0; hinfo.ptr = NULL; while ( heapwalk(&hinfo) == _HEAPOK ) if (hinfo.in_use) { mem_use+= hinfo.size; } else { mem_free += hinfo.size; } return (REAL)(mem_use+mem_free); } #else return 0.0; #endif #endif #endif #endif break; case V_MEMUSED: #if defined(M_MXFAST) && defined(IRIS) { struct mallinfo m = mallinfo(); return (REAL)(m.uordblks + m.usmblks); } #else #ifdef WIN32 { struct _heapinfo hinfo; long mem_use=0,mem_free=0; hinfo._pentry = NULL; while ( _heapwalk(&hinfo) == _HEAPOK ) if (hinfo._useflag) { mem_use+= hinfo._size; } else { mem_free += hinfo._size; } return (REAL)(mem_use); } #else return 0.0; #endif #endif break; default: sprintf(errmsg,"Internal: illegal variable number %d.\n",vartok); kb_error(1208, errmsg,RECOVERABLE); } return 0.0; } /********************************************************************** * * function: tree_copy() * * purpose: copy tree in linear postorder form * allocates space for tree in destination * */ void tree_copy(dest,src) struct expnode *dest; struct treenode *src; { struct treenode *enode; size_t count,n; if ( dest == NULL ) return; if ( dest->start ) myfree((char*)dest->start); if ( src == NULL ) { dest->start = dest->root = NULL; return; } enode=src; while ( enode->left || enode->right ) { if ( enode->left < enode->right ) enode+=enode->left; else enode += enode->right; } count = src - enode + 1; dest->start = (struct treenode *)mycalloc(count+3,sizeof(struct treenode)); dest->flag |= USERCOPY; memcpy((char*)(dest->start+2),(char*)enode,count*sizeof(struct treenode)); dest->root = dest->start + count + 1; dest->start[1].type = SETUP_FRAME_; /* make copies of strings and locallists */ for ( enode=dest->start+2, n = 0 ; n < count ; enode++,n++ ) { if ( enode->flags & HAS_STRING ) { char *s = mycalloc(strlen(enode->op1.string)+1,sizeof(char)); strcpy(s,enode->op1.string); enode->op1.string = s; } if ( enode->flags & HAS_STRING_5 ) { char *s = mycalloc(strlen(enode->op5.string)+1,sizeof(char)); strcpy(s,enode->op5.string); enode->op5.string = s; } if ( enode->flags & HAS_LOCALLIST ) { struct locallist_t *locals = (struct locallist_t *)mycalloc(1,sizeof(struct locallist_t)); *locals = *enode->op5.locals; locals->list = (struct localvar_t *)mycalloc(enode->op5.locals->count, sizeof(struct localvar_t)); memcpy(locals->list,enode->op5.locals->list,enode->op5.locals->count* sizeof(struct localvar_t)); enode->op5.locals = locals; } } /* copy root to first place */ dest->start[0] = *src; if ( dest->start[0].left ) dest->start[0].left += (int)count+1; if ( dest->start[0].right ) dest->start[0].right += (int)count+1; /* FINISH node */ dest->start[count+2].type = FINISHED; stack_usage(dest); } /* end tree_copy() */ /********************************************************************** * * function: perm_tree_copy() * * purpose: copy tree in linear postorder form * allocates permanent space for tree in destination * */ void perm_tree_copy(dest,src) struct expnode *dest; struct treenode *src; { struct treenode *enode; size_t count,n; if ( dest == NULL ) return; if ( dest->start ) free((char*)dest->start); if ( src == NULL ) { dest->start = dest->root = NULL; return; } enode=src; while ( enode->left || enode->right ) { if ( enode->left < enode->right ) enode+=enode->left; else enode += enode->right; } count = src - enode + 1; dest->start = (struct treenode *)calloc(count+3,sizeof(struct treenode)); dest->flag |= USERCOPY; memcpy((char*)(dest->start+2),(char*)enode,count*sizeof(struct treenode)); dest->root = dest->start + count + 1; dest->start[1].type = SETUP_FRAME_; /* make copies of strings */ for ( enode=dest->start+2, n = 0 ; n < count ; enode++,n++ ) { if ( enode->flags & HAS_STRING ) { char *s = calloc(strlen(enode->op1.string)+1,sizeof(char)); strcpy(s,enode->op1.string); enode->op1.string = s; } enode->flags |= PERMNODE; } /* copy root to first place */ dest->start[0] = *src; if ( dest->start[0].left ) dest->start[0].left += (int)count + 1; if ( dest->start[0].right ) dest->start[0].right += (int)count + 1; dest->start[0].flags |= PERMNODE; /* FINISH node */ dest->start[count+2].type = FINISHED; stack_usage(dest); } /******************************************************************* * * function: reduce_strings() * * purpose: converts escape sequences to ASCII in place * */ void reduce_string(s) char *s; { char *c = s; while ( *s ) /* reduce escape sequences */ if ( *s == '\\' ) { switch ( s[1] ) { case 'n' : *(c++) = '\n'; s += 2; break; case 't' : *(c++) = '\t'; s += 2; break; case 'b' : *(c++) = '\b'; s += 2; break; case 'r' : *(c++) = '\r'; s += 2; break; case '\\' : *(c++) = '\\'; s += 2; break; case 'q' : *(c++) = '"'; s += 2; break; default: *(c++) = s[1]; s += 2; break; } } else if (*s == '\n' ) s++; /* omit newlines */ else *(c++) = *(s++); *c = '\0'; } /*********************************************************************** * * function: set_body() * * purpose: set facet body, or edge body in string model. * Can set to NULLID. */ void set_body(id,b_id) element_id id; /* edge or facet, inverted for backbody */ body_id b_id; { if ( id_type(id) == FACET ) { body_id bb_id = get_facet_body(id); int newp = valid_id(b_id) ? get_body_volmeth(b_id) : 0; facetedge_id fe,start_fe; set_facet_body(id,b_id); if ( (web.representation == STRING) && everything_quantities_flag ) { /* fix up edge volume quantities */ fe = start_fe = get_facet_fe(id); do { edge_id e_id = get_fe_edge(fe); if ( valid_id(bb_id) ) { int oldp = get_body_volmeth(bb_id); unapply_method(e_id,oldp); } if ( newp ) apply_method_num(e_id,newp); else set_facet_area(id,0.0); fe = get_next_edge(fe); } while ( valid_id(fe) && !equal_id(fe,start_fe) ); } return; } /* STRING model only hereafter! */ if ( (id_type(id) != EDGE) || (web.representation != STRING) ) { sprintf(errmsg,"Trying to set body of wrong type element.\n"); kb_error(2056,errmsg,RECOVERABLE); } if ( !valid_id(b_id) ) { facetedge_id fe,fe_id,next_fe,prev_fe; facet_id f_id; body_id bb_id; /* unsetting edge body, so dissolving facetedge */ fe_id = get_edge_fe(id); f_id = get_fe_facet(fe_id); if ( !valid_id(f_id) ) return; if ( inverted(f_id) ) { fe_id = get_next_facet(fe_id); f_id = get_fe_facet(fe_id); } if ( !valid_id(f_id) ) return; if ( inverted(f_id) ) return; /* check for being at beginning or end of edge arc */ fe = fe_id; if ( valid_id(get_next_edge(fe)) && valid_id(get_prev_edge(fe)) ) { /* check for full loop */ do { fe = get_next_edge(fe); } while ( valid_id(fe) && !equal_id(fe,fe_id) ); if ( !valid_id(fe) ) { if ( verbose_flag ) { sprintf(msg,"Not unbodying edge %s since would make facet two arcs.\n", ELNAME(id)); outstring(msg); } return; } } /* Okay, ready to party. */ if ( everything_quantities_flag ) { bb_id = get_facet_body(f_id); if ( valid_id(bb_id) ) { unapply_method(id,get_body_volmeth(bb_id)); } } next_fe = get_next_edge(fe_id); prev_fe = get_prev_edge(fe_id); if ( valid_id(next_fe) ) { set_prev_edge(next_fe,NULLID); set_facet_fe(f_id,next_fe); } if ( valid_id(prev_fe) ) set_next_edge(prev_fe,NULLID); else if ( !valid_id(next_fe) ) set_facet_fe(f_id,NULLID); if ( valid_id(get_next_facet(fe_id)) ) set_edge_fe(id,get_next_facet(fe_id)); set_next_facet(get_prev_facet(fe_id),get_next_facet(fe_id)); set_prev_facet(get_next_facet(fe_id),get_prev_facet(fe_id)); free_element(fe_id); } else /* valid id */ { /* string model */ facet_id f_id = get_body_facet(b_id); facetedge_id efe,eefe,fe,start_fe; facetedge_id newfe; body_id bb_id; int inserted_flag=0; /* whether a linkup made */ if ( !valid_id(f_id) ) { sprintf(errmsg, "Need to set the body's facet before assigning edges.\n"); kb_error(2057,errmsg,RECOVERABLE); } if ( everything_quantities_flag ) { bb_id = get_facet_body(f_id); if ( valid_id(bb_id) ) { unapply_method(id,get_body_volmeth(bb_id)); } apply_method_num(id,get_body_volmeth(b_id)); } newfe = new_facetedge(f_id,id); efe = get_edge_fe(id); if ( valid_id(get_fe_facet(efe)) ) { eefe = get_next_facet(efe); set_next_facet(efe,newfe); set_next_facet(newfe,eefe); set_prev_facet(eefe,newfe); set_prev_facet(newfe,efe); } else { if (valid_id(efe)) free_element(efe); set_next_facet(newfe,newfe); set_prev_facet(newfe,newfe); set_edge_fe(id,newfe); } /* see if can place at head or end of chain */ fe = start_fe = get_facet_fe(f_id); if ( !valid_id(fe) ) { /* naked facet */ set_facet_fe(f_id,newfe); inserted_flag = 1; } else { if ( !valid_id(get_prev_edge(fe)) ) { if ( equal_id(get_fe_tailv(fe),get_edge_headv(id)) ) { set_next_edge(newfe,fe); set_prev_edge(fe,newfe); set_facet_fe(f_id,newfe); inserted_flag = 1; } } while ( valid_id(get_next_edge(fe)) && !equal_id(get_next_edge(fe),start_fe) ) fe = get_next_edge(fe); if ( !valid_id(get_next_edge(fe)) ) { if ( equal_id(get_fe_headv(fe),get_edge_tailv(id)) ) { set_next_edge(fe,newfe); set_prev_edge(newfe,fe); inserted_flag = 1; } } } if ( !inserted_flag ) { sprintf(errmsg,"Could not link edge %s onto facet %s chain.\n", ELNAME(id),ELNAME1(f_id)); kb_error(2058,errmsg,WARNING); } } } /* end set_body() */ /*************************************************************************** * * function: change_hessian_functions() * * purpose: Switch sets of Hessian functions. */ void change_hessian_functions(oldh,newh) int oldh,newh; { if ( oldh == newh ) return; switch ( newh ) { case MINDEG_FACTORING: { sp_mul_func = bk_mul; sp_AIJ_setup_func= bk_AIJ_setup; sp_constraint_setup_func = bk_constraint_setup; sp_hess_project_setup_func= BK_hess_project_setup; sp_factor_func = xmd_factor; sp_solve_func = xmd_solve; sp_solve_multi_func = xmd_solve_multi; sp_ordering_func = NULL; sp_CHinvC_func = sp_CHinvC; outstring("Using alternate minimal degree. "); switch ( oldh ) { case YSMP_FACTORING: outstring("(was YSMP)\n"); break; case MINDEG_FACTORING: outstring("(was alt min deg)\n"); break; case METIS_FACTORING: outstring("(was metis)\n"); break; } } break; case YSMP_FACTORING: { sp_mul_func = bk_mul; sp_AIJ_setup_func= bk_AIJ_setup; sp_constraint_setup_func = bk_constraint_setup; sp_hess_project_setup_func= BK_hess_project_setup; sp_factor_func = ysmp_factor; sp_CHinvC_func = sp_CHinvC; sp_solve_func = ysmp_solve; sp_solve_multi_func = ysmp_solve_multi; sp_ordering_func = NULL; outstring("Using YSMP. "); switch ( oldh ) { case YSMP_FACTORING: outstring("(was YSMP)\n"); break; case MINDEG_FACTORING: outstring("(was alt min deg)\n"); break; case METIS_FACTORING: outstring("(was metis)\n"); break; } #if defined(TC) && defined(LONGDOUBLE) erroutstring("WARNING: YSMP is buggy in this long double version.\n"); #endif } break; case METIS_FACTORING: { sp_mul_func = bk_mul; sp_AIJ_setup_func= bk_AIJ_setup; sp_constraint_setup_func = bk_constraint_setup; sp_hess_project_setup_func= BK_hess_project_setup; sp_factor_func = ysmp_factor; sp_CHinvC_func = sp_CHinvC; sp_solve_func = ysmp_solve; sp_solve_multi_func = ysmp_solve_multi; /* tree stuff not working sp_factor_func = tree_factor; sp_solve_func = tree_solve; sp_solve_multi_func = tree_solve_multi; */ sp_ordering_func = metis_order; outstring("Using Metis ordering in ysmp factoring. "); switch ( oldh ) { case YSMP_FACTORING: outstring("(was YSMP)\n"); break; case MINDEG_FACTORING: outstring("(was alt min deg)\n"); break; case METIS_FACTORING: outstring("(was metis)\n"); break; } } } ysmp_flag = newh; } /* end change_hessian_functions() */ evolver-2.30c.dfsg/src/meanint.c0000644000175300017530000021545011410765113017023 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************* * * file: meanint.c * * Purpose: Implements routines for integral of mean * curvature over a surface. Computation is * exact, as the integral can be defined for a * polyhedral surface as edge dihedral angle * times edge length. Mean curvature is average * of sectional curvatures, not sum. * * Implemented as general quantity. Uses info * passed in qinfo structure. * qinfo has vertices: tail,head,left wing, right wing * qinfo has sides: edge,left from tail, right from tail * Basic formulas: * * cos(theta) = - * _________________________________________________ * sqrt(-^2)sqrt((-^2) * * sin(theta) = ||A|| * _________________________________________________ * sqrt(-^2)sqrt((-^2) * * tan(theta) = ||A|| * _______________________ * - * * Will use qinfo.ss to hold side products. */ #include "include.h" /********************************************************************* * * function: mean_int_init() * * purpose: Check illegalities * */ void mean_int_init(mode,mi) int mode; struct method_instance *mi; { if ( web.dimension != 2 ) kb_error(1594,"mean_curvature_integral method only for 2D facets.\n",RECOVERABLE); if ( web.modeltype != LINEAR ) kb_error(1595,"mean_curvature_integral method only for LINEAR model.\n",RECOVERABLE); if ( everything_quantities_flag && mean_curv_int_flag ) GEN_QUANT(mean_curv_int_quantity_num)->modulus = globals(mean_curvature_param)->value.real; } /************************************************************** * * Function: mean_int_value() * * Purpose: Computes contribution of one edge. */ REAL mean_int_value(e_info) struct qinfo *e_info; { REAL sinq,cosq; /* proprotional to sin and cos of angle */ REAL theta; /* the angle */ REAL len; /* length of side */ REAL vol; /* volume of parallelpiped of sides */ int sign = inverted(get_fe_facet(get_edge_fe(e_info->id))) ? 1 : -1; mat_tsquare(e_info->sides[0],e_info->ss,3,SDIM); /* side products */ len = sqrt(e_info->ss[0][0]); vol = triple_prod(e_info->sides[0][0],e_info->sides[0][1],e_info->sides[0][2]); if ( vol == 0.0 ) return vol; sinq = len*vol; cosq = e_info->ss[0][1]*e_info->ss[0][2] - e_info->ss[0][0]*e_info->ss[1][2]; theta = atan2(sinq,cosq); return sign*theta*len/2; } /************************************************************** * * Function: mean_int_gradient() * * Purpose: Computes contribution of one edge to gradient. */ REAL mean_int_gradient(e_info) struct qinfo *e_info; { REAL sinq,cosq; /* proprotional to sin and cos of angle */ REAL dsinq,dcosq; /* derivatives */ REAL theta; REAL dtheta; REAL len,dlen; REAL vol,dvol; int i,j; REAL g; int sign = inverted(get_fe_facet(get_edge_fe(e_info->id))) ? 1 : -1; for ( i = 0 ; i < 4 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) e_info->grad[i][j] = 0.0; mat_tsquare(e_info->sides[0],e_info->ss,3,SDIM); /* side products */ len = sqrt(e_info->ss[0][0]); vol = triple_prod(e_info->sides[0][0],e_info->sides[0][1],e_info->sides[0][2]); if ( vol == 0.0 ) return 0.0; sinq = len*vol; cosq = e_info->ss[0][1]*e_info->ss[0][2] - e_info->ss[0][0]*e_info->ss[1][2]; theta = atan2(sinq,cosq); /* derivatives of stuff, side by side */ /* doing partial derivatives in each coordinate */ for ( i = 0 ; i < SDIM ; i++ ) { /* main edge */ dlen = e_info->sides[0][0][i]/len; dvol = e_info->sides[0][1][(i+1)%3]*e_info->sides[0][2][(i+2)%3] - e_info->sides[0][1][(i+2)%3]*e_info->sides[0][2][(i+1)%3]; dsinq = dlen*vol + len*dvol; dcosq = e_info->sides[0][1][i]*e_info->ss[0][2] + e_info->ss[0][1]*e_info->sides[0][2][i] - 2*e_info->sides[0][0][i]*e_info->ss[1][2]; dtheta = (cosq*dsinq-dcosq*sinq)/(sinq*sinq+cosq*cosq); g = sign*(dtheta*len + theta*dlen)/2; e_info->grad[1][i] += g; e_info->grad[0][i] -= g; /* left edge */ dvol = e_info->sides[0][2][(i+1)%3]*e_info->sides[0][0][(i+2)%3] - e_info->sides[0][2][(i+2)%3]*e_info->sides[0][0][(i+1)%3]; dsinq = len*dvol; dcosq = e_info->sides[0][0][i]*e_info->ss[0][2] - e_info->ss[0][0]*e_info->sides[0][2][i]; dtheta = (cosq*dsinq-dcosq*sinq)/(sinq*sinq+cosq*cosq); g = sign*dtheta*len/2; e_info->grad[2][i] += g; e_info->grad[0][i] -= g; /* right edge */ dvol = e_info->sides[0][0][(i+1)%3]*e_info->sides[0][1][(i+2)%3] - e_info->sides[0][0][(i+2)%3]*e_info->sides[0][1][(i+1)%3]; dsinq = len*dvol; dcosq = e_info->sides[0][0][i]*e_info->ss[0][1] - e_info->ss[0][0]*e_info->sides[0][1][i]; dtheta = (cosq*dsinq-dcosq*sinq)/(sinq*sinq+cosq*cosq); g = sign*dtheta*len/2; e_info->grad[3][i] += g; e_info->grad[0][i] -= g; } return sign*theta*len/2; } /************************************************************** * * Function: mean_int_hessian() * * Purpose: Computes contribution of one edge to hessian. */ REAL mean_int_hessian(e_info) struct qinfo *e_info; { REAL sinq,cosq; /* proprotional to sin and cos of angle */ REAL dsinq,dcosq; /* derivatives */ REAL theta; REAL dtheta; REAL len,dlen; REAL vol,dvol; int i,j,ii,jj; REAL g; int sign = inverted(get_fe_facet(get_edge_fe(e_info->id))) ? 1 : -1; REAL ax,ay,az,bx,by,bz,cx,cy,cz; REAL aa,ab,ac,bb,bc,cc,a,aaa,det; REAL value; /* same value and gradient calculation as mean_int_gradient() */ mat_tsquare(e_info->sides[0],e_info->ss,3,SDIM); /* side products */ len = sqrt(e_info->ss[0][0]); vol = triple_prod(e_info->sides[0][0],e_info->sides[0][1],e_info->sides[0][2]); if ( vol == 0.0 ) return 0.0; sinq = len*vol; cosq = e_info->ss[0][1]*e_info->ss[0][2] - e_info->ss[0][0]*e_info->ss[1][2]; theta = atan2(sinq,cosq); /* derivatives of stuff, side by side */ /* doing partial derivatives in each coordinate */ for ( i = 0 ; i < SDIM ; i++ ) { /* main edge */ dlen = e_info->sides[0][0][i]/len; dvol = e_info->sides[0][1][(i+1)%3]*e_info->sides[0][2][(i+2)%3] - e_info->sides[0][1][(i+2)%3]*e_info->sides[0][2][(i+1)%3]; dsinq = dlen*vol + len*dvol; dcosq = e_info->sides[0][1][i]*e_info->ss[0][2] + e_info->ss[0][1]*e_info->sides[0][2][i] - 2*e_info->sides[0][0][i]*e_info->ss[1][2]; dtheta = (cosq*dsinq-dcosq*sinq)/(sinq*sinq+cosq*cosq); g = sign*(dtheta*len + theta*dlen)/2; e_info->grad[1][i] += g; e_info->grad[0][i] -= g; /* left edge */ dvol = e_info->sides[0][2][(i+1)%3]*e_info->sides[0][0][(i+2)%3] - e_info->sides[0][2][(i+2)%3]*e_info->sides[0][0][(i+1)%3]; dsinq = len*dvol; dcosq = e_info->sides[0][0][i]*e_info->ss[0][2] - e_info->ss[0][0]*e_info->sides[0][2][i]; dtheta = (cosq*dsinq-dcosq*sinq)/(sinq*sinq+cosq*cosq); g = sign*dtheta*len/2; e_info->grad[2][i] += g; e_info->grad[0][i] -= g; /* right edge */ dvol = e_info->sides[0][0][(i+1)%3]*e_info->sides[0][1][(i+2)%3] - e_info->sides[0][0][(i+2)%3]*e_info->sides[0][1][(i+1)%3]; dsinq = len*dvol; dcosq = e_info->sides[0][0][i]*e_info->ss[0][1] - e_info->ss[0][0]*e_info->sides[0][1][i]; dtheta = (cosq*dsinq-dcosq*sinq)/(sinq*sinq+cosq*cosq); g = sign*dtheta*len/2; e_info->grad[3][i] += g; e_info->grad[0][i] -= g; } /* Now Hessian, as ground out by Mathematica, in meanint.m */ ax = e_info->sides[0][0][0]; ay = e_info->sides[0][0][1]; az = e_info->sides[0][0][2]; bx = e_info->sides[0][1][0]; by = e_info->sides[0][1][1]; bz = e_info->sides[0][1][2]; cx = e_info->sides[0][2][0]; cy = e_info->sides[0][2][1]; cz = e_info->sides[0][2][2]; aa = e_info->ss[0][0]; bb = e_info->ss[1][1]; cc = e_info->ss[2][2]; ab = e_info->ss[0][1]; ac = e_info->ss[0][2]; bc = e_info->ss[1][2]; a = sqrt(aa); aaa = a*aa; det = vol; value = a*atan2(det*a,ab*ac-aa*bc); /* Copied from Mathematica */ #define Power(q,p) ((q)*(q)) #define ArcTan(x,y) atan2((y),(x)) e_info->hess[1][1][0][0] = ((2*a*ax* ((ab*ac - aa*bc)*(ax*(-(az*by*cx) + ay*bz*cx + az*bx*cy - ay*bx*cz) + aa*(-(bz*cy) + by*cz) + Power(ax,2)*(-(bz*cy) + by*cz)) + aa*(2*ax*bc - ac*bx - ab*cx)*det))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) - (2*aaa*((ab*ac - aa*bc)*(ax* (-(az*by*cx) + ay*bz*cx + az*bx*cy - ay*bx*cz) + aa*(-(bz*cy) + by*cz) + Power(ax,2)*(-(bz*cy) + by*cz)) + aa*(2*ax*bc - ac*bx - ab*cx)*det)* ((ab*ac - aa*bc)*(-2*ax*bc + ac*bx + ab*cx) - aa*(bz*cy - by*cz)*det + ax*Power(det,2)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2) + (a*(Power(aa,2)*(az*(bc - 2*bx*cx)*(-(by*cx) + bx*cy) + ay*(bc - 2*bx*cx)*(bz*cx - bx*cz) + ax*(bc + 2*bx*cx)*(bz*cy - by*cz)) - ab*ac*Power(ax,2)*det + aa*(ab*ac*(-(az*by*cx) + ay*bz*cx + az*bx*cy - 3*ax*bz*cy - ay*bx*cz + 3*ax*by*cz) + Power(ax,2)*bc*det)))/ (Power(ab,2)*Power(ac,2) - 2*aa*ab*ac*bc + aa*(aa*Power(bc,2) + Power(det,2))) + aa*ArcTan(ab*ac - aa*bc,a*det) - Power(ax,2)*ArcTan(ab*ac - aa*bc,a*det)\ )/aaa; e_info->hess[1][1][0][1] = ((a*ay* ((ab*ac - aa*bc)*(ax*(-(az*by*cx) + ay*bz*cx + az*bx*cy - ay*bx*cz) + aa*(-(bz*cy) + by*cz) + Power(ax,2)*(-(bz*cy) + by*cz)) + aa*(2*ax*bc - ac*bx - ab*cx)*det))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) + (a*ax*((ab*ac - aa*bc)*(aa*(bz*cx - bx*cz) + Power(ay,2)*(bz*cx - bx*cz) + ay*(-(az*by*cx) + az*bx*cy - ax*bz*cy + ax*by*cz)) + aa*(2*ay*bc - ac*by - ab*cy)*det))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) + (a*(-(Power(aa,2)*(-2*ax*bc + ac*bx + ab*cx)*(bz*cx - bx*cz)) + aa*(-2*ay*bc + ac*by + ab*cy)* (ax*(-(az*by*cx) + ay*bz*cx + az*bx*cy - ay*bx*cz) + aa*(-(bz*cy) + by*cz) + Power(ax,2)*(-(bz*cy) + by*cz)) + (ab*ac - aa*bc)*(Power(ax,2)*ay*(bz*cy - by*cz) + aa*ay*(-(bz*cy) + by*cz) + ax*(ay*az*(by*cx - bx*cy) + aa*(bz*cx - bx*cz) + Power(ay,2)*(-(bz*cx) + bx*cz))) + aa*ay*(2*ax*bc - ac*bx - ab*cx)*det - Power(aa,2)*(by*cx + bx*cy)*det))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) - (2*aaa*((ab*ac - aa*bc)*(ax* (-(az*by*cx) + ay*bz*cx + az*bx*cy - ay*bx*cz) + aa*(-(bz*cy) + by*cz) + Power(ax,2)*(-(bz*cy) + by*cz)) + aa*(2*ax*bc - ac*bx - ab*cx)*det)* ((ab*ac - aa*bc)*(-2*ay*bc + ac*by + ab*cy) + aa*(bz*cx - bx*cz)*det + ay*Power(det,2)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2) - ax*ay*ArcTan(ab*ac - aa*bc,a*det))/aaa; e_info->hess[1][1][0][2] = ((a*az* ((ab*ac - aa*bc)*(ax*(-(az*by*cx) + ay*bz*cx + az*bx*cy - ay*bx*cz) + aa*(-(bz*cy) + by*cz) + Power(ax,2)*(-(bz*cy) + by*cz)) + aa*(2*ax*bc - ac*bx - ab*cx)*det))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) - (2*aaa*((ab*ac - aa*bc)*(ax* (-(az*by*cx) + ay*bz*cx + az*bx*cy - ay*bx*cz) + aa*(-(bz*cy) + by*cz) + Power(ax,2)*(-(bz*cy) + by*cz)) + aa*(2*ax*bc - ac*bx - ab*cx)*det)* ((ab*ac - aa*bc)*(-2*az*bc + ac*bz + ab*cz) - aa*(by*cx - bx*cy)*det + az*Power(det,2)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2) + (a*ax*(aa*(2*az*bc - ac*bz - ab*cz)*det + (-(ab*ac) + aa*bc)*(aa*(by*cx - bx*cy) - az*det)))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) + (a*(-(Power(aa,2)*(-2*ax*bc + ac*bx + ab*cx)*(-(by*cx) + bx*cy)) + aa*(2*az*bc - ac*bz - ab*cz)* (ax*(az*by*cx - ay*bz*cx - az*bx*cy + ay*bx*cz) + aa*(bz*cy - by*cz) + Power(ax,2)*(bz*cy - by*cz)) + aa*az*(2*ax*bc - ac*bx - ab*cx)*det - Power(aa,2)*(bz*cx + bx*cz)*det + (-(ab*ac) + aa*bc)*(aa* (ax*by*cx - ax*bx*cy + az*bz*cy - az*by*cz) + ax*az*det)))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) - ax*az*ArcTan(ab*ac - aa*bc,a*det))/aaa; e_info->hess[1][1][1][0] = ((a*ay* ((ab*ac - aa*bc)*(ax*(-(az*by*cx) + ay*bz*cx + az*bx*cy - ay*bx*cz) + aa*(-(bz*cy) + by*cz) + Power(ax,2)*(-(bz*cy) + by*cz)) + aa*(2*ax*bc - ac*bx - ab*cx)*det))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) + (a*ax*((ab*ac - aa*bc)*(aa*(bz*cx - bx*cz) + Power(ay,2)*(bz*cx - bx*cz) + ay*(-(az*by*cx) + az*bx*cy - ax*bz*cy + ax*by*cz)) + aa*(2*ay*bc - ac*by - ab*cy)*det))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) + (a*(-(Power(aa,2)*(-2*ay*bc + ac*by + ab*cy)*(-(bz*cy) + by*cz)) + aa*(2*ax*bc - ac*bx - ab*cx)* (aa*(-(bz*cx) + bx*cz) + Power(ay,2)*(-(bz*cx) + bx*cz) + ay*(az*by*cx - az*bx*cy + ax*bz*cy - ax*by*cz)) + (ab*ac - aa*bc)*(Power(ax,2)*ay*(bz*cy - by*cz) + aa*ay*(-(bz*cy) + by*cz) + ax*(ay*az*(by*cx - bx*cy) + aa*(bz*cx - bx*cz) + Power(ay,2)*(-(bz*cx) + bx*cz))) + aa*ax*(2*ay*bc - ac*by - ab*cy)*det - Power(aa,2)*(by*cx + bx*cy)*det))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) - (2*aaa*((ab*ac - aa*bc)*(aa*(bz*cx - bx*cz) + Power(ay,2)*(bz*cx - bx*cz) + ay*(-(az*by*cx) + az*bx*cy - ax*bz*cy + ax*by*cz)) + aa*(2*ay*bc - ac*by - ab*cy)*det)* ((ab*ac - aa*bc)*(-2*ax*bc + ac*bx + ab*cx) - aa*(bz*cy - by*cz)*det + ax*Power(det,2)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2) - ax*ay*ArcTan(ab*ac - aa*bc,a*det))/aaa; e_info->hess[1][1][1][1] = ((2*a*ay* ((ab*ac - aa*bc)*(aa*(bz*cx - bx*cz) + Power(ay,2)*(bz*cx - bx*cz) + ay*(-(az*by*cx) + az*bx*cy - ax*bz*cy + ax*by*cz)) + aa*(2*ay*bc - ac*by - ab*cy)*det))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) - (2*aaa*((ab*ac - aa*bc)*(aa*(bz*cx - bx*cz) + Power(ay,2)*(bz*cx - bx*cz) + ay*(-(az*by*cx) + az*bx*cy - ax*bz*cy + ax*by*cz)) + aa*(2*ay*bc - ac*by - ab*cy)*det)* ((ab*ac - aa*bc)*(-2*ay*bc + ac*by + ab*cy) + aa*(bz*cx - bx*cz)*det + ay*Power(det,2)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2) + (a*(Power(aa,2)*(az*(by*cx - bx*cy)*(-bc + 2*by*cy) - ay*(bc + 2*by*cy)*(bz*cx - bx*cz) + ax*(bc - 2*by*cy)*(-(bz*cy) + by*cz)) - ab*ac*Power(ay,2)*det + aa*(ab*ac*(-(az*by*cx) + 3*ay*bz*cx + az*bx*cy - ax*bz*cy - 3*ay*bx*cz + ax*by*cz) + Power(ay,2)*bc*det)))/ (Power(ab,2)*Power(ac,2) - 2*aa*ab*ac*bc + aa*(aa*Power(bc,2) + Power(det,2))) + aa*ArcTan(ab*ac - aa*bc,a*det) - Power(ay,2)*ArcTan(ab*ac - aa*bc,a*det)\ )/aaa; e_info->hess[1][1][1][2] = ((a*az* ((ab*ac - aa*bc)*(aa*(bz*cx - bx*cz) + Power(ay,2)*(bz*cx - bx*cz) + ay*(-(az*by*cx) + az*bx*cy - ax*bz*cy + ax*by*cz)) + aa*(2*ay*bc - ac*by - ab*cy)*det))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) - (2*aaa*((ab*ac - aa*bc)*(aa*(bz*cx - bx*cz) + Power(ay,2)*(bz*cx - bx*cz) + ay*(-(az*by*cx) + az*bx*cy - ax*bz*cy + ax*by*cz)) + aa*(2*ay*bc - ac*by - ab*cy)*det)* ((ab*ac - aa*bc)*(-2*az*bc + ac*bz + ab*cz) - aa*(by*cx - bx*cy)*det + az*Power(det,2)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2) + (a*ay*(aa*(2*az*bc - ac*bz - ab*cz)*det + (-(ab*ac) + aa*bc)*(aa*(by*cx - bx*cy) - az*det)))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) + (a*(-(Power(aa,2)*(-2*ay*bc + ac*by + ab*cy)*(-(by*cx) + bx*cy)) + aa*(2*az*bc - ac*bz - ab*cz)* (aa*(-(bz*cx) + bx*cz) + Power(ay,2)*(-(bz*cx) + bx*cz) + ay*(az*by*cx - az*bx*cy + ax*bz*cy - ax*by*cz)) + aa*az*(2*ay*bc - ac*by - ab*cy)*det - Power(aa,2)*(bz*cy + by*cz)*det + (-(ab*ac) + aa*bc)*(aa* (ay*by*cx - az*bz*cx - ay*bx*cy + az*bx*cz) + ay*az*det)))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) - ay*az*ArcTan(ab*ac - aa*bc,a*det))/aaa; e_info->hess[1][1][2][0] = ((a*az* ((ab*ac - aa*bc)*(ax*(-(az*by*cx) + ay*bz*cx + az*bx*cy - ay*bx*cz) + aa*(-(bz*cy) + by*cz) + Power(ax,2)*(-(bz*cy) + by*cz)) + aa*(2*ax*bc - ac*bx - ab*cx)*det))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) + (a*ax*(aa*(2*az*bc - ac*bz - ab*cz)*det + (-(ab*ac) + aa*bc)*(aa*(by*cx - bx*cy) - az*det)))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) - (2*aaa*((ab*ac - aa*bc)*(-2*ax*bc + ac*bx + ab*cx) - aa*(bz*cy - by*cz)*det + ax*Power(det,2))* (aa*(2*az*bc - ac*bz - ab*cz)*det + (-(ab*ac) + aa*bc)*(aa*(by*cx - bx*cy) - az*det)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2) + (a*(-(Power(aa,2)*(-2*az*bc + ac*bz + ab*cz)*(-(bz*cy) + by*cz)) + aa*ax*(2*az*bc - ac*bz - ab*cz)*det - Power(aa,2)*(bz*cx + bx*cz)*det - aa*(2*ax*bc - ac*bx - ab*cx)*(aa*(-(by*cx) + bx*cy) + az*det) + (-(ab*ac) + aa*bc)*(aa* (ax*by*cx - ax*bx*cy + az*bz*cy - az*by*cz) + ax*az*det)))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) - ax*az*ArcTan(ab*ac - aa*bc,a*det))/aaa; e_info->hess[1][1][2][1] = ((a*az* ((ab*ac - aa*bc)*(aa*(bz*cx - bx*cz) + Power(ay,2)*(bz*cx - bx*cz) + ay*(-(az*by*cx) + az*bx*cy - ax*bz*cy + ax*by*cz)) + aa*(2*ay*bc - ac*by - ab*cy)*det))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) + (a*ay*(aa*(2*az*bc - ac*bz - ab*cz)*det + (-(ab*ac) + aa*bc)*(aa*(by*cx - bx*cy) - az*det)))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) - (2*aaa*((ab*ac - aa*bc)*(-2*ay*bc + ac*by + ab*cy) + aa*(bz*cx - bx*cz)*det + ay*Power(det,2))* (aa*(2*az*bc - ac*bz - ab*cz)*det + (-(ab*ac) + aa*bc)*(aa*(by*cx - bx*cy) - az*det)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2) + (a*(-(Power(aa,2)*(-2*az*bc + ac*bz + ab*cz)*(bz*cx - bx*cz)) + aa*ay*(2*az*bc - ac*bz - ab*cz)*det - Power(aa,2)*(bz*cy + by*cz)*det + aa*(2*ay*bc - ac*by - ab*cy)*(aa*(by*cx - bx*cy) - az*det) + (-(ab*ac) + aa*bc)*(aa* (ay*by*cx - az*bz*cx - ay*bx*cy + az*bx*cz) + ay*az*det)))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) - ay*az*ArcTan(ab*ac - aa*bc,a*det))/aaa; e_info->hess[1][1][2][2] = ((2*a*az* (aa*(2*az*bc - ac*bz - ab*cz)*det + (-(ab*ac) + aa*bc)*(aa*(by*cx - bx*cy) - az*det)))/ (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) - (2*aaa*((ab*ac - aa*bc)*(-2*az*bc + ac*bz + ab*cz) - aa*(by*cx - bx*cy)*det + az*Power(det,2))* (aa*(2*az*bc - ac*bz - ab*cz)*det + (-(ab*ac) + aa*bc)*(aa*(by*cx - bx*cy) - az*det)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2) + (a*(Power(aa,2)*((ay*bz*cx - ax*bz*cy - ay*bx*cz + ax*by*cz)* (bc - 2*bz*cz) + az*(by*cx - bx*cy)*(bc + 2*bz*cz)) - ab*ac*Power(az,2)*det + aa*(ab*ac*(-3*az*by*cx + ay*bz*cx + 3*az*bx*cy - ax*bz*cy - ay*bx*cz + ax*by*cz) + Power(az,2)*bc*det)))/ (Power(ab,2)*Power(ac,2) - 2*aa*ab*ac*bc + aa*(aa*Power(bc,2) + Power(det,2))) + aa*ArcTan(ab*ac - aa*bc,a*det) - Power(az,2)*ArcTan(ab*ac - aa*bc,a*det)\ )/aaa; e_info->hess[1][2][0][0] = (-(((ab*ac - aa*bc)* (ax*(-(az*by*cx) + ay*bz*cx + az*bx*cy - ay*bx*cz) + aa*(-(bz*cy) + by*cz) + Power(ax,2)*(-(bz*cy) + by*cz)) + aa*(2*ax*bc - ac*bx - ab*cx)*det)* (2*(ab*ac - aa*bc)*(ac*ax - aa*cx) + 2*aa*(az*cy - ay*cz)*det)) + ax*((ab*ac - aa*bc)*(az*cy - ay*cz) - (ac*ax - aa*cx)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) + (ax*(ab*ac - aa*bc)*(az*cy - ay*cz) - aa*(-2*ax*bc + ac*bx + ab*cx)*(az*cy - ay*cz) + (ac*ax - aa*cx)*(ax*(-(az*by*cx) + ay*bz*cx + az*bx*cy - ay*bx*cz) + aa*(-(bz*cy) + by*cz) + Power(ax,2)*(-(bz*cy) + by*cz)) - aa*(ay*cy + az*cz)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[1][2][0][1] = (-(((ab*ac - aa*bc)* (ax*(-(az*by*cx) + ay*bz*cx + az*bx*cy - ay*bx*cz) + aa*(-(bz*cy) + by*cz) + Power(ax,2)*(-(bz*cy) + by*cz)) + aa*(2*ax*bc - ac*bx - ab*cx)*det)* (2*(ab*ac - aa*bc)*(ac*ay - aa*cy) - 2*aa*(az*cx - ax*cz)*det)) + ax*((ab*ac - aa*bc)*(-(az*cx) + ax*cz) - (ac*ay - aa*cy)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) + (-(aa*(-2*ax*bc + ac*bx + ab*cx)*(-(az*cx) + ax*cz)) + (ab*ac - aa*bc)*(-(ax*az*cx) + aa*cz + Power(ax,2)*cz) + (ac*ay - aa*cy)*(ax*(-(az*by*cx) + ay*bz*cx + az*bx*cy - ay*bx*cz) + aa*(-(bz*cy) + by*cz) + Power(ax,2)*(-(bz*cy) + by*cz)) - aa*(ay*cx - 2*ax*cy)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[1][2][0][2] = (-(((ab*ac - aa*bc)* (ax*(-(az*by*cx) + ay*bz*cx + az*bx*cy - ay*bx*cz) + aa*(-(bz*cy) + by*cz) + Power(ax,2)*(-(bz*cy) + by*cz)) + aa*(2*ax*bc - ac*bx - ab*cx)*det)* (2*(ab*ac - aa*bc)*(ac*az - aa*cz) + 2*aa*(ay*cx - ax*cy)*det)) + ax*((ab*ac - aa*bc)*(ay*cx - ax*cy) - (ac*az - aa*cz)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) + (-(aa*(-2*ax*bc + ac*bx + ab*cx)*(ay*cx - ax*cy)) + (-(ab*ac) + aa*bc)*(-(ax*ay*cx) + aa*cy + Power(ax,2)*cy) + (ac*az - aa*cz)*(ax*(-(az*by*cx) + ay*bz*cx + az*bx*cy - ay*bx*cz) + aa*(-(bz*cy) + by*cz) + Power(ax,2)*(-(bz*cy) + by*cz)) - aa*(az*cx - 2*ax*cz)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[1][2][1][0] = (-(((ab*ac - aa*bc)* (aa*(bz*cx - bx*cz) + Power(ay,2)*(bz*cx - bx*cz) + ay*(-(az*by*cx) + az*bx*cy - ax*bz*cy + ax*by*cz)) + aa*(2*ay*bc - ac*by - ab*cy)*det)* (2*(ab*ac - aa*bc)*(ac*ax - aa*cx) + 2*aa*(az*cy - ay*cz)*det)) + ay*((ab*ac - aa*bc)*(az*cy - ay*cz) - (ac*ax - aa*cx)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) + (-(aa*(-2*ay*bc + ac*by + ab*cy)*(az*cy - ay*cz)) + (-(ab*ac) + aa*bc)*(-(ay*az*cy) + aa*cz + Power(ay,2)*cz) + (ac*ax - aa*cx)*(aa*(bz*cx - bx*cz) + Power(ay,2)*(bz*cx - bx*cz) + ay*(-(az*by*cx) + az*bx*cy - ax*bz*cy + ax*by*cz)) + aa*(2*ay*cx - ax*cy)*det)*(Power(ab*ac - aa*bc,2) + aa*Power(det,2)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[1][2][1][1] = (-(((ab*ac - aa*bc)* (aa*(bz*cx - bx*cz) + Power(ay,2)*(bz*cx - bx*cz) + ay*(-(az*by*cx) + az*bx*cy - ax*bz*cy + ax*by*cz)) + aa*(2*ay*bc - ac*by - ab*cy)*det)* (2*(ab*ac - aa*bc)*(ac*ay - aa*cy) - 2*aa*(az*cx - ax*cz)*det)) + ay*((ab*ac - aa*bc)*(-(az*cx) + ax*cz) - (ac*ay - aa*cy)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) + (ay*(-(ab*ac) + aa*bc)*(az*cx - ax*cz) - aa*(-2*ay*bc + ac*by + ab*cy)*(-(az*cx) + ax*cz) + (ac*ay - aa*cy)*(aa*(bz*cx - bx*cz) + Power(ay,2)*(bz*cx - bx*cz) + ay*(-(az*by*cx) + az*bx*cy - ax*bz*cy + ax*by*cz)) - aa*(ax*cx + az*cz)*det)*(Power(ab*ac - aa*bc,2) + aa*Power(det,2)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[1][2][1][2] = (-(((ab*ac - aa*bc)* (aa*(bz*cx - bx*cz) + Power(ay,2)*(bz*cx - bx*cz) + ay*(-(az*by*cx) + az*bx*cy - ax*bz*cy + ax*by*cz)) + aa*(2*ay*bc - ac*by - ab*cy)*det)* (2*(ab*ac - aa*bc)*(ac*az - aa*cz) + 2*aa*(ay*cx - ax*cy)*det)) + ay*((ab*ac - aa*bc)*(ay*cx - ax*cy) - (ac*az - aa*cz)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) + (-(aa*(-2*ay*bc + ac*by + ab*cy)*(ay*cx - ax*cy)) + (ab*ac - aa*bc)*(aa*cx + ay*(ay*cx - ax*cy)) + (-(ac*az) + aa*cz)*(aa*(-(bz*cx) + bx*cz) + Power(ay,2)*(-(bz*cx) + bx*cz) + ay*(az*by*cx - az*bx*cy + ax*bz*cy - ax*by*cz)) - aa*(az*cy - 2*ay*cz)*det)*(Power(ab*ac - aa*bc,2) + aa*Power(det,2)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[1][2][2][0] = (az* ((ab*ac - aa*bc)*(az*cy - ay*cz) - (ac*ax - aa*cx)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) - (2*(ab*ac - aa*bc)*(ac*ax - aa*cx) + 2*aa*(az*cy - ay*cz)*det)* (aa*(2*az*bc - ac*bz - ab*cz)*det + (-(ab*ac) + aa*bc)*(aa*(by*cx - bx*cy) - az*det)) + (Power(ab*ac - aa*bc,2) + aa*Power(det,2))* (-(aa*(-2*az*bc + ac*bz + ab*cz)*(az*cy - ay*cz)) + (ab*ac - aa*bc)*(aa*cy + az*(az*cy - ay*cz)) + aa*(2*az*cx - ax*cz)*det + (ac*ax - aa*cx)*(aa*(-(by*cx) + bx*cy) + az*det)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[1][2][2][1] = (az* ((ab*ac - aa*bc)*(-(az*cx) + ax*cz) - (ac*ay - aa*cy)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) - (2*(ab*ac - aa*bc)*(ac*ay - aa*cy) - 2*aa*(az*cx - ax*cz)*det)* (aa*(2*az*bc - ac*bz - ab*cz)*det + (-(ab*ac) + aa*bc)*(aa*(by*cx - bx*cy) - az*det)) + (Power(ab*ac - aa*bc,2) + aa*Power(det,2))* (-(aa*(-2*az*bc + ac*bz + ab*cz)*(-(az*cx) + ax*cz)) + (-(ab*ac) + aa*bc)*(aa*cx + az*(az*cx - ax*cz)) + aa*(2*az*cy - ay*cz)*det + (ac*ay - aa*cy)*(aa*(-(by*cx) + bx*cy) + az*det)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[1][2][2][2] = (az* ((ab*ac - aa*bc)*(ay*cx - ax*cy) - (ac*az - aa*cz)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) - (2*(ab*ac - aa*bc)*(ac*az - aa*cz) + 2*aa*(ay*cx - ax*cy)*det)* (aa*(2*az*bc - ac*bz - ab*cz)*det + (-(ab*ac) + aa*bc)*(aa*(by*cx - bx*cy) - az*det)) + (Power(ab*ac - aa*bc,2) + aa*Power(det,2))* (az*(ab*ac - aa*bc)*(ay*cx - ax*cy) - aa*(ay*cx - ax*cy)*(-2*az*bc + ac*bz + ab*cz) - aa*(ax*cx + ay*cy)*det + (-(ac*az) + aa*cz)*(aa*(by*cx - bx*cy) - az*det)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[1][3][0][0] = (-((2*(ab*ac - aa*bc)*(ab*ax - aa*bx) - 2*aa*(az*by - ay*bz)*det)* ((ab*ac - aa*bc)*(ax*(-(az*by*cx) + ay*bz*cx + az*bx*cy - ay*bx*cz) + aa*(-(bz*cy) + by*cz) + Power(ax,2)*(-(bz*cy) + by*cz)) + aa*(2*ax*bc - ac*bx - ab*cx)*det)) + ax*((ab*ac - aa*bc)*(-(az*by) + ay*bz) - (ab*ax - aa*bx)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) + (ax*(-(ab*ac) + aa*bc)*(az*by - ay*bz) - aa*(-(az*by) + ay*bz)*(-2*ax*bc + ac*bx + ab*cx) + (ab*ax - aa*bx)*(ax*(-(az*by*cx) + ay*bz*cx + az*bx*cy - ay*bx*cz) + aa*(-(bz*cy) + by*cz) + Power(ax,2)*(-(bz*cy) + by*cz)) - aa*(ay*by + az*bz)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[1][3][0][1] = (-((2*(ab*ac - aa*bc)*(ab*ay - aa*by) + 2*aa*(az*bx - ax*bz)*det)* ((ab*ac - aa*bc)*(ax*(-(az*by*cx) + ay*bz*cx + az*bx*cy - ay*bx*cz) + aa*(-(bz*cy) + by*cz) + Power(ax,2)*(-(bz*cy) + by*cz)) + aa*(2*ax*bc - ac*bx - ab*cx)*det)) + ax*((ab*ac - aa*bc)*(az*bx - ax*bz) - (ab*ay - aa*by)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) + ((-(ab*ac) + aa*bc)*(-(ax*az*bx) + aa*bz + Power(ax,2)*bz) - aa*(az*bx - ax*bz)*(-2*ax*bc + ac*bx + ab*cx) + (ab*ay - aa*by)*(ax*(-(az*by*cx) + ay*bz*cx + az*bx*cy - ay*bx*cz) + aa*(-(bz*cy) + by*cz) + Power(ax,2)*(-(bz*cy) + by*cz)) - aa*(ay*bx - 2*ax*by)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[1][3][0][2] = (-((2*(ab*ac - aa*bc)*(ab*az - aa*bz) - 2*aa*(ay*bx - ax*by)*det)* ((ab*ac - aa*bc)*(ax*(-(az*by*cx) + ay*bz*cx + az*bx*cy - ay*bx*cz) + aa*(-(bz*cy) + by*cz) + Power(ax,2)*(-(bz*cy) + by*cz)) + aa*(2*ax*bc - ac*bx - ab*cx)*det)) + ax*((ab*ac - aa*bc)*(-(ay*bx) + ax*by) - (ab*az - aa*bz)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) + ((ab*ac - aa*bc)*(-(ax*ay*bx) + aa*by + Power(ax,2)*by) + aa*(ay*bx - ax*by)*(-2*ax*bc + ac*bx + ab*cx) + (-(ab*az) + aa*bz)*(ax* (az*by*cx - ay*bz*cx - az*bx*cy + ay*bx*cz) + aa*(bz*cy - by*cz) + Power(ax,2)*(bz*cy - by*cz)) - aa*(az*bx - 2*ax*bz)*det)*(Power(ab*ac - aa*bc,2) + aa*Power(det,2)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[1][3][1][0] = (-((2*(ab*ac - aa*bc)*(ab*ax - aa*bx) - 2*aa*(az*by - ay*bz)*det)* ((ab*ac - aa*bc)*(aa*(bz*cx - bx*cz) + Power(ay,2)*(bz*cx - bx*cz) + ay*(-(az*by*cx) + az*bx*cy - ax*bz*cy + ax*by*cz)) + aa*(2*ay*bc - ac*by - ab*cy)*det)) + ay*((ab*ac - aa*bc)*(-(az*by) + ay*bz) - (ab*ax - aa*bx)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) + ((ab*ac - aa*bc)*(-(ay*az*by) + aa*bz + Power(ay,2)*bz) + aa*(az*by - ay*bz)*(-2*ay*bc + ac*by + ab*cy) + (ab*ax - aa*bx)*(aa*(bz*cx - bx*cz) + Power(ay,2)*(bz*cx - bx*cz) + ay*(-(az*by*cx) + az*bx*cy - ax*bz*cy + ax*by*cz)) + aa*(2*ay*bx - ax*by)*det)*(Power(ab*ac - aa*bc,2) + aa*Power(det,2)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[1][3][1][1] = (-((2*(ab*ac - aa*bc)*(ab*ay - aa*by) + 2*aa*(az*bx - ax*bz)*det)* ((ab*ac - aa*bc)*(aa*(bz*cx - bx*cz) + Power(ay,2)*(bz*cx - bx*cz) + ay*(-(az*by*cx) + az*bx*cy - ax*bz*cy + ax*by*cz)) + aa*(2*ay*bc - ac*by - ab*cy)*det)) + ay*((ab*ac - aa*bc)*(az*bx - ax*bz) - (ab*ay - aa*by)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) + (ay*(ab*ac - aa*bc)*(az*bx - ax*bz) - aa*(az*bx - ax*bz)*(-2*ay*bc + ac*by + ab*cy) + (ab*ay - aa*by)*(aa*(bz*cx - bx*cz) + Power(ay,2)*(bz*cx - bx*cz) + ay*(-(az*by*cx) + az*bx*cy - ax*bz*cy + ax*by*cz)) - aa*(ax*bx + az*bz)*det)*(Power(ab*ac - aa*bc,2) + aa*Power(det,2)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[1][3][1][2] = (-((2*(ab*ac - aa*bc)*(ab*az - aa*bz) - 2*aa*(ay*bx - ax*by)*det)* ((ab*ac - aa*bc)*(aa*(bz*cx - bx*cz) + Power(ay,2)*(bz*cx - bx*cz) + ay*(-(az*by*cx) + az*bx*cy - ax*bz*cy + ax*by*cz)) + aa*(2*ay*bc - ac*by - ab*cy)*det)) + ay*((ab*ac - aa*bc)*(-(ay*bx) + ax*by) - (ab*az - aa*bz)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) + ((-(ab*ac) + aa*bc)*(aa*bx + ay*(ay*bx - ax*by)) - aa*(-(ay*bx) + ax*by)*(-2*ay*bc + ac*by + ab*cy) - (ab*az - aa*bz)*(aa*(-(bz*cx) + bx*cz) + Power(ay,2)*(-(bz*cx) + bx*cz) + ay*(az*by*cx - az*bx*cy + ax*bz*cy - ax*by*cz)) - aa*(az*by - 2*ay*bz)*det)*(Power(ab*ac - aa*bc,2) + aa*Power(det,2)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[1][3][2][0] = (az* ((ab*ac - aa*bc)*(-(az*by) + ay*bz) - (ab*ax - aa*bx)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) - (2*(ab*ac - aa*bc)*(ab*ax - aa*bx) - 2*aa*(az*by - ay*bz)*det)* (aa*(2*az*bc - ac*bz - ab*cz)*det + (-(ab*ac) + aa*bc)*(aa*(by*cx - bx*cy) - az*det)) + (Power(ab*ac - aa*bc,2) + aa*Power(det,2))* ((-(ab*ac) + aa*bc)*(aa*by + az*(az*by - ay*bz)) - aa*(-(az*by) + ay*bz)*(-2*az*bc + ac*bz + ab*cz) + aa*(2*az*bx - ax*bz)*det + (ab*ax - aa*bx)*(aa*(-(by*cx) + bx*cy) + az*det)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[1][3][2][1] = (az* ((ab*ac - aa*bc)*(az*bx - ax*bz) - (ab*ay - aa*by)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) - (2*(ab*ac - aa*bc)*(ab*ay - aa*by) + 2*aa*(az*bx - ax*bz)*det)* (aa*(2*az*bc - ac*bz - ab*cz)*det + (-(ab*ac) + aa*bc)*(aa*(by*cx - bx*cy) - az*det)) + (Power(ab*ac - aa*bc,2) + aa*Power(det,2))* ((ab*ac - aa*bc)*(aa*bx + az*(az*bx - ax*bz)) - aa*(az*bx - ax*bz)*(-2*az*bc + ac*bz + ab*cz) + aa*(2*az*by - ay*bz)*det + (ab*ay - aa*by)*(aa*(-(by*cx) + bx*cy) + az*det)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[1][3][2][2] = (az* ((ab*ac - aa*bc)*(-(ay*bx) + ax*by) - (ab*az - aa*bz)*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2)) - (2*(ab*ac - aa*bc)*(ab*az - aa*bz) - 2*aa*(ay*bx - ax*by)*det)* (aa*(2*az*bc - ac*bz - ab*cz)*det + (-(ab*ac) + aa*bc)*(aa*(by*cx - bx*cy) - az*det)) + (Power(ab*ac - aa*bc,2) + aa*Power(det,2))* (az*(-(ab*ac) + aa*bc)*(ay*bx - ax*by) - aa*(-(ay*bx) + ax*by)*(-2*az*bc + ac*bz + ab*cz) - aa*(ax*bx + ay*by)*det + (-(ab*az) + aa*bz)*(aa*(by*cx - bx*cy) - az*det)))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[2][2][0][0] = -((aa* ((ab*ac - aa*bc)*(az*cy - ay*cz) - (ac*ax - aa*cx)*det)* (2*(ab*ac - aa*bc)*(ac*ax - aa*cx) + 2*aa*(az*cy - ay*cz)*det))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2)); e_info->hess[2][2][0][1] = (aa* (-(((ab*ac - aa*bc)*(az*cy - ay*cz) - (ac*ax - aa*cx)*det)* (2*(ab*ac - aa*bc)*(ac*ay - aa*cy) - 2*aa*(az*cx - ax*cz)*det)) + (-((ac*ax - aa*cx)*(-(az*cx) + ax*cz)) + (ac*ay - aa*cy)*(az*cy - ay*cz))* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[2][2][0][2] = (aa* (-(((ab*ac - aa*bc)*(az*cy - ay*cz) - (ac*ax - aa*cx)*det)* (2*(ab*ac - aa*bc)*(ac*az - aa*cz) + 2*aa*(ay*cx - ax*cy)*det)) + ((ac*ax - aa*cx)*(-(ay*cx) + ax*cy) + (ac*az - aa*cz)*(az*cy - ay*cz))* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[2][2][1][0] = (aa* (-(((ab*ac - aa*bc)*(-(az*cx) + ax*cz) - (ac*ay - aa*cy)*det)* (2*(ab*ac - aa*bc)*(ac*ax - aa*cx) + 2*aa*(az*cy - ay*cz)*det)) + ((ac*ax - aa*cx)*(-(az*cx) + ax*cz) + (ac*ay - aa*cy)*(-(az*cy) + ay*cz))* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[2][2][1][1] = -((aa* ((ab*ac - aa*bc)*(-(az*cx) + ax*cz) - (ac*ay - aa*cy)*det)* (2*(ab*ac - aa*bc)*(ac*ay - aa*cy) - 2*aa*(az*cx - ax*cz)*det))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2)); e_info->hess[2][2][1][2] = (aa* (-(((ab*ac - aa*bc)*(-(az*cx) + ax*cz) - (ac*ay - aa*cy)*det)* (2*(ab*ac - aa*bc)*(ac*az - aa*cz) + 2*aa*(ay*cx - ax*cy)*det)) + (-((ac*ay - aa*cy)*(ay*cx - ax*cy)) + (ac*az - aa*cz)*(-(az*cx) + ax*cz))* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[2][2][2][0] = (aa* (-(((ab*ac - aa*bc)*(ay*cx - ax*cy) - (ac*az - aa*cz)*det)* (2*(ab*ac - aa*bc)*(ac*ax - aa*cx) + 2*aa*(az*cy - ay*cz)*det)) + ((ac*ax - aa*cx)*(ay*cx - ax*cy) - (ac*az - aa*cz)*(az*cy - ay*cz))* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[2][2][2][1] = (aa* (-(((ab*ac - aa*bc)*(ay*cx - ax*cy) - (ac*az - aa*cz)*det)* (2*(ab*ac - aa*bc)*(ac*ay - aa*cy) - 2*aa*(az*cx - ax*cz)*det)) + ((ac*ay - aa*cy)*(ay*cx - ax*cy) + (ac*az - aa*cz)*(az*cx - ax*cz))* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[2][2][2][2] = -((aa* (2*(ab*ac - aa*bc)*(ac*az - aa*cz) + 2*aa*(ay*cx - ax*cy)*det)* ((ab*ac - aa*bc)*(ay*cx - ax*cy) - (ac*az - aa*cz)*det))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2)); e_info->hess[2][3][0][0] = (aa* (-((2*(ab*ac - aa*bc)*(ab*ax - aa*bx) - 2*aa*(az*by - ay*bz)*det)* ((ab*ac - aa*bc)*(az*cy - ay*cz) - (ac*ax - aa*cx)*det)) + ((az*by - ay*bz)*(ac*ax - aa*cx) + (ab*ax - aa*bx)*(az*cy - ay*cz) + (Power(ay,2) + Power(az,2))*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[2][3][0][1] = (aa* (-((2*(ab*ac - aa*bc)*(ab*ay - aa*by) + 2*aa*(az*bx - ax*bz)*det)* ((ab*ac - aa*bc)*(az*cy - ay*cz) - (ac*ax - aa*cx)*det)) + (az*(ab*ac - aa*bc) - (az*bx - ax*bz)*(ac*ax - aa*cx) + (ab*ay - aa*by)*(az*cy - ay*cz) - ax*ay*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[2][3][0][2] = (aa* (-((2*(ab*ac - aa*bc)*(ab*az - aa*bz) - 2*aa*(ay*bx - ax*by)*det)* ((ab*ac - aa*bc)*(az*cy - ay*cz) - (ac*ax - aa*cx)*det)) + (-(ab*ac*ay) + aa*ay*bc + (ay*bx - ax*by)*(ac*ax - aa*cx) + (ab*az - aa*bz)*(az*cy - ay*cz) - ax*az*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[2][3][1][0] = (aa* (-((2*(ab*ac - aa*bc)*(ab*ax - aa*bx) - 2*aa*(az*by - ay*bz)*det)* ((ab*ac - aa*bc)*(-(az*cx) + ax*cz) - (ac*ay - aa*cy)*det)) + (-(ab*ac*az) + aa*az*bc + (az*by - ay*bz)*(ac*ay - aa*cy) + (ab*ax - aa*bx)*(-(az*cx) + ax*cz) - ax*ay*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[2][3][1][1] = (aa* (-((2*(ab*ac - aa*bc)*(ab*ay - aa*by) + 2*aa*(az*bx - ax*bz)*det)* ((ab*ac - aa*bc)*(-(az*cx) + ax*cz) - (ac*ay - aa*cy)*det)) + (-((az*bx - ax*bz)*(ac*ay - aa*cy)) + (ab*ay - aa*by)*(-(az*cx) + ax*cz) + (Power(ax,2) + Power(az,2))*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[2][3][1][2] = (aa* (-((2*(ab*ac - aa*bc)*(ab*az - aa*bz) - 2*aa*(ay*bx - ax*by)*det)* ((ab*ac - aa*bc)*(-(az*cx) + ax*cz) - (ac*ay - aa*cy)*det)) + (ax*(ab*ac - aa*bc) + (ay*bx - ax*by)*(ac*ay - aa*cy) + (ab*az - aa*bz)*(-(az*cx) + ax*cz) - ay*az*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[2][3][2][0] = (aa* (-((2*(ab*ac - aa*bc)*(ab*ax - aa*bx) - 2*aa*(az*by - ay*bz)*det)* ((ab*ac - aa*bc)*(ay*cx - ax*cy) - (ac*az - aa*cz)*det)) + (ay*(ab*ac - aa*bc) + (ab*ax - aa*bx)*(ay*cx - ax*cy) + (az*by - ay*bz)*(ac*az - aa*cz) - ax*az*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[2][3][2][1] = (aa* (-((2*(ab*ac - aa*bc)*(ab*ay - aa*by) + 2*aa*(az*bx - ax*bz)*det)* ((ab*ac - aa*bc)*(ay*cx - ax*cy) - (ac*az - aa*cz)*det)) + (-(ab*ac*ax) + aa*ax*bc + (ab*ay - aa*by)*(ay*cx - ax*cy) - (az*bx - ax*bz)*(ac*az - aa*cz) - ay*az*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[2][3][2][2] = (aa* (-((2*(ab*ac - aa*bc)*(ab*az - aa*bz) - 2*aa*(ay*bx - ax*by)*det)* ((ab*ac - aa*bc)*(ay*cx - ax*cy) - (ac*az - aa*cz)*det)) + ((ab*az - aa*bz)*(ay*cx - ax*cy) + (ay*bx - ax*by)*(ac*az - aa*cz) + (Power(ax,2) + Power(ay,2))*det)* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[3][3][0][0] = -((aa* ((ab*ac - aa*bc)*(-(az*by) + ay*bz) - (ab*ax - aa*bx)*det)* (2*(ab*ac - aa*bc)*(ab*ax - aa*bx) - 2*aa*(az*by - ay*bz)*det))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2)); e_info->hess[3][3][0][1] = (aa* (-(((ab*ac - aa*bc)*(-(az*by) + ay*bz) - (ab*ax - aa*bx)*det)* (2*(ab*ac - aa*bc)*(ab*ay - aa*by) + 2*aa*(az*bx - ax*bz)*det)) + ((ab*ax - aa*bx)*(-(az*bx) + ax*bz) + (ab*ay - aa*by)*(-(az*by) + ay*bz))* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[3][3][0][2] = (aa* (-(((ab*ac - aa*bc)*(-(az*by) + ay*bz) - (ab*ax - aa*bx)*det)* (2*(ab*ac - aa*bc)*(ab*az - aa*bz) - 2*aa*(ay*bx - ax*by)*det)) + (-((ab*ax - aa*bx)*(-(ay*bx) + ax*by)) + (ab*az - aa*bz)*(-(az*by) + ay*bz))* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[3][3][1][0] = (aa* (-(((ab*ac - aa*bc)*(az*bx - ax*bz) - (ab*ay - aa*by)*det)* (2*(ab*ac - aa*bc)*(ab*ax - aa*bx) - 2*aa*(az*by - ay*bz)*det)) + ((ab*ax - aa*bx)*(az*bx - ax*bz) - (ab*ay - aa*by)*(-(az*by) + ay*bz))* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[3][3][1][1] = -((aa* ((ab*ac - aa*bc)*(az*bx - ax*bz) - (ab*ay - aa*by)*det)* (2*(ab*ac - aa*bc)*(ab*ay - aa*by) + 2*aa*(az*bx - ax*bz)*det))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2)); e_info->hess[3][3][1][2] = (aa* (-(((ab*ac - aa*bc)*(az*bx - ax*bz) - (ab*ay - aa*by)*det)* (2*(ab*ac - aa*bc)*(ab*az - aa*bz) - 2*aa*(ay*bx - ax*by)*det)) + ((ab*ay - aa*by)*(ay*bx - ax*by) + (ab*az - aa*bz)*(az*bx - ax*bz))* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[3][3][2][0] = (aa* (-(((ab*ac - aa*bc)*(-(ay*bx) + ax*by) - (ab*az - aa*bz)*det)* (2*(ab*ac - aa*bc)*(ab*ax - aa*bx) - 2*aa*(az*by - ay*bz)*det)) + ((ab*ax - aa*bx)*(-(ay*bx) + ax*by) + (ab*az - aa*bz)*(az*by - ay*bz))* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[3][3][2][1] = (aa* (-(((ab*ac - aa*bc)*(-(ay*bx) + ax*by) - (ab*az - aa*bz)*det)* (2*(ab*ac - aa*bc)*(ab*ay - aa*by) + 2*aa*(az*bx - ax*bz)*det)) + ((ab*ay - aa*by)*(-(ay*bx) + ax*by) - (ab*az - aa*bz)*(az*bx - ax*bz))* (Power(ab*ac - aa*bc,2) + aa*Power(det,2))))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2); e_info->hess[3][3][2][2] = -((aa* (2*(ab*ac - aa*bc)*(ab*az - aa*bz) - 2*aa*(ay*bx - ax*by)*det)* ((ab*ac - aa*bc)*(-(ay*bx) + ax*by) - (ab*az - aa*bz)*det))/ Power(Power(ab*ac - aa*bc,2) + aa*Power(det,2),2)); /* take care of some fudge factors, including orientation */ for ( i = 1 ; i < 4 ; i++ ) for ( j = 1 ; j <= i ; j++ ) for ( ii = 0 ; ii < SDIM ; ii++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) e_info->hess[j][i][jj][ii] *= 0.5*sign; /* Fill in rest of Hessian by symmetry */ for ( i = 1 ; i < 4 ; i++ ) for ( j = i+1 ; j < 4 ; j++ ) for ( ii = 0 ; ii < SDIM ; ii++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) e_info->hess[j][i][jj][ii] = e_info->hess[i][j][ii][jj]; for ( i = 1 ; i < 4 ; i++ ) for ( j = 1 ; j < 4 ; j++ ) for ( ii = 0 ; ii < SDIM ; ii++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) { e_info->hess[0][0][ii][jj] += e_info->hess[i][j][ii][jj]; e_info->hess[0][j][ii][jj] -= e_info->hess[i][j][ii][jj]; e_info->hess[i][0][ii][jj] -= e_info->hess[i][j][ii][jj]; } return sign*theta*len/2; } /************************************************************************** mean_curvature_integral_a Same, but using tangent of dihedral angle for more stability. Using sine actually better approximates smoothed mean curvature on a sphere, but seems wildly unstable. Tangent is much more stable, but has its own cautions: 1. Use only for small angles. Tan(pi/2) = infinity, which doesn't work well. 2. Possible favors uneven triangulation. At large refinements on sphere, translation eigenvalues can become negative. Use translation-suppressing center-of-mass constraints? ***************************************************************************/ /********************************************************************* * * function: mean_int_a_init() * * purpose: Check illegalities * */ void mean_int_a_init(mode,mi) int mode; struct method_instance *mi; { if ( web.dimension != 2 ) kb_error(4594,"mean_curvature_integral_a method only for 2D facets.\n",RECOVERABLE); if ( web.modeltype != LINEAR ) kb_error(4595,"mean_curvature_integral_a method only for LINEAR model.\n",RECOVERABLE); } /*************************************************************************** * * Function: mean_int_a_all() * * Purpose: Computes contribution of one edge for value, grad, and hessian. */ REAL mean_int_a_all(e_info,mode) struct qinfo *e_info; int mode; /* METHOD_VALUE, METHOD_GRADIEN, METHOD_HESSIAN */ { REAL vol; int i,j,ii,jj; int sign = inverted(get_fe_facet(get_edge_fe(e_info->id))) ? 1 : -1; REAL ax,ay,az,bx,by,bz,cx,cy,cz; REAL aa,ab,ac,bb,bc,cc,a,aaa,det; REAL value; /* same value and gradient calculation as mean_int_gradient() */ mat_tsquare(e_info->sides[0],e_info->ss,3,SDIM); /* side products */ vol = triple_prod(e_info->sides[0][0],e_info->sides[0][1],e_info->sides[0][2]); /* if ( vol == 0.0 ) return 0.0; */ /* Now Hessian, as ground out by Mathematica, in meanint.m */ ax = e_info->sides[0][0][0]; ay = e_info->sides[0][0][1]; az = e_info->sides[0][0][2]; bx = e_info->sides[0][1][0]; by = e_info->sides[0][1][1]; bz = e_info->sides[0][1][2]; cx = e_info->sides[0][2][0]; cy = e_info->sides[0][2][1]; cz = e_info->sides[0][2][2]; aa = e_info->ss[0][0]; bb = e_info->ss[1][1]; cc = e_info->ss[2][2]; ab = e_info->ss[0][1]; ac = e_info->ss[0][2]; bc = e_info->ss[1][2]; a = sqrt(aa); aaa = a*aa; det = vol; /* Copied from Mathematica */ #undef Power #define Power(a,b) ((b==2)?(a)*(a):(a)*(a)*(a)) value = -((aa*det)/(-(ab*ac) + aa*bc))*sign/2.0; if ( mode == METHOD_VALUE ) return value; e_info->grad[1][0] = (aa*(-(ab*ac) + aa*bc)*(bz*cy - by*cz) + 2*ax*(ab*ac - aa*bc)*det + aa*(2*ax*bc - ac*bx - ab*cx)*det)/ Power(ab*ac - aa*bc,2); e_info->grad[1][1] = (aa*(ab*ac - aa*bc)*(bz*cx - bx*cz) + 2*ay*(ab*ac - aa*bc)*det + aa*(2*ay*bc - ac*by - ab*cy)*det)/ Power(ab*ac - aa*bc,2); e_info->grad[1][2] = (aa*(-(ab*ac) + aa*bc)*(by*cx - bx*cy) + 2*az*(ab*ac - aa*bc)*det + aa*(2*az*bc - ac*bz - ab*cz)*det)/ Power(ab*ac - aa*bc,2); e_info->grad[2][0] = (aa*((ab*ac - aa*bc)*(az*cy - ay*cz) - (ac*ax - aa*cx)*det))/Power(ab*ac - aa*bc,2); e_info->grad[2][1] = (aa*((ab*ac - aa*bc)*(-(az*cx) + ax*cz) - (ac*ay - aa*cy)*det))/Power(ab*ac - aa*bc,2); e_info->grad[2][2] = (aa*((ab*ac - aa*bc)*(ay*cx - ax*cy) - (ac*az - aa*cz)*det))/Power(ab*ac - aa*bc,2); e_info->grad[3][0] = (aa*((ab*ac - aa*bc)*(-(az*by) + ay*bz) - (ab*ax - aa*bx)*det))/Power(ab*ac - aa*bc,2); e_info->grad[3][1] = (aa*((ab*ac - aa*bc)*(az*bx - ax*bz) - (ab*ay - aa*by)*det))/Power(ab*ac - aa*bc,2); e_info->grad[3][2] = (aa*((ab*ac - aa*bc)*(-(ay*bx) + ax*by) - (ab*az - aa*bz)*det))/Power(ab*ac - aa*bc,2); /* take care of some fudge factors, including orientation */ for ( i = 1 ; i < 4 ; i++ ) for ( ii = 0 ; ii < SDIM ; ii++ ) e_info->grad[i][ii] *= 0.5*sign; /* set gradient of base vertex */ for ( i = 1 ; i < 4 ; i++ ) for ( ii = 0 ; ii < SDIM ; ii++ ) e_info->grad[0][ii] -= e_info->grad[i][ii]; if ( mode == METHOD_GRADIENT ) return value; e_info->hess[1][1][0][0] = (-2*aa*(-(ab*ac) + aa*bc)* (-2*ax*bc + ac*bx + ab*cx)*(bz*cy - by*cz) + 4*ax*Power(ab*ac - aa*bc,2)*(-(bz*cy) + by*cz) + 2*Power(ab*ac - aa*bc,2)*det + 4*ax*(ab*ac - aa*bc)*(2*ax*bc - ac*bx - ab*cx)*det + 2*aa*Power(-2*ax*bc + ac*bx + ab*cx,2)*det - 2*aa*(-(ab*ac) + aa*bc)*(bc - bx*cx)*det)/Power(ab*ac - aa*bc,3); e_info->hess[1][1][0][1] = (2*ax*Power(ab*ac - aa*bc,2)*(bz*cx - bx*cz) - aa*(-(ab*ac) + aa*bc)*(-2*ax*bc + ac*bx + ab*cx)*(-(bz*cx) + bx*cz) + 2*ay*Power(ab*ac - aa*bc,2)*(-(bz*cy) + by*cz) + aa*(-(ab*ac) + aa*bc)*(-2*ay*bc + ac*by + ab*cy)*(-(bz*cy) + by*cz) - 2*ay*(ab*ac - aa*bc)*(-2*ax*bc + ac*bx + ab*cx)*det - 2*ax*(ab*ac - aa*bc)*(-2*ay*bc + ac*by + ab*cy)*det + 2*aa*(-2*ax*bc + ac*bx + ab*cx)*(-2*ay*bc + ac*by + ab*cy)*det + aa*(-(ab*ac) + aa*bc)*(by*cx + bx*cy)*det)/Power(ab*ac - aa*bc,3); e_info->hess[1][1][0][2] = (2*ax*Power(ab*ac - aa*bc,2)* (-(by*cx) + bx*cy) + aa*(-(ab*ac) + aa*bc)*(-2*ax*bc + ac*bx + ab*cx)* (-(by*cx) + bx*cy) - aa*(-(ab*ac) + aa*bc)*(-2*az*bc + ac*bz + ab*cz)* (bz*cy - by*cz) + 2*az*Power(ab*ac - aa*bc,2)*(-(bz*cy) + by*cz) - 2*az*(ab*ac - aa*bc)*(-2*ax*bc + ac*bx + ab*cx)*det - 2*ax*(ab*ac - aa*bc)*(-2*az*bc + ac*bz + ab*cz)*det + 2*aa*(-2*ax*bc + ac*bx + ab*cx)*(-2*az*bc + ac*bz + ab*cz)*det + aa*(-(ab*ac) + aa*bc)*(bz*cx + bx*cz)*det)/Power(ab*ac - aa*bc,3); e_info->hess[1][1][1][0] = (2*ax*Power(ab*ac - aa*bc,2)*(bz*cx - bx*cz) - aa*(-(ab*ac) + aa*bc)*(-2*ax*bc + ac*bx + ab*cx)*(-(bz*cx) + bx*cz) + 2*ay*Power(ab*ac - aa*bc,2)*(-(bz*cy) + by*cz) + aa*(-(ab*ac) + aa*bc)*(-2*ay*bc + ac*by + ab*cy)*(-(bz*cy) + by*cz) - 2*ay*(ab*ac - aa*bc)*(-2*ax*bc + ac*bx + ab*cx)*det - 2*ax*(ab*ac - aa*bc)*(-2*ay*bc + ac*by + ab*cy)*det + 2*aa*(-2*ax*bc + ac*bx + ab*cx)*(-2*ay*bc + ac*by + ab*cy)*det + aa*(-(ab*ac) + aa*bc)*(by*cx + bx*cy)*det)/Power(ab*ac - aa*bc,3); e_info->hess[1][1][1][1] = (2* (2*ay*Power(ab*ac - aa*bc,2)*(bz*cx - bx*cz) + aa*(-(ab*ac) + aa*bc)*(-2*ay*bc + ac*by + ab*cy)*(bz*cx - bx*cz) + Power(ab*ac - aa*bc,2)*det + 2*ay*(ab*ac - aa*bc)*(2*ay*bc - ac*by - ab*cy)*det + aa*Power(-2*ay*bc + ac*by + ab*cy,2)*det - aa*(-(ab*ac) + aa*bc)*(bc - by*cy)*det))/Power(ab*ac - aa*bc,3); e_info->hess[1][1][1][2] = (-(aa*(-(ab*ac) + aa*bc)* (-2*ay*bc + ac*by + ab*cy)*(by*cx - bx*cy)) + 2*ay*Power(ab*ac - aa*bc,2)*(-(by*cx) + bx*cy) + 2*az*Power(ab*ac - aa*bc,2)*(bz*cx - bx*cz) + aa*(-(ab*ac) + aa*bc)*(-2*az*bc + ac*bz + ab*cz)*(bz*cx - bx*cz) - 2*az*(ab*ac - aa*bc)*(-2*ay*bc + ac*by + ab*cy)*det - 2*ay*(ab*ac - aa*bc)*(-2*az*bc + ac*bz + ab*cz)*det + 2*aa*(-2*ay*bc + ac*by + ab*cy)*(-2*az*bc + ac*bz + ab*cz)*det + aa*(-(ab*ac) + aa*bc)*(bz*cy + by*cz)*det)/Power(ab*ac - aa*bc,3); e_info->hess[1][1][2][0] = (2*ax*Power(ab*ac - aa*bc,2)* (-(by*cx) + bx*cy) + aa*(-(ab*ac) + aa*bc)*(-2*ax*bc + ac*bx + ab*cx)* (-(by*cx) + bx*cy) - aa*(-(ab*ac) + aa*bc)*(-2*az*bc + ac*bz + ab*cz)* (bz*cy - by*cz) + 2*az*Power(ab*ac - aa*bc,2)*(-(bz*cy) + by*cz) - 2*az*(ab*ac - aa*bc)*(-2*ax*bc + ac*bx + ab*cx)*det - 2*ax*(ab*ac - aa*bc)*(-2*az*bc + ac*bz + ab*cz)*det + 2*aa*(-2*ax*bc + ac*bx + ab*cx)*(-2*az*bc + ac*bz + ab*cz)*det + aa*(-(ab*ac) + aa*bc)*(bz*cx + bx*cz)*det)/Power(ab*ac - aa*bc,3); e_info->hess[1][1][2][1] = (-(aa*(-(ab*ac) + aa*bc)* (-2*ay*bc + ac*by + ab*cy)*(by*cx - bx*cy)) + 2*ay*Power(ab*ac - aa*bc,2)*(-(by*cx) + bx*cy) + 2*az*Power(ab*ac - aa*bc,2)*(bz*cx - bx*cz) + aa*(-(ab*ac) + aa*bc)*(-2*az*bc + ac*bz + ab*cz)*(bz*cx - bx*cz) - 2*az*(ab*ac - aa*bc)*(-2*ay*bc + ac*by + ab*cy)*det - 2*ay*(ab*ac - aa*bc)*(-2*az*bc + ac*bz + ab*cz)*det + 2*aa*(-2*ay*bc + ac*by + ab*cy)*(-2*az*bc + ac*bz + ab*cz)*det + aa*(-(ab*ac) + aa*bc)*(bz*cy + by*cz)*det)/Power(ab*ac - aa*bc,3); e_info->hess[1][1][2][2] = (4*az*Power(ab*ac - aa*bc,2)* (-(by*cx) + bx*cy) - 2*aa*(-(ab*ac) + aa*bc)*(by*cx - bx*cy)* (-2*az*bc + ac*bz + ab*cz) + 2*Power(ab*ac - aa*bc,2)*det + 4*az*(ab*ac - aa*bc)*(2*az*bc - ac*bz - ab*cz)*det + 2*aa*Power(-2*az*bc + ac*bz + ab*cz,2)*det - 2*aa*(-(ab*ac) + aa*bc)*(bc - bz*cz)*det)/Power(ab*ac - aa*bc,3); e_info->hess[1][2][0][0] = (2*ax*Power(ab*ac - aa*bc,2)*(az*cy - ay*cz) + aa*(-(ab*ac) + aa*bc)*(-2*ax*bc + ac*bx + ab*cx)*(az*cy - ay*cz) + aa*(-(ab*ac) + aa*bc)*(-(ac*ax) + aa*cx)*(bz*cy - by*cz) - 2*ax*(ab*ac - aa*bc)*(ac*ax - aa*cx)*det - 2*aa*(-(ac*ax) + aa*cx)*(-2*ax*bc + ac*bx + ab*cx)*det + aa*(-(ab*ac) + aa*bc)*(ay*cy + az*cz)*det)/Power(ab*ac - aa*bc,3); e_info->hess[1][2][0][1] = (aa*Power(ab*ac - aa*bc,2)*cz + 2*ax*Power(ab*ac - aa*bc,2)*(-(az*cx) + ax*cz) + aa*(-(ab*ac) + aa*bc)*(-2*ax*bc + ac*bx + ab*cx)*(-(az*cx) + ax*cz) + aa*(-(ab*ac) + aa*bc)*(-(ac*ay) + aa*cy)*(bz*cy - by*cz) - 2*ax*(ab*ac - aa*bc)*(ac*ay - aa*cy)*det + 2*aa*(-2*ax*bc + ac*bx + ab*cx)*(ac*ay - aa*cy)*det + aa*(-(ab*ac) + aa*bc)*(ay*cx - 2*ax*cy)*det)/Power(ab*ac - aa*bc,3); e_info->hess[1][2][0][2] = -((aa*Power(ab*ac - aa*bc,2)*cy + 2*ax*Power(ab*ac - aa*bc,2)*(-(ay*cx) + ax*cy) + aa*(-(ab*ac) + aa*bc)*(-2*ax*bc + ac*bx + ab*cx)*(-(ay*cx) + ax*cy) + aa*(-(ab*ac) + aa*bc)*(-(ac*az) + aa*cz)*(-(bz*cy) + by*cz) + 2*ax*(ab*ac - aa*bc)*(ac*az - aa*cz)*det - 2*aa*(2*ax*bc - ac*bx - ab*cx)*(-(ac*az) + aa*cz)*det - aa*(-(ab*ac) + aa*bc)*(az*cx - 2*ax*cz)*det)/Power(ab*ac - aa*bc,3)); e_info->hess[1][2][1][0] = -((aa*Power(ab*ac - aa*bc,2)*cz + 2*ay*Power(ab*ac - aa*bc,2)*(-(az*cy) + ay*cz) + aa*(-(ab*ac) + aa*bc)*(-2*ay*bc + ac*by + ab*cy)*(-(az*cy) + ay*cz) + aa*(-(ab*ac) + aa*bc)*(-(ac*ax) + aa*cx)*(bz*cx - bx*cz) + 2*ay*(ab*ac - aa*bc)*(ac*ax - aa*cx)*det + 2*aa*(-(ac*ax) + aa*cx)*(-2*ay*bc + ac*by + ab*cy)*det + aa*(-(ab*ac) + aa*bc)*(2*ay*cx - ax*cy)*det)/Power(ab*ac - aa*bc,3)); e_info->hess[1][2][1][1] = -((2*ay*Power(ab*ac - aa*bc,2)* (az*cx - ax*cz) + aa*(-(ab*ac) + aa*bc)*(-2*ay*bc + ac*by + ab*cy)* (az*cx - ax*cz) + aa*(-(ab*ac) + aa*bc)*(-(ac*ay) + aa*cy)* (bz*cx - bx*cz) + 2*ay*(ab*ac - aa*bc)*(ac*ay - aa*cy)*det + 2*aa*(-(ac*ay) + aa*cy)*(-2*ay*bc + ac*by + ab*cy)*det - aa*(-(ab*ac) + aa*bc)*(ax*cx + az*cz)*det)/Power(ab*ac - aa*bc,3)); e_info->hess[1][2][1][2] = (aa*Power(ab*ac - aa*bc,2)*cx + 2*ay*Power(ab*ac - aa*bc,2)*(ay*cx - ax*cy) + aa*(-(ab*ac) + aa*bc)*(-2*ay*bc + ac*by + ab*cy)*(ay*cx - ax*cy) + aa*(-(ab*ac) + aa*bc)*(-(ac*az) + aa*cz)*(-(bz*cx) + bx*cz) - 2*ay*(ab*ac - aa*bc)*(ac*az - aa*cz)*det + 2*aa*(-2*ay*bc + ac*by + ab*cy)*(ac*az - aa*cz)*det + aa*(-(ab*ac) + aa*bc)*(az*cy - 2*ay*cz)*det)/Power(ab*ac - aa*bc,3); e_info->hess[1][2][2][0] = (aa*Power(ab*ac - aa*bc,2)*cy + aa*(-(ab*ac) + aa*bc)*(-(ac*ax) + aa*cx)*(by*cx - bx*cy) + 2*az*Power(ab*ac - aa*bc,2)*(az*cy - ay*cz) + aa*(-(ab*ac) + aa*bc)*(-2*az*bc + ac*bz + ab*cz)*(az*cy - ay*cz) - 2*az*(ab*ac - aa*bc)*(ac*ax - aa*cx)*det - 2*aa*(-(ac*ax) + aa*cx)*(-2*az*bc + ac*bz + ab*cz)*det - aa*(-(ab*ac) + aa*bc)*(2*az*cx - ax*cz)*det)/Power(ab*ac - aa*bc,3); e_info->hess[1][2][2][1] = -((aa*Power(ab*ac - aa*bc,2)*cx + aa*(-(ab*ac) + aa*bc)*(-(ac*ay) + aa*cy)*(-(by*cx) + bx*cy) + 2*az*Power(ab*ac - aa*bc,2)*(az*cx - ax*cz) + aa*(-(ab*ac) + aa*bc)*(-2*az*bc + ac*bz + ab*cz)*(az*cx - ax*cz) + 2*az*(ab*ac - aa*bc)*(ac*ay - aa*cy)*det + 2*aa*(-(ac*ay) + aa*cy)*(-2*az*bc + ac*bz + ab*cz)*det + aa*(-(ab*ac) + aa*bc)*(2*az*cy - ay*cz)*det)/Power(ab*ac - aa*bc,3)); e_info->hess[1][2][2][2] = (2*az*Power(ab*ac - aa*bc,2)*(ay*cx - ax*cy) + aa*(-(ab*ac) + aa*bc)*(by*cx - bx*cy)*(-(ac*az) + aa*cz) + aa*(-(ab*ac) + aa*bc)*(ay*cx - ax*cy)*(-2*az*bc + ac*bz + ab*cz) + aa*(-(ab*ac) + aa*bc)*(ax*cx + ay*cy)*det - 2*az*(ab*ac - aa*bc)*(ac*az - aa*cz)*det - 2*aa*(-(ac*az) + aa*cz)*(-2*az*bc + ac*bz + ab*cz)*det)/ Power(ab*ac - aa*bc,3); e_info->hess[1][3][0][0] = (2*ax*Power(ab*ac - aa*bc,2)* (-(az*by) + ay*bz) - aa*(-(ab*ac) + aa*bc)*(az*by - ay*bz)* (-2*ax*bc + ac*bx + ab*cx) + aa*(-(ab*ac) + aa*bc)*(-(ab*ax) + aa*bx)*(bz*cy - by*cz) - 2*ax*(ab*ac - aa*bc)*(ab*ax - aa*bx)*det + aa*(-(ab*ac) + aa*bc)*(ay*by + az*bz)*det - 2*aa*(-(ab*ax) + aa*bx)*(-2*ax*bc + ac*bx + ab*cx)*det)/ Power(ab*ac - aa*bc,3); e_info->hess[1][3][0][1] = -((aa*Power(ab*ac - aa*bc,2)*bz + 2*ax*Power(ab*ac - aa*bc,2)*(-(az*bx) + ax*bz) - aa*(-(ab*ac) + aa*bc)*(az*bx - ax*bz)*(-2*ax*bc + ac*bx + ab*cx) + aa*(-(ab*ac) + aa*bc)*(-(ab*ay) + aa*by)*(-(bz*cy) + by*cz) + 2*ax*(ab*ac - aa*bc)*(ab*ay - aa*by)*det - aa*(-(ab*ac) + aa*bc)*(ay*bx - 2*ax*by)*det + 2*aa*(-(ab*ay) + aa*by)*(-2*ax*bc + ac*bx + ab*cx)*det)/ Power(ab*ac - aa*bc,3)); e_info->hess[1][3][0][2] = (aa*Power(ab*ac - aa*bc,2)*by + 2*ax*Power(ab*ac - aa*bc,2)*(-(ay*bx) + ax*by) - aa*(-(ab*ac) + aa*bc)*(ay*bx - ax*by)*(-2*ax*bc + ac*bx + ab*cx) + aa*(-(ab*ac) + aa*bc)*(-(ab*az) + aa*bz)*(bz*cy - by*cz) - 2*ax*(ab*ac - aa*bc)*(ab*az - aa*bz)*det + aa*(-(ab*ac) + aa*bc)*(az*bx - 2*ax*bz)*det - 2*aa*(-(ab*az) + aa*bz)*(-2*ax*bc + ac*bx + ab*cx)*det)/ Power(ab*ac - aa*bc,3); e_info->hess[1][3][1][0] = (aa*Power(ab*ac - aa*bc,2)*bz + 2*ay*Power(ab*ac - aa*bc,2)*(-(az*by) + ay*bz) - aa*(-(ab*ac) + aa*bc)*(az*by - ay*bz)*(-2*ay*bc + ac*by + ab*cy) + aa*(-(ab*ac) + aa*bc)*(-(ab*ax) + aa*bx)*(-(bz*cx) + bx*cz) - 2*ay*(ab*ac - aa*bc)*(ab*ax - aa*bx)*det - aa*(-(ab*ac) + aa*bc)*(2*ay*bx - ax*by)*det - 2*aa*(-(ab*ax) + aa*bx)*(-2*ay*bc + ac*by + ab*cy)*det)/ Power(ab*ac - aa*bc,3); e_info->hess[1][3][1][1] = -((-2*ay*Power(ab*ac - aa*bc,2)* (az*bx - ax*bz) - aa*(-(ab*ac) + aa*bc)*(az*bx - ax*bz)* (-2*ay*bc + ac*by + ab*cy) + aa*(-(ab*ac) + aa*bc)*(-(ab*ay) + aa*by)*(bz*cx - bx*cz) + 2*ay*(ab*ac - aa*bc)*(ab*ay - aa*by)*det - aa*(-(ab*ac) + aa*bc)*(ax*bx + az*bz)*det + 2*aa*(-(ab*ay) + aa*by)*(-2*ay*bc + ac*by + ab*cy)*det)/ Power(ab*ac - aa*bc,3)); e_info->hess[1][3][1][2] = -((aa*Power(ab*ac - aa*bc,2)*bx + 2*ay*Power(ab*ac - aa*bc,2)*(ay*bx - ax*by) + aa*(-(ab*ac) + aa*bc)*(ay*bx - ax*by)*(-2*ay*bc + ac*by + ab*cy) + aa*(-(ab*ac) + aa*bc)*(-(ab*az) + aa*bz)*(bz*cx - bx*cz) + 2*ay*(ab*ac - aa*bc)*(ab*az - aa*bz)*det - aa*(-(ab*ac) + aa*bc)*(az*by - 2*ay*bz)*det + 2*aa*(-(ab*az) + aa*bz)*(-2*ay*bc + ac*by + ab*cy)*det)/ Power(ab*ac - aa*bc,3)); e_info->hess[1][3][2][0] = -((aa*Power(ab*ac - aa*bc,2)*by + 2*az*Power(ab*ac - aa*bc,2)*(az*by - ay*bz) + aa*(-(ab*ac) + aa*bc)*(-(ab*ax) + aa*bx)*(-(by*cx) + bx*cy) + aa*(-(ab*ac) + aa*bc)*(az*by - ay*bz)*(-2*az*bc + ac*bz + ab*cz) + 2*az*(ab*ac - aa*bc)*(ab*ax - aa*bx)*det + aa*(-(ab*ac) + aa*bc)*(2*az*bx - ax*bz)*det + 2*aa*(-(ab*ax) + aa*bx)*(-2*az*bc + ac*bz + ab*cz)*det)/ Power(ab*ac - aa*bc,3)); e_info->hess[1][3][2][1] = (aa*Power(ab*ac - aa*bc,2)*bx + 2*az*Power(ab*ac - aa*bc,2)*(az*bx - ax*bz) + aa*(-(ab*ac) + aa*bc)*(-(ab*ay) + aa*by)*(by*cx - bx*cy) + aa*(-(ab*ac) + aa*bc)*(az*bx - ax*bz)*(-2*az*bc + ac*bz + ab*cz) - 2*az*(ab*ac - aa*bc)*(ab*ay - aa*by)*det - aa*(-(ab*ac) + aa*bc)*(2*az*by - ay*bz)*det - 2*aa*(-(ab*ay) + aa*by)*(-2*az*bc + ac*bz + ab*cz)*det)/ Power(ab*ac - aa*bc,3); e_info->hess[1][3][2][2] = (2*az*Power(ab*ac - aa*bc,2)* (-(ay*bx) + ax*by) + aa*(-(ab*ac) + aa*bc)*(-(ab*az) + aa*bz)* (by*cx - bx*cy) - aa*(-(ab*ac) + aa*bc)*(ay*bx - ax*by)* (-2*az*bc + ac*bz + ab*cz) + aa*(-(ab*ac) + aa*bc)*(ax*bx + ay*by)*det - 2*az*(ab*ac - aa*bc)*(ab*az - aa*bz)*det - 2*aa*(-(ab*az) + aa*bz)*(-2*az*bc + ac*bz + ab*cz)*det)/ Power(ab*ac - aa*bc,3); e_info->hess[2][2][0][0] = (2*aa*(ac*ax - aa*cx)* (-((ab*ac - aa*bc)*(az*cy - ay*cz)) + (ac*ax - aa*cx)*det))/ Power(ab*ac - aa*bc,3); e_info->hess[2][2][0][1] = (aa* (-((ab*ac - aa*bc)*(ac*ax - aa*cx)*(-(az*cx) + ax*cz)) + (ab*ac - aa*bc)*(ac*ay - aa*cy)*(-(az*cy) + ay*cz) + 2*(ac*ax - aa*cx)*(ac*ay - aa*cy)*det))/Power(ab*ac - aa*bc,3); e_info->hess[2][2][0][2] = (aa* ((ab*ac - aa*bc)*(ac*ax - aa*cx)*(-(ay*cx) + ax*cy) - (ab*ac - aa*bc)*(ac*az - aa*cz)*(az*cy - ay*cz) + 2*(ac*ax - aa*cx)*(ac*az - aa*cz)*det))/Power(ab*ac - aa*bc,3); e_info->hess[2][2][1][0] = (aa* (-((ab*ac - aa*bc)*(ac*ax - aa*cx)*(-(az*cx) + ax*cz)) + (ab*ac - aa*bc)*(ac*ay - aa*cy)*(-(az*cy) + ay*cz) + 2*(ac*ax - aa*cx)*(ac*ay - aa*cy)*det))/Power(ab*ac - aa*bc,3); e_info->hess[2][2][1][1] = (2*aa*(ac*ay - aa*cy)* ((ab*ac - aa*bc)*(az*cx - ax*cz) + (ac*ay - aa*cy)*det))/ Power(ab*ac - aa*bc,3); e_info->hess[2][2][1][2] = (aa* (-((ab*ac - aa*bc)*(ac*ay - aa*cy)*(ay*cx - ax*cy)) + (ab*ac - aa*bc)*(ac*az - aa*cz)*(az*cx - ax*cz) + 2*(ac*ay - aa*cy)*(ac*az - aa*cz)*det))/Power(ab*ac - aa*bc,3); e_info->hess[2][2][2][0] = (aa* ((ab*ac - aa*bc)*(ac*ax - aa*cx)*(-(ay*cx) + ax*cy) - (ab*ac - aa*bc)*(ac*az - aa*cz)*(az*cy - ay*cz) + 2*(ac*ax - aa*cx)*(ac*az - aa*cz)*det))/Power(ab*ac - aa*bc,3); e_info->hess[2][2][2][1] = (aa* (-((ab*ac - aa*bc)*(ac*ay - aa*cy)*(ay*cx - ax*cy)) + (ab*ac - aa*bc)*(ac*az - aa*cz)*(az*cx - ax*cz) + 2*(ac*ay - aa*cy)*(ac*az - aa*cz)*det))/Power(ab*ac - aa*bc,3); e_info->hess[2][2][2][2] = (2*aa*(ac*az - aa*cz)* (-((ab*ac - aa*bc)*(ay*cx - ax*cy)) + (ac*az - aa*cz)*det))/ Power(ab*ac - aa*bc,3); e_info->hess[2][3][0][0] = (aa* ((ab*ac - aa*bc)*(az*by - ay*bz)*(ac*ax - aa*cx) - (ab*ac - aa*bc)*(ab*ax - aa*bx)*(az*cy - ay*cz) + (Power(ay,2) + Power(az,2))*(ab*ac - aa*bc)*det + 2*(ab*ax - aa*bx)*(ac*ax - aa*cx)*det))/Power(ab*ac - aa*bc,3); e_info->hess[2][3][0][1] = (aa* (az*Power(ab*ac - aa*bc,2) - (ab*ac - aa*bc)*(az*bx - ax*bz)*(ac*ax - aa*cx) + (ab*ac - aa*bc)*(ab*ay - aa*by)*(-(az*cy) + ay*cz) - ax*ay*(ab*ac - aa*bc)*det - 2*(ab*ay - aa*by)*(-(ac*ax) + aa*cx)*det))/ Power(ab*ac - aa*bc,3); e_info->hess[2][3][0][2] = (aa* (-(ay*Power(ab*ac - aa*bc,2)) + (ab*ac - aa*bc)*(ay*bx - ax*by)*(ac*ax - aa*cx) - (ab*ac - aa*bc)*(ab*az - aa*bz)*(az*cy - ay*cz) - ax*az*(ab*ac - aa*bc)*det - 2*(ab*az - aa*bz)*(-(ac*ax) + aa*cx)*det))/ Power(ab*ac - aa*bc,3); e_info->hess[2][3][1][0] = (aa* (-(az*Power(ab*ac - aa*bc,2)) + (ab*ac - aa*bc)*(az*by - ay*bz)*(ac*ay - aa*cy) - (ab*ac - aa*bc)*(ab*ax - aa*bx)*(-(az*cx) + ax*cz) - ax*ay*(ab*ac - aa*bc)*det - 2*(ab*ax - aa*bx)*(-(ac*ay) + aa*cy)*det))/ Power(ab*ac - aa*bc,3); e_info->hess[2][3][1][1] = (aa* (-((ab*ac - aa*bc)*(az*bx - ax*bz)*(ac*ay - aa*cy)) + (ab*ac - aa*bc)*(ab*ay - aa*by)*(az*cx - ax*cz) + (Power(ax,2) + Power(az,2))*(ab*ac - aa*bc)*det + 2*(ab*ay - aa*by)*(ac*ay - aa*cy)*det))/Power(ab*ac - aa*bc,3); e_info->hess[2][3][1][2] = (aa* (ax*Power(ab*ac - aa*bc,2) + (ab*ac - aa*bc)*(ay*bx - ax*by)*(ac*ay - aa*cy) + (ab*ac - aa*bc)*(ab*az - aa*bz)*(az*cx - ax*cz) - ay*az*(ab*ac - aa*bc)*det - 2*(ab*az - aa*bz)*(-(ac*ay) + aa*cy)*det))/ Power(ab*ac - aa*bc,3); e_info->hess[2][3][2][0] = (aa* (ay*Power(ab*ac - aa*bc,2) + (ab*ac - aa*bc)*(ab*ax - aa*bx)*(-(ay*cx) + ax*cy) + (ab*ac - aa*bc)*(az*by - ay*bz)*(ac*az - aa*cz) - ax*az*(ab*ac - aa*bc)*det - 2*(ab*ax - aa*bx)*(-(ac*az) + aa*cz)*det))/ Power(ab*ac - aa*bc,3); e_info->hess[2][3][2][1] = (aa* (-(ax*Power(ab*ac - aa*bc,2)) - (ab*ac - aa*bc)*(ab*ay - aa*by)*(ay*cx - ax*cy) - (ab*ac - aa*bc)*(az*bx - ax*bz)*(ac*az - aa*cz) - ay*az*(ab*ac - aa*bc)*det - 2*(ab*ay - aa*by)*(-(ac*az) + aa*cz)*det))/ Power(ab*ac - aa*bc,3); e_info->hess[2][3][2][2] = (aa* (-((ab*ac - aa*bc)*(ab*az - aa*bz)*(ay*cx - ax*cy)) + (ab*ac - aa*bc)*(ay*bx - ax*by)*(ac*az - aa*cz) + (Power(ax,2) + Power(ay,2))*(ab*ac - aa*bc)*det + 2*(ab*az - aa*bz)*(ac*az - aa*cz)*det))/Power(ab*ac - aa*bc,3); e_info->hess[3][3][0][0] = (2*aa*(ab*ax - aa*bx)* ((ab*ac - aa*bc)*(az*by - ay*bz) + (ab*ax - aa*bx)*det))/ Power(ab*ac - aa*bc,3); e_info->hess[3][3][0][1] = (aa* ((ab*ac - aa*bc)*(ab*ax - aa*bx)*(-(az*bx) + ax*bz) - (ab*ac - aa*bc)*(ab*ay - aa*by)*(-(az*by) + ay*bz) + 2*(ab*ax - aa*bx)*(ab*ay - aa*by)*det))/Power(ab*ac - aa*bc,3); e_info->hess[3][3][0][2] = (aa* (-((ab*ac - aa*bc)*(ab*ax - aa*bx)*(-(ay*bx) + ax*by)) + (ab*ac - aa*bc)*(ab*az - aa*bz)*(az*by - ay*bz) + 2*(ab*ax - aa*bx)*(ab*az - aa*bz)*det))/Power(ab*ac - aa*bc,3); e_info->hess[3][3][1][0] = (aa* ((ab*ac - aa*bc)*(ab*ax - aa*bx)*(-(az*bx) + ax*bz) - (ab*ac - aa*bc)*(ab*ay - aa*by)*(-(az*by) + ay*bz) + 2*(ab*ax - aa*bx)*(ab*ay - aa*by)*det))/Power(ab*ac - aa*bc,3); e_info->hess[3][3][1][1] = (2*aa*(ab*ay - aa*by)* (-((ab*ac - aa*bc)*(az*bx - ax*bz)) + (ab*ay - aa*by)*det))/ Power(ab*ac - aa*bc,3); e_info->hess[3][3][1][2] = (aa* ((ab*ac - aa*bc)*(ab*ay - aa*by)*(ay*bx - ax*by) - (ab*ac - aa*bc)*(ab*az - aa*bz)*(az*bx - ax*bz) + 2*(ab*ay - aa*by)*(ab*az - aa*bz)*det))/Power(ab*ac - aa*bc,3); e_info->hess[3][3][2][0] = (aa* (-((ab*ac - aa*bc)*(ab*ax - aa*bx)*(-(ay*bx) + ax*by)) + (ab*ac - aa*bc)*(ab*az - aa*bz)*(az*by - ay*bz) + 2*(ab*ax - aa*bx)*(ab*az - aa*bz)*det))/Power(ab*ac - aa*bc,3); e_info->hess[3][3][2][1] = (aa* ((ab*ac - aa*bc)*(ab*ay - aa*by)*(ay*bx - ax*by) - (ab*ac - aa*bc)*(ab*az - aa*bz)*(az*bx - ax*bz) + 2*(ab*ay - aa*by)*(ab*az - aa*bz)*det))/Power(ab*ac - aa*bc,3); e_info->hess[3][3][2][2] = (2*aa*(ab*az - aa*bz)* ((ab*ac - aa*bc)*(ay*bx - ax*by) + (ab*az - aa*bz)*det))/ Power(ab*ac - aa*bc,3); /* take care of some fudge factors, including orientation */ for ( i = 1 ; i < 4 ; i++ ) for ( j = 1 ; j <= i ; j++ ) for ( ii = 0 ; ii < SDIM ; ii++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) e_info->hess[j][i][jj][ii] *= 0.5*sign; /* Fill in rest of Hessian by symmetry */ for ( i = 1 ; i < 4 ; i++ ) for ( j = i+1 ; j < 4 ; j++ ) for ( ii = 0 ; ii < SDIM ; ii++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) e_info->hess[j][i][jj][ii] = e_info->hess[i][j][ii][jj]; for ( i = 1 ; i < 4 ; i++ ) for ( j = 1 ; j < 4 ; j++ ) for ( ii = 0 ; ii < SDIM ; ii++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) { e_info->hess[0][0][ii][jj] += e_info->hess[i][j][ii][jj]; e_info->hess[0][j][ii][jj] -= e_info->hess[i][j][ii][jj]; e_info->hess[i][0][ii][jj] -= e_info->hess[i][j][ii][jj]; } return value; } REAL mean_int_a_value(e_info) struct qinfo *e_info; { return mean_int_a_all(e_info,METHOD_VALUE); } REAL mean_int_a_gradient(e_info) struct qinfo *e_info; { return mean_int_a_all(e_info,METHOD_GRADIENT); } REAL mean_int_a_hessian(e_info) struct qinfo *e_info; { return mean_int_a_all(e_info,METHOD_HESSIAN); } evolver-2.30c.dfsg/src/simplex.c0000644000175300017530000017042611410765113017054 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************* * * file: simplex.c * * Contents: Functions calculating energy, volume, and their * gradients for the LINEAR simplex model. */ #include "include.h" /********************************************************************** * * function: calc_simplex_energy() * * purpose: Calculates energy due to one simplex facet. * Uses Gram determinant to find simplex area. * */ void calc_simplex_energy(f_id) facet_id f_id; { vertex_id *v = get_facet_vertices(f_id); REAL side[MAXCOORD][MAXCOORD]; REAL *x[MAXCOORD]; REAL matspace[MAXCOORD][MAXCOORD]; REAL *mat[MAXCOORD]; REAL energy; REAL det; int k,j; x[0] = get_coord(v[0]); for ( k = 1 ; k <= web.dimension ; k++ ) { x[k] = get_coord(v[k]); for ( j = 0 ; j < SDIM ; j++ ) side[k-1][j] = x[k][j] - x[0][j]; } for ( k = 0 ; k < web.dimension ; k++ ) { mat[k] = matspace[k]; /* set up for matrix.c */ for ( j = 0 ; j <= k ; j++ ) mat[j][k] = mat[k][j] = SDIM_dot(side[j],side[k]); } det = determinant(mat,web.dimension); if ( det <= 0.0 ) energy = 0.0; else energy = sqrt(det)/web.simplex_factorial; web.total_area += energy; set_facet_area(f_id,energy); if ( get_fattr(f_id) & DENSITY ) energy *= get_facet_density(f_id); binary_tree_add(web.total_energy_addends,energy); } /********************************************************************** * * function: calc_simplex_forces() * * purpose: Calculates vertex forces due to one simplex facet. * */ void calc_simplex_forces(f_id) facet_id f_id; { vertex_id *v = get_facet_vertices(f_id); REAL side[MAXCOORD][MAXCOORD]; REAL *x[MAXCOORD]; REAL *f[MAXCOORD]; REAL matspace[MAXCOORD][MAXCOORD]; REAL *mat[MAXCOORD]; REAL force; int k,j,i; REAL det; REAL factor; x[0] = get_coord(v[0]); f[0] = get_force(v[0]); for ( k = 1 ; k <= web.dimension ; k++ ) { x[k] = get_coord(v[k]); f[k] = get_force(v[k]); for ( j = 0 ; j < SDIM ; j++ ) side[k-1][j] = x[k][j] - x[0][j]; } for ( k = 0 ; k < web.dimension ; k++ ) { mat[k] = matspace[k]; /* set up for matrix.c */ for ( j = 0 ; j <= k ; j++ ) mat[j][k] = mat[k][j] = SDIM_dot(side[j],side[k]); } det = det_adjoint(mat,web.dimension); /* leaves mat adjoint transpose */ if ( det <= 0.0 ) return; /* degenerate triangle */ if ( get_fattr(f_id) & DENSITY ) factor = get_facet_density(f_id)/web.simplex_factorial/sqrt(det); else factor = 1.0/web.simplex_factorial/sqrt(det); for ( k = 0 ; k < web.dimension ; k++ ) for ( i = 0 ; i < web.dimension ; i++ ) { REAL *ss,*ff,*ff0, fudge = factor*mat[k][i]; for ( j=SDIM, ff0=f[0], ff=f[k+1],ss=side[i] ; j ; j-- ) { *(ff++) -= force = fudge*(*(ss++)); *(ff0++) += force; } } /* accumulate 1/n area around each vertex to scale motion */ { REAL energy = sqrt(det)/web.simplex_factorial; for ( k = 0 ; k <= web.dimension ; k++ ) add_vertex_star(v[k],energy); } } /********************************************************************** * * function: calc_simplex_volume() * * purpose: Calculates volume due to one simplex facet. * Symmetric style only. * */ void calc_simplex_volume(f_id) facet_id f_id; { vertex_id *v = get_facet_vertices(f_id); REAL *x[MAXCOORD]; REAL matspace[MAXCOORD][MAXCOORD]; REAL *mat[MAXCOORD]; int k,j; body_id b_id0,b_id1; REAL vol; if ( get_fattr(f_id) & NONCONTENT ) return; b_id0 = get_facet_body(f_id); b_id1 = get_facet_body(facet_inverse(f_id)); if ( !valid_id(b_id0) && !valid_id(b_id1) ) return; for ( k = 0 ; k <= web.dimension ; k++ ) { x[k] = get_coord(v[k]); mat[k] = matspace[k]; for ( j = 0 ; j < SDIM ; j++ ) mat[k][j] = x[k][j]; } vol = determinant(mat,web.dimension+1); vol /= web.simplex_factorial*(web.dimension+1); /* add to body volumes */ if ( valid_id(b_id0) ) add_body_volume(b_id0,vol); if ( valid_id(b_id1) ) add_body_volume(b_id1,-vol); } /****************************************************************** * * Function: simplex_grad_l() * * Purpose: Compute volume gradients for vertices on facets. */ void simplex_grad_l() { body_id bi_id; /* identifier for body i */ body_id bj_id; /* identifier for body j */ vertex_id *v; REAL *x[MAXCOORD]; REAL matspace[MAXCOORD][MAXCOORD]; REAL *mat[MAXCOORD]; int k,j,i; facet_id f_id; volgrad *vgptr; FOR_ALL_FACETS(f_id) { if ( get_fattr(f_id) & NONCONTENT ) return; bi_id = get_facet_body(f_id); bj_id = get_facet_body(facet_inverse(f_id)); if ( !valid_id(bi_id) && !valid_id(bj_id) ) continue; v = get_facet_vertices(f_id); for ( k = 0 ; k <= web.dimension ; k++ ) { x[k] = get_coord(v[k]); mat[k] = matspace[k]; for ( j = 0 ; j < SDIM ; j++ ) mat[k][j] = x[k][j]; } det_adjoint(mat,web.dimension+1); /* mat adjoint transpose */ if ( valid_id(bi_id) && (get_battr(bi_id) & (PRESSURE|FIXEDVOL)) ) { for ( k = 0 ; k <= web.dimension ; k++ ) { vgptr = get_bv_new_vgrad(get_body_fixnum(bi_id),v[k]); vgptr->bb_id = bi_id; for ( i = 0 ; i < SDIM ; i++ ) vgptr->grad[i] += mat[i][k]/volume_factorial; } } if ( valid_id(bj_id) && (get_battr(bj_id) & (PRESSURE|FIXEDVOL)) ) { for ( k = 0 ; k <= web.dimension ; k++ ) { vgptr = get_bv_new_vgrad(get_body_fixnum(bj_id),v[k]); vgptr->bb_id = bj_id; for ( i = 0 ; i < SDIM ; i++ ) vgptr->grad[i] -= mat[i][k]/volume_factorial; } } } /* end facet loop */ } /************************************************************************** * * function: refine_simplex() * * purpose: * Recursive refining of a simplex divided at the midpoints of its edges. */ void refine_simplex(dim,vlist,elist,slist) int dim; /* dimension of simplex */ int *vlist; /* vertices of simplex */ struct divedge *elist; /* edges of simplex */ struct simplex *slist; /* generated simplices */ { int v1,v2; int basept; int numedges = (dim*(dim+1))/2; int snum = 0; /* simplex number */ int pnum; /* point number in simplex */ struct divedge *newelist = (struct divedge *)temp_calloc(numedges,sizeof(struct divedge)); int i,j; int newfaces; int edgenum; int lowpt,lowpti=0; /* pick base point as lowest numbered midpoint on list */ for ( i = 0 , lowpt = 0x7FFF7FFF ; i < numedges ; i++ ) { if ( elist[i].divpt < lowpt ) { lowpt = elist[i].divpt; lowpti = i; } } v1 = elist[lowpti].endpt[0]; v2 = elist[lowpti].endpt[1]; basept = elist[lowpti].divpt; /* do simple opposite simplices */ /* one for each vertex not an endpoint on base edge */ for ( i = 0 ; i <= dim ; i++ ) { if ( (vlist[i] == v1) || (vlist[i] == v2) ) continue; /* on base edge */ for ( j = 0, pnum = 0 ; j < numedges ; j++ ) if ( (elist[j].endpt[0]==vlist[i]) || (elist[j].endpt[1]==vlist[i])) slist[snum].pt[pnum++] = elist[j].divpt; slist[snum].pt[pnum] = basept; /* add common basepoint */ snum++; } if ( dim <= 2 ) goto exxit; /* end recursion */ /* now do compound opposite faces */ /* first, opposite v1 */ /* first, get edges after base not including v2 */ for ( i = 0, edgenum = 0 ; i < numedges ; i++ ) if ( (elist[i].endpt[0]!=v1) && (elist[i].endpt[1]!=v1) ) newelist[edgenum++] = elist[i]; /* get vertex list in good order with v1 first */ for ( i = 0 ; i <= dim ; i++ ) if ( v1 == vlist[i] ) { vlist[i] = vlist[0]; vlist[0] = v1; break; } /* recurse */ refine_simplex(dim-1,vlist+1,newelist,slist+snum); /* add base point */ newfaces = (1<<(dim-1))-dim; for ( i = 0 ; i < newfaces ; i++ ) slist[snum++].pt[dim] = basept; /* second, opposite v2 */ /* first, get edges after base not including v2 */ for ( i = 0, edgenum = 0 ; i < numedges ; i++ ) if ( (elist[i].endpt[0]!=v2) && (elist[i].endpt[1]!=v2) ) newelist[edgenum++] = elist[i]; /* get vertex list in good order with v2 first */ for ( i = 0 ; i <= dim ; i++ ) if ( v2 == vlist[i] ) { vlist[i] = vlist[0]; vlist[0] = v2; break; } /* recurse */ refine_simplex(dim-1,vlist+1,newelist,slist+snum); /* add base point */ newfaces = (1<<(dim-1))-dim; for ( i = 0 ; i < newfaces ; i++ ) slist[snum++].pt[dim] = basept; exxit: temp_free((char*)newelist); } static struct simplex *slist; static struct simplex *slist_edge; static int dim; static int count,scount; static REAL (*vcoord)[MAXCOORD]; /* temporary explicit refinements */ static struct simplex slist3[8] = { {{0,4,5,6}}, {{1,4,8,7}}, {{2,5,7,9}}, {{3,6,9,8}}, {{4,5,8,7}}, {{4,8,5,6}}, {{6,8,5,9}}, {{5,8,7,9}} }; static struct simplex slist2[4] = { {{0,3,4}},{{1,5,3}},{{2,4,5}},{{3,5,4}}}; static struct simplex slist1[2] = { {{0,2}}, {{2,1}}}; struct simplex *slistmake ARGS((int)); /*********************************************************************** * * function: refine_simplex_init() * * purpose: set up data structures needed in simplex refining. */ void refine_simplex_init( ) { dim = web.dimension; if ((dim < 1) || (dim > MAXCOORD) ) kb_error(1545,"Simplex dimension must be between 1 and MAXCOORD.\n",RECOVERABLE); switch ( web.dimension ) { case 4: slist = slistmake(dim); slist_edge = slist3; break; case 3: slist = slist3; slist_edge = slist2; return; case 2: slist = slist2; slist_edge = slist1; return; case 1: slist = slist1; slist_edge = slist1; return; default: slist = slistmake(dim); slist_edge = slistmake(dim-1); } } /*********************************************************************** * * function: slistmake() * * purpose: Make list of refined simplices of a simplex. */ struct simplex *slistmake(sdim) int sdim; { int vlist[MAXCOORD+1]; int i,j; struct divedge *elist; int numedges; int pnum,snum=0; struct simplex *sslist; vcoord = (REAL(*)[MAXCOORD])temp_calloc((MAXCOORD+1)*(MAXCOORD+2)/2*MAXCOORD, sizeof(REAL)); /* set up initial arrays */ /* outer vertices */ for ( i = 0 ; i <= sdim ; i++ ) { vlist[i] = i; if ( i > 0 ) vcoord[i][i-1] = 2.0; } /* edges */ numedges = (sdim*(sdim+1))/2; elist = (struct divedge *)temp_calloc(numedges,sizeof(struct divedge)); for ( i = 0, count = 0 ; i < sdim ; i++ ) for ( j = i+1 ; j <= sdim ; j++ ) { int pt,k; elist[count].endpt[0] = i; elist[count].endpt[1] = j; elist[count].divpt = pt = sdim+1 + count; count++; for ( k = 0 ; k < sdim ; k++ ) vcoord[pt][k] = (vcoord[i][k] + vcoord[j][k])/2.0; } scount = (1 << sdim); sslist = (struct simplex *)mycalloc(scount,sizeof(struct simplex)); /* do simplices on corners */ for ( i = 0 ; i <= sdim ; i++ ) { for ( j = 0, pnum = 0 ; j < numedges ; j++ ) if ( (elist[j].endpt[0]==vlist[i]) || (elist[j].endpt[1]==vlist[i])) sslist[snum].pt[pnum++] = elist[j].divpt; sslist[snum].pt[pnum] = vlist[i]; /* add common basepoint */ snum++; } /* do interior blob */ if ( sdim > 1 ) refine_simplex(sdim,vlist,elist,sslist+snum); check_orientation(sslist,sdim); temp_free((char*)vcoord); temp_free((char*)elist); return sslist; } /************************************************************************ * * function: check_orientation() * * purpose: check orientation of simplices in a subdivision. */ void check_orientation(sslist,sdim) struct simplex *sslist; int sdim; { int i,j,k; MAT2D(mat,MAXCOORD,MAXCOORD); for ( i = 0 ; i < scount ; i++ ) { /* load matrix with side vectors of simplex */ for ( j = 0 ; j < sdim ; j++ ) for ( k = 0 ; k < sdim ; k++ ) mat[j][k] = vcoord[sslist[i].pt[j+1]][k] - vcoord[sslist[i].pt[0]][k]; /* test and exchange if necessary */ if ( determinant(mat,sdim) < 0.0 ) { j = sslist[i].pt[0]; sslist[i].pt[0] = sslist[i].pt[1]; sslist[i].pt[1] = j; } } } /* Now come the routines for refining all the simplices. Idea is to go through simplices refining and keep subdivided edges in hash table to prevent duplication */ /* hashtable stuff */ struct entry { vertex_id endpt[2], divpt; }; static struct entry *simhashtable; static int maxload; /* maximum entries before expanding */ static int hashentries; /* current number of entries */ static int hashmask; /* for getting applicable bits of hash key */ static int tablesize; /* current table size, power of 2 */ #define hash_1(id) (((id)*701)>>7) /*#define hash_2(id) (((id)*1003)>>+7) */ #define hash_2(id) 1 void init_hash_table() { hashentries = 0; tablesize = 64; hashmask = tablesize-1; maxload = 48; while ( maxload < web.skel[EDGE].count ) { hashmask += tablesize; tablesize *= 2; maxload *= 2; } simhashtable = (struct entry *)mycalloc(tablesize,sizeof(struct entry)); } void rehash() { struct entry *oldhashtable = simhashtable; int i,j; struct entry *h; unsigned int hash1,hash2,hash; int recount=0; simhashtable = (struct entry *)mycalloc(2*tablesize,sizeof(struct entry)); /* rehash all entries */ hashmask += tablesize; for ( i = 0, h = oldhashtable ; i < tablesize ; i++,h++ ) { if ( !h->divpt ) continue; hash1 = (unsigned int)(hash_1(h->endpt[0]) + hash_1(h->endpt[1])); hash2 = (unsigned int)(hash_2(h->endpt[0]) + hash_2(h->endpt[1])); hash = hash1 % hashmask; for ( j = 0 ; j < hashentries ; j++ ) { if ( !simhashtable[hash].divpt ) break; /* empty spot */ hash += hash2; hash %= hashmask; } if ( simhashtable[hash].divpt ) kb_error(1546,"Fatal hash conflict in rehash.\n",UNRECOVERABLE); simhashtable[hash] = *h; /* move in entry */ recount++; } if ( recount != hashentries ) { end_hash_table(); kb_error(1547,"Hash table expansion error.\n",RECOVERABLE); } tablesize <<= 1; /* REAL tablesize */ maxload <<= 1; myfree((char*)oldhashtable); } void end_hash_table() { myfree((char*)simhashtable); simhashtable = NULL; } vertex_id simplex_edge_divide(v1,v2) vertex_id v1,v2; { vertex_id newv; REAL *x1,*x2; REAL newx[MAXCOORD]; unsigned int hash1,hash2,hash; int i; conmap_t conmap[MAXCONPER]; /* get vertices in canonical order */ if ( v1 > v2 ) { vertex_id temp = v1; v1 = v2; v2 = temp;} /* look up in hash table */ hash1 = (unsigned int)(hash_1(v1) + hash_1(v2)); hash2 = (unsigned int)(hash_2(v1) + hash_2(v2)); hash = hash1 % hashmask; for ( i = 0 ; i <= hashentries ; i++ ) { if ( !simhashtable[hash].divpt ) break; /* missing */ if ( (simhashtable[hash].endpt[0]==v1) && (simhashtable[hash].endpt[1]==v2) ) return simhashtable[hash].divpt; /* found! */ hash += hash2; hash %= hashmask; } if ( simhashtable[hash].divpt ) kb_error(1548,"Fatal hash conflict.\n",UNRECOVERABLE); /* need to insert new edge and dividing point */ simhashtable[hash].endpt[0] = v1; simhashtable[hash].endpt[1] = v2; x1 = get_coord(v1); x2 = get_coord(v2); for ( i = 0 ; i < SDIM ; i++ ) newx[i] = (x1[i] + x2[i])/2; simhashtable[hash].divpt = newv = new_vertex(newx,NULLID); set_attr(newv,get_vattr(v1)&get_vattr(v2)); get_v_common_conmap(v1,v2,conmap); set_v_conmap(newv,conmap); if ( (get_vattr(v1)&BOUNDARY) && (get_boundary(v1) == get_boundary(v2)) ) set_boundary_num(newv,get_boundary(v1)->num); hashentries++; /* see if simhashtable needs expanding */ if ( hashentries > maxload ) rehash(); return newv; } /*********************************************************************** * * function: refine_all_simplices() * * purpose: execute global refinement in simplex model. * * algorithm: For each simplex, sorts vertex in geometric order * (for consistency in refining neighboring simplices, and * for consistency in refining covering space), subdivides * each 1D edge (with hash list for previously created points), * and uses precomputed decompostion. */ static REAL sort_vector[10] = { 1.428573894832849893, 0.38278949278293994, -1.91078013284098892134, 0.6836515788935513, 0.23888983762994894, -0.8892745828781876439, 0.19926767848989298, 2.598989877489299135, 0.799299992976615}; void refine_all_simplices() { facet_id f_id; edge_id e_id; int i,j; vertex_id vlist[(MAXCOORD+1)*(MAXCOORD+2)/2]; /* full list of vertices */ REAL sortval[MAXCOORD+1]; int svcount; ATTR attr; int flip; /* inversion counter */ init_hash_table(); /* for subdivided edges */ FOR_ALL_FACETS(f_id) { vertex_id *v = get_facet_vertices(f_id); attr = get_fattr(f_id); if ( attr & NEWELEMENT ) continue; /* transfer old vertices to start of full list */ /* sorting in geometric order, for adjacent subdivision consistency */ for ( svcount = 0,flip=0 ; svcount <= web.dimension ; svcount++ ) { /* insertion sort */ REAL sortv = SDIM_dot(sort_vector,get_coord(v[svcount])) + 10*machine_eps * loc_ordinal(v[svcount]); for ( i = svcount ; i > 0 ; i-- ) if ( sortval[i-1] > sortv ) {vlist[i] = vlist[i-1]; sortval[i] = sortval[i-1]; flip++;} else break; vlist[i] = v[svcount]; sortval[i] = sortv; } /* go through edges, subdividing */ for ( i = 0 ; i < web.dimension ; i++ ) for ( j = i+1 ; j <= web.dimension ; j++ ) vlist[svcount++] = simplex_edge_divide(vlist[i],vlist[j]); /* construct new simplices using pre-computed decomposition */ /* copy relevant properties of old facet */ set_attr(f_id,NEWELEMENT); /* so don't repeat refinement */ for ( i = 0 ; i < (1<start ) if ( !eval(show_expr[FACET],NULL,f_id,NULL) ) continue; gdata[0].id = f_id; gdata[0].color = get_facet_color(f_id); gdata[0].backcolor = get_facet_backcolor(f_id); if ( slice_view_flag ) { /* do slices */ int ii; m = 0; for ( i = 0 ; i < web.dimension ; i++ ) { x = get_coord(v[i]); for ( ii = i+1 ; ii <= web.dimension ; ii++ ) { REAL denom=0.0; REAL numer=slice_coeff[SDIM]; REAL lambda; REAL *xx = get_coord(v[ii]); for ( j = 0 ; j < SDIM ; j++ ) { numer -= slice_coeff[j]*xx[j]; denom += slice_coeff[j]*(x[j] - xx[j]); } if ( denom == 0.0 ) continue; lambda = numer/denom; if ( (lambda < 0.0) || (lambda > 1.0) ) continue; for ( j = 0 ; j < SDIM ; j++ ) gdata[m].x[j] = lambda*x[j] + (1-lambda)*xx[j]; m++; } } for ( i = 0 ; i <= m-FACET_VERTS ; i++ ) { gdata[i].id = f_id; gdata[i].color = get_facet_color(f_id); gdata[i].backcolor = get_facet_backcolor(f_id); (*graph_facet)(gdata+i,NULLID); } } else /* do all combos of 3 vertices */ for ( i = 0 ; i <= web.dimension ; i++ ) { gdata[0].v_id = v[i]; /* for those who can use it */ x = get_coord(v[i]); for ( m = 0 ; m < SDIM ; m++ ) gdata[0].x[m] = (float)x[m]; for ( j = i+1 ; j <= web.dimension ; j++ ) { gdata[1].v_id = v[j]; /* for those who can use it */ x = get_coord(v[j]); for ( m = 0 ; m < SDIM ; m++ ) gdata[1].x[m] = (float)x[m]; if ( web.representation == STRING ) (*graph_edge)(gdata,NULLID); else for ( k = j+1 ; k <= web.dimension ; k++ ) { gdata[2].v_id = v[k]; /* for those who can use it */ x = get_coord(v[k]); for ( m = 0 ; m < SDIM ; m++ ) gdata[2].x[m] = (float)x[m]; (*graph_facet)(gdata,NULLID); } } } } } /****************************************************************** * * function: calc_simplex_edge_energy() * * purpose: find energy contribution of edge integrand * */ void calc_simplex_edge_energy(e_id) edge_id e_id; { struct constraint *constr; int i,j,k,m; REAL energy = 0.0; REAL side[MAXCOORD][MAXCOORD]; REAL *sides[MAXCOORD]; REAL green[MAXCONCOMP]; conmap_t *conmap; REAL midpt[MAXCOORD]; /* evaluation point for integrand */ int sign; REAL kvector[MAXCONCOMP]; /* k-vector representing simplex */ REAL *x[MAXCOORD]; vertex_id *v; conmap = get_e_constraint_map(e_id); if ( get_eattr(e_id) & NEGBOUNDARY ) sign = -1; else sign = 1; if ( inverted(e_id) ) sign = -sign; v = get_edge_vertices(e_id); x[0] = get_coord(v[0]); for ( k = 1 ; k < web.dimension ; k++ ) { x[k] = get_coord(v[k]); for ( j = 0 ; j < SDIM ; j++ ) side[k-1][j] = x[k][j] - x[0][j]; sides[k-1] = side[k-1]; /* pointers for matrix routines */ } exterior_product(sides,kvector,web.dimension-1,SDIM); for ( j = 1 ; j <= (int)conmap[0]; j++ ) { constr = get_constraint(conmap[j]&CONMASK); if ( !(constr->attr & CON_ENERGY) ) continue; for ( k = 0 ; k < 1 /* gauss1D_num */ ; k++ ) { for ( i = 0 ; i < SDIM ; i++ ) { midpt[i] = 0.0; for ( m = 0 ; m < web.dimension ; m++ ) midpt[i] += x[m][i]/web.dimension; } for ( i = 0 ; i < constr->compcount ; i++ ) green[i] = eval(constr->envect[i],midpt,NULLID,NULL); energy += sign*dot(kvector,green,constr->compcount); } } binary_tree_add(web.total_energy_addends, energy/web.simplex_factorial*web.dimension); } /****************************************************************** * * function: calc_simplex_edge_force() * * purpose: find force contribution of edge integrand * */ void calc_simplex_edge_force(e_id) edge_id e_id; { struct constraint *constr; int i,j,k,m; REAL *hforce,*tforce; REAL side[MAXCOORD][MAXCOORD]; REAL *sides[MAXCOORD]; REAL green[MAXCONCOMP]; REAL green_deriv[MAXCONCOMP][MAXCOORD]; conmap_t * conmap; REAL midpt[MAXCOORD]; /* evaluation point for integrand */ int sign; REAL kvector[MAXCONCOMP]; /* k-vector representing simplex */ REAL *x[MAXCOORD]; vertex_id *v; REAL fudge = web.simplex_factorial/web.dimension; REAL grad[MAXCOORD]; conmap = get_e_constraint_map(e_id); if ( get_eattr(e_id) & NEGBOUNDARY ) sign = -1; else sign = 1; if ( inverted(e_id) ) sign = -sign; v = get_edge_vertices(e_id); x[0] = get_coord(v[0]); for ( k = 1 ; k < web.dimension ; k++ ) { x[k] = get_coord(v[k]); for ( j = 0 ; j < SDIM ; j++ ) side[k-1][j] = x[k][j] - x[0][j]; sides[k-1] = side[k-1]; /* pointers for matrix routines */ } exterior_product(sides,kvector,web.dimension-1,SDIM); tforce = get_force(v[0]); for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { constr = get_constraint(conmap[j]&CONMASK); if ( !(constr->attr & CON_ENERGY) ) continue; for ( k = 0 ; k < 1 /* gauss1D_num */ ; k++ ) { for ( i = 0 ; i < SDIM ; i++ ) { midpt[i] = 0.0; for ( m = 0 ; m < web.dimension ; m++ ) midpt[i] += x[m][i]/web.dimension; } for ( i = 0 ; i < constr->compcount ; i++ ) eval_all(constr->envect[i],midpt,SDIM,&green[i], green_deriv[i],e_id); /* constraint value and derivs */ /* part due to motion of midpoint changing integrand */ for ( m = 0 ; m < SDIM ; m++ ) for ( i = 0 , grad[m] = 0.0 ; i < constr->compcount ; i++ ) grad[m] += kvector[i]*green_deriv[i][m]; for ( i = 0 ; i < web.dimension ; i++ ) { hforce = get_force(v[i]); for ( m = 0 ; m < SDIM ; m++ ) hforce[m] -= grad[m]/web.dimension/fudge; } /* part due to changing kvector */ for ( i = 0 ; i < web.dimension-1 ; i++ ) /* side by side */ { hforce = get_force(v[i+1]); for ( m = 0 ; m < SDIM ; m++ ) { REAL f; sides[i] = identmat[m]; exterior_product(sides,kvector,web.dimension-1,SDIM); f = dot(kvector,green,constr->compcount)/fudge; hforce[m] -= f; tforce[m] += f; } sides[i] = side[i]; } } } } /****************************************************************** * * function: simplex_edge_hessian() * * purpose: find hessian contribution of edge integrand * For linear integrands only. * */ void simplex_edge_hessian(S,e_id,first,second) struct linsys *S; edge_id e_id; REAL **first; /* first[vert][dim] */ REAL ****second; /* second[vert][vert][dim][dim] */ { struct constraint *constr; int i,j,k,m,n,p; REAL side[MAXCOORD][MAXCOORD]; REAL *sides[MAXCOORD]; REAL green[MAXCONCOMP]; REAL green_deriv_space[MAXCONCOMP][MAXCOORD]; REAL *green_deriv[MAXCONCOMP]; conmap_t * conmap; REAL midpt[MAXCOORD]; /* evaluation point for integrand */ int sign; REAL kvector[MAXCONCOMP]; /* k-vector representing simplex */ REAL *x[MAXCOORD]; vertex_id *v; REAL fudge = web.simplex_factorial/web.dimension; REAL grad[MAXCOORD]; for ( i = 0 ; i < MAXCONCOMP ; i++ ) green_deriv[i] = green_deriv_space[i]; conmap = get_e_constraint_map(e_id); if ( get_eattr(e_id) & NEGBOUNDARY ) sign = -1; else sign = 1; if ( inverted(e_id) ) sign = -sign; v = get_edge_vertices(e_id); x[0] = get_coord(v[0]); for ( k = 1 ; k < web.dimension ; k++ ) { x[k] = get_coord(v[k]); for ( j = 0 ; j < SDIM ; j++ ) side[k-1][j] = x[k][j] - x[0][j]; sides[k-1] = side[k-1]; /* pointers for matrix routines */ } exterior_product(sides,kvector,web.dimension-1,SDIM); for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { constr = get_constraint(conmap[j]&CONMASK); if ( !(constr->attr & CON_ENERGY) ) continue; for ( k = 0 ; k < 1 /* gauss1D_num */ ; k++ ) { for ( i = 0 ; i < SDIM ; i++ ) { midpt[i] = 0.0; for ( m = 0 ; m < web.dimension ; m++ ) midpt[i] += x[m][i]/web.dimension; } for ( i = 0 ; i < constr->compcount ; i++ ) eval_all(constr->envect[i],midpt,SDIM,&green[i], green_deriv[i],e_id); /* constraint value and derivs */ /* part due to motion of midpoint changing integrand */ vec_mat_mul(kvector,green_deriv,grad,constr->compcount,SDIM); for ( i = 0 ; i < web.dimension ; i++ ) { for ( m = 0 ; m < SDIM ; m++ ) first[i][m] += grad[m]/web.dimension/fudge; } /* part due to changing kvector */ for ( i = 0 ; i < web.dimension-1 ; i++ ) /* side by side */ { for ( m = 0 ; m < SDIM ; m++ ) { REAL f; sides[i] = identmat[m]; exterior_product(sides,kvector,web.dimension-1,SDIM); f = dot(kvector,green,constr->compcount)/fudge; first[i+1][m] += f; first[0][m] -= f; vec_mat_mul(kvector,green_deriv,grad,constr->compcount,SDIM); for ( n = 0 ; n < web.dimension - 1 ; n++ ) for ( p = 0 ; p < SDIM ; p++ ) { f = grad[p]/web.dimension/fudge; second[i+1][n+1][m][p] += f; second[i+1][0][m][p] += f; second[0][n+1][m][p] -= f; second[0][0][m][p] -= f; second[n+1][i+1][p][m] += f; second[n+1][0][p][m] -= f; second[0][i+1][p][m] += f; second[0][0][p][m] -= f; } } sides[i] = side[i]; } /* part due to changing kvector - 2 components */ for ( i = 0 ; i < web.dimension-1 ; i++ ) /* side by side */ for ( n = 0 ; n < web.dimension - 1 ; n++ ) { if ( i == n ) continue; for ( p = 0 ; p < SDIM ; p++ ) for ( m = 0 ; m < SDIM ; m++ ) { REAL f; sides[i] = identmat[m]; sides[n] = identmat[p]; exterior_product(sides,kvector,web.dimension-1,SDIM); f = dot(kvector,green,constr->compcount)/fudge; second[i+1][n+1][m][p] += f; second[0][n+1][m][p] -= f; second[i+1][0][m][p] -= f; second[0][0][m][p] += f; sides[i] = side[i]; sides[n] = side[n]; } } } } } /********************************************************************* * * function: simplex_long_edges() * * purpose: divide long 1-D edges in simplex model. * Method: Make list of all edges * Find long edge * Put in bisecting vertex * Find adjacent simplices * Divide adjacent simplices in two * * Returns number of edges divided */ /* working list of edges and adjacent facets */ #define MAXFACET 10 struct w_edge { vertex_id v[2]; /* endpoints */ int fcount; /* number of adjacent simplices */ facet_id f[MAXFACET]; /* adjacent simplices */ }; /* initial edge list for sorting */ struct s_edge { vertex_id v[2]; /* endpoints in native order */ facet_id f; /* facet with edge */ }; int se_comp ARGS((struct s_edge *,struct s_edge *)); int se_comp(a,b) /* comparison function for sorting edge list */ struct s_edge *a,*b; { if ( a->f < b->f ) return -1; if ( a->f > b->f ) return 1; return 0; } /********************************************************************** * * function: simplex_long_edges() * * purpose: refine all 1-edges greater than given length. */ #ifdef ANSI_DEF int simplex_long_edges(REAL max_len) #else int simplex_long_edges(max_len) REAL max_len; #endif { struct s_edge *se,*se_ptr; int se_count; /* number of edges in se */ struct w_edge *we,*we_ptr; int we_count; /* number of edges in we */ facet_id f_id; /* inverse index facet to edge list, indexed by facet ordinal */ struct w_edge **fedge; int fedge_max; /* number of fedge pointers allocated */ int edges_per = (web.dimension*(web.dimension+1))/2; int divcount = 0; /* number of edges divided */ int i,j,k,m,n; vertex_id v0,v1; struct w_edge **fl,**newfl; int top; /* make initial edge list */ se_count = web.skel[FACET].count*edges_per; se = (struct s_edge*)temp_calloc(se_count,sizeof(struct s_edge)); se_ptr = se; FOR_ALL_FACETS(f_id) { vertex_id *v = get_facet_vertices(f_id); for ( i = 0 ; i < web.dimension ; i++ ) /* tail vertex */ for ( j = i+1 ; j <= web.dimension ; j++ ) /* head vertex */ { se_ptr->f = f_id; if ( v[i] < v[j] ) { se_ptr->v[0] = v[i]; se_ptr->v[1] = v[j]; } else { se_ptr->v[0] = v[j]; se_ptr->v[1] = v[i]; } se_ptr++; } } /* sort list */ qsort((char*)se,se_count,sizeof(struct s_edge),FCAST se_comp); /* count edges and max facets per edge */ v0 = v1 = NULLVERTEX; /* track current edge */ for ( k = 0, se_ptr = se, we_count = top = 0 ; k < se_count ; k++, se_ptr++ ) { if ( (se_ptr->v[0] == v0) && (se_ptr->v[1] == v1) ) /* same as before */ top++; else /* new */ { if ( top > MAXFACET ) kb_error(1549,"Edge adjacent to too many simplices.\n",RECOVERABLE); v0 = se_ptr->v[0]; v1 =se_ptr->v[1]; we_count++; } } /* make working lists */ we = (struct w_edge*)temp_calloc(we_count,sizeof(struct w_edge)); fedge_max = web.skel[FACET].max_ord+100; fedge = (struct w_edge**)temp_calloc(fedge_max*edges_per, sizeof(struct w_edge *)); v0 = v1 = NULLVERTEX; /* track current edge */ for ( k = 0, se_ptr = se, we_ptr = we-1 ; k < se_count ; k++, se_ptr++ ) { if ( (se_ptr->v[0] != v0) || (se_ptr->v[1] != v1) ) /* new */ { we_ptr++; v0 = we_ptr->v[0] = se_ptr->v[0]; v1 = we_ptr->v[1] = se_ptr->v[1]; } we_ptr->f[we_ptr->fcount++] = se_ptr->f; fl=fedge+edges_per*loc_ordinal(se_ptr->f) ; while ( *fl ) fl++; *fl = we_ptr; /* inverse list */ } temp_free((char*)se); /* don't need anymore */ /* go through working list, chopping long edges */ for ( we_ptr = we, k = 0 ; k < we_count ; k++,we_ptr++ ) { REAL *x1, *x2; REAL newx[MAXCOORD]; REAL side[MAXCOORD]; conmap_t conmap[MAXCONPER]; vertex_id newv; facet_id newf; REAL len; v0 = we_ptr->v[0]; v1 = we_ptr->v[1]; x1 = get_coord(v0); x2 = get_coord(v1); for ( i = 0 ; i < SDIM ; i++ ) side[i] = x1[i] - x2[i]; len = sqrt(SDIM_dot(side,side)); if ( len < max_len ) continue; /* now have long edge */ /* put vertex in middle */ for ( i = 0 ; i < SDIM ; i++ ) newx[i] = (x1[i] + x2[i])/2; newv = new_vertex(newx,NULLID); set_attr(newv,get_vattr(v0)&get_vattr(v1)); get_v_common_conmap(v0,v1,conmap); for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { int c = conmap[j] & CONMASK; set_v_constraint_map(newv,c); if ( get_v_constraint_status(v0,c) && get_v_constraint_status(v1,c) ) set_v_constraint_status(newv,c); } if ( get_vattr(v0) & BOUNDARY ) if ( get_boundary(v0) == get_boundary(v1) ) set_boundary_num(newv,get_boundary(v0)->num); /* construct new simplices */ for ( n = 0 ; n < we_ptr->fcount ; n++ ) { vertex_id *v,*oldv; f_id = we_ptr->f[n]; /* copy relevant properties of old facet */ newf = dup_facet(f_id); v = get_facet_vertices(newf); oldv = get_facet_vertices(f_id); for ( j = 0 ; j <= web.dimension ; j++ ) { if ( equal_id(v0,oldv[j]) ) v[j] = newv; else v[j] = oldv[j]; if ( equal_id(v1,oldv[j]) ) oldv[j] = newv; } /* update working list */ if ( web.skel[FACET].max_ord >= fedge_max ) { fedge_max = web.skel[FACET].max_ord+100; fedge = (struct w_edge **)temp_realloc((char*)fedge, fedge_max*edges_per*sizeof(struct w_edge *)); } fl = fedge + loc_ordinal(f_id)*edges_per; newfl = fedge + loc_ordinal(newf)*edges_per; for ( m = 0 ; mv[0] == v0 ) continue; /* is ok */ if ( (*fl)->v[1] == v1 ) { /* replace f_id with newf */ for ( j = 0 ; j < (*fl)->fcount ; j++ ) if ( f_id == (*fl)->f[j] ) { (*fl)->f[j] = newf; break; } *(newfl++) = *fl; /* transfer edge to new facet */ *fl = NULL; } else /* add newf to edge */ { if ( (*fl)->fcount >= MAXFACET ) kb_error(1550,"Edge has too many simplices.\n",RECOVERABLE); (*fl)->f[(*fl)->fcount++] = newf; *(newfl++) = *fl; /* add edge to new facet */ } } } divcount++; } temp_free((char*)we); temp_free((char*)fedge); return divcount; } /********************************************************************** * * function: simplex_tiny_edges() * * purpose: delete all 1-edges shorter than given length. */ #ifdef ANSI_DEF int simplex_tiny_edges(REAL min_len) #else int simplex_tiny_edges(min_len) REAL min_len; #endif { int delcount = 0; /* number of edges divided */ int i,j,k; facet_id f_id; FOR_ALL_FACETS(f_id) { vertex_id *fv = get_facet_vertices(f_id); int del; /* check it really exists */ if ( !(get_fattr(f_id) & ALLOCATED) ) continue; /* pairs of edges */ del = 0; for ( i = 0 ; i <= web.dimension ; i++ ) { for ( j = i+1 ; j <= web.dimension ; j++ ) { REAL *x = get_coord(fv[i]); REAL *y = get_coord(fv[j]); REAL dist; for ( k = 0, dist = 0.0 ; k < SDIM ; k++ ) dist += (x[k]-y[k])*(x[k]-y[k]); dist = sqrt(dist); if ( dist >= min_len ) continue; /* now delete */ del = simplex_delete_edge(fv[i],fv[j]); delcount += del; if ( del ) break; } if ( del ) break; } } if ( delcount ) top_timestamp = ++global_timestamp; return delcount; } /*********************************************************************** * * function simplex_delete_edge() * * purpose: delete 1-edge between two given vertices. * Checks for consistent boundaries and constraints. * Does not move kept vertex. * Returns 0 or 1 edges deleted. */ int simplex_delete_edge(v1,v2) vertex_id v1,v2; { vertex_id keepv; /* vertex to keep */ vertex_id throwv; /* vertex to delete */ int attri,attrj; facet_id f_id,fstart,f_ids[100]; int k,m,n; /* check attributes */ keepv = NULLID; attri = get_vattr(v1); attrj = get_vattr(v2); if ( (attri & BOUNDARY) && (attrj & CONSTRAINT) ) return 0; if ( (attri & CONSTRAINT) && (attrj & BOUNDARY) ) return 0; if ( (attri & BOUNDARY) && (attrj & BOUNDARY) && (get_boundary(v1) != get_boundary(v2)) ) return 0; if ( attri & BOUNDARY ) keepv = v1; else if ( attri & BOUNDARY ) keepv = v2; if ( (attri & CONSTRAINT) && (attrj & CONSTRAINT) ) { conmap_t *conmapi = get_v_constraint_map(v1); conmap_t *conmapj = get_v_constraint_map(v2); int common,kk; for ( k = 1, common=0 ; k <= (int)conmapi[0] ; k++ ) for ( kk = 1 ; kk <= (int)conmapj[0] ; kk++ ) if ( (conmapi[k]&CONMASK) == (conmapj[kk]&CONMASK) ) common++; if ( conmapi[0] < conmapj[0] ) { if ( common < (int)conmapi[0] ) return 0; keepv = v2; } else { if ( common < (int)conmapj[0] ) return 0; keepv = v1; } } else if ( attri & CONSTRAINT ) keepv = v1; else if ( attrj & CONSTRAINT ) keepv = v2; if ( keepv == NULLID ) keepv = v1; throwv = (keepv==v1) ? v2 : v1; /* change vertex in facets around throwv */ /* and delete all facets around keepv that have throwv */ f_id = fstart = get_vertex_first_facet(throwv); for ( n = 0 ; n < 100 ; ) { f_ids[n++] = f_id; f_id = get_next_vertex_facet(throwv,f_id); if ( equal_id (f_id,fstart) ) break; } for ( m = 0 ; m < n ; m++ ) { vertex_id * fv = get_facet_vertices(f_ids[m]); for ( k = 0 ; k <= web.dimension ; k++ ) { if ( equal_id(fv[k],keepv) ) { free_element(f_ids[m]); break; } if ( equal_id(fv[k],throwv) ) fv[k] = keepv; } } free_element(throwv); return 1; } /*********************************************************************** * * function simplex_delete_facet() * * purpose: Deletes facet by deleting shortest 1-edge. */ int simplex_delete_facet(f_id) facet_id f_id; { int j,k,m,n; struct sid { vertex_id v1,v2; REAL length; } sids[MAXCOORD*MAXCOORD],*ss; int sscount; vertex_id *v; REAL sum; v = get_facet_vertices(f_id); for ( m = 0,ss=sids,sscount = 0 ; m < web.dimension ; m++ ) for ( n = m+1 ; m <= web.dimension ; m++,ss++,sscount++ ) { REAL *x1 = get_coord(v[m]); REAL *x2 = get_coord(v[n]); for ( k = 0, sum = 0.0 ; k < web.dimension ; k++ ) sum += (x1[k]-x2[k])*(x1[k]-x2[k]); for ( j = sscount ; j > 0 ; j-- ) if ( sids[j-1].length < sum ) break; else sids[j] = sids[j-1]; sids[j].length = sum; sids[j].v1 = v[m]; sids[j].v2 = v[n]; } for ( k = 0 ; k < sscount ; k++ ) if ( simplex_delete_edge(sids[k].v1,sids[k].v2) ) return 1; return 0; } /******************************************************************** * * function: simplex_hessian() * * purpose: Overall control for simplex hessian * */ void simplex_hessian(S,rhs) struct linsys *S; REAL *rhs; { REAL **first; REAL ****second; REAL **p1,**p2; int i,j,k; int head,tail; edge_id e_id; REAL g[MAXCOORD]; /* projected to constraint */ /* do facets */ simplex_facet_hessian(S,rhs); /* do edges */ first = dmatrix(0,web.dimension-1,0,SDIM-1); second = dmatrix4(web.dimension,web.dimension,SDIM,SDIM); p1 = dmatrix(0,MAXCOORD-1,0,MAXCOORD-1); p2 = dmatrix(0,MAXCOORD-1,0,MAXCOORD-1); FOR_ALL_EDGES(e_id) { struct hess_verlist *v[MAXCOORD]; vertex_id *v_id = get_edge_vertices(e_id); for ( i = 0 ; i < web.dimension ; i++ ) v[i] = get_vertex_vhead(v_id[i]); memset((char*)first[0],0,sizeof(REAL)*web.dimension*SDIM); memset((char*)second[0][0][0],0,sizeof(REAL)* web.dimension*web.dimension*SDIM*SDIM); simplex_edge_hessian(S,e_id,first,second); /* first derivatives on right hand side */ for ( i = 0 ; i < web.dimension ; i++ ) { if ( v[i]->proj ) { vec_mat_mul(first[i],v[i]->proj,g,SDIM,v[i]->freedom); for ( k = 0 ; k < v[i]->freedom ; k++ ) rhs[v[i]->rownum+k] -= g[k]; } else for ( k = 0 ; k < v[i]->freedom ; k++ ) rhs[v[i]->rownum+k] -= first[i][k]; } /* second derivatives */ for ( i = 0 ; i < web.dimension ; i++ ) for ( j = i ; j < web.dimension ; j++ ) { if ( (v[i]->freedom==0) || (v[j]->freedom==0) ) continue; if ( loc_ordinal(v_id[i]) > loc_ordinal(v_id[j]) ) { tail = i; head = j; } else { tail = j; head = i; } fill_mixed_entry(S,v_id[tail],v_id[head],second[tail][head]); } } if ( first ) free_matrix(first); if ( second ) free_matrix4(second); if ( p1 ) free_matrix(p1); if ( p2 ) free_matrix(p2); } /******************************************************************** * * function: simplex_facet_hessian() * * purpose: fill in hessian matrix with area derivatives * */ void simplex_facet_hessian(S,rhs) struct linsys *S; REAL *rhs; { int i,j,k; int m; facet_id f_id; REAL **hess,**p1,**p2; REAL **A; /* dots of sides matrix, and later inverse */ REAL **AS; /* sides x A inverse */ REAL **SAS; /* sides x A inverse x sides */ REAL **coord; /* vertex cooordinates */ REAL Q; /* determinant of A*/ REAL coeff; /* common factor */ REAL **side; /* sides of facet cone, from origin of homogenous */ int head,tail; /* fill in sparse matrix rows and volume constraint rows */ p1 = dmatrix(0,MAXCOORD-1,0,MAXCOORD-1); p2 = dmatrix(0,MAXCOORD-1,0,MAXCOORD-1); hess = dmatrix(0,MAXCOORD-1,0,MAXCOORD-1); A = dmatrix(0,MAXCOORD,0,MAXCOORD); AS = dmatrix(0,MAXCOORD-1,0,MAXCOORD-1); SAS = dmatrix(0,MAXCOORD-1,0,MAXCOORD-1); coord = dmatrix(0,MAXCOORD,0,MAXCOORD); side = dmatrix(0,MAXCOORD,0,MAXCOORD); FOR_ALL_FACETS(f_id) { struct hess_verlist *v[MAXCOORD+1]; vertex_id *v_id = get_facet_vertices(f_id); get_facet_verts(f_id,coord,NULL); for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) side[i][j] = coord[i][j] - coord[web.dimension][j]; /* matrix of inner products */ for ( i = 0 ; i < web.dimension ; i++ ) for ( j = i ; j < web.dimension ; j++ ) A[i][j] = A[j][i] = SDIM_dot(side[i],side[j]); Q = det_adjoint(A,web.dimension); if ( Q == 0.0 ) continue; for ( i = 0 ; i < web.dimension ; i++ ) for ( j = i ; j < web.dimension ; j++ ) A[i][j] = A[j][i] /= Q; /* make adjoint into inverse */ /* now make rows and columns of expanded A add to 0 for last vertex */ A[web.dimension][web.dimension] = 0.0; for ( i = 0 ; i < web.dimension ; i++ ) { REAL sum; for ( j = 0, sum = 0.; j < web.dimension ; j++ ) sum += A[i][j]; A[i][web.dimension] = A[web.dimension][i] = -sum; A[web.dimension][web.dimension] += sum; } mat_mult(A,side,AS,web.dimension+1,web.dimension,SDIM); tr_mat_mul(side,AS,SAS,web.dimension,SDIM,SDIM); coeff = get_facet_density(v_id[i])*sqrt(Q)/web.simplex_factorial; for ( i = 0 ; i <= web.dimension ; i++ ) v[i] = get_vertex_vhead(v_id[i]); /* first derivatives of area */ for ( i = 0 ; i <= web.dimension ; i++ ) { REAL g[MAXCOORD]; /* projected to constraint */ if ( v[i]->freedom == 0 ) continue; if ( v[i]->proj ) { vec_mat_mul(AS[i],v[i]->proj,g,SDIM,v[i]->freedom); for ( k = 0 ; k < v[i]->freedom ; k++ ) rhs[v[i]->rownum+k] -= coeff*g[k]; } else for ( k = 0 ; k < v[i]->freedom ; k++ ) rhs[v[i]->rownum+k] -= coeff*AS[i][k]; } /* second derivatives */ for ( i = 0 ; i <= web.dimension ; i++ ) for ( j = i ; j <= web.dimension ; j++ ) { if ( (v[i]->freedom==0) || (v[j]->freedom==0) ) continue; if ( loc_ordinal(v_id[i]) > loc_ordinal(v_id[j]) ) { tail = i; head = j; } else { tail = j; head = i; } for ( m = 0 ; m < SDIM ; m++ ) for ( k = 0 ; k < SDIM ; k++ ) hess[m][k] = coeff*((m==k?A[tail][head]:0.0) - AS[tail][k]* AS[head][m] - SAS[m][k]*A[tail][head] + AS[tail][m]*AS[head][k]); fill_mixed_entry(S,v_id[tail],v_id[head],hess); } } if ( hess ) free_matrix(hess); if ( p1 ) free_matrix(p1); if ( p2 ) free_matrix(p2); if ( A ) free_matrix(A); if ( AS ) free_matrix(AS); if ( SAS ) free_matrix(SAS); if ( coord ) free_matrix(coord); if ( side ) free_matrix(side); } /* end simplex_hessian() */ /********************************************************************* simplex_vector_integral method Integral of vectorfield over facet. nD facet in (n+1)D only. *********************************************************************/ REAL simplex_vector_integral_all ARGS((struct qinfo*,int)); /********************************************************************* * * function: simplex_vector_integral_init() * * purpose: Check illegalities * */ void simplex_vector_integral_init(mode,mi) int mode; struct method_instance *mi; { if ( web.dimension != SDIM-1 ) kb_error(1551,"simplex_vector_integral method only for N-1 D facets in N D.\n", RECOVERABLE); if ( web.modeltype != LINEAR ) kb_error(1552,"simplex_vector_integral method only for LINEAR model.\n", RECOVERABLE); } /********************************************************************* * * function: simplex_vector_integral() * * purpose: method value * */ REAL simplex_vector_integral(f_info) struct qinfo *f_info; { int i,m,j; REAL value=0.0; MAT2D(mat,MAXCOORD,MAXCOORD); REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; for ( m = 0 ; m < gauss2D_num ; m++ ) { for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) mat[i][j] = f_info->sides[0][i][j]; for ( j = 0 ; j < SDIM ; j++ ) mat[web.dimension][j] = eval(METH_INSTANCE(f_info->method)->expr[j],f_info->gauss_pt[m],NULLID,NULL); value += gauss2Dwt[m]*det_adjoint(mat,SDIM); } return sign*value/web.simplex_factorial; } /********************************************************************* * * function: simplex_vector_integral_grad() * * purpose: method gradient * */ REAL simplex_vector_integral_grad(f_info) struct qinfo *f_info; { int i,m,j,k,jj; REAL value = 0.0; REAL val[MAXCOORD]; REAL derivs[MAXCOORD][MAXCOORD]; MAT2D(mat,MAXCOORD,MAXCOORD); REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; for ( m = 0 ; m < gauss2D_num ; m++ ) { REAL weight = sign*gauss2Dwt[m]; for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) mat[i][j] = f_info->sides[0][i][j]; /* mat destroyed by det */ for ( j = 0 ; j < SDIM ; j++ ) { eval_all(METH_INSTANCE(f_info->method)->expr[j],f_info->gauss_pt[m],SDIM,val+j, derivs[j],f_info->id); mat[web.dimension][j] = val[j]; } value += weight*det_adjoint(mat,SDIM); for ( k = 0 ; k < ctrl_num-1; k++ ) for ( j = 0 ; j < SDIM ; j++ ) { f_info->grad[k+1][j] += weight*mat[j][k]; f_info->grad[0][j] -= weight*mat[j][k]; } for ( k = 0 ; k < ctrl_num ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) { f_info->grad[k][j] += weight *gpoly[m][k]*derivs[jj][j]*mat[jj][web.dimension]; } } for ( j = 0 ; j < SDIM ; j++ ) for ( k = 0 ; k < f_info->vcount ; k++ ) f_info->grad[k][j] /= web.simplex_factorial; return value/web.simplex_factorial; } /********************************************************************* * * function: simplex_vector_integral_hess() * * purpose: method hessian * */ REAL simplex_vector_integral_hess(f_info) struct qinfo *f_info; { return simplex_vector_integral_all(f_info,METHOD_HESSIAN); } REAL simplex_vector_integral_all(f_info,mode) struct qinfo *f_info; int mode; { int i,m,j,k,jj,ii,kk; REAL sum,value = 0.0; REAL val[MAXCOORD]; REAL derivs[MAXCOORD][MAXCOORD]; MAT3D(seconds,MAXCOORD,MAXCOORD,MAXCOORD); REAL ****ada; REAL **tr; MAT2D(mat,MAXCOORD,MAXCOORD); REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; struct gauss_lag *gl = (web.modeltype == LAGRANGE) ? &gauss_lagrange[web.dimension][web.gauss2D_order] : NULL; int cpts = (web.modeltype == LAGRANGE) ? gl->lagpts : ctrl_num; REAL **gp = (web.modeltype == LAGRANGE) ? gl->gpoly : gpoly; ada = dmatrix4(cpts,SDIM,SDIM,SDIM); tr = dmatrix(0,cpts,0,SDIM); for ( m = 0 ; m < gauss2D_num ; m++ ) { REAL det; REAL weight = sign*gauss2Dwt[m]/web.simplex_factorial; for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) mat[i][j] = f_info->sides[0][i][j]; /* mat destroyed by det */ for ( j = 0 ; j < SDIM ; j++ ) { eval_second(METH_INSTANCE(f_info->method)->expr[j],f_info->gauss_pt[m], SDIM,val+j, derivs[j],seconds[j],f_info->id); mat[web.dimension][j] = val[j]; } det = det_adjoint(mat,SDIM); value += weight*det; if ( mode == METHOD_VALUE ) continue; for ( k = 0 ; k < cpts ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) { for ( i = 0 ; i < SDIM ; i++ ) { for ( jj = 0; jj < SDIM ; jj++ ) ada[k][j][i][jj] = gp[m][k]*mat[i][web.dimension]*derivs[jj][j]; } } for ( k = 1 ; k < cpts ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) { for ( i = 0 ; i < SDIM ; i++ ) { ada[k][j][i][j] += mat[i][k-1]; ada[0][j][i][j] -= mat[i][k-1]; } } for ( k = 0 ; k < cpts ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) { for ( i = 0,sum = 0.0 ; i < SDIM ; i++) sum += ada[k][j][i][i]; tr[k][j] = sum; } /* gradient */ for ( k = 0 ; k < cpts; k++ ) for ( j = 0 ; j < SDIM ; j++ ) { f_info->grad[k][j] += weight*tr[k][j]; } if ( mode == METHOD_GRADIENT ) continue; /* hessian */ for ( k = 0 ; k < cpts ; k++ ) for ( kk = 0 ; kk < cpts ; kk++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) { REAL h; h = tr[k][j]*tr[kk][jj]/det; for ( i = 0 ; i < SDIM ; i++ ) h += gp[m][k]*gp[m][kk]*seconds[i][j][jj]*mat[i][web.dimension]; for ( i = 0 ; i < SDIM ; i++ ) for ( ii = 0 ; ii < SDIM ; ii++ ) h -= ada[k][j][i][ii]*ada[kk][jj][ii][i]/det; f_info->hess[k][kk][j][jj] += weight*h; } } free_matrix(tr); free_matrix4(ada); return value; } /********************************************************************* simplex_k_vector_integral method Integral of (n-k)vectorfield over k-facet. Will do edge equally well. *********************************************************************/ REAL simplex_k_vector_integral_all ARGS((struct qinfo*,int)); /********************************************************************* * * function: simplex_k_vector_integral_init() * * purpose: Check illegalities * */ void simplex_k_vector_integral_init(mode,mi) int mode; struct method_instance *mi; { if ( web.modeltype == QUADRATIC ) kb_error(1553,"simplex_k_vector_integral method not for QUADRATIC model.\n", RECOVERABLE); } /********************************************************************* * * function: simplex_k_vector_integral() * * purpose: method value * */ REAL simplex_k_vector_integral(f_info) struct qinfo *f_info; { int i,m,j,k; REAL value=0.0; MAT2D(mat,MAXCOORD,MAXCOORD); REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; int order = METH_INSTANCE(f_info->method)->vec_order; if ( web.modeltype == LAGRANGE ) return lagrange_k_vector_integral(f_info); for ( m = 0 ; m < gauss2D_num ; m++ ) { for ( i = 0 ; i < order ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) mat[i][j] = f_info->sides[0][i][j]; for ( k = 0 ; k+order < SDIM ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) mat[k+order][j] = eval(METH_INSTANCE(f_info->method)->expr[j+k*SDIM], f_info->gauss_pt[m],NULLID,NULL); value += gauss2Dwt[m]*det_adjoint(mat,SDIM); } return sign*value/web.simplex_factorial; } /********************************************************************* * * function: simplex_k_vector_integral_grad() * * purpose: method gradient * */ REAL simplex_k_vector_integral_grad(f_info) struct qinfo *f_info; { int i,m,j,k,jj; REAL value = 0.0; REAL val[MAXCOORD]; REAL derivs[MAXCOORD][MAXCOORD][MAXCOORD]; MAT2D(mat,MAXCOORD,MAXCOORD); REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; int order = METH_INSTANCE(f_info->method)->vec_order; if ( web.modeltype == LAGRANGE ) return lagrange_k_vector_integral_grad(f_info); for ( m = 0 ; m < gauss2D_num ; m++ ) { REAL weight = sign*gauss2Dwt[m]; for ( i = 0 ; i < order ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) mat[i][j] = f_info->sides[0][i][j]; /* mat destroyed by det */ for ( k = 0 ; k+order < SDIM ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) { eval_all(METH_INSTANCE(f_info->method)->expr[j+k*SDIM], f_info->gauss_pt[m],SDIM,val+j,derivs[k][j],f_info->id); mat[k+order][j] = val[j]; } value += weight*det_adjoint(mat,SDIM); for ( k = 0 ; k < ctrl_num-1; k++ ) for ( j = 0 ; j < SDIM ; j++ ) { f_info->grad[k+1][j] += weight*mat[j][k]; f_info->grad[0][j] -= weight*mat[j][k]; } for ( k = 0 ; k < ctrl_num ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) for ( i = 0 ; i+order < SDIM ; i++ ) { f_info->grad[k][j] += weight *gpoly[m][k]*derivs[i][jj][j]*mat[jj][i+order]; } } for ( j = 0 ; j < SDIM ; j++ ) for ( k = 0 ; k < f_info->vcount ; k++ ) f_info->grad[k][j] /= web.simplex_factorial; return value/web.simplex_factorial; } /********************************************************************* * * function: simplex_k_vector_integral_hess() * * purpose: method hessian and gradient * */ REAL simplex_k_vector_integral_hess(f_info) struct qinfo *f_info; { if ( web.modeltype != LAGRANGE ) kb_error(1554,"simplex_k_vector_integral hess only for LAGRANGE.\n", RECOVERABLE); return lagrange_k_vector_integral_hess(f_info); } /*********************************************************************** * * function: simplex_vertex_normal() * * purpose: calculate normal to surface at vertex. * Current version assumes no singular points, * and just averages all adjacent facet normals * to get volume gradient. * * Note: Kludge on TRIPLE_PT vertices returns normal to constraint * if one, for wetcone stuff. * * Output: normal basis * Return value: dimension of normal space */ int simplex_vertex_normal(v_id,norm) vertex_id v_id; REAL **norm; /* for return */ { facet_id f_id,startf; MAT2D(sides,MAXCOORD,MAXCOORD); int i,j; if ( SDIM > web.dimension+1 ) { kb_error(2182,"Can do hessian_normal only for hypersurfaces in simplex model.\n",RECOVERABLE ); } if ( get_vattr(v_id) & TRIPLE_PT ) { REAL xx[MAXCOORD],val; int at = find_attribute(VERTEX,"mirror"); int connum; if ( at < 0 ) return 0; connum = *(int*)get_extra(v_id,at); for ( i = 0 ; i < SDIM ; i++ ) xx[i] = 0.0; eval_all(get_constraint(connum)->formula,xx,SDIM,&val,norm[0],v_id); } else /* regular point */ { for ( i = 0 ; i < SDIM ; i++ ) norm[0][i] = 0.0; startf = f_id = get_vertex_first_facet(v_id); do { get_facet_verts(f_id,sides,NULL); for ( i = 1 ; i <= web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) sides[i][j] -= sides[0][j]; det_adjoint(sides,SDIM); for ( i = 0 ; i < SDIM ; i++ ) norm[0][i] += sides[i][0]; f_id = get_next_vertex_facet(v_id,f_id); } while ( !equal_element(f_id,startf) ); } /* end regular point */ return 1; } evolver-2.30c.dfsg/src/dump.c0000644000175300017530000022413711410765113016337 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************* * * file: dump.c * * Contents: Functions for making ASCII dump file of surface. */ char *color_names[] = { "clear", "black", "blue", "green", "cyan", "red", "magenta", "brown", "lightgray", "darkgray", "lightblue", "lightgreen", "lightcyan", "lightred", "lightmagenta", "yellow", "white", }; #define COLORNAME(n) (((n)<-1)||((n)>WHITE)?"black":color_names[(n)+1]) #include "include.h" #include "lex.h" #include "ytab.h" void list_forwards ARGS((void)); /******************************************************** * * Function: dump() * * Purpose: Dumps configuration to ASCII file in format * suitable for read-in. This is just the * interactive part, which calls do_dump(). * */ void dump() { char name[200]; char defaultname[200]; /* construct default name */ strncpy(defaultname,datafilename,sizeof(defaultname)-5); strcat(defaultname,".dmp"); sprintf(msg,"Enter name of dump file (%s): ",defaultname); prompt(msg,name,sizeof(name)); if ( name[0] == 0 ) do_dump(defaultname); else do_dump(name); } /**************************************************************************** * * function: print_data_value() * * purpose: print string representation of datatype value. */ void print_data_value(dest,type,src) char *dest; int type; void *src; { switch ( type ) { case REAL_TYPE: sprintf(dest, #ifdef LONGDOUBLE "%2.*Lg",DPREC,*(REAL *)src); #else "%2.15g",*(REAL *)src); #endif break; case INTEGER_TYPE: sprintf(dest,"%d",*(int*)src); break; case UINT_TYPE: sprintf(dest,"%u",*(unsigned int*)src); break; case CHAR_TYPE: sprintf(dest,"%d",*(char*)src); break; case UCHAR_TYPE: sprintf(dest,"%u",*(unsigned char*)src); break; case SHORT_TYPE: sprintf(dest,"%d",*(short int*)src); break; case USHORT_TYPE: sprintf(dest,"%u",*(unsigned short int*)src); break; case LONG_TYPE: sprintf(dest,"%ld",*(long int*)src); break; case ULONG_TYPE: sprintf(dest,"%lu",*(unsigned long int*)src); break; case PTR_TYPE: sprintf(dest,"%p",*(char**)src); break; case VERTEX_TYPE: sprintf(dest,"vertex[%s]",ELNAME(*(element_id*)src)); break; case EDGE_TYPE: sprintf(dest,"edge[%s]",ELNAME(*(element_id*)src)); break; case FACET_TYPE: sprintf(dest,"facet[%s]",ELNAME(*(element_id*)src)); break; case BODY_TYPE: sprintf(dest,"body[%s]",ELNAME(*(element_id*)src)); break; case FACETEDGE_TYPE: sprintf(dest,"facetedge[%s]",ELNAME(*(element_id*)src)); break; case BOUNDARY_TYPE: sprintf(dest,"%s",web.boundaries[*(int*)src].name); break; case CONSTRAINT_TYPE: sprintf(dest,"%s",get_constraint(*(int*)src)->name); break; case QUANTITY_TYPE: sprintf(dest,"%s",GEN_QUANT(*(int*)src)->name); break; case INSTANCE_TYPE: sprintf(dest,"%s",METH_INSTANCE(*(int*)src)->name); break; case PROCEDURE_TYPE: sprintf(dest,"%s",globals(*(int*)src)->name); break; default: if ( type >= 0 && type < NUMDATATYPES ) sprintf(errmsg,"Printing of type \"%s\" not implemented.\n", datatype_name[type]); else sprintf(errmsg,"Illegal datatype %d\n",type); kb_error(3667,errmsg,RECOVERABLE); } } /*********************************************************************** * * function: print_array() * * purpose: Print array elements in bracket-delimited form. Used in * top_dump and print array. */ void print_array(a,datastart) struct array *a; void *datastart; /* if not in struct array */ { int k; int spots[100]; int depth; char * spot; spot = datastart ? datastart : ((char*)a + a->datastart); if ( a->dim == 0 ) { print_data_value(msg+strlen(msg),a->datatype,spot); strcat(msg,"\n"); outstring(msg); return; } outstring("{"); depth = 1; spots[depth] = 0; for (;;) { msg[0] = 0; if ( spots[depth] < a->sizes[depth-1] && depth < a->dim ) { outstring("{"); depth++; spots[depth] = 0; continue; } if ( depth == a->dim ) { for ( k = 0 ; k < a->sizes[depth-1] ; k++ ) { print_data_value(msg+strlen(msg),a->datatype,spot); spot += a->itemsize; if ( k < a->sizes[depth-1] - 1 ) strcat(msg,","); if ( (strlen(msg) > 60) && (k < a->sizes[depth-1] - 1) ) { outstring(msg); strcpy(msg,"\n "); } } outstring(msg); } outstring("}"); depth--; if ( depth > 0 && (spots[depth]++ < a->sizes[depth-1]-1) ) { int i; outstring(",\n"); for ( i = 0 ; i < depth ; i++ ) outstring(" "); } if ( depth == 0 ) break; if ( breakflag ) break; /* in case of runaway print, user interrupt */ } outstring("\n"); } /* end print_array */ /*********************************************************************** * * function: print_array_atttribute() * * purpose: Print attribute array elements in bracket-delimited form. Used in * element dump. */ void print_array_attribute(ex,datastart) struct extra *ex; void *datastart; { int k; int spots[100]; int depth; char * spot = datastart; size_t linelength; if ( ex->array_spec.dim == 0 ) /* scalar */ { print_data_value(msg,ex->type,spot); outstring(msg); return; } outstring("{"); depth = 1; spots[depth] = 0; linelength = 0; for (;;) { msg[0] = 0; if ( (spots[depth] < ex->array_spec.sizes[depth-1]) && (depth < ex->array_spec.dim) ) { outstring("{"); depth++; spots[depth] = 0; continue; } if ( depth == ex->array_spec.dim ) { for ( k = 0 ; k < ex->array_spec.sizes[depth-1] ; k++ ) { print_data_value(msg+strlen(msg),ex->type,spot); spot += ex->array_spec.itemsize; if ( k < ex->array_spec.sizes[depth-1] - 1 ) { strcat(msg,","); if ( linelength+strlen(msg) > 70 ) { outstring(msg); outstring("\n "); linelength = 0; msg[0] = 0; } } } outstring(msg); linelength += strlen(msg); } outstring("}"); depth--; if ( depth > 0 && (spots[depth]++ < ex->array_spec.sizes[depth-1]-1) ) { outstring(","); if ( linelength > 70 ) { outstring("\n "); linelength = 0; } } if ( depth == 0 ) break; } outstring(" "); } /* end print_array_attribute() */ /************************************************************************ * * function: top_dump() * * purpose: Write out top part of datafile. */ void top_dump(fd) FILE *fd; /* destination file */ { int i,j,k; struct gen_quant *q; struct method_instance *mi; FILE *old_fd = outfd; int e_type; outfd = fd; sprintf(msg,"// datafilename: %s\n",datafilename); outstring(msg); if ( needed_version[0] ) { sprintf(msg,"\nEvolver_version \"%s\" // minimal version needed\n\n", needed_version); outstring(msg); } if ( warnings_suppressed_count ) outstring("\n");for ( i = 0 ; i < warnings_suppressed_count ; i++ ) { sprintf(msg,"suppress_warning %d\n",warnings_suppressed[i]); outstring(msg); } if ( i ) outstring("\n"); if ( keep_macros_flag ) { outstring("keep_macros\n"); dump_macros(); } if ( volume_method_name[0] ) { sprintf(msg,"volume_method_name \"%s\"\n",volume_method_name); outstring(msg); } if ( length_method_name[0] ) { sprintf(msg,"length_method_name \"%s\"\n",length_method_name); outstring(msg); } if ( area_method_name[0] ) { sprintf(msg,"area_method_name \"%s\"\n",area_method_name); outstring(msg); } #ifdef LONGDOUBLE sprintf(msg,"// Total energy: %2.*Lg\n",DPREC,web.total_energy); #else sprintf(msg,"// Total energy: %2.15g\n",web.total_energy); #endif outstring(msg); /* dynamic libraries */ for ( i = 0 ; i < MAX_DLL ; i++ ) if ( dll_list[i].name ) { sprintf(msg,"LOAD_LIBRARY \"%s\"\n",dll_list[i].name); outstring(msg); } /* model info dump */ if ( web.representation == SIMPLEX ) outstring("SIMPLEX_REPRESENTATION\n"); if ( SDIM != 3 ) { sprintf(msg,"SPACE_DIMENSION %d\n",SDIM); outstring(msg); } if ( web.representation == STRING ) outstring("STRING\n\n"); else if ( web.representation == SOAPFILM ) outstring("SOAPFILM\n\n"); else { sprintf(msg,"SURFACE_DIMENSION %d\n\n",web.dimension); outstring(msg); } if ( web.modeltype == LINEAR ) outstring("LINEAR\n\n"); else if ( web.modeltype == LAGRANGE ) { sprintf(msg, "LAGRANGE\nlagrange_order %d\n\n",web.lagrange_order); outstring(msg); } else outstring("QUADRATIC\n\n"); if ( web.symmetry_flag && !web.torus_flag ) { sprintf(msg,"SYMMETRY_GROUP \"%s\"\n\n",symmetry_name); outstring(msg); } if ( web.motion_flag ) /* do scale before parameters, since scale is parameter attribute */ { sprintf(msg,"SCALE: %2.15g FIXED\n\n",(DOUBLE)web.scale); outstring(msg); } else if ( web.scale != 0.1 ) { sprintf(msg,"SCALE: %2.15g\n\n",(DOUBLE)web.scale); outstring(msg); } /* global variables */ for ( i = 0 ; i < web.global_count ; i++ ) { if ( square_curvature_flag && (i == (square_curvature_param&GLOBMASK)) ) continue; if ( mean_curv_int_flag && (i == (mean_curvature_param&GLOBMASK)) ) continue; if ( sqgauss_flag && (i == (sqgauss_param|GLOBMASK)) ) continue; if ( globals(i)->flags & GLOB_LOCALVAR ) continue; if ( globals(i)->flags & FILE_VALUES ) { sprintf(msg,"PARAMETER %s ",globals(i)->name); outstring(msg); if ( reflevel == 0 ) sprintf(msg,"PARAMETER_FILE \"%s\" \n", globals(i)->value.file.value_file); else sprintf(msg,"PARAMETER_FILE \"%s\" \n","not dumped"); outstring(msg); } else if ( (globals(i)->flags & ORDINARY_PARAM) && !(globals(i)->flags & (INTERNAL_NAME|QUANTITY_TYPES)) ) { if ( globals(i)->flags & OPTIMIZING_PARAMETER ) sprintf(msg,"OPTIMIZING_PARAMETER %s ",globals(i)->name); else sprintf(msg,"PARAMETER %s ",globals(i)->name); outstring(msg); #ifdef LONGDOUBLE sprintf(msg,"= %2.*Lg ",DPREC,globals(i)->value.real); outstring(msg); #else sprintf(msg,"= %2.15g ",globals(i)->value.real); outstring(msg); #endif if ( globals(i)->attr.varstuff.delta != OPTPARAM_DELTA && globals(i)->attr.varstuff.delta != 0.0 ) { #ifdef LONGDOUBLE sprintf(msg,"pdelta = %2.*Lg ",DPREC,globals(i)->attr.varstuff.delta); outstring(msg); #else sprintf(msg,"pdelta = %2.15g ",globals(i)->attr.varstuff.delta); outstring(msg); #endif } if ( globals(i)->attr.varstuff.pscale != 1.0 && globals(i)->attr.varstuff.pscale != 0.0 ) { #ifdef LONGDOUBLE sprintf(msg,"pscale = %2.*Lg ",DPREC,globals(i)->attr.varstuff.pscale); outstring(msg); #else sprintf(msg,"pscale = %2.15g ",globals(i)->attr.varstuff.pscale); outstring(msg); #endif } outstring("\n"); } else if ( globals(i)->flags & ARRAY_PARAM ) { struct array *a = globals(i)->attr.arrayptr; if ( !(globals(i)->flags & INTERNAL_NAME) ) if ( a ) /* declaration might not have been executed */ { sprintf(msg,"define %s",globals(i)->name); strcat(msg," "); strcat(msg,datatype_name[a->datatype]); for ( j = 0 ; j < a->dim ; j++ ) sprintf(msg+strlen(msg),"[%d]",a->sizes[j]); outstring(msg); outstring(" =\n"); print_array(a,NULL); outstring("\n"); } } } list_forwards(); /* constraint names, etc. */ list_top_procedures(LIST_FULL); /* protos, then full */ if ( web.full_flag ) outstring("TORUS_FILLED\n"); else if ( web.torus_flag ) outstring("TORUS\n"); if ( web.torus_flag || torus_period_expr[0][0].root ) { outstring("PERIODS\n"); for ( i = 0 ; i < SDIM ; i++ ) { for ( j = 0 ; j < SDIM ; j++ ) { outstring(" "); outstring(print_express(&torus_period_expr[i][j],' ')); } outstring("\n"); } outstring("//Numerical values of periods:\n"); for ( i = 0 ; i < SDIM ; i++ ) { outstring("// "); for ( j = 0 ; j < SDIM ; j++ ) { #ifdef LONGDOUBLE sprintf(msg," %*.*Lf ",DWIDTH,DPREC,web.torus_period[i][j]); #else sprintf(msg," %18.15f ",web.torus_period[i][j]); #endif outstring(msg); } outstring("\n"); } outstring("\n"); } if ( torus_display_period_expr[0][0].root ) { outstring("DISPLAY_PERIODS\n"); for ( i = 0 ; i < SDIM ; i++ ) { for ( j = 0 ; j < SDIM ; j++ ) { outstring(" "); outstring(print_express(&torus_display_period_expr[i][j],' ')); } outstring("\n"); } outstring("//Numerical values of periods:\n"); for ( i = 0 ; i < SDIM ; i++ ) { outstring("// "); for ( j = 0 ; j < SDIM ; j++ ) { #ifdef LONGDOUBLE sprintf(msg," %*.*Lf ",DWIDTH,DPREC,web.torus_display_period[i][j]); #else sprintf(msg," %18.15f ",web.torus_display_period[i][j]); #endif outstring(msg); } outstring("\n"); } outstring("\n"); outstring("DISPLAY_ORIGIN\n"); for ( i = 0 ; i < SDIM ; i++ ) { #ifdef LONGDOUBLE sprintf(msg," %*.*Lf ",DWIDTH,DPREC,web.display_origin[i]); #else sprintf(msg," %18.15f ",web.display_origin[i]); #endif outstring(msg); outstring("\n"); } } outstring("\n"); if ( transform_gen_count > 0 ) { int n = web.torus_flag?transform_gen_count-SDIM:transform_gen_count; if ( n > 0 ) { sprintf(msg,"VIEW_TRANSFORM_GENERATORS %d \n",n); outstring(msg); for ( k = 0 ; k < n ; k++ ) { if ( transform_gen_swap[k] ) outstring("swap_colors\n"); for ( i = 0 ; i <= SDIM ;i++ ) { for ( j = 0 ; j <= SDIM ; j++ ) { outstring(print_express(&view_transform_gens_expr[k][i][j],' ')); outstring(" "); } outstring("\n"); } outstring("\n"); } } } else if ( transform_count > 1 ) { sprintf(msg,"VIEW_TRANSFORMS %d \n",transform_count-1); outstring(msg); for ( k = 1 ; k < transform_count ; k++ ) { if ( transform_colors[k] == SWAP_COLORS ) outstring("swap_colors\n"); else if ( transform_colors[k] != SAME_COLOR ) { sprintf(msg,"color %s\n",COLORNAME(transform_colors[k])); outstring(msg); } for ( i = 0 ; i <= SDIM ;i++ ) { for ( j = 0 ; j <= SDIM ; j++ ) { sprintf(msg,"%18.15g ",(DOUBLE)view_transforms[k][i][j]); outstring(msg); } outstring("\n"); } outstring("\n"); } } if ( web.meritfactor != 0.0 ) { sprintf(msg,"MERIT_FACTOR: %2.15g\n\n",(DOUBLE)web.meritfactor); outstring(msg); } if ( web.gravflag ) #ifdef LONGDOUBLE { sprintf(msg,"GRAVITY_CONSTANT: %2.*Lg\n\n",DPREC,web.grav_const); outstring(msg); } #else { sprintf(msg,"GRAVITY_CONSTANT: %2.15g\n\n",web.grav_const); outstring(msg); } #endif if ( web.diffusion_const != 0.0 ) #ifdef LONGDOUBLE { sprintf(msg,"DIFFUSION: %2.*Lg\n\n",DPREC,web.diffusion_const); outstring(msg); } #else { sprintf(msg,"DIFFUSION: %2.15g\n\n",web.diffusion_const); outstring(msg); } #endif if ( autochop_flag ) { sprintf(msg,"AUTOCHOP %2.15g\n\n",(DOUBLE)autochop_length); outstring(msg); } if ( web.homothety ) { sprintf(msg,"HOMOTHETY %2.15g\n\n",(DOUBLE)homothety_target); outstring(msg); } if ( match_id_flag ) outstring("KEEP_ORIGINALS\n\n"); if ( autopop_flag ) outstring("AUTOPOP\n\n"); if ( immediate_autopop_flag ) outstring("IMMEDIATE_AUTOPOP\n\n"); if ( autopop_quartic_flag ) outstring("AUTOPOP_QUARTIC\n\n"); if ( effective_area_flag ) outstring("EFFECTIVE_AREA\n\n"); if ( boundary_curvature_flag ) outstring("BOUNDARY_CURVATURE\n\n"); if ( normal_curvature_flag ) outstring("NORMAL_CURVATURE\n\n"); if ( total_time > 0.0 ) { sprintf(msg,"TOTAL_TIME %g\n\n",(DOUBLE)total_time); outstring(msg); } if ( runge_kutta_flag ) outstring("RUNGE_KUTTA\n\n"); if ( square_curvature_flag ) { sprintf(msg,"SQUARE_CURVATURE: %2.15g\n\n", (DOUBLE)globals(square_curvature_param)->value.real); outstring(msg); } if ( sqgauss_flag ) { sprintf(msg,"SQUARE_GAUSSIAN_CURVATURE: %2.15g\n\n", (DOUBLE)globals(sqgauss_param)->value.real); outstring(msg); } if ( mean_curv_int_flag ) { sprintf(msg,"MEAN_CURVATURE_INTEGRAL: %2.15g\n\n", (DOUBLE)globals(square_curvature_param)->value.real); outstring(msg); } if ( web.wulff_flag ) { sprintf(msg,"WULFF: \"%s\"\n\n",web.wulff_name); outstring(msg); } if ( phase_flag ) { sprintf(msg,"PHASEFILE \"%s\"\n\n",phase_file_name); outstring(msg); } if ( web.spring_constant != 1.0 ) { sprintf(msg,"GAP_CONSTANT: %2.15g\n\n",(DOUBLE)web.spring_constant); outstring(msg); } if ( web.area_norm_flag ) { sprintf(msg,"AREA_NORMALIZATION \n\n"); outstring(msg); } if ( web.jiggle_flag ) { sprintf(msg,"JIGGLE\n\n"); outstring(msg); } if ( web.temperature != 0.05 ) { sprintf(msg,"TEMPERATURE: %2.15g\n\n",(DOUBLE)web.temperature); outstring(msg); } if ( web.pressure != 0.0 ) { sprintf(msg,"PRESSURE: %2.15g\n\n",(DOUBLE)web.pressure); outstring(msg); } if ( web.maxscale != 1.0 ) { sprintf(msg,"SCALE_LIMIT: %2.15g\n\n",(DOUBLE)web.maxscale); outstring(msg); } if ( web.gauss1D_order != 3 ) { sprintf(msg,"INTEGRAL_ORDER_1D: %d\n\n",web.gauss1D_order); outstring(msg); } if ( (web.dimension > 1) && (web.gauss2D_order != 6) ) { sprintf(msg,"INTEGRAL_ORDER_2D: %d\n\n",web.gauss2D_order); outstring(msg); } if ( web.maxcon ) { sprintf(msg,"CONSTRAINT_TOLERANCE: %2.15g\n\n",(DOUBLE)web.tolerance); outstring(msg); } if ( web.symmetric_content ) { sprintf(msg,"SYMMETRIC_CONTENT\n\n"); outstring(msg); } /* Too many problems for too little use if ( loc_ordinal(web.zoom_v) >= 0 ) { sprintf(msg,"ZOOM_VERTEX %s\n\n",ELNAME(web.zoom_v)); outstring(msg); } if ( web.zoom_radius < 9000.0 ) { sprintf(msg,"ZOOM_RADIUS %2.15g\n\n",(DOUBLE)web.zoom_radius); outstring(msg); } */ if ( klein_metric_flag ) { sprintf(msg,"KLEIN_METRIC\n\n"); outstring(msg); } if ( web.conformal_flag ) { sprintf(msg,"CONFORMAL_METRIC\n"); outstring(msg); /* metric expression */ outstring(print_express(&web.metric[0][0],'X')); outstring("\n\n"); } else if ( web.metric_flag && web.metric[0][0].root ) { sprintf(msg,"METRIC\n"); outstring(msg); /* metric expressions */ for ( i = 0 ; i < SDIM ; i++ ) { for ( j = 0 ; j < SDIM ; j++ ) { outstring(" "); outstring(print_express(&web.metric[i][j],'X')); } outstring("\n"); } outstring("\n"); } /* extra attributes for elements */ for ( e_type = VERTEX ; e_type <= FACETEDGE ; e_type++ ) { struct extra *ex; for ( i = 0, ex = EXTRAS(e_type) ; i < web.skel[e_type].extra_count ; i++, ex++ ) { if ( !(ex->flags & DUMP_ATTR) ) continue; sprintf(msg,"\ndefine %s attribute %s %s",typenames[e_type], ex->name, datatype_name[ex->type]); for ( j = 0 ; j < ex->array_spec.dim ; j++) sprintf(msg+strlen(msg),"[%d]",ex->array_spec.sizes[j]); if ( ex->code.start ) sprintf(msg+strlen(msg), " // function definition in READ section"); strcat(msg,"\n"); outstring(msg); } } /* view matrix */ outstring("VIEW_MATRIX \n"); for ( i = 0 ; i <= SDIM ;i++ ) { for ( j = 0 ; j <= SDIM ; j++ ) { sprintf(msg,"%18.15f ", (DOUBLE)view[i][j]); outstring(msg); } outstring("\n"); } /* Clipping and slicing planes */ outstring("clip_coeff = {"); for ( i = 0 ; i < MAXCLIPS ; i++ ) { int nonzero = 0; for ( j = 0 ; j <= SDIM ; j++ ) if ( clip_coeff[i][j] ) nonzero = 1; if ( nonzero ) { if ( i > 0 ) outstring(",{"); else outstring("{"); for ( j = 0 ; j <= SDIM ; j++ ) { sprintf(msg,"%8.5f",clip_coeff[i][j]); outstring(msg); if ( j < SDIM ) outstring(","); } outstring("}"); } } outstring("}\n\n"); outstring("slice_coeff = {"); for ( j = 0 ; j <= SDIM ; j++ ) { sprintf(msg,"%8.5f",slice_coeff[j]); outstring(msg); if ( j < SDIM ) outstring(","); } outstring("}\n\n"); if ( mobility_flag ) /* after element attributes, since it can use them */ { if ( mobility_tensor_flag ) { outstring("MOBILITY_TENSOR\n"); /* mobility tensor expressions */ for ( i = 0 ; i < SDIM ; i++ ) { for ( j = 0 ; j < SDIM ; j++ ) { outstring(" "); outstring(print_express(&mobility_tensor[i][j],'X')); } outstring("\n"); } outstring("\n"); } else { outstring("MOBILITY: "); outstring(print_express(&mobility_formula,'X')); outstring("\n\n"); } } /* unattached instances, probably from compound quantities */ for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { mi = METH_INSTANCE(k); if ( mi->quant >= 0 ) continue; /* has a quantity */ if ( mi->flags & (Q_DELETED|IMPLICIT_INSTANCE|DEFAULT_INSTANCE) ) continue; list_method_instance(k); } /* named quantities */ for ( k = 0 ; k < gen_quant_count ; k++ ) { q = GEN_QUANT(k); if ( q->flags & (DEFAULT_QUANTITY|Q_DELETED) ) continue; list_quantity(k); } if ( hessian_special_normal_expr[0].start ) { outstring("\nhessian_special_normal_vector\n"); for ( i = 0 ; i < SDIM ; i++ ) { sprintf(msg,"c%d: ",i+1); outstring(msg); outstring(print_express(hessian_special_normal_expr+i,'x')); outstring("\n"); } outstring("\n"); } /* print boundary info */ for ( i = 0 ; i < web.bdrymax ; i++ ) { struct boundary * bdry = web.boundaries + i; if ( !(bdry->attr & IN_USE) ) continue; list_boundary(i); } /* print constraint info */ for ( i = 0 ; i < web.maxcon ; i++ ) { struct constraint *con = get_constraint(i); if ( !(con->attr & IN_USE) ) continue; list_constraint(i); } /* windup */ outfd = old_fd; } /* end top_dump() */ /********************************************************************** * * function: dump_macros() * * purpose: print macros in dumpfile if keep_macros_flag * */ void dump_macros() { int n; if ( !keep_macros_flag ) return; outstring("\n"); for ( n = 0 ; n < macro_count ; n++ ) { sprintf(msg,"#define %s ",macros[n].name); outstring(msg); outstring(macro_subs+macros[n].offset); outstring("\n"); } outstring("\n"); } /********************************************************************** * * function: dump_method_specs() * * purpose: list attributes of method instance */ void dump_method_specs(meth) int meth; { struct method_instance *mi = METH_INSTANCE(meth); int j; if ( mi->flags & GLOBAL_INST ) outstring(" global "); if ( mi->flags & IGNORE_CONSTR ) outstring(" ignore_constraints "); if ( mi->flags & IGNORE_FIXED ) outstring(" ignore_fixed "); if ( mi->flags & ELEMENT_MODULUS_FLAG ) { outstring(" element_modulus "); outstring(EXTRAS(mi->type)[mi->elmodulus].name); } if ( mi->modulus != 1.0 ) { #ifdef LONGDOUBLE sprintf(msg," modulus %3.*Lg ",DPREC,mi->modulus); #else sprintf(msg," modulus %3.15g ",mi->modulus); #endif outstring(msg); } if ( mi->flags & METH_PARAMETER_1 ) { #ifdef LONGDOUBLE sprintf(msg," parameter_1 %3.*Lg ",DPREC,mi->parameter_1); #else sprintf(msg," parameter_1 %3.15g ",mi->parameter_1); #endif outstring(msg); } outstring("\n"); if ( basic_gen_methods[mi->gen_method].spec_flags & SPEC_SCALAR ) { outstring("scalar_integrand: "); outstring(print_express(mi->expr[0],'X')); outstring("\n"); } if ( basic_gen_methods[mi->gen_method].spec_flags & SPEC_VECTOR ) { outstring("vector_integrand: \n"); for ( j = 0 ; j < SDIM ; j++ ) { sprintf(msg,"Q%1d: ",j+1); outstring(msg); outstring(print_express(mi->expr[j],'X')); outstring("\n"); } } if ( basic_gen_methods[mi->gen_method].spec_flags & SPEC_KVECTOR ) { sprintf(msg,"k_vector_order %d\nvector_integrand: \n",mi->vec_order); outstring(msg); for ( j = 0 ; j < (SDIM-mi->vec_order)*SDIM ; j++ ) { sprintf(msg,"Q%1d: ",j+1); outstring(msg); outstring(print_express(mi->expr[j],'X')); outstring("\n"); } } if ( basic_gen_methods[mi->gen_method].spec_flags & SPEC_2FORM ) { outstring("form_integrand: \n"); for ( j = 0 ; j < (SDIM*(SDIM-1))/2 ; j++ ) { sprintf(msg,"Q%1d: ",j+1); outstring(msg); outstring(print_express(mi->expr[j],'X')); outstring("\n"); } } } /* end dump_method_specs() */ /************************************************************************* * * function: vertex_dump() * * purpose: write one vertex definition to datafile * */ void vertex_dump(v_id,fd) vertex_id v_id; FILE *fd; /* destination file */ { int i; REAL *x; FILE *old_fd = outfd; struct extra *ex; int attr = get_vattr(v_id); vertex_id orig; outfd = fd; if ( !valid_id(v_id) ) return; x = get_coord(v_id); if ( attr & BOUNDARY ) { /* print boundary parameters */ struct boundary *bdry = get_boundary(v_id); REAL *param = get_param(v_id); sprintf(msg,"%3s",ELNAME(v_id)); outstring(msg); for ( i = 0 ; i < bdry->pcount ; i++ ) #ifdef LONGDOUBLE { sprintf(msg," %*.*Lg",DWIDTH,DPREC,param[i]); outstring(msg); } #else { sprintf(msg," %17.15g",param[i]); outstring(msg); } #endif sprintf(msg," boundary %s ",bdry->name); outstring(msg); sprintf(msg," /* ("); outstring(msg); for ( i = 0; i < SDIM ; i++) #ifdef LONGDOUBLE { sprintf(msg," %*.*Lg",DWIDTH,DPREC,x[i]); outstring(msg); } #else { sprintf(msg," %17.15g",x[i]); outstring(msg); } #endif sprintf(msg,") */"); outstring(msg); } /* end BOUNDARY parameters */ else { /* print regular coordinates */ sprintf(msg,"%3s ",ELNAME(v_id)); outstring(msg); for ( i = 0; i < SDIM ; i++) #ifdef LONGDOUBLE { sprintf(msg," %*.*Lg",DWIDTH,DPREC,x[i]); outstring(msg); } #else { sprintf(msg," %17.15g",x[i]); outstring(msg); } #endif } if ( attr & CONSTRAINT ) { conmap_t * conmap = get_v_constraint_map(v_id); struct constraint *con; sprintf(msg," constraints "); outstring(msg); for ( i = 1 ; i <= (int)conmap[0] ; i++ ) { con = get_constraint(conmap[i]); if ( !(con->attr & GLOBAL) ) { if ( con->attr & NAMED_THING ) { sprintf(msg,"%s ",con->name); outstring(msg); } else { sprintf(msg,"%d ",conmap[i]&CONMASK); outstring(msg); } } } } if ( attr & FIXED ) { sprintf(msg, " fixed "); outstring(msg); } if ( attr & HIT_PARTNER ) outstring(" hit_partner "); if ( attr & AXIAL_POINT ) { sprintf(msg, " axial_point "); outstring(msg); } if ( attr & BARE_NAKED ) { sprintf(msg, " bare "); outstring(msg); } orig = get_original(v_id); if ( valid_id(orig) && !equal_element(orig,v_id) ) { sprintf(msg," original %s",ELNAME(orig)); outstring(msg); } if ( elptr(v_id)->method_count ) { struct method_instance *mi; int meth_offset = EXTRAS(VERTEX)[web.meth_attr[VERTEX]].offset; int *methlist = (int*)((char*)elptr(v_id) + meth_offset); for ( i = 0 ; i < (int)elptr(v_id)->method_count ; i++ ) { mi = METH_INSTANCE(abs(methlist[i])); if ( GEN_QUANT(mi->quant)->flags & (DEFAULT_QUANTITY|Q_DELETED) ) continue; if ( mi->flags & IMPLICIT_INSTANCE ) sprintf(msg," %s ",GEN_QUANT(mi->quant)->name); else sprintf(msg," %s%c ",mi->name,methlist[i]<0?'-':' '); outstring(msg); } } /* extra attributes */ for ( i = 0, ex = EXTRAS(VERTEX) ; i < web.skel[VERTEX].extra_count; i++,ex++ ) { char *at = get_v_extra(v_id,i); if ( !(ex->flags & DUMP_ATTR) ) continue; sprintf(msg," %s ",ex->name); outstring(msg); print_array_attribute(ex,at); } sprintf(msg,"\n"); outstring(msg); outfd = old_fd; } /* end vertex_dump() */ /*************************************************************************** * * function: edge_dump() * * purpose: write out one edge definition * */ void edge_dump(e_id,fd) edge_id e_id; FILE *fd; /* destination file */ { int i; int attr; FILE *old_fd = outfd; struct extra *ex; edge_id orig; outfd = fd; if ( !valid_id(e_id) ) return; attr = get_eattr(e_id); sprintf(msg,"%3s ",ELNAME(e_id)); outstring(msg); if ( web.modeltype == LAGRANGE ) { vertex_id *v = get_edge_vertices(e_id); int num = web.skel[EDGE].ctrlpts; for ( i = 0 ; i < num ; i++ ) { sprintf(msg,"%3s ",ELNAME(v[i])); outstring(msg); } } else { if ( web.representation == SIMPLEX ) { vertex_id *v = get_edge_vertices(e_id); for ( i = 0 ; i <= web.dimension-1 ; i++ ) { sprintf(msg,"%3s ",ELNAME(v[i])); outstring(msg); } } else { sprintf(msg,"%3s %3s ", ELNAME(get_edge_tailv(e_id)), ELNAME1(get_edge_headv(e_id))); outstring(msg); } if ( web.modeltype == QUADRATIC ) { sprintf(msg," /*midpt*/ %3s ",ELNAME(get_edge_midv(e_id))); outstring(msg); } } if ( web.torus_flag ) { WRAPTYPE wrap = get_edge_wrap(e_id); WRAPTYPE w = wrap; int smallwrap = 1; /* whether can do with - * + */ for ( i = 0 ; i < SDIM ; i++, w >>= TWRAPBITS ) switch ( w & WRAPMASK ) { case NEGWRAP: case 0: case POSWRAP: break; default : smallwrap = 0; } if ( !smallwrap ) { sprintf(msg," wrap 0x%06lX\n",wrap); outstring(msg); } else for ( i = 0 ; i < SDIM ; i++, wrap >>= TWRAPBITS ) switch ( wrap & WRAPMASK ) { case NEGWRAP : sprintf(msg," -"); outstring(msg); break; case 0 : sprintf(msg," *"); outstring(msg); break; case POSWRAP : sprintf(msg," +"); outstring(msg); break; default : sprintf(errmsg,"Bad wrap %lX on edge %s period %d\n", wrap&WRAPMASK,ELNAME(e_id),i+1); kb_error(1005,errmsg,WARNING); sprintf(msg," bad"); outstring(msg); break; } } else if ( web.symmetry_flag ) { WRAPTYPE wrap = get_edge_wrap(e_id); sprintf(msg," wrap %ld ",wrap); outstring(msg); } if ( (attr & DENSITY) || ((web.representation==STRING) && (get_edge_density(e_id) != 1.0))) #ifdef LONGDOUBLE { sprintf(msg," density %1.*Lg ",DPREC,get_edge_density(e_id)); outstring(msg); } #else { sprintf(msg," density %1.15g ",get_edge_density(e_id)); outstring(msg); } #endif if ( attr & BOUNDARY ) { struct boundary *bdry = get_edge_boundary(e_id); sprintf(msg," boundary %s ",bdry->name); outstring(msg); } if ( attr & CONSTRAINT ) { conmap_t * conmap = get_e_constraint_map(e_id); struct constraint *con; sprintf(msg," constraints "); outstring(msg); for ( i = 1 ; i <= (int)conmap[0] ; i++ ) { con = get_constraint(conmap[i]); if ( !(con->attr & GLOBAL) ) { if ( con->attr & NAMED_THING ) { sprintf(msg,"%s ",con->name); outstring(msg); } else { sprintf(msg,"%d ",conmap[i]&CONMASK); outstring(msg); } } } } if ( attr & NEGBOUNDARY ) outstring(" orientation -1 "); if ( attr & FIXED ) { sprintf(msg,"fixed "); outstring(msg); } if ( attr & BARE_NAKED ) { sprintf(msg," bare "); outstring(msg); } if ( attr & NO_REFINE ) outstring(" no_refine "); if ( attr & NONCONTENT ) outstring(" noncontent "); if ( get_edge_color(e_id) != DEFAULT_EDGE_COLOR ) { sprintf(msg," color %s ",COLORNAME(get_edge_color(e_id))); outstring(msg); } if ( (fd == old_fd) && (web.representation != SIMPLEX) ) { calc_edge(e_id); sprintf(msg," /*length %g*/ ",(DOUBLE)get_edge_length(e_id)); outstring(msg); } if ( elptr(e_id)->method_count ) { struct method_instance *mi; int meth_offset = EXTRAS(EDGE)[web.meth_attr[EDGE]].offset; int *methlist = (int*)((char*)elptr(e_id) + meth_offset); for ( i = 0 ; i < elptr(e_id)->method_count ; i++ ) { int mm = methlist[i]; mi = METH_INSTANCE(abs(mm)); if ( mi->quant >= 0 && (GEN_QUANT(mi->quant)->flags & (DEFAULT_QUANTITY|Q_DELETED)) ) continue; if ( mi->quant >= 0 && (mi->flags & IMPLICIT_INSTANCE) ) sprintf(msg,"%s%c ",GEN_QUANT(mi->quant)->name,(mm<0?'-':' ')); else sprintf(msg,"%s%c ",mi->name,(mm<0?'-':' ')); outstring(msg); } } orig = get_original(e_id); if ( valid_id(orig) && !equal_element(orig,e_id) ) { sprintf(msg," original %s",ELNAME(orig)); outstring(msg); } /* extra attributes */ for ( i = 0, ex = EXTRAS(EDGE) ; i < web.skel[EDGE].extra_count; i++,ex++ ) { char *at = get_e_extra(e_id,i); if ( !(ex->flags & DUMP_ATTR) ) continue; if ( ex->array_spec.datacount == 0 ) continue; sprintf(msg," %s ",ex->name); outstring(msg); print_array_attribute(ex,at); } outstring("\n"); outfd = old_fd; } /* end edge_dump() */ /*************************************************************************** * * function: facet_dump() * * purpose: write out one facet definition * */ void facet_dump(f_id,fd) facet_id f_id; FILE *fd; /* destination file */ { int i; int per_line = 0; edge_id e_id; facetedge_id fe; FILE *old_fd = outfd; struct extra *ex; int attr = get_fattr(f_id); facet_id orig; outfd = fd; if ( !valid_id(f_id) ) return; { sprintf(msg,"%3s ",ELNAME(f_id)); outstring(msg); } if ( web.representation == SIMPLEX ) { vertex_id *v = get_facet_vertices(f_id); int num; if (web.modeltype == LAGRANGE) num = binom_coeff(web.lagrange_order+web.dimension,web.dimension); else num = web.dimension+1; for ( i = 0 ; i < num ; i++ ) { sprintf(msg," %3s",ELNAME(v[i])); outstring(msg); } } else { facetedge_id first_fe; fe = first_fe = get_facet_fe(f_id); if ( valid_id(fe) ) do { e_id = get_fe_edge(fe); sprintf(msg," %s",SELNAME(e_id) ); outstring(msg); per_line++; if ( per_line >= 10 ) { sprintf(msg," \\\n "); outstring(msg); per_line = 0; } fe = get_next_edge(fe); } while ( valid_id(fe) && !equal_id(fe,first_fe) ); if ( (web.modeltype == LAGRANGE) && (web.representation != STRING) ) { int num = binom_coeff(web.lagrange_order+web.dimension,web.dimension); vertex_id *v = get_facet_vertices(f_id); outstring(" vertices "); for ( i = 0 ; i < num ; i++ ) { sprintf(msg,"%3s ",ELNAME(v[i])); outstring(msg); } } } if ( attr & BOUNDARY ) { struct boundary *bdry = get_facet_boundary(f_id); sprintf(msg," boundary %s ",bdry->name); outstring(msg); } if ( attr & CONSTRAINT ) { conmap_t * conmap = get_f_constraint_map(f_id); struct constraint *con; sprintf(msg," constraints "); outstring(msg); for ( i = 1 ; i <= (int)conmap[0] ; i++ ) { con = get_constraint(conmap[i]); if ( !(con->attr & GLOBAL) ) { if ( con->attr & NAMED_THING ) { sprintf(msg,"%s ",con->name); outstring(msg); } else { sprintf(msg,"%d ",conmap[i]&CONMASK); outstring(msg); } } } } if ( attr & NEGBOUNDARY ) outstring(" orientation -1 "); if ( attr & DENSITY ) #ifdef LONGDOUBLE { sprintf(msg," density %1.*Lg ",DPREC,get_facet_density(f_id)); outstring(msg); } #else { sprintf(msg," density %1.15g ",get_facet_density(f_id)); outstring(msg); } #endif if ( attr&FIXED ) outstring(" fixed " ); if ( attr & NO_REFINE ) outstring(" no_refine "); if ( attr & NONCONTENT ) outstring(" noncontent "); if ( attr & NODISPLAY ) outstring(" nodisplay"); if ( get_tag(f_id) ) { sprintf(msg," tag %d ",get_tag(f_id)); outstring(msg); } if ( phase_flag && (web.representation == STRING) ) { sprintf(msg," phase %d ",get_f_phase(f_id)); outstring(msg); } if ( get_facet_color(f_id) != DEFAULT_FACET_COLOR ) { sprintf(msg," color %s ",COLORNAME(get_facet_color(f_id))); outstring(msg); } if ( get_facet_backcolor(f_id) != get_facet_color(f_id) ) { sprintf(msg," backcolor %s ",COLORNAME(get_facet_backcolor(f_id))); outstring(msg); } if ( elptr(f_id)->method_count ) { struct method_instance *mi; int meth_offset = EXTRAS(FACET)[web.meth_attr[FACET]].offset; int *methlist = (int*)((char*)elptr(f_id) + meth_offset); int methcount = (int)elptr(f_id)->method_count; for ( i = 0 ; i < methcount ; i++ ) { int mm = methlist[i]; mi = METH_INSTANCE(abs(mm)); if ( mi->quant >= 0 && (GEN_QUANT(mi->quant)->flags & (DEFAULT_QUANTITY|Q_DELETED)) ) continue; if ( mi->quant >= 0 && (mi->flags & IMPLICIT_INSTANCE) ) sprintf(msg," %s%c ",GEN_QUANT(mi->quant)->name,(mm<0?'-':' ')); else sprintf(msg," %s%c ",mi->name,(mm<0?'-':' ')); outstring(msg); } } sprintf(msg," /*area %g*/", (DOUBLE)get_facet_area(f_id)); outstring(msg); orig = get_original(f_id); if ( valid_id(orig) && !equal_element(orig,f_id) ) { sprintf(msg," original %s",ELNAME(orig)); outstring(msg); } /* extra attributes */ for ( i = 0, ex = EXTRAS(FACET) ; i < web.skel[FACET].extra_count; i++,ex++ ) { char *at = get_f_extra(f_id,i); if ( !(ex->flags & DUMP_ATTR) ) continue; if ( ex->array_spec.datacount == 0 ) continue; sprintf(msg," %s ",ex->name); outstring(msg); print_array_attribute(ex,at); } outstring("\n"); outfd = old_fd; } /* end facet_dump() */ /************************************************************************* * * function: body_dump() * * purpose: Write out definition of one body. * */ void body_dump(b_id,fd) body_id b_id; FILE *fd; /* destination file */ { REAL den; int per_line = 0; facet_id startf,f_id; int i; FILE *old_fd = outfd; struct extra *ex; REAL bvol; body_id orig; int maxcount = 2*web.skel[FACET].count; int count = 0; outfd = fd; if ( !valid_id(b_id) ) return; /* make_bfacet_lists(); */ sprintf(msg,"%3s ",ELNAME(b_id)); outstring(msg); startf = f_id = get_body_facet(b_id); while ( valid_id(f_id) ) { sprintf(msg," %s",SELNAME(f_id)); outstring(msg); per_line++; if ( per_line >= 10 ) { sprintf(msg," \\\n "); outstring(msg); per_line = 0; } f_id = get_next_body_facet(f_id); if ( equal_id(f_id,startf) ) break; if ( count++ > maxcount ) { sprintf(errmsg,"Internal error: body %s facet list not closed.\n", ELNAME(b_id)); kb_error(4221,errmsg,RECOVERABLE); } } bvol = get_body_volume(b_id); if ( get_battr(b_id) & FIXEDVOL ) { sprintf(msg, #ifdef LONGDOUBLE " volume %1.*Lg /*actual: %1.*Lg*/ lagrange_multiplier %1.*Lg ", DPREC,get_body_fixvol(b_id), DPREC, bvol,DPREC,get_body_pressure(b_id)); #else " volume %1.15g /*actual: %1.15g*/ lagrange_multiplier %1.15g ", get_body_fixvol(b_id), bvol,get_body_pressure(b_id)); #endif outstring(msg); } if ( (get_body_volconst(b_id) != 0.0) ) #ifdef LONGDOUBLE { sprintf(msg, " volconst %1.*Lg ",DPREC,get_body_volconst(b_id)); #else { sprintf(msg, " volconst %1.15g ",get_body_volconst(b_id)); #endif outstring(msg); } if ( web.torus_flag && ((bvol > web.torusv) || (bvol < 0.0)) ) #ifdef LONGDOUBLE { sprintf(msg, " actual_volume %1.*Lg ",DPREC,bvol); #else { sprintf(msg, " actual_volume %1.15g ",bvol); #endif outstring(msg); } if ( get_battr(b_id) & PRESSURE ) #ifdef LONGDOUBLE { sprintf(msg," pressure %1.*Lg",DPREC, get_body_pressure(b_id)); outstring(msg); } #else { sprintf(msg," pressure %1.15g", get_body_pressure(b_id)); outstring(msg); } #endif den = get_body_density(b_id); if ( den != 0.0 ) #ifdef LONGDOUBLE { sprintf(msg," density %1.*Lg ",DPREC,den); outstring(msg); } #else { sprintf(msg," density %1.15g ",den); outstring(msg); } #endif if ( phase_flag && (web.representation == SOAPFILM) ) { sprintf(msg," PHASE %d ",get_b_phase(b_id)); outstring(msg); } orig = get_original(b_id); if ( valid_id(orig) && !equal_element(orig,b_id) ) { sprintf(msg," original %s",ELNAME(orig)); outstring(msg); } if ( elptr(b_id)->method_count ) { struct method_instance *mi; int meth_offset = EXTRAS(BODY)[web.meth_attr[BODY]].offset; int *methlist = (int*)((char*)elptr(b_id) + meth_offset); for ( i = 0 ; i < (int)elptr(b_id)->method_count ; i++ ) { mi = METH_INSTANCE(abs(methlist[i])); if ( mi->flags & IMPLICIT_INSTANCE ) sprintf(msg,"%s ",GEN_QUANT(mi->quant)->name); else sprintf(msg,"%s%c ",mi->name,methlist[i]>0?' ':'-'); outstring(msg); } } /* extra attributes */ for ( i = 0, ex = EXTRAS(BODY) ; i < web.skel[BODY].extra_count; i++,ex++ ) { char *at = get_b_extra(b_id,i); if ( !(ex->flags & DUMP_ATTR) ) continue; if ( ex->array_spec.datacount == 0 ) continue; sprintf(msg," %s ",ex->name); outstring(msg); print_array_attribute(ex,at); } outstring("\n"); outfd = old_fd; } /* end body_dump */ static int readflag; /* whether READ section has been started */ static FILE *dumpfd; /************************************************************************** * * function: toggle_save() * * purpose: write ON toggle state to standard output (which has been * redirected for dump purposes) * */ void toggle_save(togglename) char *togglename; { if ( readflag == 0 ) { outstring("\nread\n"); readflag = 1; } sprintf(msg,"%s on\n",togglename); outstring(msg); } /************************************************************************** * * function: toggle_save_off() * * purpose: write OFF toggle state to standard output (which has been * redirected for dump purposes) * */ void toggle_save_off(togglename) /* for default on toggles */ char *togglename; { if ( readflag == 0 ) { outstring("\nread\n"); readflag = 1; } sprintf(msg,"%s off\n",togglename); outstring(msg); } /*********************************************************************** * * function: do_dump() * * purpose: dump whole datafile. Open file, call section dumps. * */ void do_dump(name) char *name; { vertex_id v_id; edge_id e_id; facet_id f_id; body_id b_id; char defaultname[100]; int old_quiet = quiet_flag; quiet_flag = 0; /* so outstring actually produces output */ if ( name == NULL ) { /* construct default name */ strncpy(defaultname,datafilename,sizeof(defaultname)-5); #ifdef MPI_EVOLVER sprintf(defaultname+strlen(defaultname),".task%d",this_task); #endif strcat(defaultname,".dmp"); name = defaultname; } sprintf(msg,"Dumping to %s.\n",name); outstring(msg); dumpfd = fopen(name,"w"); if ( dumpfd == NULL ) { sprintf(errmsg,"Cannot open file %s.\n",name); kb_error(1006,errmsg,RECOVERABLE); } fprintf(dumpfd,"// %s: Dump of structure.\n\n",name); top_dump(dumpfd); /* vertex dump */ fputs("\nvertices /* coordinates */ \n",dumpfd); MFOR_ALL_VERTICES(v_id) vertex_dump(v_id,dumpfd); /* edge dump */ fputs("\nedges \n",dumpfd); MFOR_ALL_EDGES(e_id) edge_dump(e_id,dumpfd); /* facet dump */ if ( web.representation == SIMPLEX ) fputs("\nfaces /* vertex set */ \n",dumpfd); else fputs("\nfaces /* edge loop */ \n",dumpfd); MFOR_ALL_FACETS(f_id) facet_dump(f_id,dumpfd); /* body dump */ #ifndef MPI_EVOLVER calc_content(Q_FIXED|Q_INFO); #endif fputs("\nbodies /* facets */\n",dumpfd); MFOR_ALL_BODIES(b_id) body_dump(b_id,dumpfd); bottom_dump(dumpfd); fclose(dumpfd); quiet_flag = old_quiet; } /* end do_dump() */ /******************************************************************** * * function: list_proc() * * purpose: list one procedure, recursing if necessary for * dependencies. */ void list_proc(g,level) struct global *g; /* global variable of procedure */ int level; /* for recursion depth check */ { struct expnode *ex = &g->value.proc; struct treenode *node; int pp; g->attr.procstuff.proc_timestamp = proc_timestamp; if ( level > web.global_count ) { sprintf(errmsg,"'%s' involved in cyclic definition.\n",g->name); kb_error(1007,errmsg,WARNING); } else { /* check dependencies */ for ( node = ex->start ; node != ex->root ; node ++ ) if ( node->type == PROCEDURE_ ) { pp = node->op1.name_id; if ( globals(pp)->attr.procstuff.proc_timestamp < proc_timestamp ) list_proc(globals(pp),level+1); } } /* print */ if ( g->flags & PERMANENT ) sprintf(msg,"%s ::= ",g->name); else sprintf(msg,"%s := ",g->name); outstring(msg); if ( g->value.proc.root==NULL ) outstring("{}"); else { if ( g->value.proc.root->type != COMMAND_BLOCK_ ) outstring("{"); outstring(print_express(&g->value.proc,'X')); if ( g->value.proc.root->type != COMMAND_BLOCK_ ) outstring("}"); } outstring("\n"); g->attr.procstuff.proc_timestamp = proc_timestamp; } /* end list_proc() */ /******************************************************************** * * function: list_function() * * purpose: list one procedure, recursing if necessary for * dependencies. */ void list_function(g) struct global *g; { if ( g->value.proc.root == NULL ) { outstring("// "); outstring(g->name); outstring(" body definition has not been executed yet.\n\n"); } else { outstring(print_express(&g->value.proc,'X')); outstring("\n\n"); } } /* end list_function() */ /******************************************************************** * * function: list_procedure() * * purpose: list one procedure, */ void list_procedure(g) struct global *g; { if ( g->value.proc.root == NULL ) { outstring("// "); outstring(g->name); outstring(" body definition has not been executed yet.\n\n"); } else { outstring(print_express(&g->value.proc,'X')); outstring("\n\n"); } } /* end list_procedure() */ /******************************************************************** * * function: list_function_proto() * * purpose: list one function prototype */ void list_function_proto(g) struct global *g; { struct expnode ex = g->value.proc; /* copy, since will modify */ if ( !ex.root ) return; outstring("function "); outstring(datatype_name[ex.root->op4.ret_type]); outstring(" "); outstring(g->name); if ( ex.root == NULL ) outstring("(); // definition has not been executed yet.\n"); else { outstring("("); ex.root += ex.root->left; /* so just does function head */ outstring(print_express(&ex,'X')); outstring(");\n"); } } /* end list_function() */ /******************************************************************** * * function: list_procedure_proto() * * purpose: list one procedure prototype. */ void list_procedure_proto(g) struct global *g; { struct expnode ex = g->value.proc; outstring("procedure "); outstring(g->name); if ( g->attr.procstuff.argcount == 0 ) { if ( g->flags & PROCEDURE_NAME ) outstring("();\n"); else outstring("\n"); return; } if ( ex.root == NULL ) outstring("(); // definition has not been executed yet.\n"); else { outstring("("); ex.root += ex.root->left; /* so just does procedure head */ outstring(print_express(&ex,'X')); outstring(");\n"); } } /* end list_procedure_proto() */ /********************************************************************* * * function: list_top_procedures() * * purpose: list argument-style functions and procedures in top of datafile. * Note this lists globals of type FUNCTION_NAME and PROCEDURE_NAME. * Only fully does those originally declared in top section. * For SUBROUTINE, see list_procedures(). * */ void list_top_procedures(mode) int mode; /* LIST_PROTO or LIST_FULL */ { int i; int protoflag = 0; /* whether printed prototype comment */ proc_timestamp++; /* prototypes */ for ( i = 0 ; i < web.global_count ; i++ ) { if ( globals(i)->flags & FUNCTION_NAME ) { if ( protoflag == 0 ) { outstring("\n// Function and procedure prototypes\n"); protoflag = 1; } list_function_proto(globals(i)); } if ( globals(i)->flags & PROCEDURE_NAME ) { if ( protoflag == 0 ) { outstring("\n// Function and procedure prototypes\n"); protoflag = 1; } list_procedure_proto(globals(i)); } } if ( protoflag ) outstring("// End prototypes\n\n"); if ( mode == LIST_PROTO ) return; /* full function definitions */ for ( i = 0 ; i < web.global_count ; i++ ) { int flags = globals(i)->flags; if ( (flags & FUNCTION_NAME) && (flags & IN_DATAFILE_TOP) ) list_function(globals(i)); if ( (flags & PROCEDURE_NAME) && (flags & IN_DATAFILE_TOP) ) list_procedure(globals(i)); } } /* end list_top_procedures() */ /********************************************************************* * * function: list_procedures() * * purpose: list defined procedures * Note this lists globals of type SUBROUTINE. * */ void list_procedures(mode) int mode; /* LIST_PROTO or LIST_FULL */ { int i; int perm_comment_flag = 0; int witharg_comment_flag = 0; int without_comment_flag = 0; int forward_comment_flag = 0; int redefheader; proc_timestamp++; /* redefined single letters dummy defs (i.e. forward decls) */ redefheader = 0; if ( mode != LIST_PROTO ) for ( i = 0 ; i < 128 ; i++ ) if ( single_redefine[i].start ) { if ( !redefheader ) { outstring("// Redefined single letter commands forward declarations: \n"); redefheader = 1; } sprintf(msg,"%c :::= {} \n",i); outstring(msg); } if ( mode != LIST_PROTO ) { /* function definition forward decls */ for ( i = 0 ; i < web.global_count ; i++ ) { if ( globals(i)->flags & SUBROUTINE ) { if ( !forward_comment_flag ) { outstring("//Procedures and functions forward declarations:\n"); forward_comment_flag = 1; } sprintf(msg,"%s := {}\n",globals(i)->name); outstring(msg); } } for ( i = 0 ; i < web.perm_global_count ; i++ ) { if ( perm_globals(i)->flags & SUBROUTINE ) { if ( !perm_comment_flag ) { outstring("// Permanent procedures and functions:\n"); perm_comment_flag = 1; } sprintf(msg,"%s ::= {}\n",perm_globals(i)->name); outstring(msg); } } } /* full function definitions */ for ( i = 0 ; i < web.global_count ; i++ ) { if ( globals(i)->flags & SUBROUTINE ) if ( globals(i)->attr.procstuff.proc_timestamp < proc_timestamp ) { if ( !without_comment_flag ) { outstring("// Procedures without arguments:\n"); without_comment_flag = 1; } if ( mode == LIST_PROTO ) list_procedure_proto(globals(i)); else list_proc(globals(i),0); } } for ( i = 0 ; i < web.perm_global_count ; i++ ) { if ( perm_globals(i)->flags & SUBROUTINE ) { if ( !perm_comment_flag ) { outstring("// Permanent procedures and functions:\n"); perm_comment_flag = 1; } if ( mode == LIST_PROTO ) list_procedure_proto(perm_globals(i)); else list_proc(perm_globals(i),0); } } /* full function definitions */ for ( i = 0 ; i < web.global_count ; i++ ) { int flags = globals(i)->flags; if ( (flags & FUNCTION_NAME) && !(flags & IN_DATAFILE_TOP) ) { if ( !witharg_comment_flag ) { outstring("// Procedures and functions with argument lists:\n"); witharg_comment_flag = 1; } if ( mode == LIST_PROTO ) list_function_proto(globals(i)); else list_function(globals(i)); } if ( (flags & PROCEDURE_NAME) && !(flags & IN_DATAFILE_TOP) ) { if ( !witharg_comment_flag ) { outstring("// Procedures and functions with argument lists:\n"); witharg_comment_flag = 1; } if ( mode == LIST_PROTO ) list_procedure_proto(globals(i)); else list_procedure(globals(i)); } } /* redefined single letters */ redefheader = 0; for ( i = 0 ; i < 128 ; i++ ) if ( single_redefine[i].start ) { if ( !redefheader ) { outstring("// Redefined single letter commands: \n"); redefheader = 1; } if ( mode == LIST_PROTO ) { sprintf(msg,"%c",i); outstring(msg); } else { sprintf(msg,"%c :::= ",i); outstring(msg); if ( single_redefine[i].root->type != COMMAND_BLOCK_ ) outstring("{"); outstring(print_express(&single_redefine[i],'X')); if ( single_redefine[i].root->type != COMMAND_BLOCK_ ) outstring("}"); } outstring("\n"); } } /* end list_procedures() */ /**************************************************************************** * * function: bottom_dump() * * purpose: print info at bottom of fe dump file. * Called also by bottominfo command. */ void bottom_dump(fd) FILE *fd; { int i; FILE *old_fd = outfd; int e_type; outfd = fd; readflag = 0; outstring("\nread\n"); readflag = 1; /* string variables */ for ( i = 0 ; i < web.global_count ; i++ ) { if ( globals(i)->flags & STRINGVAL ) { sprintf(msg,"%s := ",globals(i)->name); convert_string(globals(i)->value.string,msg+strlen(msg)); strcat(msg,"\n"); outstring(msg); } } /* extra attributes with function definitions */ for ( e_type = VERTEX ; e_type <= FACETEDGE ; e_type++ ) { struct extra *ex; for ( i = 0, ex = EXTRAS(e_type) ; i < web.skel[e_type].extra_count ; i++, ex++ ) { int k; if ( !(ex->flags & DUMP_ATTR) ) continue; if ( !ex->code.start ) continue; sprintf(msg,"\ndefine %s attribute %s %s",typenames[e_type], ex->name, datatype_name[ex->type]); for ( k = 0 ; k < ex->array_spec.dim ; k++ ) sprintf(msg+strlen(msg),"[%d]",ex->array_spec.sizes[k]); outstring(msg); outstring(" function \n { "); outstring(print_express(&ex->code,'X')); outstring(" }\n"); } } list_procedures(LIST_FULL); if ( show_expr[EDGE] ) { outstring(print_express(show_command+EDGE,'X')); outstring("\n"); } if ( show_expr[FACET] ) { outstring(print_express(show_command+FACET,'X')); outstring("\n"); } /* misc state saving */ outstring("\n"); switch(torus_display_mode) { case TORUS_RAW_MODE: outstring("raw_cells\n"); break; case TORUS_CLIPPED_MODE: outstring("clipped\n"); break; case TORUS_CONNECTED_MODE: outstring("connected\n"); break; } if ( window_aspect_ratio != 0.0 ) { sprintf(msg,"window_aspect_ratio := %18.15f\n",window_aspect_ratio); outstring(msg); } if ( clip_view_flag ) toggle_save("clip_view"); if ( slice_view_flag ) toggle_save("slice_view"); if ( visibility_test ) toggle_save("visibility_test"); if ( full_bounding_box_flag ) toggle_save("full_bounding_box"); if ( gridflag > 0 ) toggle_save("gridflag"); if ( gridflag == 0 ) toggle_save_off("gridflag"); if ( user_thickness_flag ) { sprintf(msg,"thickness := %g\n",(double)thickness); outstring(msg); } if ( hessian_slant_cutoff != 0.0 ) { sprintf(msg,"hessian_slant_cutoff := %g\n",(double)hessian_slant_cutoff); outstring(msg); } if ( everything_quantities_flag ) outstring("convert_to_quantities\n"); if ( bezier_flag ) outstring("bezier_basis on\n"); if ( conj_grad_flag ) toggle_save("conj_grad"); if ( quantities_only_flag && !everything_quantities_flag) toggle_save("quantities_only"); if ( !ribiere_flag ) toggle_save_off("ribiere"); if ( assume_oriented_flag ) toggle_save("assume_oriented"); if ( view_4D_flag ) toggle_save("view_4D"); if ( kusner_flag ) toggle_save("kusner"); if ( estimate_flag ) toggle_save("estimate"); if ( unit_normal_flag ) toggle_save("deturck"); if ( sqgauss_flag ) toggle_save("sqgauss"); if ( autopop_flag ) toggle_save("autopop"); if ( autochop_flag ) toggle_save("autochop"); if ( circular_arc_flag ) toggle_save("circular_arc_draw"); if ( rgb_colors_flag ) toggle_save("rgb_colors"); if ( kraynikpopedge_flag ) toggle_save("kraynikpopedge"); if ( !kraynikpopvertex_flag ) toggle_save("kraynikpopvertexedge"); if ( quiet_flag ) toggle_save("quiet"); if ( augmented_hessian_flag > 0 ) toggle_save("augmented_hessian"); if ( augmented_hessian_flag == 0 ) toggle_save_off("augmented_hessian"); if ( hessian_special_normal_flag ) toggle_save("hessian_special_normal"); if ( !hessian_quiet_flag ) toggle_save_off("hessian_quiet"); if ( quiet_go_flag ) toggle_save("quietgo"); if ( old_area_flag ) toggle_save("old_area"); if ( approx_curve_flag ) toggle_save("approx_curv"); if ( runge_kutta_flag ) toggle_save("runge_kutta"); if ( check_increase_flag ) toggle_save("check_increase"); if ( web.area_norm_flag ) toggle_save("area_normalization"); if ( conf_edge_curv_flag ) toggle_save("conf_edge"); if ( effective_area_flag ) toggle_save("effective_area"); if ( web.torus_body_flag ) toggle_save("connected"); if ( web.torus_clip_flag ) toggle_save("clipped"); if ( thickenflag ) toggle_save("thicken"); if ( !innerflag ) toggle_save_off("show_inner"); if ( !outerflag ) toggle_save_off("show_outer"); if ( colorflag ) { toggle_save("colormap"); sprintf(msg,"colorfile := \"%s\"\n",cmapname); } if ( dirichlet_flag ) toggle_save("dirichlet_mode"); if ( sobolev_flag ) toggle_save("sobolev_mode"); if ( pop_disjoin_flag ) toggle_save("pop_disjoin"); if ( pop_enjoin_flag ) toggle_save("pop_enjoin"); if ( pop_to_face_flag ) toggle_save("pop_to_face"); if ( pop_to_edge_flag ) toggle_save("pop_to_edge"); if ( ps_colorflag > 0 ) toggle_save("pscolorflag"); if ( ps_colorflag == 0 ) toggle_save_off("pscolorflag"); if ( labelflag == 0 ) toggle_save_off("labelflag"); if ( labelflag > 0 ) toggle_save("labelflag"); if ( crossingflag > 0 ) toggle_save("crossingflag"); if ( crossingflag == 0 ) toggle_save_off("crossingflag"); if ( smooth_graph_flag ) toggle_save("smooth_graph"); if ( hessian_by_diff_flag ) toggle_save("hessian_diff"); if ( !hessian_normal_flag ) toggle_save("hessian_normal"); if ( hessian_normal_perp_flag ) toggle_save("hessian_normal_perp"); if ( hessian_normal_one_flag ) toggle_save("hessian_normal_one"); if ( hessian_double_normal_flag ) toggle_save("hessian_double_normal"); if ( post_project_flag ) toggle_save("post_project"); if ( mean_curv_int_flag ) toggle_save("mean_curvature_integral"); if ( normal_curvature_flag ) toggle_save("normal_curvature"); if ( div_normal_curvature_flag ) toggle_save("div_normal_curvature"); if ( !shading_flag ) toggle_save_off("shading"); if ( !color_flag ) toggle_save_off("facet_colors"); if ( boundary_curvature_flag ) toggle_save("boundary_curvature"); if ( normal_motion_flag ) toggle_save("normal_motion"); if ( check_pinning_flag ) toggle_save("pinning"); if ( !metric_convert_flag ) toggle_save_off("metric_conversion"); if ( autorecalc_flag ) toggle_save("autorecalc"); if ( make_pos_def_flag ) toggle_save("force_pos_def"); if ( !gv_binary_flag ) toggle_save_off("gv_binary"); if ( self_similar_flag ) toggle_save("self_similar"); if ( interp_bdry_param ) toggle_save("interp_bdry_param"); if ( web.bodycount && !web.gravflag ) toggle_save_off("gravity"); if ( hessian_linear_metric_flag ) { toggle_save("linear_metric"); #ifdef LONGDOUBLE sprintf(msg,"linear_metric_mix := %2.*Lg\n",DPREC,linear_metric_mix); #else sprintf(msg,"linear_metric_mix := %2.15g\n",(DOUBLE)linear_metric_mix); #endif outstring(msg); } if ( transform_expr[0] ) { sprintf(msg,"transform_expr \"%s\"\n",transform_expr); outstring(msg); } if ( !transforms_flag && (transform_count>1)) toggle_save_off("transforms"); outstring("\n\n"); if ( web.target_tolerance != DEFAULT_TARGET_TOLERANCE ) { sprintf(msg,"target_tolerance := %2.15g\n",(DOUBLE)web.target_tolerance); outstring(msg); } if ( brightness != DEFAULT_BRIGHTNESS ) { sprintf(msg,"brightness := %2.15g\n",(DOUBLE)brightness); outstring(msg); } /* windup */ outfd = old_fd; } /* end bottom_dump */ /********************************************************************* * * function: facetedge_dump() * * purpose: List facetedge with facet and edge links. Meant for * screen display for debugging, not standard dump file. * */ void facetedge_dump(fe,fd) facetedge_id fe; FILE *fd; { edge_id e_id; facet_id f_id; facetedge_id nf = get_next_facet(fe); facetedge_id pf = get_prev_facet(fe); facetedge_id ne = get_next_edge(fe); facetedge_id pe = get_prev_edge(fe); FILE *old_fd = outfd; int i; struct extra *ex; outfd = fd; e_id = get_fe_edge(fe); f_id = get_fe_facet(fe); sprintf(msg,"%6s %5s %5s %8s %8s %8s %8s", ELNAME(fe), SELNAME1(e_id), SELNAME2(f_id), (valid_id(pe) ? SELNAME3(pe) : "0"), (valid_id(ne) ? SELNAME4(ne) : "0"), (valid_id(pf) ? SELNAME5(pf) : "0"), (valid_id(nf) ? SELNAME6(nf) : "0")); outstring(msg); /* extra attributes */ for ( i = 0, ex = EXTRAS(FACETEDGE) ; i < web.skel[FACETEDGE].extra_count; i++,ex++ ) { char *at = get_extra(fe,i); if ( !(ex->flags & DUMP_ATTR) ) continue; if ( ex->array_spec.datacount == 0 ) continue; sprintf(msg," %s ",ex->name); print_array_attribute(ex,at); } outstring("\n"); outfd = old_fd; } /* end facetedge_dump() */ /********************************************************************* * * Function: dump_force() * * Purpose: List forces at vertices for debugging purposes. */ void dump_force() { char name[400]; FILE *fd; vertex_id v_id; int i; FILE *old_fd = outfd; prompt("Enter name of dump file: ",name,sizeof(name)); if ( name[0] == '\0' ) return; fd = fopen(name,"w"); if ( fd == NULL ) { sprintf(errmsg,"Cannot open file %s.\n",name); kb_error(1008,errmsg,RECOVERABLE); } outfd = fd; sprintf(msg,"%s: Dump of force.\n\n",name); outstring(msg); /* vertex dump */ outstring("\nvertices coordinates \n"); FOR_ALL_VERTICES(v_id) { REAL *x,*f,mag; x = get_coord(v_id); switch(SDIM) { case 2: #ifdef LONGDOUBLE sprintf(msg,"%3s %17.*Lf %17.*Lf |x| = %17.*Lf ", ELNAME(v_id),DPREC,x[0],DPREC,x[1],DPREC,sqrt(SDIM_dot(x,x))); #else sprintf(msg,"%3s %17.15f %17.15f |x| = %17.15f ", ELNAME(v_id),x[0],x[1],sqrt(SDIM_dot(x,x))); #endif outstring(msg); break; case 3: #ifdef LONGDOUBLE sprintf(msg,"%3s %17.*Lf %17.*Lf %17.*Lf |x| = %17.*Lf ", ELNAME(v_id),DPREC,x[0],DPREC,x[1],DPREC,x[2],DPREC,sqrt(SDIM_dot(x,x))); #else sprintf(msg,"%3s %17.15f %17.15f %17.15f |x| = %17.15f ", ELNAME(v_id),x[0],x[1],x[2],sqrt(SDIM_dot(x,x))); #endif outstring(msg); break; } if ( get_vattr(v_id) & CONSTRAINT ) { conmap_t *conmap = get_v_constraint_map(v_id); struct constraint *con; sprintf(msg," constraints "); outstring(msg); for ( i = 1 ; i <= (int)conmap[0] ; i++ ) { con = get_constraint(conmap[i]); if ( !(con->attr & GLOBAL) ) { if ( con->attr & NAMED_THING ) { sprintf(msg,"%s ",con->name); outstring(msg); } else { sprintf(msg,"%d ",conmap[i]&CONMASK); outstring(msg); } } } } if ( get_vattr(v_id)&FIXED ) { sprintf(msg, " fixed "); outstring(msg); } sprintf(msg,"\n"); outstring(msg); f = get_force(v_id); mag = sqrt(SDIM_dot(f,f)); switch(SDIM) { case 2: #ifdef LONGDOUBLE sprintf(msg,"f: %17.*Lf %17.*Lf |f| = %g\n\n", DPREC,f[0],DPREC,f[1],(DOUBLE)mag); outstring(msg); #else sprintf(msg,"f: %17.15f %17.15f |f| = %g\n\n", f[0],f[1],(DOUBLE)mag); outstring(msg); #endif break; case 3: #ifdef LONGDOUBLE sprintf(msg,"f: %17.*Lf %17.*Lf %17.*Lf |f| = %g\n\n", DPREC,f[0],DPREC,f[1],DPREC,f[2],(DOUBLE)mag); outstring(msg); #else sprintf(msg,"f: %17.15f %17.15f %17.15f |f| = %g\n\n", f[0],f[1],f[2],(DOUBLE)mag); outstring(msg); #endif break; } } fclose(fd); outfd = old_fd; } /* end dump_force */ /*********************************************************************************** * * function: list_attributes() * * purpose: List all defined attributes of elements (the ones with entries * in attribute table, not flags like FIXED). * */ void list_attributes() { int n,k,j; struct extra *ex; outstring( "Extra attributes (and dynamically allocated internal attributes)\n"); for ( n = 0 ; n < NUMELEMENTS ; n++ ) { sprintf(msg,"%s attributes:\n",typenames[n]); outstring(msg); for ( k = 0 ; k < web.skel[n].extra_count ; k++ ) { ex = EXTRAS(n)+k; sprintf(msg,"%32s %s",ex->name,datatype_name[ex->type]); for ( j = 0 ; j < ex->array_spec.dim ; j++ ) sprintf(msg+strlen(msg),"[%d]",ex->array_spec.sizes[j]); strcat(msg,"\n"); outstring(msg); } } } /* end list_attributes() */ /************************************************************************** * * Function: list_quantity() * * Purpose: List quantity in top of datafile, and for "list quantity" * command. */ void list_quantity(k) int k; /* quantity number */ { struct gen_quant *q; int i; struct method_instance *mi; q = GEN_QUANT(k); /* first, its methods */ outstring("\n"); for ( i = 0 ; i < q->method_count ; i++ ) { mi = METH_INSTANCE(q->meth_inst[i]); if ( mi->flags & (IMPLICIT_INSTANCE|DEFAULT_INSTANCE) ) continue; outstring("METHOD_INSTANCE "); outstring(mi->name); outstring(" METHOD "); outstring(basic_gen_methods[mi->gen_method].name); dump_method_specs(q->meth_inst[i]); } switch ( q->flags & (Q_INFO|Q_FIXED|Q_ENERGY|Q_CONSERVED) ) { case Q_INFO: sprintf(msg,"QUANTITY %s INFO_ONLY ",q->name); outstring(msg); break; case Q_CONSERVED: #ifdef LONGDOUBLE sprintf(msg,"QUANTITY %s CONSERVED lagrange_multiplier %2.*Lg", q->name,DPREC,q->pressure); #else sprintf(msg,"QUANTITY %s CONSERVED lagrange_multiplier %2.15g", q->name,q->pressure); #endif outstring(msg); break; case Q_FIXED: #ifdef LONGDOUBLE sprintf(msg,"QUANTITY %s FIXED = %2.*Lg lagrange_multiplier %2.*Lg", q->name,DPREC,q->target,DPREC,q->pressure); #else sprintf(msg,"QUANTITY %s FIXED = %2.15g lagrange_multiplier %2.15g", q->name,q->target,q->pressure); #endif outstring(msg); break; case Q_ENERGY: sprintf(msg,"QUANTITY %s ENERGY ",q->name); outstring(msg); break; } if ( q->tolerance >= 0.0 ) #ifdef LONGDOUBLE { sprintf(msg," tolerance %2.*Lg ",DPREC,q->tolerance); outstring(msg); } #else { sprintf(msg," tolerance %2.15g ",q->tolerance); outstring(msg); } #endif if ( q->modulus != 1.0 ) #ifdef LONGDOUBLE { sprintf(msg," modulus %2.*Lg ",DPREC,q->modulus); outstring(msg); } #else { sprintf(msg," modulus %2.15g ",q->modulus); outstring(msg); } #endif if ( q->volconst != 0.0 ) #ifdef LONGDOUBLE { sprintf(msg," volconst %2.*Lg ",DPREC,q->volconst); outstring(msg); } #else { sprintf(msg," volconst %2.15g ",q->volconst); outstring(msg); } #endif if ( q->flags & Q_COMPOUND ) { outstring(" function "); outstring(print_express(&q->expr,'\0')); } else for ( i = 0 ; i < q->method_count ; i++ ) { mi = METH_INSTANCE(q->meth_inst[i]); outstring(" method "); if ( mi->flags & (IMPLICIT_INSTANCE|DEFAULT_INSTANCE) ) { outstring(basic_gen_methods[mi->gen_method].name); dump_method_specs(q->meth_inst[i]); } else outstring(mi->name); } outstring("\n"); } /************************************************************************** * * Function: list_method_instance() * * Purpose: List method instance in top of datafile, and implement * "list method_instance" command. */ void list_method_instance(k) int k; { struct method_instance *mi = METH_INSTANCE(k); outstring("METHOD_INSTANCE "); outstring(mi->name); outstring(" METHOD "); outstring(basic_gen_methods[mi->gen_method].name); dump_method_specs(k); } /************************************************************************** * * Function: list_constraint() * * Purpose: List constraint in top of datafile, and implement * "list constraint" command. */ void list_constraint(cnum) int cnum; { struct constraint *con = get_constraint(cnum); int j; if ( con->attr & NAMED_THING ) sprintf(msg,"\nCONSTRAINT %s ",con->name); else sprintf(msg,"\nCONSTRAINT %d ",cnum); outstring(msg); if ( con->attr & NONWALL ) outstring(" NONWALL "); if ( con->attr & B_CONVEX ) outstring(" CONVEX "); if ( con->attr & NONNEGATIVE ) outstring(" NONNEGATIVE"); if ( con->attr & NONPOSITIVE ) outstring(" NONPOSITIVE"); if ( con->attr & GLOBAL ) outstring(" GLOBAL"); if ( con->content_rank ) { sprintf(msg," content_rank %d\n",con->content_rank); outstring(msg); } outstring("\nFUNCTION: "); outstring(print_express(con->formula,'X')); outstring("\n"); if ( (con->attr & CON_ENERGY) ) { outstring("ENERGY \n"); for ( j = 0 ; j < con->compcount ; j++ ) { sprintf(msg,"E%1d: ",j+1); outstring(msg); outstring(print_express(con->envect[j],'X')); outstring("\n"); } } if ( con->attr & CON_CONTENT ) { outstring("CONTENT \n"); for ( j = 0 ; j < con->compcount ; j++ ) { sprintf(msg,"C%1d: ",j+1); outstring(msg); outstring(print_express(con->convect[j],'X')); outstring("\n"); } } } /************************************************************************** * * Function: list_boundary() * * Purpose: List boundary in top of datafile, and implement * "list boundary" command. */ void list_boundary(bnum) int bnum; { struct boundary * bdry = web.boundaries + bnum; int j; if ( bdry->attr & NAMED_THING ) sprintf(msg,"\nBOUNDARY %s PARAMETERS %d",bdry->name,bdry->pcount); else sprintf(msg,"\nBOUNDARY %d PARAMETERS %d",bnum,bdry->pcount); outstring(msg); if ( bdry->attr & B_CONVEX ) outstring(" CONVEX"); if ( bdry->attr & NONWALL ) outstring(" NONWALL "); if ( bdry->attr & PARTNER_HITTING ) outstring(" PARTNER_HITTING "); if ( bdry->content_rank ) { sprintf(msg," content_rank %d\n",bdry->content_rank); outstring(msg); } outstring("\n"); for ( j = 0 ; j < SDIM ; j++ ) { sprintf(msg,"X%1d: ",j+1); outstring(msg); outstring(print_express(bdry->coordf[j],'P')); outstring("\n"); } if ( (bdry->attr & CON_ENERGY) ) { outstring("ENERGY \n"); for ( j = 0 ; j < bdry->compcount ; j++ ) { sprintf(msg,"E%1d: ",j+1); outstring(msg); outstring(print_express(bdry->envect[j],'X')); outstring("\n"); } } if ( bdry->attr & CON_CONTENT ) { outstring("CONTENT \n"); for ( j = 0 ; j < bdry->compcount ; j++ ) { sprintf(msg,"C%1d: ",j+1); outstring(msg); outstring(print_express(bdry->convect[j],'X')); outstring("\n"); } } outstring("\n"); } /* end list_boundaryconstraint() */ /************************************************************************* * * Function: list_forwards() * * Purpose: list forward declared names */ void list_forwards() { int i; int title_flag = 0; /* constraints */ for ( i = 0 ; i < web.maxcon ; i++ ) { struct constraint *con = get_constraint(i); if ( !(con->attr & IN_USE) ) continue; if ( isdigit(con->name[0]) ) continue; if ( !title_flag ) outstring("\n// Forward declarations\n"); title_flag = 1; outstring("constraint "); outstring(con->name); outstring(";\n"); } /* boundaries */ for ( i = 0 ; i < web.bdrymax ; i++ ) { struct boundary *bdry = web.boundaries + i; if ( !(bdry->attr & IN_USE) ) continue; if ( isdigit(bdry->name[0]) ) continue; if ( !title_flag ) outstring("\n// Forward declarations\n"); title_flag = 1; outstring("boundary "); outstring(bdry->name); outstring(";\n"); } /* quantities */ for ( i = 0 ; i < gen_quant_count ; i++ ) { if ( GEN_QUANT(i)->flags & DEFAULT_QUANTITY ) continue; if ( !title_flag ) outstring("\n// Forward declarations\n"); title_flag = 1; outstring("quantity "); outstring(GEN_QUANT(i)->name); outstring(";\n"); } /* method_instances */ for ( i = LOW_INST ; i < meth_inst_count ; i++ ) { if ( METH_INSTANCE(i)->flags & (IMPLICIT_INSTANCE|DEFAULT_INSTANCE) ) continue; if ( !title_flag ) outstring("\n// Forward declarations\n"); title_flag = 1; outstring("method_instance "); outstring(METH_INSTANCE(i)->name); outstring(";\n"); } if ( title_flag ) outstring("// End forward declarations\n\n"); } /* end list_forwards() */ evolver-2.30c.dfsg/src/metis.c0000644000175300017530000007746311410765113016523 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************** * * File: metis.c * * Contents: Interface to MeTiS library of George Karypis * and Vipin Kumar, U Minnesota, karypis@cs.umn.edu, kumar@cs.umn.edu * Define -DMETIS on compiler command line and link libmetis.a * if you want to use these routines. */ #include "include.h" #ifndef METIS void metis_partition(parts,algorithm) int parts; int algorithm; /* METIS_ or KMETIS_ */ { kb_error(1623,"This Evolver not compiled with METIS library.\n",RECOVERABLE); } void metis_partition_dual(parts,algorithm) int parts; int algorithm; /* METIS_ or KMETIS_ */ { kb_error(1624,"This Evolver not compiled with METIS library.\n",RECOVERABLE); } void metis_partition_body(parts,algorithm) int parts; int algorithm; /* METIS_ or KMETIS_ */ { kb_error(3778,"This Evolver not compiled with METIS library.\n",RECOVERABLE); } void metis_order(S) struct linsys *S; { kb_error(1625,"This Evolver not compiled with METIS library.\n",RECOVERABLE); } void metis_vertex_order(parts) int parts; { kb_error(1626,"This Evolver not compiled with METIS library.\n",RECOVERABLE); } void metis_partition_plain(parts,algorithm) int parts; int algorithm; /* METIS_ or KMETIS_ */ { kb_error(6345,"This Evolver not compiled with METIS library.\n",RECOVERABLE); } #else #include "ytab.h" #include "metis.h" /************************************************************************ * * function: metis_partition_plain() * * purpose: Use metis to partition vertices. Passes actual vertex-edge * graph to metis. Only does end-edge vertices. Leaves * partition number in vertex attribute vpart. */ void metis_partition_plain(parts,algorithm) int parts; int algorithm; /* METIS_ or KMETIS_ */ { int i; GraphType graph; int *partition, options[10], edgecut, nparts, numbering; vertex_id v_id; edge_id e_id,start_e; int j,k; int vpart; int weightflag = 0; if ( web.representation == SIMPLEX ) kb_error(1627,"Cannot do Metis on simplex model.\n",RECOVERABLE); if ( web.skel[VERTEX].count != web.skel[VERTEX].max_ord+1 ) kb_error(1628,"Need packed vertex numbering to do Metis.\n",RECOVERABLE); /* use partition attribute for contiguous numbering */ vpart = find_attribute(VERTEX,"vpart"); if ( vpart < 0 ) { int one = 1; vpart = add_attribute(VERTEX,"vpart",INTEGER_TYPE,0,&one,1,NULL); } k = 0; FOR_ALL_VERTICES(v_id) if ( !(get_vattr(v_id) & (Q_MIDPOINT|Q_MIDEDGE|Q_MIDFACET)) ) ((int*)get_extra(v_id,vpart))[0] = k++; nparts = parts; memset((char*)&graph,0,sizeof(graph)); /* construct graph data */ graph.xadj = (int*)temp_calloc(web.skel[VERTEX].count+1,sizeof(int)); graph.adjncy = (int*)temp_calloc(2*web.skel[EDGE].count,sizeof(int)); j = 0; i = 0; FOR_ALL_VERTICES(v_id) if ( !(get_vattr(v_id) & (Q_MIDPOINT|Q_MIDEDGE|Q_MIDFACET)) ) { e_id = start_e = get_vertex_edge(v_id); do { vertex_id vv_id = get_edge_headv(e_id); graph.adjncy[j++] = ((int*)get_extra(vv_id,vpart))[0]; e_id = get_next_tail_edge(e_id); } while ( !equal_element(e_id,start_e) ); graph.xadj[++i] = j; } /* do partition */ options[0] = 0; /* take defaults */ numbering = 0; graph.nvtxs = web.skel[VERTEX].count; partition = (int*)temp_calloc(graph.nvtxs,sizeof(int)); METIS_PartGraphKway(&graph.nvtxs, graph.xadj, graph.adjncy, NULL, NULL, &weightflag,&numbering, &nparts, options, &edgecut, partition); /* label things */ FOR_ALL_VERTICES(v_id) if ( !(get_vattr(v_id) & (Q_MIDPOINT|Q_MIDEDGE|Q_MIDFACET)) ) { int *vspot = ((int*)get_extra(v_id,vpart)); *vspot = partition[*vspot]; } /* free storage */ temp_free((char*)graph.xadj); temp_free((char*)graph.adjncy); temp_free((char*)partition); } /************************************************************************ * * function: metis_partition_dual() * * purpose: Use metis to partition facets. Passes dual facet-edge * graph to metis. * For string model, treats vertices and edges as graph * nodes. */ void metis_partition_dual(parts,algorithm) int parts; int algorithm; /* METIS_ or KMETIS_ */ { int i,k; GraphType graph; int *partition, options[10], edgecut, nparts, numbering; vertex_id v_id; edge_id e_id; int j; int weightflag = 0; if ( web.representation == SIMPLEX ) kb_error(1629,"Cannot do Metis on simplex model.\n",RECOVERABLE); if ( parts < 2 ) kb_error(3632,"Number of METIS partitions must be at least 2.\n", RECOVERABLE); nparts = parts; memset((char*)&graph,0,sizeof(graph)); if ( web.representation == STRING ) { int epart; int adj; /* total adjacencies */ edge_id ee; /* get partition attribute */ epart = find_attribute(EDGE,"epart"); if ( epart < 0 ) { int one = 1; epart = add_attribute(EDGE,"epart",INTEGER_TYPE,0,&one,1,NULL); } /* use partition attribute for contiguous numbering */ k = 0; FOR_ALL_EDGES(e_id) ((int*)get_extra(e_id,epart))[0] = k++; /* count adjacencies */ adj = 0; FOR_ALL_VERTICES(v_id) { int valence = 0; e_id = ee = get_vertex_edge(v_id); do{ valence++; ee = get_next_tail_edge(ee); } while ( !equal_id(ee,e_id) ); adj += (valence-1)*valence; } /* construct graph data */ graph.xadj = (int*)temp_calloc(web.skel[EDGE].count+1,sizeof(int)); graph.adjncy = (int*)temp_calloc(adj,sizeof(int)); j = 0; i = 0; FOR_ALL_EDGES(e_id) { ee = get_next_tail_edge(e_id); while ( !equal_id(ee,e_id) ) { graph.adjncy[j++] = ((int*)get_extra(ee,epart))[0]; ee = get_next_tail_edge(ee); } ee = get_next_head_edge(e_id); while ( !equal_id(ee,e_id) ) { graph.adjncy[j++] = ((int*)get_extra(ee,epart))[0]; ee = get_next_head_edge(ee); } graph.xadj[++i] = j; } /* do partition */ options[0] = 0; /* take defaults */ numbering = 0; graph.nvtxs = web.skel[EDGE].count; partition = (int*)temp_calloc(graph.nvtxs,sizeof(int)); METIS_PartGraphKway(&graph.nvtxs, graph.xadj, graph.adjncy, NULL, NULL, &weightflag,&numbering, &nparts, options, &edgecut, partition); /* label things */ FOR_ALL_EDGES(e_id) { int *espot = ((int*)get_extra(e_id,epart)); *espot = partition[*espot]; } } else /* SOAPFILM */ { int fpart; int adj; /* total adjacencies */ facet_id f_id; /* get facet partition attribute */ fpart = find_attribute(FACET,"fpart"); if ( fpart < 0 ) { int one = 1; fpart = add_attribute(FACET,"fpart",INTEGER_TYPE,0,&one,1,NULL); } /* use partition attribute to hold contiguous ordinals */ k = 0; FOR_ALL_FACETS(f_id) ((int*)get_extra(f_id,fpart))[0] = k++; /* count adjacencies */ adj = 0; FOR_ALL_EDGES(e_id) { int valence = get_edge_valence(e_id); adj += (valence-1)*valence; } /* construct graph data */ graph.xadj = (int*)temp_calloc(web.skel[FACET].count+1,sizeof(int)); graph.adjncy = (int*)temp_calloc(adj,sizeof(int)); j = 0; i = 0; FOR_ALL_FACETS(f_id) { facetedge_id start_fe = get_facet_fe(f_id); facetedge_id fe; for ( k = 0 ; k < FACET_EDGES ; k++ ) { fe = get_next_facet(start_fe); while ( !equal_id(fe,start_fe) ) { facet_id ff_id = get_fe_facet(fe); graph.adjncy[j++] = ((int*)get_extra(ff_id,fpart))[0]; fe = get_next_facet(fe); } start_fe = get_next_edge(start_fe); } graph.xadj[++i] = j; } /* do partition */ options[0] = 0; /* take defaults */ numbering = 0; graph.nvtxs = web.skel[FACET].count; partition = (int*)temp_calloc(graph.nvtxs,sizeof(int)); METIS_PartGraphKway(&graph.nvtxs, graph.xadj, graph.adjncy, NULL, NULL, &weightflag,&numbering, &nparts, options, &edgecut, partition); /* label things */ FOR_ALL_FACETS(f_id) { int *fspot = ((int*)get_extra(f_id,fpart)); *fspot = partition[*fspot]; } } /* free storage */ temp_free((char*)graph.xadj); temp_free((char*)graph.adjncy); temp_free((char*)partition); } /* end metis_partition_dual */ /************************************************************************ * * function: metis_partition_body() * * purpose: Use metis to partition bodies. Passes dual body-body * graph to metis. * For string model, treats vertices and edges as graph * nodes. */ struct bb_adj { int b1,b2; int weight; }; int adjcomp(struct bb_adj *a, struct bb_adj *b) { if ( a->b1 < b->b1 ) return -1; if ( a->b1 > b->b1 ) return 1; if ( a->b2 < b->b2 ) return -1; if ( a->b2 > b->b2 ) return 1; return 0; } void metis_partition_body(parts,algorithm) int parts; int algorithm; /* METIS_ or KMETIS_ */ { int i,m; GraphType graph; int *partition, options[10], edgecut, nparts, numbering; int adjcount; /* total adjacencies */ facet_id f_id; int *bdy_to_inx; body_id *inx_to_bdy; int bcount = web.skel[BODY].count; int weightflag; struct bb_adj *adjlist; body_id b_id; int bod; int bpart; int inx,spot; if ( web.representation == STRING ) kb_error(3770,"Cannot do body Metis in string model. \n",RECOVERABLE); if ( parts < 2 ) kb_error(3771,"Number of METIS partitions must be at least 2.\n", RECOVERABLE); nparts = parts; memset((char*)&graph,0,sizeof(graph)); bpart = find_attribute(BODY,"bpart"); if ( bpart < 0 ) { int one = 1; bpart = add_attribute(BODY,"bpart",INTEGER_TYPE,0,&one,1,NULL); } /* get contiguous indexes for bodies */ bdy_to_inx = (int*)temp_calloc(web.skel[BODY].max_ord+1,sizeof(int)); inx_to_bdy = (body_id*)temp_calloc(bcount,sizeof(body_id)); inx = 0; FOR_ALL_BODIES(b_id) { int ord = ordinal(b_id); bdy_to_inx[ord] = inx; inx_to_bdy[inx] = b_id; inx++; } if ( inx != bcount ) kb_error(3772,"internal error - body count disagreement \n",RECOVERABLE); /* gather adjacencies */ adjlist = (struct bb_adj *)temp_calloc(2*web.skel[FACET].count+1, sizeof(struct bb_adj)); adjcount = 0; FOR_ALL_FACETS(f_id) { body_id b_id = get_facet_body(f_id); body_id bb_id = get_facet_body(inverse_id(f_id)); int ord1,ord2; if ( !valid_id(b_id) || !valid_id(bb_id) ) continue; ord1 = ordinal(b_id); ord2 = ordinal(bb_id); if ( ord1 == ord2 ) continue; /* metis wants adjacencies in both directions */ adjlist[adjcount].b1 = bdy_to_inx[ord1]; adjlist[adjcount].b2 = bdy_to_inx[ord2]; adjlist[adjcount].weight = 1; adjcount++; adjlist[adjcount].b1 = bdy_to_inx[ord2]; adjlist[adjcount].b2 = bdy_to_inx[ord1]; adjlist[adjcount].weight = 1; adjcount++; } /* sort */ qsort((void*)adjlist,adjcount,sizeof(struct bb_adj),FCAST adjcomp); /* uniquify and gather number of facets as weights */ for ( i = 1, m = 0 ; i < adjcount ; i++ ) { if ( (adjlist[i].b1 != adjlist[m].b1) || (adjlist[i].b2 != adjlist[m].b2) ) { m++; adjlist[m] = adjlist[i]; } else adjlist[m].weight += adjlist[i].weight; } adjcount = m; adjlist = (struct bb_adj*)temp_realloc((char*)adjlist, adjcount*sizeof(struct bb_adj)); /* construct graph data */ graph.xadj = (idxtype*)temp_calloc(bcount+1,sizeof(idxtype)); graph.vwgt = (idxtype*)temp_calloc(bcount,sizeof(idxtype)); graph.adjncy = (idxtype*)temp_calloc(adjcount,sizeof(idxtype)); graph.adjwgt = (idxtype*)temp_calloc(adjcount,sizeof(idxtype)); for ( bod = 0, spot = 0 ; spot < adjcount ; spot++ ) { while ( adjlist[spot].b1 != bod ) { bod++; graph.xadj[bod] = spot; } graph.adjncy[spot] = adjlist[spot].b2; graph.adjwgt[spot] = adjlist[spot].weight; graph.vwgt[bod] += adjlist[spot].weight; } bod++; graph.xadj[bod] = adjcount; temp_free((char*)adjlist); /* do partition */ options[0] = 0; /* take defaults */ numbering = 0; /* C-style 0-based indexing */ graph.nvtxs = bcount; weightflag = 3; /* both weights */ partition = (int*)temp_calloc(graph.nvtxs,sizeof(int)); METIS_PartGraphKway(&graph.nvtxs, graph.xadj, graph.adjncy, graph.vwgt, graph.adjwgt, &weightflag,&numbering, &nparts, options, &edgecut, partition); /* label things */ for ( i = 0 ; i < bcount ; i++ ) ((int*)get_extra(inx_to_bdy[i],bpart))[0] = partition[i]; /* free storage */ temp_free((char*)graph.xadj); temp_free((char*)graph.vwgt); temp_free((char*)graph.adjncy); temp_free((char*)graph.adjwgt); temp_free((char*)partition); temp_free((char*)bdy_to_inx); temp_free((char*)inx_to_bdy); } /************************************************************************ * * function: metis_vertex_order() * * purpose: Use metis to order vertices for sparse factoring. * Passes actual vertex-edge graph to metis. * Just demo. */ void metis_vertex_order(mmdswitch) int mmdswitch; /* size of subgraph to stop at */ { int i; GraphType graph; int options[10], numbering; vertex_id v_id; edge_id e_id,start_e; int j; int *perm,*iperm; if ( web.representation == SIMPLEX ) kb_error(1634,"Cannot do Metis on simplex model.\n",RECOVERABLE); if ( web.skel[VERTEX].count != web.skel[VERTEX].max_ord+1 ) kb_error(1635,"Need packed vertex numbering to do Metis.\n",RECOVERABLE); memset((char*)&graph,0,sizeof(graph)); /* construct graph data */ graph.xadj = (int*)temp_calloc(web.skel[VERTEX].count+1,sizeof(int)); graph.adjncy = (int*)temp_calloc(2*web.skel[EDGE].count,sizeof(int)); j = 0; i = 0; FOR_ALL_VERTICES(v_id) { e_id = start_e = get_vertex_edge(v_id); do { graph.adjncy[j++] = loc_ordinal(get_edge_headv(e_id)); e_id = get_next_tail_edge(e_id); } while ( !equal_element(e_id,start_e) ); graph.xadj[++i] = j; } /* do partition */ numbering = 0; graph.nvtxs = web.skel[VERTEX].count; perm = (int*)temp_calloc(graph.nvtxs,sizeof(int)); iperm = (int*)temp_calloc(graph.nvtxs,sizeof(int)); #ifndef METIS2 options[0] = 1; /* enable options, METIS 4.0 version */ options[1] = 3; /* Sorted Heavy-Edge Matching */ options[2] = 1; /* Edge-based region growing */ options[3] = 2; /* One-sided node FM refinement */ options[4] = 0; /* debugging only */ options[5] = 0; /* no compression */ options[6] = 0; /* don't order dense columns last */ options[7] = 1; /* number of separators to find each step */ METIS_NodeND(&graph.nvtxs, graph.xadj, graph.adjncy,&numbering,options,perm,iperm); puts("No tree available in METIS-4\n"); #else /* metis 2 with my modifications */ options[0] = 1; /* enable options, METIS 2.0 version */ options[1] = 100; /* coarsen to */ options[2] = 21; /* MType SHEM */ options[3] = 2; /* IPType GGGP */ options[4] = 13; /* RType BGKLR */ OMETIS(&graph.nvtxs, graph.xadj, graph.adjncy,&options,&numbering, perm,iperm,&mmdswitch); /* print separation tree (just a temporary thing */ puts("node subtree size separator"); for ( i = 0 ; i < graph.nvtxs ; i++ ) if ( stree[i].nvtxs > 0 ) printf("%4d. %d %d-%d \n",i,stree[i].nvtxs,stree[i].lo, stree[i].hi); myfree((char*)stree); #endif /* free storage */ temp_free((char*)graph.xadj); temp_free((char*)graph.adjncy); temp_free((char*)perm); temp_free((char*)iperm); } /************************************************************************ * * function: metis_order() * * purpose: Use metis to order linear system for sparse factoring. * Passes actual vertex-edge graph to metis. * Returns A_OFF-based permutation for ysmp. */ void metis_order(S) struct linsys *S; /* system to order */ { int i,k; GraphType graph; int options[10], numbering; int j; int tot; memset((char*)&graph,0,sizeof(graph)); /* construct graph data */ /* metis requires full graph matrix, not just symmetric part */ graph.xadj = (int*)temp_calloc(S->N+1,sizeof(int)); graph.adjncy = (int*)temp_calloc(2*S->IA[S->N],sizeof(int)); /* first have to count neighbors into graph.xadj */ for ( i = 0 ; i < S->N ; i++ ) { int end; graph.xadj[i] += S->IA[i+1] - S->IA[i] - 1; /* not self */ end = S->IA[i+1] - A_OFF; for ( j = S->IA[i]+1 - A_OFF ; j < end ; j++ ) graph.xadj[S->JA[j] - A_OFF]++; } /* reset xadj to starts of intervals */ for ( tot = 0, i = 0 ; i < S->N ; i++ ) { int num = graph.xadj[i]; graph.xadj[i] = tot; tot += num; } graph.xadj[S->N] = tot; /* insert adjacencies */ for ( i = 0 ; i < S->N ; i++ ) { int end = S->IA[i+1] - A_OFF; for ( j = S->IA[i]+1 - A_OFF ; j < end ; j++ ) { graph.adjncy[graph.xadj[i]++] = S->JA[j] - A_OFF; graph.adjncy[graph.xadj[S->JA[j] - A_OFF]++] = i; } } /* reset xadj */ for ( i = S->N ; i > 0 ; i-- ) graph.xadj[i] = graph.xadj[i-1]; graph.xadj[0] = 0; /* do partition */ options[0] = 0; /* default options */ memset(options,0,sizeof(options)); numbering = 0; graph.nvtxs = S->N; if ( !S->P ) S->P = (int*)temp_calloc(graph.nvtxs,sizeof(int)); if ( !S->IP ) S->IP = (int*)temp_calloc(graph.nvtxs,sizeof(int)); METIS_NodeND(&graph.nvtxs, graph.xadj, graph.adjncy,&numbering,options, S->P,S->IP); /* adjust zero based indexing */ /* ysmp wants 1 based */ if ( numbering == 0 ) for ( k = 0 ; k < S->N ; k++ ) { S->P[k]++; S->IP[k]++; } S->flags |= S_ORDERFOUND; #ifdef METIS2 /* find length of stree list and clean up stree fields */ S->maxsepsize = 0; for ( k = 0, S->streemax = 1, s = S->stree ; k <= S->streemax ; k++,s++ ) { if ( s->nvtxs < 0 ) { s->lo = s->hi = s->isleaf = 0;} s->u.info.size = 0; s->u.info.vlist = NULL; s->u.info.mat = NULL; if ( S->maxsepsize < (s->hi-s->lo) ) S->maxsepsize = (s->hi-s->lo); if ( (s->nvtxs > 0) && !(s->isleaf & 1) ) S->streemax = 2*k+1; } #endif #ifdef PRINTSEPTREE /* print separation tree (just a temporary thing */ for ( i = 0 ; i <= S->streemax ; i++ ) if ( S-> [i].nvtxs > 0 ) printf("%4d. %d %d-%d \n",i,S->stree[i].nvtxs,S->stree[i].lo, S->stree[i].hi); #endif /* free storage */ temp_free((char*)graph.xadj); temp_free((char*)graph.adjncy); /* if ( !hessian_quiet_flag ) tree_analyze(S); */ } #endif /*************************************************************************** * * function: do_tree_factor() * * purpose: do actual factoring of matrix. * Meant to be run in parallel in shared memory. * Each proc starts at end of stree and works back * by nproc-size steps, waiting until sons done. * Unfortunately, supernode blocks are not necessarily * dense in A, just the fill is. */ void do_tree_factor(S) struct linsys *S; { int me = GET_THREAD_ID; int spot; /* stree index */ int blocks; int i,j; size_t k; REAL *mat; int *work; /* some workspace */ size_t sepsize; /* size of separator */ size_t heapsize; /* number of lists to merge */ struct hp { int row; /* which row entry from */ int index; /* index into pJA */ int col; /* column, from pJA or sentinel */ } *heap; work = (int*)temp_calloc(S->N*sizeof(int)+(S->maxsepsize+5)*sizeof(struct hp),1); blocks = S->streemax/nprocs; spot = blocks*nprocs + me; if ( spot > S->streemax ) spot -= nprocs; for ( ; spot >= 1 ; spot -= nprocs ) /* metis starts at stree[1] */ { /* factor one node */ SepNodeType *stree = S->stree + spot; /* current node */ SepNodeType *left = stree + spot,*right = left+1; /* sons */ SepNodeType *son; int *varlist; /* temporary variable list */ int *vtop; /* pointer into varlist */ size_t varcount; struct hp h; int hspot,next; int kk; REAL *base; /* pivot row */ int row; if ( stree->nvtxs <= 0 ) { stree->isleaf |= 2; continue; } if ( !stree->isleaf ) /* busy wait for sons */ while ( !left->isleaf || !right->isleaf ) ; sepsize = stree->hi - stree->lo; heapsize = stree->isleaf ? sepsize : sepsize+2; /* heap merge columns and sons */ heap = (struct hp*) work; varlist = (int*)((char*)work+heapsize*sizeof(struct hp)); /* populate heap */ for ( k = 0 ; k < sepsize ; k++ ) { heap[k].row = (int)(stree->lo + k); heap[k].index = S->pIA[heap[k].row]; heap[k].col = S->pJA[heap[k].index]; } if ( !stree->isleaf) { size_t lsep = left->hi-left->lo; size_t rsep = right->hi - right->lo; if ( left->u.info.size > lsep ) { heap[sepsize].row = -1; /* left son */ heap[sepsize].index = (int)lsep; heap[sepsize].col = left->u.info.vlist[lsep]; } else { heap[sepsize].row = -3; heap[sepsize].index = 0; heap[sepsize].col = MAXINT; } if ( right->u.info.size > rsep ) { heap[sepsize+1].row = -2; /* right son */ heap[sepsize+1].index = (int)rsep; heap[sepsize+1].col = right->u.info.vlist[rsep]; } else { heap[sepsize+1].row = -3; heap[sepsize+1].index = 0; heap[sepsize+1].col = MAXINT; } } /* initial ordering */ for ( k = 0 ; k < heapsize ; k++ ) { int parent = (int)((k-1)>>1); hspot = (int)k; h = heap[hspot]; while ( hspot > 0 ) { if ( h.col < heap[parent].col ) { heap[hspot] = heap[parent]; hspot = parent; parent = (parent-1)>>1; } else break; } heap[hspot] = h; } /* heap sort */ vtop = varlist; *vtop = heap[0].col; while ( heap[0].row >= -2 ) { if ( heap[0].col > *vtop ) *(++vtop) = heap[0].col; /* add to list */ /* delete from heap */ switch ( heap[0].row ) { case -2: if ( heap[0].index+1 < (int)right->u.info.size ) { /* have more */ h.index = heap[0].index + 1; h.col = right->u.info.vlist[h.index]; h.row = -2; } else { /* end of right son */ h.row = -3; h.col = MAXINT; /* sentinel */ } break; case -1: if ( heap[0].index+1 < (int)left->u.info.size ) { /* have more */ h.index = heap[0].index + 1; h.col = left->u.info.vlist[h.index]; h.row = -1; } else { /* end of left son */ h.row = -3; h.col = MAXINT; /* sentinel */ } break; default: if ( heap[0].index+1 < S->pIA[heap[0].row+1] ) { /* have more */ h.index = heap[0].index + 1; h.col = S->pJA[h.index]; h.row = heap[0].row; } else { /* end of left son */ h.row = -3; h.col = MAXINT; /* sentinel */ } break; } /* percolate up heap */ hspot = 0; for (;;) { next = 2*hspot+1; if ( next >= (int)heapsize ) break; if ( (next+1 < (int)heapsize) && (heap[next+1].col < heap[next].col) ) next++; /* right son is lower */ if ( h.col > heap[next].col ) { heap[hspot] = heap[next]; hspot = next; continue; } break; } heap[hspot] = h; } /* allocate variable list */ stree->u.info.size = varcount = vtop - varlist + 1; stree->u.info.vlist = (int*)mycalloc(stree->u.info.size,sizeof(int)); for ( k = 0 ; k < varcount ; k++ ) stree->u.info.vlist[k] = varlist[k]; for ( k = 0 ; k < varcount ; k++ ) work[stree->u.info.vlist[k]] = (int)k; /* inverse mapping */ /* allocate matrix */ stree->u.info.mat =(REAL*) mycalloc(varcount*(varcount+1)/2,sizeof(REAL)); /* fill matrix */ base = stree->u.info.mat; for ( i = stree->lo ; i < stree->hi ; base+=(varcount-i-1+stree->lo),i++ ) { for ( j = S->pIA[i] ; j < S->pIA[i+1] ; j++ ) base[work[S->pJA[j]]] += S->pA[j]; /* add in shift and metric */ if ( hessian_linear_metric_flag ) { if ( S->lambda != 0.0 ) { int end = Met.pIA[i+1]; for ( j = Met.pIA[i] ; j < end ; j++ ) base[work[Met.pJA[j]]] -= S->lambda*Met.pA[j]; } } else if ( web.area_norm_flag ) base[i-stree->lo] -= S->lambda*Met.A[S->P[i]]; /* special metric */ else base[i-stree->lo] -= S->lambda; } if ( !stree->isleaf ) for ( son = left, kk = 0 ; kk < 2 ; kk++, son=right ) { int sonsepsize = son->hi - son->lo; REAL *sonsrc = son->u.info.mat + sonsepsize*son->u.info.size - (((sonsepsize-1)*sonsepsize)>>1); for ( k = sonsepsize ; k < son->u.info.size ; k++ ) { row = work[son->u.info.vlist[k]]; mat = stree->u.info.mat + row*varcount - (((row-1)*row)>>1) - row; for ( j = (int)k ; j < (int)son->u.info.size ; j++,sonsrc++ ) mat[work[son->u.info.vlist[j]]] += *sonsrc; } } /* factor */ base = stree->u.info.mat; for ( row = 0 ; row < (int)sepsize ; row++, base += varcount-row ) { REAL piv; if ( fabs(base[row]) <= hessian_epsilon) { S->zero++; base[row] = 1.0; continue; } if ( base[row] > 0.0 ) S->pos++; else S->neg++; piv = 1/base[row]; mat = base + varcount; for ( i = row+1 ; i < (int)varcount ; i++ ) { REAL q = base[i]*piv; for ( j = i ; j < (int)varcount ; j++ ) *(mat++) -= q*base[j]; base[i] = q; } } /* mark as done */ stree->isleaf |= 2; } temp_free((char*)work); } /*************************************************************************** * * function: tree_factor() * * purpose: minimal degree factoring of system using tree decomp * */ void tree_factor(S) struct linsys *S; { if ( S->N <= 0 ) { kb_error(1638,"Empty linear system.\n",WARNING); return; } S->neg = S->zero = S->pos = 0; S->degencon = 0; sparse_permute(S); if ( hessian_linear_metric_flag && (S->lambda != 0.0) ) { memcpy((char*)Met.P,(char*)S->P,S->N*sizeof(int)); memcpy((char*)Met.IP,(char*)S->IP,S->N*sizeof(int)); sparse_permute(&Met); } /* this set up to be done in parallel */ #ifdef SGI_MULTI if ( mpflag == M_INACTIVE ) m_rele_procs(); mpflag = M_ACTIVE; m_fork(do_tree_factor,S); m_park_procs(); mpflag = M_INACTIVE; #else do_tree_factor(S); #endif if ( !hessian_quiet_flag ) { /* A few statistics */ REAL flops = 0.0; REAL critflops[MAXPROCS]; /* critical path flops */ size_t fill = 0; size_t fillspace = 0; int k; for ( k = 0 ; k < nprocs ; k++ ) critflops[k] = 0.0; for ( k = 0 ; k < S->streemax ; k++ ) { int sep = S->stree[k].hi - S->stree[k].lo; size_t size = S->stree[k].u.info.size; size_t rem = size - sep; size_t fl; if ( S->stree[k].nvtxs <= 0 ) continue; fillspace += size*(size+1)/2; fill += size*sep - sep*(sep-1)/2; fl = size*(size+1)*(size+2)/6 - rem*(rem+1)*(rem+2)/6; critflops[k % nprocs] += (REAL)fl; flops += (REAL)fl; } if ( nprocs > 1 ) { REAL critpath = 0.0; outstring("Proc Flops\n"); for ( k = 0 ; k < nprocs ; k++ ) { sprintf(msg,"%2d %12g\n",k+1,(DOUBLE)critflops[k]); outstring(msg); } for ( k = 1 ; k <= S->streemax ; k *= 2 ) { int sep = S->stree[k].hi - S->stree[k].lo; size_t size = S->stree[k].u.info.size; size_t rem = size - sep; critpath += (REAL)(size*(size+1)*(size+2)/6 - rem*(rem+1)*(rem+2)/6); } sprintf(msg,"Critical path flops: %12.0f\n",(DOUBLE)critpath); outstring(msg); } sprintf(msg,"Total fill: %d Total flops(flop=mul+add): %g Fillspace: %d\n", fill,(DOUBLE)flops,fillspace); outstring(msg); } } /************************************************************************* * * function: do_tree_solve() * * purpose: nitty-gritty solution of system. * Uses stree; goes leaf-to-root to solve LY=B * then root-to-leaf to solve UX = Y. * NOT set up to run in parallel. * Parallel would need U stored columnwise instead of rowwise. */ void do_tree_solve(S,BB,Y) struct linsys *S; REAL *BB; /* incoming and outgoing */ REAL *Y; /* intermediate */ { SepNodeType *stree; int spot; int n; REAL *e; /* solve U^T Y = B */ for ( spot = S->streemax; spot >= 1 ; spot-- ) /* metis starts at stree[1] */ { stree = S->stree + spot; for ( n = stree->lo, e = stree->u.info.mat ; n < stree->hi ; n++ ) { int start; REAL y; int i,*jp; y = Y[n] = BB[n]; Y[n] /= *e; e++; /* having saved Y[n], can divide by diag */ start = n-stree->lo+1; for ( i=start, jp = stree->u.info.vlist+start ; i < (int)stree->u.info.size ; i++,e++,jp++ ) BB[*jp] -= (*e)*y; } } /* solve U BB = Y */ for ( spot = 1 ; spot <= S->streemax ; spot++ ) { stree = S->stree + spot; for ( n = stree->hi-1 ; n >= stree->lo ; n-- ) { int row,i,*jp; REAL y = Y[n]; REAL *estart; row = n - stree->lo; estart = stree->u.info.mat + row*stree->u.info.size - ((row*(row-1))/2) - row; for ( i=row+1, jp=stree->u.info.vlist+i ; i < (int)stree->u.info.size ; i++,jp++ ) y -= estart[i]*BB[*jp]; BB[n] = y; } } } /************************************************************************* * * function: tree_solve() * * purpose: solve factored system for given right hand side. * Factor stored in stree, permuted order * */ void tree_solve(S,B,x) struct linsys *S; /* factored system */ REAL *B; /* incoming right hand side */ REAL *x; /* solution, may be rhs */ { int n; /* row index */ REAL *BB,*Y; BB = (REAL*)temp_calloc(S->N,sizeof(REAL)); /* intermediate solutions */ Y = (REAL*)temp_calloc(S->N,sizeof(REAL)); /* intermediate solutions */ for ( n = 0 ; n < S->N ; n++ ) BB[n] = B[S->P[n]]; /* permute */ /* NOT set up for parallel */ do_tree_solve(S,BB,Y); /* unpermute */ for ( n = 0 ; n < S->N ; n++ ) x[S->P[n]] = BB[n]; temp_free((char*)Y); temp_free((char*)BB); } /************************************************************************* * * function: tree_solve_multi() * * purpose: solve factored system for multiple right hand sides * */ void tree_solve_multi(S,B,x,rk) struct linsys *S; /* factored system */ REAL **B; /* incoming right hand side */ REAL **x; /* solution, may be rhs */ int rk; /* number of right sides */ { int k; for ( k = 0 ; k < rk ; k++ ) tree_solve(S,B[k],x[k]); } /************************************************************************ * * function: tree_analyze() * * purpose: Try to reverse-engineer factor tree from order permutation. */ void tree_analyze(S) struct linsys *S; { int k; /* end of block */ int n; /* member of block */ /* get things lined up in S->pIA and S->pJA so we can analyze */ if ( S->pIA == NULL ) sparse_permute(S); /* start at end, looking for dense blocks */ for ( k = S->N - 1 ; k >= 0 ; ) { for ( n = k-1 ; n >= 0 ; n-- ) { if ( S->pIA[n+1]-S->pIA[n] < k-n || S->pJA[S->pIA[n]+(k-n)] != k ) break; } printf("Dense block %d to %d\n",n+1,k); k = n; } } evolver-2.30c.dfsg/src/alice.c0000644000175300017530000002165411410765113016446 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************* * * file: alice.c * * purpose: Do tests of smoothed curvature for Alice Underwood * Also Craig Carter's energy method. * */ #include "include.h" REAL xalice[MAXCOORD+4]; /* center of test bump */ REAL valice[MAXCOORD+4] = { 0.0, 0.0, 1.0 }; /* vector of test bump */ REAL radius; /* characteristic scale of bump */ REAL calc_bump ARGS((REAL*)); REAL calc_bump(pt) REAL *pt; /* coordinates */ { REAL xx[MAXCOORD]; int i; REAL value; REAL r; for ( i = 0 ; i < SDIM ; i++ ) xx[i] = pt[i] - xalice[i]; /* function is fourth-order bump */ r = SDIM_dot(xx,xx)/radius/radius; value = 1/(1+r)/(1+r); return value; } void alice() { edge_id f_id; REAL sum=0.0; char p[90]; sprintf(p,"Enter center of test function (%f %f %f): ", (DOUBLE)xalice[0],(DOUBLE)xalice[1],(DOUBLE)xalice[2]); prompt(p,msg,msgmax); #ifdef LONGDOUBLE sscanf(msg,"%Lf %Lf %Lf %Lf %Lf", xalice,xalice+1,xalice+2,xalice+3,xalice+4); #else sscanf(msg,"%lf %lf %lf %lf %lf", xalice,xalice+1,xalice+2,xalice+3,xalice+4); #endif sprintf(p,"Enter vector of test function (%f %f %f): ", (DOUBLE)valice[0],(DOUBLE)valice[1],(DOUBLE)valice[2]); prompt(p,msg,msgmax); #ifdef LONGDOUBLE sscanf(msg,"%Lf %Lf %Lf %Lf %Lf", valice,valice+1,valice+2,valice+3,valice+4); #else sscanf(msg,"%lf %lf %lf %lf %lf", valice,valice+1,valice+2,valice+3,valice+4); #endif sprintf(p,"Enter characteristic size of test function (%f): ",(DOUBLE)radius); prompt(p,msg,msgmax); #ifdef LONGDOUBLE sscanf(msg,"%Lf", &radius); #else sscanf(msg,"%lf", &radius); #endif /* integrate over every edge */ FOR_ALL_FACETS(f_id) { REAL midpt[MAXCOORD]; REAL norm[FACET_EDGES][MAXCOORD]; REAL *x[FACET_EDGES+1]; edge_id e_id[FACET_EDGES]; REAL a,b,s0s0,s0s1,s1s1,surd; int i,j,m; facetedge_id fe_id; REAL side[FACET_EDGES][MAXCOORD]; REAL len[FACET_EDGES]; /* get sides */ fe_id = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { e_id[i] = get_fe_edge(fe_id); get_edge_side(e_id[i],side[i]); x[i] = get_coord(get_edge_headv(e_id[i])); fe_id = get_next_edge(fe_id); } x[FACET_EDGES] = x[0]; /* easy wrap */ s0s0 = SDIM_dot(side[0],side[0]); s0s1 = SDIM_dot(side[1],side[0]); s1s1 = SDIM_dot(side[1],side[1]); len[0] = sqrt(s0s0); len[1] = sqrt(s1s1); len[2] = sqrt(s0s0 + s1s1 + 2*s0s1); surd = sqrt(s0s0*s1s1 - s0s1*s0s1); a = s0s0/surd; b = s0s1/surd; for ( i = 0 ; i < SDIM ; i++ ) { norm[0][i] = -a*side[1][i] + b*side[0][i]; norm[2][i] = a*side[1][i] - b*side[0][i]; } a = s1s1/surd; b = s0s1/surd; for ( i = 0 ; i < SDIM ; i++ ) { norm[1][i] = a*side[0][i] - b*side[1][i]; norm[2][i] += -a*side[0][i] + b*side[1][i]; } /* along each edge */ for ( j = 0 ; j < FACET_EDGES ; j++ ) { if ( get_eattr(e_id[j]) & (FIXED|CONSTRAINT|BOUNDARY) ) continue; for ( m = 0 ; m < gauss1D_num ; m++ ) { for ( i = 0 ; i < SDIM ; i++ ) midpt[i] = gauss1Dpt[m]*x[j][i] + (1 - gauss1Dpt[m])*x[j+1][i]; sum += gauss1Dwt[m]*SDIM_dot(valice,norm[j]) *calc_bump(midpt)*len[j]; } } } sprintf(msg,"Variation: %f\n",(DOUBLE)(sum/radius/radius)); outstring(msg); } /****************************************************************** Craig Carter's energy Given bodies $B_1$ and $B_2$ in $R^3$, define the energy E = \int_{B_1}\int_{B_2} {1 \over |z_1 - z_2|^{p} } d^3 z_2 d^3 z_1 This reduces to E = {1\over (3-p)(2-p)}\sum_{F_2\in\partial B_2}\sum_{F_1\in\partial B_1} N_1 \cdot N_2 \int_{F_2}\int_{F_1}{1\over |z_1 - z_2|^{p-2}} d^2 z_1 d^2 z_2. And if we crudely approximate with centroids $\bar z_1$ and $\bar z_2$, E = {1\over (3-p)(2-p)}\sum_{F_2\in\partial B_2}\sum_{F_1\in\partial B_1} {A_1 \cdot A_2 \over |\bar z_1 - \bar z_2|^{p-2}}, where $A_1$ and $A_2$ are unnormalized area vectors for the facets. ******************************************************************/ /*************************************************************** * * function: carter_energy_init() * * purpose: initialization for carter_energy() and * carter_energy_gradient(). * * No special prep. */ #define CARTER_POWER_NAME "carter_power" REAL carter_power; void carter_energy_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { int param; param = lookup_global(CARTER_POWER_NAME); if ( param < 0 ) /* missing, so add */ { param = add_global(CARTER_POWER_NAME); globals(param)->value.real = 6.0; /* default */ globals(param)->flags |= ORDINARY_PARAM | RECALC_PARAMETER | ALWAYS_RECALC; } carter_power = globals(param)->value.real; } /************************************************************** * * function: carter_energy() * * purpose: calculates energy of one pair of facets, if on different bodies. * * input: info about facet is in qinfo structure. * */ REAL carter_energy(f_info) struct qinfo *f_info; { facet_id f1 = f_info->id,f2; REAL **x=f_info->x; MAT2D(y,FACET_VERTS,MAXCOORD); /* vertex coordinates */ REAL energy = 0.0; REAL t1[MAXCOORD],t2[MAXCOORD]; REAL det,rj,r[MAXCOORD]; int j; body_id b_id = get_facet_body(f_info->id); body_id bb_id; if ( !valid_id(b_id) ) return 0.0; FOR_ALL_FACETS(f2) { if ( f2 <= f1 ) continue; /* each pair once */ bb_id = get_facet_body(f2); if ( !valid_id(bb_id) ) continue; if ( equal_id(b_id,bb_id) ) continue; get_facet_verts(f2,y,NULL); for (j=0; jsides[0][0],t1)*SDIM_dot(f_info->sides[0][1],t2) - SDIM_dot(f_info->sides[0][0],t2)*SDIM_dot(f_info->sides[0][1],t1); energy += det/pow(rj,carter_power/2-1); } return energy/(3-carter_power)/(2-carter_power)/4; } /************************************************************** * * function: carter_energy_gradient() * * purpose: calculates gradient and energy of one pair of facets, * if on different bodies. * * input: info about facet is in qinfo structure. * */ REAL carter_energy_gradient(f_info) struct qinfo *f_info; { facet_id f1 = f_info->id,f2; REAL **x=f_info->x; MAT2D(y,FACET_VERTS,MAXCOORD); /* vertex coordinates */ REAL energy = 0.0; REAL t1[MAXCOORD],t2[MAXCOORD]; REAL rjgrad[FACET_VERTS][MAXCOORD]; REAL detgrad[FACET_VERTS][MAXCOORD]; REAL det,rj,r[MAXCOORD]; int j,k; body_id b_id = get_facet_body(f_info->id); body_id bb_id; REAL s1t1,s1t2,s2t1,s2t2; REAL p,pp; if ( !valid_id(b_id) ) return 0.0; for ( k = 0 ; k < FACET_VERTS ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[k][j] = 0.0; FOR_ALL_FACETS(f2) { if ( f2 == f1 ) continue; /* no self energy */ bb_id = get_facet_body(f2); if ( !valid_id(bb_id) ) continue; if ( equal_id(b_id,bb_id) ) continue; get_facet_verts(f2,y,NULL); for (j=0; jsides[0][0],t1); s1t2 = SDIM_dot(f_info->sides[0][0],t2); s2t1 = SDIM_dot(f_info->sides[0][1],t1); s2t2 = SDIM_dot(f_info->sides[0][1],t2); det = s1t1*s2t2 - s1t2*s2t1; for ( j = 0 ; j < SDIM ; j++ ) { detgrad[0][j] = -t1[j]*s2t2 + t2[j]*s2t1 - t2[j]*s1t1 + t1[j]*s1t2; detgrad[1][j] = t1[j]*s2t2 - t2[j]*s2t1; detgrad[2][j] = t2[j]*s1t1 - t1[j]*s1t2; } p = 1/pow(rj,carter_power/2-1); pp = (carter_power/2-1)*det*p/rj; energy += det*p; for ( k = 0 ; k < FACET_VERTS ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[k][j] += (detgrad[k][j]*p - pp*rjgrad[k][j]); } for ( k = 0 ; k < FACET_VERTS ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[k][j] /= (3-carter_power)*(2-carter_power)*4 ; return energy/(3-carter_power)/(2-carter_power)/4/2; } evolver-2.30c.dfsg/src/knot1.c0000644000175300017530000016364011410765113016427 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /****************************************************************** * * file: knotenergy.c * * Contents: Functions calculating knot energy and its gradient * * Has both equilibrium charge distribution * (for use with length constraint) and * uniform charge disitribution. * Also has thickness (ropelength) added 01Jun by JMS */ #include "include.h" /* for accessing knot energy exponent as adjustable parameter */ int exponent_param; /* parameter number */ #define KNOTPOWER_NAME "knot_power" /* name in datafile */ static int ke_power_flag; /* 0: real, 1: integer, -1: log */ static int ke_power_i; /* half of even integer power */ static int ke_power_l; /* log_2 of power */ static REAL ke_power; /* half of power */ #define CHARGE_ATTR_NAME "node_charge" int charge_attr = -1; /* prototypes to keep some compilers happy */ REAL radp ARGS((REAL *, REAL *, REAL *)); REAL dradp ARGS((REAL*, REAL*, REAL*, REAL*, REAL)); void xfacet_knot_energy_init ARGS((int,struct method_instance *)); REAL xfacet_knot_energy ARGS(( struct qinfo *)); REAL xfacet_knot_energy_gradient ARGS(( struct qinfo *)); REAL rad ARGS((REAL*,REAL*,REAL*)); /*************************************************************** * * function: knot_power_init() * * purpose: initialization for all knot_energies which have variable power */ void knot_power_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { charge_attr = find_attribute(VERTEX,CHARGE_ATTR_NAME); /* see if charge attribute */ exponent_param = lookup_global(KNOTPOWER_NAME); if ( exponent_param < 0 ) /* missing, so add */ { exponent_param = add_global(KNOTPOWER_NAME); globals(exponent_param)->value.real = 2.0; /* default */ globals(exponent_param)->flags |= ORDINARY_PARAM | RECALC_PARAMETER | ALWAYS_RECALC; } ke_power = globals(exponent_param)->value.real/2; /* since rr is squared */ ke_power_i = (int) ke_power; ke_power_flag = 0; /* use REAL by default */ if (ke_power<0.) return; /* don't do anything special for negative powers */ ke_power_l = (int) (log(ke_power)/log(2.)); /* see if power of 2 */ if ( fabs(ke_power_l - log(ke_power)/log(2.)) < 1e-8 && ke_power_l <= 20) ke_power_flag = -1; else if ( fabs(ke_power_i - ke_power) < 1e-12 && ke_power <= 150) ke_power_flag = 1; } #define ke_pow(r,p) \ {if (ke_power_flag>0) {int i; for (p = r,i=1; iid), *b, *c; REAL thick = 0.0; /* for this vertex */ edge_id e_id; /* edge to create triple */ FOR_ALL_EDGES(e_id) { vertex_id eh_id = get_edge_headv(e_id); vertex_id et_id = get_edge_tailv(e_id); if ( v_info->id == eh_id || v_info->id == et_id ) continue; /* incident */ b = get_coord(eh_id); c = get_coord(et_id); thick += radp(a,b,c); } return thick; } /************************************************************** * * function: knot_thickness_0_gradient() * * purpose: gradient of knot_thickness_0 * */ REAL knot_thickness_0_gradient(v_info) struct qinfo *v_info; { REAL *a , *b, *c, *d; REAL thick = 0.0; /* thickness at this vertex */ edge_id e_id; /* edge to create triple */ vertex_id v_id, v1_id, v2_id; /* vertex to create triple */ a = get_coord(v_info->id); FOR_ALL_EDGES(e_id) { vertex_id eh_id = get_edge_headv(e_id); vertex_id et_id = get_edge_tailv(e_id); if ( v_info->id == eh_id || v_info->id == et_id ) continue; /* incident */ b = get_coord(eh_id); c = get_coord(et_id); thick += dradp(a,b,c, v_info->grad[0], 1.); } e_id = get_vertex_edge(v_info->id); b = get_coord(v1_id=get_edge_headv(e_id)); e_id = get_next_tail_edge(e_id); /* assuming <= two edges per vertex */ c = get_coord(v2_id=get_edge_headv(e_id)); /* b and c are now two nbrs of v */ if (v1_id != v2_id) /* skip this if v had valence 1 */ dradp(a,b,c, v_info->grad[0], 2.); /* case of triples (v,v1,v2),(v,v2,v1) */ FOR_ALL_VERTICES(v_id) { if (v_id == v_info->id || v_id == v1_id || v_id == v2_id) continue; d = get_coord(v_id); dradp(a,b,d, v_info->grad[0], 1.); if (v1_id != v2_id) dradp(a,c,d, v_info->grad[0], 1.); } return thick; } /************************************************************** * * function: knot_thickness() * * purpose: calculates global radius of curvature at one vertex v, * as min of r(v,eh,et). * Because of "min", this has no gradient. * It should be used with "min(vertex,thick)", not "total thick" * * input: info about vertex is in qinfo structure. * */ REAL knot_thickness(v_info) struct qinfo *v_info; { REAL *a = get_coord(v_info->id), *b, *c; REAL r, thick = -1.; edge_id e_id; /* edge to create triple */ FOR_ALL_EDGES(e_id) { vertex_id eh_id = get_edge_headv(e_id); vertex_id et_id = get_edge_tailv(e_id); if ( v_info->id == eh_id || v_info->id == et_id ) continue; /* incident */ b = get_coord(eh_id); c = get_coord(et_id); r = rad(a,b,c); if (thick < 0 || r < thick) thick = r; } return thick; /* returns -1 if component only has 2 edges */ } /************************************************************** * * function: knot_thickness2() * * purpose: calculates global radius of curvature at one vertex v, * as min of r(v,w1,w2) where w1 and w2 are nbrs of some w. * Because of "min", this has no gradient. * It should be used with "min(vertex,thick)", not "total thick" * * input: info about vertex is in qinfo structure. * */ REAL knot_thickness2(v_info) struct qinfo *v_info; { REAL *a = get_coord(v_info->id), *b, *c; REAL r, thick = -1.; vertex_id v_id,w_id,w1_id,w2_id; /* vertex and neighbors to create triple */ edge_id e_id; v_id = v_info->id; FOR_ALL_VERTICES(w_id) { e_id = get_vertex_edge(w_id); if ((w1_id = get_edge_headv(e_id))==v_id) continue; b = get_coord(w1_id); e_id = get_next_tail_edge(e_id); /* assuming two edges per vertex */ if ((w2_id = get_edge_headv(e_id))==v_id) continue; c = get_coord(w2_id); r = rad(a,b,c); if (thick < 0 || r < thick) thick = r; } return thick; } /************************************************************** * * function: knot_local_thickness() * * purpose: calculates local radius of curvature at one vertex v, * as r(v1,v,v2), where v1 and v2 are nbrs of v. * No gradient or L^p version for now; intended for use at * individual vertices. * * input: info about vertex is in qinfo structure. * */ REAL knot_local_thickness(v_info) struct qinfo *v_info; { REAL *a = get_coord(v_info->id), *b, *c; edge_id e_id; /* edge to create triple */ e_id = get_vertex_edge(v_info->id); b = get_coord(get_edge_headv(e_id)); e_id = get_next_tail_edge(e_id); /* assuming exactly two edges per vertex */ c = get_coord(get_edge_headv(e_id)); /* b and c are two nbrs of v */ return rad(b,a,c); } /************************************************************** * * function: knot_thickness_p2() * * purpose: calculates global radius of curvature at one vertex v, * as Lp integral of r(v,w1,w2). Includes factors of length at v and w. * Here w1 and w2 are the two neighbors of vertex w. * This has not been extended to allow open arcs (valence 1 verts). * * input: info about vertex is in qinfo structure. * */ REAL knot_thickness_id ARGS((vertex_id)); REAL knot_thickness_id2 ARGS((vertex_id)); REAL knot_thickness_p2(v_info) struct qinfo *v_info; { return knot_thickness_id2(v_info->id); } REAL knot_thickness_id2(v_id) vertex_id v_id; { REAL *a = get_coord(v_id), *b, *c; REAL thick = 0.0; /* for this vertex */ vertex_id w_id,w1_id,w2_id; /* vertex and neighbors to create triple */ edge_id e_id; /* temp edge */ FOR_ALL_VERTICES(w_id) { if (w_id==v_id) continue; /* don't include local thickness at v */ e_id = get_vertex_edge(w_id); if ((w1_id = get_edge_headv(e_id))==v_id) continue; b = get_coord(w1_id); e_id = get_next_tail_edge(e_id); /* assuming two edges per vertex */ if ((w2_id = get_edge_headv(e_id))==v_id) continue; c = get_coord(w2_id); thick += radp(a,b,c) * get_vertex_star(w_id); } return thick * get_vertex_star(v_id); } /************************************************************** * * function: knot_thickness_p2_gradient() * * purpose: calculates gradient of global radius of curvature at one vertex v, * as Lp integral of r(v,w1,w2). Includes factors of length at v and w. * Here w1 and w2 are the two neighbors of vertex w. * * input: info about vertex is in qinfo structure. * */ REAL knot_thickness_p2_gradient(v_info) struct qinfo *v_info; { vertex_id v_id = v_info->id; REAL *a = get_coord(v_id), *b, *c, *d, *bb, *cc; REAL temp[MAXCOORD], e1[MAXCOORD], e2[MAXCOORD]; REAL l1, l2, lv1, lv2, lv; REAL thick = 0.0; /* for this vertex */ vertex_id v1_id,v2_id, w_id,w1_id,w2_id, v11_id,v22_id; /* neighbors */ edge_id e_id, e1_id, e2_id; /* temp edges */ int i; e1_id = get_vertex_edge(v_id); v1_id=get_edge_headv(e1_id); lv1 = get_vertex_star(v1_id); get_edge_side(e1_id,e1); l1 = get_edge_length(e1_id); e2_id = get_next_tail_edge(e1_id); /* assuming two edges per vertex */ v2_id=get_edge_headv(e2_id); lv2 = get_vertex_star(v2_id); /* v1 and v2 are two nbrs of v and lv1,lv2 are their star lengths */ get_edge_side(e2_id,e2); l2 = get_edge_length(e2_id); /* e1, e2 are edge vectors out of v; l1, l2 are their lengths */ for ( i = 0 ; i < SDIM ; i++ ) { e1[i] /= 2*l1; e2[i] /= 2*l2; /* now they're half unit vectors */ temp[i] = 0; } lv = get_vertex_star(v_id); /* should be half of l1+l2 */ FOR_ALL_VERTICES(w_id) { if (w_id == v_id) continue; e_id = get_vertex_edge(w_id); if ((w1_id = get_edge_headv(e_id))==v_id) continue; e_id = get_next_tail_edge(e_id); /* assuming two edges per vertex */ if ((w2_id = get_edge_headv(e_id))==v_id) continue; thick += dradp(a,get_coord(w1_id),get_coord(w2_id), temp, get_vertex_star(w_id)); } /* now temp is sum_w l(w)*d_v(rp(v,w1,w2)) */ for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] += (temp[i]*lv - thick*(e1[i]+e2[i])); /* so far we should have the gradient of knot_thickness_p2(v_info) */ /* which is: d_v(l(v))*thick + l(v)*temp */ thick *= lv; /* this is the energy at v, to be returned */ /* Now we must compute derivative due to v of all other terms, first just its effect on length for energy of neighbors, then when v is playing the role of w (affects only length) or w1 or w2 */ for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] -= ( knot_thickness_id2(v1_id)*e1[i]/lv1 + knot_thickness_id2(v2_id)*e2[i]/lv2 ); /* this is effect of v on l(v1) and l(v2) in energy at v1 and v2 */ b = get_coord(v1_id); c = get_coord(v2_id); /* will need these nbrs */ e_id = get_next_head_edge(e1_id); /* move out beyond v1 */ bb = get_coord(v11_id=get_edge_tailv(e_id)); /* coords of next vertex */ e_id = get_next_head_edge(e2_id); /* move out beyond v2 */ cc = get_coord(v22_id=get_edge_tailv(e_id)); /* coords of next vertex */ FOR_ALL_VERTICES(w_id) { /* here w is playing the role of v, and v that of w (or w1 or w2) */ REAL t,lw; if (w_id == v_id) continue; /* not allowed in any of the three */ d = get_coord(w_id); lw = get_vertex_star(w_id); if (w_id != v1_id && w_id != v2_id) { t = radp(d,b,c) * lw; for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] -= t * (e1[i]+e2[i]); /* v as w affects len only */ } /* now, when v1=b is w, v=a is w1, and v11=bb is w2 */ if (w_id != v1_id && w_id != v11_id) { t = dradp(a,bb,d, v_info->grad[0], lv1*lw) / lv1; /* effect on radp() */ for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] -= t*e1[i]; /* finally, effects on l(v1) */ } /* now, when v2=c is w, v=a is w2, and cc is w1 */ if (w_id != v2_id && w_id != v22_id) { t = dradp( a,cc,d, v_info->grad[0], lv2*lw ) / get_vertex_star(v2_id); for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] -= t*e2[i]; } } return thick * get_vertex_star(v_id); } /************************************************************** * * function: knot_thickness_p() * * purpose: calculates global radius of curvature at one vertex v, * as Lp integral of r(v,w,w). Includes factors of length at v and w. * * input: info about vertex is in qinfo structure. * */ REAL knot_thickness_id(); REAL knot_thickness_p(v_info) struct qinfo *v_info; { return knot_thickness_id(v_info->id); } REAL knot_thickness_id(v_id) vertex_id v_id; { REAL *a = get_coord(v_id), *b, *c; REAL thick = 0.0; /* for this vertex */ edge_id e_id; /* edge to create triple */ FOR_ALL_EDGES(e_id) { vertex_id eh_id = get_edge_headv(e_id); vertex_id et_id = get_edge_tailv(e_id); if ( v_id == eh_id || v_id == et_id ) continue; /* incident */ b = get_coord(eh_id); c = get_coord(et_id); thick += radp(a,b,c) * get_edge_length(e_id); } return thick * get_vertex_star(v_id); /* this is rp(v,eh,et)*l(e)*l(v) */ } /************************************************************** * * function: knot_thickness_p_gradient() * * purpose: gradient of knot_thickness_p * */ REAL knot_thickness_p_gradient(v_info) struct qinfo *v_info; { REAL *a , *b, *c, *d; REAL temp[MAXCOORD], e1[MAXCOORD], e2[MAXCOORD], l1, l2; REAL thick = 0.0; /* thickness at this vertex */ edge_id e_id; /* edge to create triple */ vertex_id w_id, v1_id, v2_id; /* vertex to create triple */ int i; a = get_coord(v_info->id); e_id = get_vertex_edge(v_info->id); v1_id=get_edge_headv(e_id); get_edge_side(e_id,e1); l1 = get_edge_length(e_id); e_id = get_next_tail_edge(e_id); /* assuming <= two edges per vertex */ v2_id=get_edge_headv(e_id); /* v1 and v2 are two nbrs of v */ get_edge_side(e_id,e2); l2 = get_edge_length(e_id); if (v1_id == v2_id) for (i=0; iid == eh_id || v_info->id == et_id ) continue; /* incident */ b = get_coord(eh_id); c = get_coord(et_id); thick += dradp(a,b,c,temp, get_edge_length(e_id)); } /* now temp is sum_e l(e)*d_v(rp(v,eh,et)) */ for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] += (temp[i] * get_vertex_star(v_info->id) - thick * (e1[i]/l1 + e2[i]/l2)/2); /* so far we should have the gradient of knot_thickness_p(v_info) */ /* which is: d_v(l(v))*thick + l(v)*temp */ thick *= get_vertex_star(v_info->id); /* Next we consider the triple v1,v,v2, which enters into thickness at v1 and v2 */ b = get_coord(v1_id); c = get_coord(v2_id); if (v1_id != v2_id) /* skip if v valence 1 */ { dradp(a,b,c, v_info->grad[0], /* triples (v,v1,v2), (v,v2,v1) */ l1*get_vertex_star(v2_id) + l2*get_vertex_star(v1_id)); /* this term gets d_v rp(a,b,c) times appropriate lengths */ for ( i = 0 ; i < SDIM ; i++ ) { v_info->grad[0][i] -= radp(a,b,c) * /* - is from orientation of ei */ ( get_vertex_star(v2_id)*e1[i]/l1 + get_vertex_star(v1_id)*e2[i]/l2 ); /* term above accounts for changing lengths at v as l(e) factor */ v_info->grad[0][i] -= ( knot_thickness_id(v1_id)*e1[i]/l1/get_vertex_star(v1_id)/2 + knot_thickness_id(v2_id)*e2[i]/l2/get_vertex_star(v2_id)/2 ); /* this is effect of v on l(v1) and l(v2) in energy at v1 and v2 */ } } FOR_ALL_VERTICES(w_id) { if (w_id == v_info->id || w_id == v1_id || w_id == v2_id) continue; d = get_coord(w_id); dradp(a,b,d, v_info->grad[0], l1*get_vertex_star(w_id)); if (v1_id != v2_id) dradp(a,c,d, v_info->grad[0], l2*get_vertex_star(w_id)); /* these are changes d_v (rp(w,ei)) times lengths, i=1,2 */ for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] -= ( radp(a,b,d) * get_vertex_star(w_id) * e1[i]/l1 + radp(a,c,d) * get_vertex_star(w_id) * e2[i]/l2 ); /* v's effect on l(ei), i=1,2 */ } return thick; } /************************************************************** * * function: knot_energy() * * purpose: calculates energy of one vertex due to potential * with all others. * * input: info about vertex is in qinfo structure. * */ REAL knot_energy(v_info) struct qinfo *v_info; { REAL *x = get_coord(v_info->id); REAL energy = 0.0; /* for this vertex */ vertex_id v_id; /* other vertex */ int i; REAL d[MAXCOORD]; /* difference vector between vertices */ REAL rr,p; REAL vcharge = charge_attr >= 0 ? *((REAL*)get_extra(v_info->id,charge_attr)) : 1.; FOR_ALL_VERTICES(v_id) { REAL *y = get_coord(v_id); REAL charges; if ( v_id <= v_info->id ) continue; /* each pair once */ charges = charge_attr >= 0 ? *((REAL*)get_extra(v_id,charge_attr))*vcharge : 1.; for ( i = 0 ; i < SDIM ; i++ ) d[i] = x[i] - y[i]; rr = SDIM_dot(d,d); ke_pow(rr,p); energy += charges/p; /* inverse power potential */ } return 2*energy; /* since each pair once */ } /************************************************************** * * function: knot_energy_gradient() * * purpose: calculates energy of one vertex due to potential * with all others. * * input: info about vertex is in global qinfo structure. * */ REAL knot_energy_gradient(v_info) struct qinfo *v_info; { REAL *x = get_coord(v_info->id); REAL energy = 0.0; /* for this vertex */ vertex_id v_id; int i; REAL d[MAXCOORD]; /* difference vector between vertices */ REAL rr,p,fact; REAL vcharge = charge_attr >= 0 ? *((REAL*)get_extra(v_info->id,charge_attr)) : 1.; FOR_ALL_VERTICES(v_id) { REAL *y = get_coord(v_id); REAL charges; if ( equal_id(v_info->id,v_id) ) continue; charges = charge_attr >= 0 ? *((REAL*)get_extra(v_id,charge_attr))*vcharge : 1.; for ( i = 0 ; i < SDIM ; i++ ) d[i] = x[i] - y[i]; rr = SDIM_dot(d,d); ke_pow(rr,p); energy += charges/p; fact = 2*ke_power/(p*rr); for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] -= 2*charges*fact*d[i]; } return energy; } /******************************************************* * * function: knot_energy_hessian() * * purpose: calculate knot energy hessian */ REAL knot_energy_hessian(v_info) struct qinfo *v_info; { REAL *x = get_coord(v_info->id); REAL energy = 0.0; /* for this vertex */ vertex_id w_id; int i,j; REAL d[MAXCOORD]; /* difference vector between vertices */ REAL rr,p,fact; MAT2D(hessvw,MAXCOORD,MAXCOORD); MAT2D(hessvv,MAXCOORD,MAXCOORD); REAL modulus; REAL vcharge = charge_attr >= 0 ? *((REAL*)get_extra(v_info->id,charge_attr)) : 1.; modulus = METH_INSTANCE(v_info->method)->modulus *GEN_QUANT(METH_INSTANCE(v_info->method)->quant)->modulus; /* need modulus due to direct insertion into hessian */ for(i=0;iid,w_id) ) continue; charges = charge_attr >= 0 ? *((REAL*)get_extra(w_id,charge_attr))*vcharge : 1.; for ( i = 0 ; i < SDIM ; i++ ) d[i] = x[i] - y[i]; rr = SDIM_dot(d,d); ke_pow(rr,p); energy += charges/p; fact = 2*ke_power/(p*rr); for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] -= 2*charges*fact*d[i]; for ( i=0; iS, v_info->id, w_id, hessvw); } fill_self_entry(v_info->S, v_info->id, hessvv); return modulus*energy; } /********************************************************************/ /* This set of routines is for the gradient^2 of the last energy */ /* assuming the points are constrained to the unit sphere */ static REAL *charge_grads = NULL; static int cg_nverts = 0; /*************************************************************** * * function: charge_gradient_init() * * purpose: initialization for charge_gradient() and * charge_gradient_gradient(). */ void charge_gradient_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { vertex_id u_id,v_id; /* two vertices */ int nv; knot_power_init(mode,mi); if ((nv = web.skel[VERTEX].max_ord+1) != cg_nverts) { if (charge_grads) list_free((char*)charge_grads,ETERNAL_BLOCK); charge_grads = (REAL*)my_list_calloc(nv, MAXCOORD*sizeof(REAL),ETERNAL_BLOCK); cg_nverts = nv; } FOR_ALL_VERTICES(u_id) { int i; REAL *x = get_coord(u_id); REAL *w = charge_grads+MAXCOORD*ordinal(u_id); for ( i = 0 ; i < SDIM ; i++ ) w[i] = 0.; FOR_ALL_VERTICES(v_id) { REAL *y = get_coord(v_id); REAL d[MAXCOORD]; /* difference vector */ REAL rr; if (u_id==v_id) continue; for ( i = 0 ; i < SDIM ; i++ ) d[i] = x[i] - y[i]; rr = SDIM_dot(d,d); for ( i = 0 ; i < SDIM ; i++ ) w[i] -= d[i]/pow(rr,(2*ke_power+2)/2); } } } /************************************************************** * * function: charge_gradient() * * purpose: calculates force^2 on one vertex from charges at all others * * input: info about vertex is in qinfo structure. * */ REAL charge_gradient(v_info) struct qinfo *v_info; { REAL *x = get_coord(v_info->id); REAL *w = charge_grads+MAXCOORD*ordinal(v_info->id); REAL wx = SDIM_dot(w,x); return SDIM_dot(w,w) - wx*wx; } /************************************************************** * * function: charge_gradient_gradient() * * purpose: calculates energy of one vertex due to potential * with all others. * * input: info about vertex is in global qinfo structure. * */ REAL charge_gradient_gradient(v_info) struct qinfo *v_info; { REAL *xi = get_coord(v_info->id); vertex_id v_id; int i; REAL d[MAXCOORD]; /* difference vector between vertices */ REAL *wi,*wj; /* force vectors at vertices */ REAL yi[MAXCOORD], yd[MAXCOORD]; /* more forces */ REAL rr,p,ddd; wi = charge_grads+MAXCOORD*ordinal(v_info->id); ddd = SDIM_dot(wi,xi); for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] = -2*ddd*wi[i]; /* diagonal term in gradient */ for ( i = 0 ; i < SDIM ; i++ ) yi[i] = wi[i] - ddd*xi[i]; FOR_ALL_VERTICES(v_id) { REAL *xj = get_coord(v_id); if ( equal_id(v_info->id,v_id) ) continue; for ( i = 0 ; i < SDIM ; i++ ) d[i] = xi[i] - xj[i]; rr = SDIM_dot(d,d); wj = charge_grads+MAXCOORD*ordinal(v_id); ddd = SDIM_dot(wj,xj); for ( i = 0 ; i < SDIM ; i++ ) yd[i] = yi[i] - wj[i] + ddd*xj[i]; ddd = SDIM_dot(yd,d); p = pow(rr,(2*ke_power+2)/2); /* power of distance */ for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] += (-2*yd[i] + 2*(2*ke_power+2)*ddd*d[i]/rr)/p; } return SDIM_dot(yi,yi); } /* Next set of routines is for uniform charge distribution */ /*************************************************************** * * function: uniform_knot_energy_init() * * purpose: initialization for knot_energy() and * knot_energy_gradient() and knot_thickness_p(). */ void uniform_knot_energy_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { edge_id e_id; vertex_id v_id; knot_power_init(mode,mi); FOR_ALL_VERTICES(v_id) { set_vertex_star(v_id,0.); get_vertex_evalence(v_id); } FOR_ALL_EDGES(e_id) { calc_edge(e_id); add_vertex_star(get_edge_tailv(e_id),get_edge_length(e_id)/2); add_vertex_star(get_edge_headv(e_id),get_edge_length(e_id)/2); } /* each vertex_star is now half the total length of incident edges */ } /************************************************************** * * function: uniform_knot_energy() * * purpose: calculates energy of one vertex due to potential * with all others. Charge at vertex is length of star. * * input: info about vertex is in global qinfo structure. * */ REAL uniform_knot_energy(v_info) struct qinfo *v_info; { REAL *x = get_coord(v_info->id); REAL energy = 0.0; /* for this vertex */ vertex_id v_id; /* other vertex */ int i; REAL d[MAXCOORD]; /* difference vector between vertices */ REAL rr; REAL ti; /* length weights of vertices, ti for home vertex */ REAL p; ti = get_vertex_star(v_info->id); FOR_ALL_VERTICES(v_id) { REAL *y = get_coord(v_id); if ( v_id <= v_info->id ) continue; /* each pair once */ for ( i = 0 ; i < SDIM ; i++ ) d[i] = x[i] - y[i]; rr = SDIM_dot(d,d); ke_pow(rr,p); energy += ti*get_vertex_star(v_id)/p; } return 2*energy; /* since each pair once */ } /************************************************************** * * function: uniform_knot_energy_gradient() * * purpose: calculates energy of one vertex due to potential * with all others. * * input: info about vertex is in global qinfo structure. * */ REAL uniform_knot_energy_gradient(v_info) struct qinfo *v_info; { REAL *x = get_coord(v_info->id); REAL energy = 0.0; /* for this vertex */ vertex_id v_id; int i; REAL d[MAXCOORD]; /* difference vector between vertices */ REAL rr,p; REAL ti,tj; /* length weights of vertices, ti for home vertex */ edge_id e_id; REAL left[MAXCOORD],right[MAXCOORD]; /* edge vectors */ REAL sum,sumleft,sumright; REAL *xleft,*xright; REAL leftmag,rightmag; for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] = 0.0; /* initialize gradient */ ti = get_vertex_star(v_info->id); e_id = get_vertex_edge(v_info->id); xright = get_coord(get_edge_headv(e_id)); e_id = get_next_tail_edge(e_id); /* assuming exactly two edges per vertex */ xleft = get_coord(get_edge_headv(e_id)); sum = sumleft = sumright = 0.0; FOR_ALL_VERTICES(v_id) { REAL *y = get_coord(v_id); REAL fact; tj = get_vertex_star(v_id); for ( i = 0 ; i < SDIM ; i++ ) d[i] = x[i] - y[i]; rr = SDIM_dot(d,d); if (v_id != v_info->id) { ke_pow(rr,p); fact = ti*tj/p; energy += fact; fact *= 4*ke_power/rr; for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] -= fact*d[i]; sum += tj/p; } for ( i = 0 ; i < SDIM ; i++ ) d[i] = xleft[i] - y[i]; rr = SDIM_dot(d,d); if ( rr > 1e-18) /* don't do self */ { ke_pow(rr,p); sumleft += tj/p; } for ( i = 0 ; i < SDIM ; i++ ) d[i] = xright[i] - y[i]; rr = SDIM_dot(d,d); if ( rr > 1e-18) /* don't do self */ { ke_pow(rr,p); sumright += tj/p; } } for ( i = 0 ; i < SDIM ; i++ ) { left[i] = x[i] - xleft[i]; right[i] = x[i] - xright[i]; } leftmag = sqrt(SDIM_dot(left,left)); rightmag = sqrt(SDIM_dot(right,right)); for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] += ((left[i]/leftmag + right[i]/rightmag)*sum + left[i]/leftmag*sumleft + right[i]/rightmag*sumright); return energy; } /****************************************************************** * * function: uniform_normalization() * * purpose: calculates internal knot energy to normalize * singular divergence of integral. */ REAL uniform_normalization(v_info) struct qinfo *v_info; { vertex_id v_id; edge_id e_id,ee_id; REAL ti,tj; REAL dist=0.,energy=0.; REAL power; e_id = get_vertex_edge(v_info->id); if ( !valid_id(e_id) ) return 0.0; ti = get_edge_length(e_id); ee_id = get_next_tail_edge(e_id); /* assuming exactly two edges per vertex */ ti = (ti + get_edge_length(ee_id))/2; power = globals(exponent_param)->value.real; for ( v_id = get_edge_headv(e_id) ; v_id != v_info->id ; e_id = ee_id, v_id = get_edge_headv(e_id) ) { dist += (tj = get_edge_length(e_id)); ee_id = inverse_id(get_next_head_edge(e_id)); tj = (tj + get_edge_length(ee_id))/2; energy += ti*tj/pow(dist,power); } return 2*energy; } /****************************************************************** * * function: uniform_binormalization() * * purpose: calculates internal knot energy to normalize * singular divergence of integral. Uses shorter distance. */ REAL uniform_binormalization(v_info) struct qinfo *v_info; { vertex_id v_id; edge_id e_id,ee_id; REAL ti,tj; REAL dist=0.,energy=0.,comp_len; REAL power; e_id = get_vertex_edge(v_info->id); if ( !valid_id(e_id) ) return 0.0; comp_len = ti = get_edge_length(e_id); ee_id = get_next_tail_edge(e_id); /* assuming exactly two edges per vertex */ ti = (ti + get_edge_length(ee_id))/2; power = globals(exponent_param)->value.real; for ( ee_id = inverse_id(get_next_head_edge(e_id)); ee_id != e_id; ee_id = inverse_id(get_next_head_edge(ee_id)) ) comp_len += get_edge_length(ee_id); for ( v_id = get_edge_headv(e_id) ; v_id != v_info->id ; e_id = ee_id, v_id = get_edge_headv(e_id) ) { dist += (tj = get_edge_length(e_id)); ee_id = inverse_id(get_next_head_edge(e_id)); tj = (tj + get_edge_length(ee_id))/2; energy += ti*tj/pow((2*distvalue.real = 4.0; /* default */ globals(exponent_param)->flags |= ORDINARY_PARAM | RECALC_PARAMETER | ALWAYS_RECALC; } { REAL *x; vertex_id v_id,vv_id; int i; REAL d[MAXCOORD]; /* difference vector between vertices */ REAL rr; REAL power; REAL tj; facetedge_id fe; REAL area,s11,s12,s22,side1[MAXCOORD],side2[MAXCOORD]; facet_id f_id; power = globals(exponent_param)->value.real; /* will need areas of all vertex stars */ FOR_ALL_VERTICES(v_id) set_vertex_star(v_id,0.0); FOR_ALL_FACETS(f_id) { fe = get_facet_fe(f_id); get_edge_side(get_fe_edge(fe),side1); get_edge_side(get_fe_edge(get_next_edge(fe)),side2); s11 = SDIM_dot(side1,side1); s22 = SDIM_dot(side2,side2); s12 = SDIM_dot(side1,side2); area = sqrt(s11*s22 - s12*s12)/2; for ( i = 0 ; i < FACET_VERTS ; i++ ) { vv_id = get_fe_tailv(fe); add_vertex_star(vv_id,area/3); fe = get_next_edge(fe); } } /* basic sums */ if ( f_sums ) myfree((char*)f_sums); f_sums = (REAL *)mycalloc(web.skel[VERTEX].max_ord+1,sizeof(REAL)); FOR_ALL_VERTICES(v_id) { int ordv = ordinal(v_id); REAL val; REAL ti = get_vertex_star(v_id); x = get_coord(v_id); FOR_ALL_VERTICES(vv_id) { REAL *y = get_coord(vv_id); int ordvv = ordinal(vv_id); if ( ordvv <= ordv ) continue; tj = get_vertex_star(vv_id); for ( i = 0 ; i < SDIM ; i++ ) d[i] = x[i] - y[i]; rr = SDIM_dot(d,d); val = pow(rr,power/2); /* inverse power potential */ f_sums[ordv] += tj/val; f_sums[ordvv] += ti/val; } } } } /************************************************************** * * function: facet_knot_energy() * * purpose: calculates energy of one vertex due to potential * with all others. Charge at vertex is area of star. * * input: info about vertex is in global qinfo structure. * */ REAL facet_knot_energy(v_info) struct qinfo *v_info; { REAL ti,energy; ti = get_vertex_star(v_info->id); energy = ti*f_sums[ordinal(v_info->id)]; return energy; /* because pair counts in both orders */ } /************************************************************** * * function: facet_knot_energy_gradient() * * purpose: calculates energy of one vertex due to potential * with all others. * * input: info about vertex is in qinfo structure. * */ REAL facet_knot_energy_gradient(v_info) struct qinfo *v_info; { REAL *x = get_coord(v_info->id); vertex_id v_id; int i; REAL d[MAXCOORD]; /* difference vector between vertices */ REAL rr,p; REAL power,halfpower; REAL ti,tj; /* length weights of vertices, ti for home vertex */ facetedge_id fe,start_fe,next_fe; REAL sumi,sumj,sumjj,area; REAL s11,s12,s22,side1[MAXCOORD],side2[MAXCOORD]; REAL da[MAXCOORD],sum[MAXCOORD]; ti = get_vertex_star(v_info->id); power = globals(exponent_param)->value.real; halfpower = power/2; for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] = 0.0; /* intialize gradient */ /* distant change part */ for ( i = 0 ; i < SDIM ; i++ ) sum[i] = 0.0; FOR_ALL_VERTICES(v_id) { REAL *y = get_coord(v_id); tj = get_vertex_star(v_id); for ( i = 0 ; i < SDIM ; i++ ) d[i] = x[i] - y[i]; rr = SDIM_dot(d,d); if ( rr > 1e-12 ) /* don't do self */ { p = power*tj/rr/pow(rr,halfpower); /* inverse power potential */ for ( i = 0 ; i < SDIM ; i++ ) sum[i] -= p*d[i]; } } for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] += 2*ti*sum[i]; /* area change part */ /* go around all neighbor vertices */ start_fe = get_vertex_fe(v_info->id); sumi = f_sums[ordinal(v_info->id)]; if ( valid_id(start_fe) ) for ( fe = start_fe ; ; ) { next_fe = inverse_id(get_next_facet(get_prev_edge(fe))); get_edge_side(get_fe_edge(fe),side1); get_edge_side(get_fe_edge(get_next_edge(fe)),side2); s11 = SDIM_dot(side1,side1); s12 = SDIM_dot(side1,side2); s22 = SDIM_dot(side2,side2); area = sqrt(s11*s22 - s12*s12); for ( i = 0 ; i < SDIM ; i++ ) da[i] = (s12*side2[i] - s22*side1[i])/area/6; sumj = f_sums[ordinal(get_fe_headv(fe))]; sumjj = f_sums[ordinal(get_fe_headv(next_fe))]; for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] += 2*(sumj*da[i] + sumjj*da[i] + sumi*da[i]); if ( next_fe == start_fe ) break; fe = next_fe; } return 2*ti*sumi; } /**************************************************************************/ /**************************************************************************/ /**************************************************************************** * * function: facet_knot_energy_fix() * * purpose: provide adjacent vertex correction to facet_knot_energy * */ static REAL *fix_sums; /* for sums to other vertices connected by edges */ /*************************************************************** * * function: facet_knot_energy_fix_init() * */ void facet_knot_energy_fix_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { edge_id e_id; REAL *x; vertex_id v_id,vv_id; int i; REAL d[MAXCOORD]; /* difference vector between vertices */ REAL rr; REAL power; facetedge_id fe; REAL area,s11,s12,s22,side1[MAXCOORD],side2[MAXCOORD]; facet_id f_id; exponent_param = lookup_global(SURF_KNOTPOW_NAME); if ( exponent_param < 0 ) /* missing, so add */ { exponent_param = add_global(SURF_KNOTPOW_NAME); globals(exponent_param)->value.real = 4.0; /* default */ globals(exponent_param)->flags |= ORDINARY_PARAM | RECALC_PARAMETER | ALWAYS_RECALC; } /* will need areas of all vertex stars */ FOR_ALL_VERTICES(v_id) set_vertex_star(v_id,0.0); FOR_ALL_FACETS(f_id) { fe = get_facet_fe(f_id); get_edge_side(get_fe_edge(fe),side1); get_edge_side(get_fe_edge(get_next_edge(fe)),side2); s11 = SDIM_dot(side1,side1); s22 = SDIM_dot(side2,side2); s12 = SDIM_dot(side1,side2); area = sqrt(s11*s22 - s12*s12)/2; for ( i = 0 ; i < 3 ; i++ ) { vv_id = get_fe_tailv(fe); add_vertex_star(vv_id,area/3); fe = get_next_edge(fe); } } /* get basic sums */ power = globals(exponent_param)->value.real/2; /* since rr already square */ if ( fix_sums ) myfree((char*)fix_sums); fix_sums = (REAL *)mycalloc(web.skel[VERTEX].max_ord+1,sizeof(REAL)); FOR_ALL_EDGES(e_id) { REAL val; int ordv; REAL *y; REAL ti,tj; int ordvv; v_id = get_edge_tailv(e_id); vv_id = get_edge_headv(e_id); ordv = ordinal(v_id); ordvv = ordinal(vv_id); x = get_coord(v_id); y = get_coord(vv_id); ti = get_vertex_star(v_id); tj = get_vertex_star(vv_id); for ( i = 0 ; i < SDIM ; i++ ) d[i] = x[i] - y[i]; rr = SDIM_dot(d,d); val = pow(rr,power); /* inverse power potential */ fix_sums[ordv] += tj/val; fix_sums[ordvv] += ti/val; } } /************************************************************** * * function: facet_knot_energy_fix() * * purpose: calculates energy of one vertex due to potential * with its neighbors. Charge at vertex is area of star. * * input: info about vertex is in global qinfo structure. * */ REAL facet_knot_energy_fix(v_info) struct qinfo *v_info; { REAL ti,energy; ti = get_vertex_star(v_info->id); energy = ti*fix_sums[ordinal(v_info->id)]; return energy; } /************************************************************** * * function: facet_knot_energy_fix_gradient() * * purpose: calculates energy of one vertex due to potential * with all others. * * input: info about vertex is in global qinfo structure. * */ REAL facet_knot_energy_fix_gradient(v_info) struct qinfo *v_info; { REAL *x = get_coord(v_info->id); vertex_id v_id; int i; REAL d[MAXCOORD]; /* difference vector between vertices */ REAL rr,p; REAL power,halfpower; REAL ti,tj; /* length weights of vertices, ti for home vertex */ facetedge_id fe,start_fe,next_fe; REAL sumi,sumj,sumjj,area; REAL s11,s12,s22,side1[MAXCOORD],side2[MAXCOORD]; REAL da[MAXCOORD],sum[MAXCOORD]; ti = get_vertex_star(v_info->id); power = globals(exponent_param)->value.real; halfpower = power/2; for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] = 0.0; /* intialize gradient */ /* distant change part */ for ( i = 0 ; i < SDIM ; i++ ) sum[i] = 0.0; /* go around all neighbor vertices */ start_fe = get_vertex_fe(v_info->id); if (valid_id(start_fe)) for ( fe = start_fe ; ; ) { REAL *y; v_id = get_fe_headv(fe); y = get_coord(v_id); tj = get_vertex_star(v_id); for ( i = 0 ; i < SDIM ; i++ ) d[i] = x[i] - y[i]; rr = SDIM_dot(d,d); if ( rr > 1e-12 ) /* don't do self */ { p = power*tj/rr/pow(rr,halfpower); /* inverse power potential */ for ( i = 0 ; i < SDIM ; i++ ) sum[i] -= p*d[i]; } next_fe = inverse_id(get_next_facet(get_prev_edge(fe))); if ( next_fe == start_fe ) break; fe = next_fe; } for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] += ti*sum[i]; /* area change part */ /* go around all neighbor vertices */ start_fe = get_vertex_fe(v_info->id); sumi = fix_sums[ordinal(v_info->id)]; if (valid_id(start_fe)) for ( fe = start_fe ; ; ) { next_fe = inverse_id(get_next_facet(get_prev_edge(fe))); get_edge_side(get_fe_edge(fe),side1); get_edge_side(get_fe_edge(get_next_edge(fe)),side2); s11 = SDIM_dot(side1,side1); s12 = SDIM_dot(side1,side2); s22 = SDIM_dot(side2,side2); area = sqrt(s11*s22 - s12*s12); for ( i = 0 ; i < SDIM ; i++ ) da[i] = (s12*side2[i] - s22*side1[i])/area/6; sumj = fix_sums[ordinal(get_fe_headv(fe))]; sumjj = fix_sums[ordinal(get_fe_headv(next_fe))]; for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] += sumj*da[i] + sumjj*da[i] + sumi*da[i]; if ( next_fe == start_fe ) break; fe = next_fe; } return ti*sumi; } /**************************************************************************/ /**************************************************************************/ /*************************************************************** * * function: xfacet_knot_energy_init() * * purpose: initialization for knot_energy() and * knot_energy_gradient(). * Does not do fsums in attempt to be more efficient with MPI */ void xfacet_knot_energy_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { vertex_id v_id,vv_id; int i; facetedge_id fe; REAL area,s11,s12,s22,side1[MAXCOORD],side2[MAXCOORD]; facet_id f_id; exponent_param = lookup_global(SURF_KNOTPOW_NAME); if ( exponent_param < 0 ) /* missing, so add */ { exponent_param = add_global(SURF_KNOTPOW_NAME); globals(exponent_param)->value.real = 4.0; /* default */ globals(exponent_param)->flags |= ORDINARY_PARAM | RECALC_PARAMETER | ALWAYS_RECALC; } /* will need areas of all vertex stars */ FOR_ALL_VERTICES(v_id) set_vertex_star(v_id,0.0); FOR_ALL_FACETS(f_id) { fe = get_facet_fe(f_id); get_edge_side(get_fe_edge(fe),side1); get_edge_side(get_fe_edge(get_next_edge(fe)),side2); s11 = SDIM_dot(side1,side1); s22 = SDIM_dot(side2,side2); s12 = SDIM_dot(side1,side2); area = sqrt(s11*s22 - s12*s12)/2; for ( i = 0 ; i < 3 ; i++ ) { vv_id = get_fe_tailv(fe); add_vertex_star(vv_id,area/3); fe = get_next_edge(fe); } } } /************************************************************** * * function: facet_knot_energy() * * purpose: calculates energy of one vertex due to potential * with all others. Charge at vertex is area of star. * * input: info about vertex is in global qinfo structure. * */ REAL xfacet_knot_energy(v_info) struct qinfo *v_info; { REAL ti,energy; ti = get_vertex_star(v_info->id); energy = ti*f_sums[ordinal(v_info->id)]; return energy; } /************************************************************** * * function: facet_knot_energy_gradient() * * purpose: calculates energy of one vertex due to potential * with all others. * * input: info about vertex is in qinfo structure. * */ REAL xfacet_knot_energy_gradient(v_info) struct qinfo *v_info; { REAL *x = get_coord(v_info->id); vertex_id v_id; int i; REAL d[MAXCOORD]; /* difference vector between vertices */ REAL rr,p; REAL power,halfpower; REAL ti,tj; /* length weights of vertices, ti for home vertex */ facetedge_id fe,start_fe,next_fe; REAL sumi,sumj,sumjj,area; REAL s11,s12,s22,side1[MAXCOORD],side2[MAXCOORD]; REAL da[MAXCOORD],sum[MAXCOORD]; ti = get_vertex_star(v_info->id); power = globals(exponent_param)->value.real; halfpower = power/2; for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] = 0.0; /* intialize gradient */ /* distant change part */ for ( i = 0 ; i < SDIM ; i++ ) sum[i] = 0.0; FOR_ALL_VERTICES(v_id) { REAL *y = get_coord(v_id); tj = get_vertex_star(v_id); for ( i = 0 ; i < SDIM ; i++ ) d[i] = x[i] - y[i]; rr = SDIM_dot(d,d); if ( rr > 1e-12 ) /* don't do self */ { p = power*tj/rr/pow(rr,halfpower); /* inverse power potential */ for ( i = 0 ; i < SDIM ; i++ ) sum[i] -= p*d[i]; } } for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] += ti*sum[i]; /* area change part */ /* go around all neighbor vertices */ start_fe = get_vertex_fe(v_info->id); sumi = f_sums[ordinal(v_info->id)]; if ( valid_id(start_fe) ) for ( fe = start_fe ; ; ) { next_fe = inverse_id(get_next_facet(get_prev_edge(fe))); get_edge_side(get_fe_edge(fe),side1); get_edge_side(get_fe_edge(get_next_edge(fe)),side2); s11 = SDIM_dot(side1,side1); s12 = SDIM_dot(side1,side2); s22 = SDIM_dot(side2,side2); area = sqrt(s11*s22 - s12*s12); for ( i = 0 ; i < SDIM ; i++ ) da[i] = (s12*side2[i] - s22*side1[i])/area/6; sumj = f_sums[ordinal(get_fe_headv(fe))]; sumjj = f_sums[ordinal(get_fe_headv(next_fe))]; for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] += sumj*da[i] + sumjj*da[i] + sumi*da[i]; if ( next_fe == start_fe ) break; fe = next_fe; } return ti*sumi; } /**************************************************************************/ /************************************************************************** buck knot energy Suggested by Gregory Buck Between pairs of edges, energy is 1/(d1+d2+d3+d4-2(L1+l2)) where d's are cross distances between vertices and L's are edge lengths. Idea is to provide infinite barrier to edge crossing. ******************************************************************/ /************************************************************** * * function: buck_knot_energy() * * purpose: calculates energy of one vertex due to potential * with all others. Charge at vertex is area of star. * * input: info about vertex is in global qinfo structure. * */ REAL buck_knot_energy(v_info) struct qinfo *v_info; { edge_id e1 = v_info->id,e2; REAL *x1,*x2,*yy1,*y2; /* end coordinates */ REAL d1,d2,d3,d4,L1,L2; REAL power,denom; REAL energy = 0.0; REAL dx[MAXCOORD]; int j; power = globals(exponent_param)->value.real; x1 = get_coord(get_edge_tailv(e1)); x2 = get_coord(get_edge_headv(e1)); for ( j = 0 ; j < SDIM ; j++ ) dx[j] = x2[j] - x1[j]; L1 = sqrt(SDIM_dot(dx,dx)); FOR_ALL_EDGES(e2) { if ( e2 <= e1 ) continue; /* each pair once */ yy1 = get_coord(get_edge_tailv(e2)); y2 = get_coord(get_edge_headv(e2)); for ( j = 0 ; j < SDIM ; j++ ) dx[j] = y2[j] - yy1[j]; L2 = sqrt(SDIM_dot(dx,dx)); for ( j = 0 ; j < SDIM ; j++ ) dx[j] = yy1[j] - x1[j]; d1 = sqrt(SDIM_dot(dx,dx)); if ( d1 <= 0.0 ) continue; /* prevent division by 0 */ for ( j = 0 ; j < SDIM ; j++ ) dx[j] = yy1[j] - x2[j]; d2 = sqrt(SDIM_dot(dx,dx)); if ( d2 <= 0.0 ) continue; /* for adjacent edges */ for ( j = 0 ; j < SDIM ; j++ ) dx[j] = x2[j] - y2[j]; d3 = sqrt(SDIM_dot(dx,dx)); if ( d3 <= 0.0 ) continue; /* prevent division by 0 */ for ( j = 0 ; j < SDIM ; j++ ) dx[j] = y2[j] - x1[j]; d4 = sqrt(SDIM_dot(dx,dx)); if ( d4 <= 0.0 ) continue; /* prevent division by 0 */ denom = d1+d2+d3+d4-2*(L1+L2); if ( denom <= 0.0 ) kb_error(1543,"Buck denominator nonpositive.\n",RECOVERABLE); energy += pow(denom ,-power); } return 2*energy; /* since each pair once */ } /************************************************************** * * function: buck_knot_energy_gradient() * * purpose: calculates energy of one vertex due to potential * with all others. * * input: info about vertex is in global qinfo structure. * */ REAL buck_knot_energy_gradient(v_info) struct qinfo *v_info; { edge_id e1 = v_info->id,e2; REAL *x1,*x2,*yy1,*y2; /* end coordinates */ REAL d1,d2,d3,d4,L1,L2; REAL power; REAL energy = 0.0; REAL denom,coeff; int i,j; REAL dL1[MAXCOORD], dL2[MAXCOORD], dd1[MAXCOORD], dd2[MAXCOORD], dd3[MAXCOORD], dd4[MAXCOORD]; for ( i = 0 ; i < 2 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) v_info->grad[i][j] = 0.0; power = globals(exponent_param)->value.real; x1 = get_coord(get_edge_tailv(e1)); x2 = get_coord(get_edge_headv(e1)); for ( j = 0 ; j < SDIM ; j++ ) dL1[j] = x2[j] - x1[j]; L1 = sqrt(SDIM_dot(dL1,dL1)); FOR_ALL_EDGES(e2) { yy1 = get_coord(get_edge_tailv(e2)); y2 = get_coord(get_edge_headv(e2)); for ( j = 0 ; j < SDIM ; j++ ) dL2[j] = y2[j] - yy1[j]; L2 = sqrt(SDIM_dot(dL2,dL2)); for ( j = 0 ; j < SDIM ; j++ ) dd1[j] = x1[j] - yy1[j]; d1 = sqrt(SDIM_dot(dd1,dd1)); if ( d1 <= 0.0 ) continue; /* prevent division by 0 */ for ( j = 0 ; j < SDIM ; j++ ) dd2[j] = yy1[j] - x2[j]; d2 = sqrt(SDIM_dot(dd2,dd2)); if ( d2 <= 0.0 ) continue; /* for identical vertices */ for ( j = 0 ; j < SDIM ; j++ ) dd3[j] = x2[j] - y2[j]; d3 = sqrt(SDIM_dot(dd3,dd3)); if ( d3 <= 0.0 ) continue; /* since contribution */ for ( j = 0 ; j < SDIM ; j++ ) dd4[j] = y2[j] - x1[j]; d4 = sqrt(SDIM_dot(dd4,dd4)); if ( d4 <= 0.0 ) continue; /* fixed at 0. Burma Shave */ denom = d1+d2+d3+d4-2*(L1+L2); if ( denom <= 0.0 ) kb_error(1544,"Buck denominator nonpositive.\n",RECOVERABLE); energy += pow(denom ,-power); coeff = -2*power*pow(denom,-power-1); for ( j = 0 ; j < SDIM ; j++ ) { v_info->grad[0][j] += coeff*(dd1[j]/d1 - dd4[j]/d4 + 2*dL1[j]/L1); v_info->grad[1][j] += coeff*(dd3[j]/d3 - dd2[j]/d2 - 2*dL1[j]/L1); } } return energy; } /******************* bi_surface named method ********************** Double integral over surface, i.e. all pairs of vertices weighted with adjacent facet areas. Adapted from facet_knot_energy. Uses arbitrary formula for energy, function of vector between vertices, instead of just power rule. Formula is "scalar_integrand" in datafile definition. Also, vertex pairs it is evaluated over can be controlled. Only those with different values of the vertex attribute "bi_surface_attr" will be included, if bi_surface_attr is defined; otherwise all pairs. ***********************************************************************/ /*************************************************************** * * function: bi_surface_init() * * purpose: initialization for bi_surface() */ int bi_surface_attr; /* number of attribute */ char *bi_surface_attr_name = "bi_surface_attr"; REAL *bi_sums; /* values for vertices */ void bi_surface_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { REAL *x; vertex_id v_id,vv_id; int i; REAL d[MAXCOORD]; /* difference vector between vertices */ REAL rr; REAL tj; facetedge_id fe; REAL area,s11,s12,s22,side1[MAXCOORD],side2[MAXCOORD]; facet_id f_id; bi_surface_attr = find_attribute(VERTEX,bi_surface_attr_name); if ( bi_surface_attr >= 0 ) { /* check proper type */ if ( EXTRAS(VERTEX)[bi_surface_attr].type != INTEGER_TYPE ) kb_error(4927, "The type of vertex attribute bi_surface_attr must be integer.\n", RECOVERABLE); } /* will need areas of all vertex stars */ FOR_ALL_VERTICES(v_id) set_vertex_star(v_id,0.0); FOR_ALL_FACETS(f_id) { fe = get_facet_fe(f_id); get_edge_side(get_fe_edge(fe),side1); get_edge_side(get_fe_edge(get_next_edge(fe)),side2); s11 = SDIM_dot(side1,side1); s22 = SDIM_dot(side2,side2); s12 = SDIM_dot(side1,side2); area = sqrt(s11*s22 - s12*s12)/2; for ( i = 0 ; i < FACET_VERTS ; i++ ) { vv_id = get_fe_tailv(fe); add_vertex_star(vv_id,area/3); fe = get_next_edge(fe); } } /* basic sums */ if ( bi_sums ) myfree((char*)bi_sums); bi_sums = (REAL *)mycalloc(web.skel[VERTEX].max_ord+1,sizeof(REAL)); FOR_ALL_VERTICES(v_id) { int ordv = ordinal(v_id); int vattr; REAL val; REAL ti = get_vertex_star(v_id); x = get_coord(v_id); if ( bi_surface_attr >= 0) vattr = *(int*)get_extra(v_id,bi_surface_attr); FOR_ALL_VERTICES(vv_id) { int vvattr; REAL *y = get_coord(vv_id); int ordvv = ordinal(vv_id); if ( ordvv <= ordv ) continue; if ( bi_surface_attr >= 0) { vvattr = *(int*)get_extra(vv_id,bi_surface_attr); if ( vvattr == vattr ) continue; /* only do for different values */ } tj = get_vertex_star(vv_id); for ( i = 0 ; i < SDIM ; i++ ) d[i] = x[i] - y[i]; rr = SDIM_dot(d,d); val = eval(mi->expr[0],d,v_id,NULL); bi_sums[ordv] += tj*val; bi_sums[ordvv] += ti*val; } } } /************************************************************** * * function: bi_surface_energy() * * purpose: calculates energy of one vertex due to potential * with all others. Charge at vertex is area of star. * * input: info about vertex is in global qinfo structure. * */ REAL bi_surface_energy(v_info) struct qinfo *v_info; { REAL ti,energy; ti = get_vertex_star(v_info->id); energy = ti*bi_sums[ordinal(v_info->id)]; return energy; /* because pair counts in both orders */ } /************************************************************** * * function: bi_surface_gradient() * * purpose: calculates energy of one vertex due to potential * with all others. * * input: info about vertex is in qinfo structure. * */ REAL bi_surface_gradient(v_info) struct qinfo *v_info; { REAL *x = get_coord(v_info->id); vertex_id v_id; int i; REAL d[MAXCOORD]; /* difference vector between vertices */ REAL power,halfpower; REAL ti,tj; /* length weights of vertices, ti for home vertex */ facetedge_id fe,start_fe,next_fe; REAL sumi,sumj,sumjj,area; REAL s11,s12,s22,side1[MAXCOORD],side2[MAXCOORD]; REAL da[MAXCOORD],sum[MAXCOORD]; int vattr; struct method_instance *mi = METH_INSTANCE(v_info->method); if ( bi_surface_attr >= 0) vattr = *(int*)get_extra(v_info->id,bi_surface_attr); ti = get_vertex_star(v_info->id); power = globals(exponent_param)->value.real; halfpower = power/2; for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] = 0.0; /* intialize gradient */ /* distant change part */ for ( i = 0 ; i < SDIM ; i++ ) sum[i] = 0.0; FOR_ALL_VERTICES(v_id) { REAL *y = get_coord(v_id); REAL val; REAL partials[MAXCOORD]; if ( bi_surface_attr >= 0) { int vvattr = *(int*)get_extra(v_id,bi_surface_attr); if ( vvattr == vattr ) continue; } else if ( equal_id(v_id,v_info->id) ) continue; /* don't do self */ tj = get_vertex_star(v_id); for ( i = 0 ; i < SDIM ; i++ ) d[i] = x[i] - y[i]; eval_all(mi->expr[0],d,3,&val,partials,v_info->id); for ( i = 0 ; i < SDIM ; i++ ) sum[i] += tj*partials[i]; } for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] += 2*ti*sum[i]; /* area change part */ /* go around all neighbor vertices */ start_fe = get_vertex_fe(v_info->id); sumi = bi_sums[ordinal(v_info->id)]; if ( valid_id(start_fe) ) for ( fe = start_fe ; ; ) { next_fe = inverse_id(get_next_facet(get_prev_edge(fe))); get_edge_side(get_fe_edge(fe),side1); get_edge_side(get_fe_edge(get_next_edge(fe)),side2); s11 = SDIM_dot(side1,side1); s12 = SDIM_dot(side1,side2); s22 = SDIM_dot(side2,side2); area = sqrt(s11*s22 - s12*s12); for ( i = 0 ; i < SDIM ; i++ ) da[i] = (s12*side2[i] - s22*side1[i])/area/6; sumj = bi_sums[ordinal(get_fe_headv(fe))]; sumjj = bi_sums[ordinal(get_fe_headv(next_fe))]; for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] += 2*(sumj*da[i] + sumjj*da[i] + sumi*da[i]); if ( next_fe == start_fe ) break; fe = next_fe; } return 2*ti*sumi; } /**************************************************************************/ /**************************************************************************/ evolver-2.30c.dfsg/src/hidim.c0000644000175300017530000001214111410765113016452 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************* * * file: hidim.c * * Contents: Functions calculating energy and its * gradients for the LINEAR SOAPFILM model * in arbitrary ambient dimension. * Plain area only; no wulff vectors or surface energy * or gravity. */ #include "include.h" /************************************************************************ * * Calculates all forces on control points due to facet and * accumulates them at each control point. */ void facet_force_l_hi_d(f_id) facet_id f_id; { REAL side[FACET_EDGES][MAXCOORD]; int i,j,k; REAL area; facetedge_id fe_id; REAL *x[FACET_VERTS]; REAL *force[FACET_VERTS]; REAL wee_area = 0.0000000001*web.min_area; REAL factor,s1s1,s1s2,s2s2; vertex_id v_id[MAXCOORD+1]; edge_id e_id[FACET_EDGES]; /* get side vectors */ fe_id = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++, fe_id = get_next_edge(fe_id) ) { v_id[i] = get_fe_tailv(fe_id); e_id[i] = get_fe_edge(fe_id); x[i] = get_coord(v_id[i]); force[i] = get_force(v_id[i]); } /* calculate sides and product matrix */ for ( i = 0 ; i < FACET_EDGES ; i++ ) { int ii = (i+1)%FACET_EDGES; for ( j = 0 ; j < SDIM ; j++ ) side[i][j] = x[ii][j] - x[i][j]; } s1s1 = SDIM_dot(side[0],side[0]); s1s2 = SDIM_dot(side[0],side[1]); s2s2 = SDIM_dot(side[1],side[1]); area = sqrt(s1s1*s2s2 - s1s2*s1s2)/2; /* an error check, and accommodation for possibly deliberately degenerate triangles on boundary */ if ( area < wee_area ) { facetedge_id ffe; ffe = fe_id; outstring("WARNING! Zero area triangle!\n"); outstring("Facet-edges and sides: \n"); for ( i = 0 ; i < FACET_EDGES ; i++, ffe = get_next_edge(ffe) ) { sprintf(msg," %8lX %18.15f %18.15f %18.15f\n",(unsigned long)ffe, (DOUBLE)side[i][0],(DOUBLE)side[i][1],(DOUBLE)side[i][2]); outstring(msg); } prompt("Hit RETURN to continue.",msg,msgmax); } set_facet_area(f_id,area); /* half of parallelogram magnitude */ if ( get_fattr(f_id) & DENSITY ) factor = get_facet_density(f_id)/area/4; else factor = 1/area/4; if ( ((square_curvature_flag | mean_curv_int_flag) & EVALUATE) && !kusner_flag && !conf_edge_curv_flag ) sqcurve_force(v_id,e_id,side); /* accumulate star area around each vertex and edge */ { fe_id = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++, fe_id = get_next_edge(fe_id) ) { edge_id ee_id = get_fe_edge(fe_id); vertex_id vv_id = get_edge_headv(ee_id); add_vertex_star(vv_id,area); add_edge_star(ee_id,area); } } /* force on each vertex */ for ( k = 0 ; k < SDIM ; k++ ) { REAL temp1,temp2; temp1 = (side[0][k]*s2s2 - side[1][k]*s1s2)*factor; temp2 = (side[1][k]*s1s1 - side[0][k]*s1s2)*factor; force[0][k] += temp1; force[1][k] -= temp1 - temp2; force[2][k] -= temp2; } } /********************************************************************* * * Function: facet_energy_l_hi_d() * * Purpose: Calculates energy due to facet for LINEAR SOAPFILM. * For arbitrary ambient dimension. */ void facet_energy_l_hi_d(f_id) facet_id f_id; { REAL side[FACET_EDGES][MAXCOORD]; int i,j; REAL energy; facetedge_id fe_id; REAL *x[FACET_VERTS]; REAL s1s1,s1s2,s2s2; vertex_id v_id[MAXCOORD+1]; /* get side vectors */ fe_id = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++, fe_id = get_next_edge(fe_id) ) { v_id[i] = get_fe_tailv(fe_id); x[i] = get_coord(v_id[i]); } /* calculate sides and product matrix */ for ( i = 0 ; i < FACET_EDGES ; i++ ) { int ii = (i+1)%FACET_EDGES; for ( j = 0 ; j < SDIM ; j++ ) side[i][j] = x[ii][j] - x[i][j]; } s1s1 = SDIM_dot(side[0],side[0]); s1s2 = SDIM_dot(side[0],side[1]); s2s2 = SDIM_dot(side[1],side[1]); energy = sqrt(s1s1*s2s2 - s1s2*s1s2)/2; /* half of parallelogram */ web.total_area += energy; set_facet_area(f_id,energy); if ( get_fattr(f_id) & DENSITY ) energy *= get_facet_density(f_id); web.total_energy += energy; /* do square curvature if wanted */ if ( ((square_curvature_flag | mean_curv_int_flag) & EVALUATE ) && !kusner_flag && !conf_edge_curv_flag ) sqcurve_energy(v_id,side); /* accumulate 1/3 area around each vertex to scale motion */ if ( web.area_norm_flag ) { fe_id = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++, fe_id = get_next_edge(fe_id) ) { vertex_id vv_id = get_fe_headv(fe_id); add_vertex_star(vv_id,energy); } } } evolver-2.30c.dfsg/src/storage.c0000644000175300017530000016155411410765113017041 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /******************************************************************** * * File: storage.c * * Purpose: File defining details of storage implementation. * All machine-dependent gory details should be here * (purely private details) or in storage.h * (for inclusion in other source files that need to know). * * This version has element ids implemented as longs. */ #include "include.h" struct blocklist_struct *blocklist[NUMELEMENTS]; /* list of allocated blocks */ int blockcount[NUMELEMENTS]; /* how many blocks allocated */ int blockmax[NUMELEMENTS]; /* length of blocklist */ int sparse_ibase_flag; /* for permitting sparse ibase */ /* individual indirect block pointer arrays, handy for debugging */ /* set to web.skel[].ibase */ INDIRECT_TYPE *vibase; INDIRECT_TYPE *eibase; INDIRECT_TYPE *fibase; INDIRECT_TYPE *bibase; INDIRECT_TYPE *feibase; /* The web structure */ struct webstruct web; element_id NULLVERTEX = (element_id)VERTEX << TYPESHIFT, NULLEDGE = (element_id)EDGE << TYPESHIFT, NULLFACET = (element_id)FACET << TYPESHIFT, NULLBODY = (element_id)BODY << TYPESHIFT, NULLFACETEDGE = (element_id)FACETEDGE << TYPESHIFT; #ifdef ELPTRFUNC struct element *elptr(id) element_id id; { int type = id_type(id); #ifdef MPI_EVOLVER if ( id_task(id) != this_task ) return mpi_remote_elptr(id); #endif return (struct element *)(web.skel[type].ibase[id & OFFSETMASK]); } #endif int oid(id) element_id id; { return inverted(id) ? -(ordinal(id)+1) : (ordinal(id) +1) ; } /* handy for debugging */ struct vertex * Vptr ARGS((element_id)); struct edge * Eptr ARGS((element_id)); struct facet * Fptr ARGS((element_id)); struct body * Bptr ARGS((element_id)); struct facetedge * Feptr ARGS((element_id)); struct vertex * Vptr(id) vertex_id id; { return vptr(id); } struct edge * Eptr(id) edge_id id; { return eptr(id); } struct facet * Fptr(id) facet_id id; { return fptr(id); } struct body * Bptr(id) body_id id; { return bptr(id); } struct facetedge * Feptr(id) facetedge_id id; { return feptr(id); } /*********************************************************************** * * function: expand() * * purpose: Increase size of element structures. Works only for * indexed or indirect id. Will also decrease size. */ void expand(type,newsize) int type; /* VERTEX, etc. */ int newsize; /* new size of structure */ { char *newblock,*oldblock; int oldsize = web.sizes[type]; int count = web.skel[type].maxcount; int i,n; char *newptr,*oldptr; int copysize; /* round up newsize to alignment for doubles */ newsize = ((newsize+sizeof(REAL)-1)/sizeof(REAL))*sizeof(REAL); if ( newsize == web.sizes[type] ) return; /* don't have to expand */ web.sizes[type] = newsize; if ( count == 0 ) return; /* don't have to reallocate space */ copysize = (newsize < oldsize) ? newsize : oldsize; ENTER_GRAPH_MUTEX; /* Don't mess with structures while graph thread using them */ #ifdef HASH_ID web.skel[type].freehead = NULL; #endif for ( n = 0 ; n < blockcount[type] ; n++ ) { INDIRECT_TYPE *iptr; char *spot; struct blocklist_struct *b = blocklist[type] + n; newblock = mycalloc(b->count,newsize); for ( i = 0, oldptr = (char*)(b->blockptr), newptr = newblock; i < b->count ; i++, oldptr += oldsize, newptr += newsize ) { memcpy(newptr,oldptr,copysize); #ifdef MPI_EVOLVER { element_id id = ((struct element*)oldptr)->self_id; if ( id_task(id) != this_task ) { /* update pointer in remote_elements */ remote_elements[type].ibase[id_task(id)][ordinal(id)] = (struct element *)newptr; } } #endif } oldblock = (char*)b->blockptr; b->blockptr = (struct element*)newblock; /* update indirect pointers */ /* keeping in mind structures not necessarily in id order due to reorder command */ for ( i=0,spot=newblock, iptr=web.skel[type].ibase+b->start_ord ; icount ; i++,spot+=newsize,iptr++ ) { #ifdef MPI_EVOLVER element_id id = ((struct element *)spot)->local_id; #else element_id id = ((struct element *)spot)->self_id; #endif #ifdef HASH_ID { struct element *el_ptr; el_ptr = (struct element *)spot; if ( el_ptr->self_id ) /* replace in hash table */ elhash_replace(id,el_ptr); else /* add to freelist */ { *(struct element **)&(el_ptr->forechain) = web.skel[type].freehead; web.skel[type].freehead = el_ptr; } } #else web.skel[type].ibase[ordinal(id)] = (struct element *)spot; #endif } myfree(oldblock); } LEAVE_GRAPH_MUTEX; parallel_update_flag[type] = 1; } /*********************************************************************** * * function: extend() * * purpose: allocate more empty element structures */ void extend(type,mode) int type; int mode; /* EXTEND_BATCH or EXTEND_FOR_REFINE */ { #ifdef HASH_ID elhash_extend(type,mode); #else int number=0; /* of new items to allocate */ char *newblock; INDIRECT_TYPE *newiblock; element_id id,backid; struct element *newptr = NULL; int k; int allocsize; long oldnum = web.skel[type].maxcount; long newnum=0; int neword; ENTER_GRAPH_MUTEX; /* Don't mess with structures while graph thread using them */ if ( blockcount[type] >= blockmax[type] ) { blocklist[type] = (struct blocklist_struct*) kb_realloc((char*)(blocklist[type]), (blockcount[type]+BATCHSIZE)*sizeof(struct blocklist_struct)); blockmax[type] += BATCHSIZE; } if ( mode == EXTEND_BATCH ) { /* calculate number of structures to fit in block size just under 2^n */ allocsize = BATCHSIZE*web.sizes[type]; k = 0x100 ; while ( k < allocsize ) k <<= 1 ; number = (k-16)/web.sizes[type]; /* maybe room for block header */ newnum = web.skel[type].maxcount + number; } else if ( mode == EXTEND_FOR_REFINE ) { /* increase by 2^surface_dimension factor */ if ( type == BODY ) goto extend_exit; /* don't need more bodies */ number = web.skel[type].count*(1< OFFSETMASK ) { sprintf(errmsg, "Trying to allocate more %s than ID format allows, %Ld\n", typenames[type],OFFSETMASK); kb_error(3712,errmsg,RECOVERABLE); } newblock = mycalloc(number,web.sizes[type]); blocklist[type][blockcount[type]].start_ord = oldnum ; blocklist[type][blockcount[type]].count = number ; blocklist[type][blockcount[type]++].blockptr = (struct element *)newblock; while ( newnum > web.skel[type].ialloc ) { if ( web.skel[type].ibase == NULL ) { newiblock = (INDIRECT_TYPE*)mycalloc(number,sizeof(INDIRECT_TYPE)); web.skel[type].ialloc = number; } else { int allocnum = web.skel[type].ialloc < 100 ? 100 : 2*web.skel[type].ialloc; newiblock = (INDIRECT_TYPE*)kb_realloc((char*)(web.skel[type].ibase), allocnum*sizeof(INDIRECT_TYPE)); web.skel[type].ialloc = allocnum; } web.skel[type].ibase = newiblock; switch(type) { case VERTEX: vibase = newiblock; break; case EDGE: eibase = newiblock; break; case FACET: fibase = newiblock; break; case BODY: bibase = newiblock; break; case FACETEDGE: feibase = newiblock; break; } } /* add to end of freelist */ /* find first empty slot */ if ( sparse_ibase_flag ) { /* search for empty ibase slots from start */ for ( neword = 0 ; neword < web.skel[type].ialloc ; neword++ ) if ( web.skel[type].ibase[neword] == NULL ) break; } else /* dense ibase */ neword = web.skel[type].maxcount; id = ((element_id)type << TYPESHIFT) | VALIDMASK | neword; #ifdef MPI_EVOLVER id |= (element_id)this_task << TASK_ID_SHIFT; #endif if ( valid_id(web.skel[type].freelast) ) { backid = web.skel[type].freelast; elptr(backid)->forechain = id; } else { backid = NULLID; web.skel[type].free = id; } for ( k = 0 ; k < number ; k++ ) { newptr = (struct element *)(newblock + k*web.sizes[type]); web.skel[type].ibase[neword] = newptr; newptr->self_id = id; #ifdef MPI_EVOLVER newptr->local_id = id; #endif if ( k < number-1 ) { if ( sparse_ibase_flag ) { for ( neword = 0 ; neword < web.skel[type].ialloc ; neword++ ) if ( web.skel[type].ibase[neword] == NULL ) break; } else neword++; id = ((element_id)type << TYPESHIFT) | VALIDMASK | neword; #ifdef MPI_EVOLVER id |= (element_id)this_task << TASK_ID_SHIFT; #endif newptr->forechain = id; } newptr->backchain = backid; backid = newptr->self_id; } web.skel[type].freelast = backid; web.skel[type].maxcount += number; extend_exit: LEAVE_GRAPH_MUTEX; /* end ifdef HASH_ID */ #endif } /************************************************************************ * * function: new_element() * * purpose: Allocate new element from freelist * */ element_id new_element(type,parent,desired_id) int type; element_id parent; /* for inherited stuff */ element_id desired_id; /* or NULLID */ { element_id newid; struct element *newptr,*last; ORDTYPE ord; #ifdef HASH_ID newptr = elhash_new_element(type,desired_id); newid = newptr->self_id; #else newid = web.skel[type].free; if ( !valid_id(newid) ) /* free list empty */ { if ( aggregate_depth == 0 ) { free_discards(DISCARDS_SOME); newid = web.skel[type].free; } } if ( !valid_id(newid) ) /* free list empty */ { extend(type,EXTEND_BATCH); newid = web.skel[type].free; } newptr = elptr(newid); /* remove from free chain */ web.skel[type].free = newptr->forechain; if ( valid_id(web.skel[type].free) ) elptr(web.skel[type].free)->backchain = NULLID; else web.skel[type].freelast = NULLID; #endif /* clean out old info */ ord = ordinal(newid); memset((char *)newptr,0,web.sizes[type]); if ( ord > web.skel[type].max_ord ) { web.skel[type].max_ord = ord; } newptr->original = NULLID; #ifndef HASH_ID if ( match_id_flag && !addload_flag && datafile_flag ) { /* link in numerical order */ int i; struct element *prev,*next; for ( i = ord-1 ; i >= 0 ; i-- ) { prev = web.skel[type].ibase[i]; if ( prev && (prev->attr & ALLOCATED) ) { if ( valid_id(prev->forechain) ) { next = elptr(prev->forechain); next->backchain = newid; } newptr->forechain = prev->forechain; prev->forechain = newid; newptr->backchain = prev->self_id; break; } } if ( i < 0 ) /* no predecessor */ { newptr->forechain = web.skel[type].used; if ( valid_id(web.skel[type].used) ) { next = elptr(web.skel[type].used); next->backchain = newid; } web.skel[type].used = newid; } if ( !valid_id(newptr->forechain) ) web.skel[type].last = newid; } else #endif { /* add to end of in-use chain */ newptr->forechain = NULLID; newptr->backchain = web.skel[type].last; if ( valid_id(web.skel[type].last) ) { last = elptr(web.skel[type].last); /* find end of in-use chain */ last->forechain = newid; } else { web.skel[type].used = newid; } web.skel[type].last = newid; } newptr->attr = ALLOCATED | NEWELEMENT; newptr->self_id = newid; #ifdef MPI_EVOLVER newptr->local_id = newid; #endif web.skel[type].count++; #ifdef MPI_EVOLVER /* kludge to guarantee max_ord gives enough spaces */ if ( web.skel[type].count > web.skel[type].max_ord+1 ) web.skel[type].max_ord = web.skel[type].count-1; #endif /* inherited attributes and named methods from parent */ if ( valid_id(parent) ) { struct element *e_ptr = elptr(parent); int *instlist = (int*)((char*)e_ptr + get_meth_offset(id_type(parent))); int i; newptr->attr |= e_ptr->attr & (NODISPLAY|FIXED|BOUNDARY|NEGBOUNDARY| BDRY_ENERGY|CONSTRAINT|BDRY_CONTENT|NEGCONSTRAINT|BARE_NAKED); if ( e_ptr->attr & CONSTRAINT ) { conmap_t *parent_conmap=NULL; switch ( id_type(parent) ) { case VERTEX: parent_conmap = get_v_constraint_map(parent); break; case EDGE : parent_conmap = get_e_constraint_map(parent); break; case FACET : parent_conmap = get_f_constraint_map(parent); break; } switch ( type ) { case VERTEX: set_v_conmap(newid,parent_conmap); break; case EDGE : set_e_conmap(newid,parent_conmap); break; case FACET : set_f_conmap(newid,parent_conmap); break; } } if ( e_ptr->attr & BOUNDARY ) { int bnum=0; switch ( id_type(parent) ) { case VERTEX: bnum = get_vertex_boundary_num(parent); break; case EDGE : bnum = get_edge_boundary_num(parent); break; case FACET : bnum = get_facet_boundary_num(parent); break; } switch ( type ) { case VERTEX: set_boundary_num(newid,bnum); break; case EDGE : set_edge_boundary_num(newid,bnum); break; case FACET : set_facet_boundary_num(newid,bnum); break; } } for ( i = 0 ; i < (int)e_ptr->method_count ; i++ ) { if ( METH_INSTANCE(abs(instlist[i]))->type <= type ) apply_method_num(newid,instlist[i]); } } return newid; } /* end new_element() */ /************************************************************************** * * function: free_element() * * purpose: mark element as unallocated, but leave it in the chain of * used elements. Moved to freelist later by free_discards. */ void free_element(id) element_id id; { struct element *ptr; int type = id_type(id); if ( !valid_element(id) ) { sprintf(errmsg, "Internal error: Trying to free invalid element %s id %s \n", typenames[type],ELNAME(id)); kb_error(1311,errmsg,WARNING); return; } if ( type == EDGE ) /* remove from vertex lists */ { vertex_id tailv = get_edge_tailv(id); vertex_id headv = get_edge_headv(id); vertex_id midv; int i; remove_vertex_edge(tailv,id); remove_vertex_edge(headv,inverse_id(id)); if ( web.modeltype == QUADRATIC ) { midv = get_edge_midv(id); if ( valid_id(midv) ) free_element(midv); } if ( web.modeltype == LAGRANGE ) { vertex_id *v = get_edge_vertices(id); for ( i = 1 ; i < web.lagrange_order ; i++ ) { if ( valid_id(v[i]) ) free_element(v[i]); } } } if ( type == FACET ) { /* remove from body facet lists */ set_facet_body(id,NULLID); set_facet_body(inverse_id(id),NULLID); } ptr = elptr(id); if ( !(ptr->attr & ALLOCATED) ) { sprintf(errmsg, "Internal error: Trying to free unallocated element type %d id %s \n", type,ELNAME(id)); kb_error(1313,errmsg,WARNING); return; } ptr->attr &= ~ALLOCATED; #ifdef MPI_EVOLVER if ( id_task(id) != this_task ) { struct element *e; mpi_remove_remote(id); if ( valid_id(ptr->forechain) ) { e = elptr(ptr->forechain); e->backchain = ptr->local_id; } else web.skel[type].last = ptr->local_id; if ( valid_id(ptr->backchain) ) { e = elptr(ptr->backchain); e->forechain = ptr->local_id; } else web.skel[type].used = ptr->local_id; } else mpi_remove_export(id); /* remove from export list */ #endif #ifdef OLDDISCARD /* remove from in-use list */ if ( valid_id(ptr->forechain) ) elptr(ptr->forechain)->backchain = ptr->backchain; else web.skel[type].last = ptr->backchain; /**** DON'T DO THIS! Don't mess with forechains until free_discards()! */ if ( valid_id(ptr->backchain) ) { /* elptr(ptr->backchain)->forechain = ptr->forechain; */ } else web.skel[type].used = ptr->forechain; /*****/ /* add to discard list */ /* save forechain for generators */ ptr->backchain = web.skel[type].discard; web.skel[type].discard = id & (TYPEMASK | VALIDMASK | OFFSETMASK); /* clear old bits, keep only needed */ #else web.skel[type].discard_count++; #endif web.skel[type].count--; } /* reclaim element from discard list */ void unfree_element(id) element_id id; { struct element *ptr; int type = id_type(id); if ( !valid_id(id) ) { sprintf(errmsg,"Internal error: Trying to unfree invalid id %08lX \n", (unsigned long)id); kb_error(1314,errmsg,RECOVERABLE); } ptr = elptr(id); if ( ptr->attr & ALLOCATED ) { sprintf(errmsg, "Internal error: Trying to unfree allocated element id %08lX \n", (unsigned long)id); kb_error(1315,errmsg,RECOVERABLE); } ptr->attr |= ALLOCATED; #ifdef MPI_EVOLVER if ( id_task(id) != this_task ) mpi_unfree_element(id,ptr); #endif web.skel[type].discard_count--; web.skel[type].count++; } /* index as id */ element_id get_ordinal_id(type,ord) int type; /* type of element */ int ord; /* ordinal of element, signed */ { element_id id; struct element *ep; if ( (type < 0) || (type > NUMELEMENTS) ) return NULLID; if ( abs(ord) > web.skel[type].max_ord ) return NULLID; id = ((element_id)type << TYPESHIFT) | VALIDMASK | abs(ord); if ( ord < 0 ) invert(id); #ifdef MPI_EVOLVER id |= (element_id)this_task << TASK_ID_SHIFT; #endif ep = elptr(id); if ( ep == NULL ) return NULLID; if ( ep->attr & ALLOCATED ) return id; return NULLID; } /* completion of partial id */ element_id get_full_id(type,partid) int type; /* type of element */ element_id partid; /* ordinal of element, 0-based, with sign bit and mpi task number */ /* partid should also have VALIDMASK if not known bad */ { element_id id; struct element *ep; int task = id_task(partid); int ord = (int)(partid & OFFSETMASK); if ( (type < 0) || (type > NUMELEMENTS) ) return NULLID; #ifdef MPI_EVOLVER if ( task == this_task ) #endif if ( ord > web.skel[type].max_ord ) return NULLID; #ifdef MPI_EVOLVER if ( task >= mpi_nprocs ) return NULLID; #endif id = partid | ((element_id)type << TYPESHIFT); ep = elptr(id|VALIDMASK); if ( ep == NULL ) return NULLID; if ( ep->attr & ALLOCATED ) return id|VALIDMASK; return NULLID; } int generate_all(type,idptr,sentinel) /* re-entrant */ int type; element_id *idptr; element_id *sentinel; /* to record original end of list */ { struct element *ptr; if ( !valid_id(*idptr) ) /* first time */ { *idptr = web.skel[type].used; *sentinel = web.skel[type].last; /* may be a discard */ if ( !valid_id(*idptr) ) return 0; ptr = elptr(*idptr); if ( (ptr->attr & ALLOCATED) #ifdef MPI_EVOLVER && (id_task(ptr->self_id) == this_task) #endif ) return 1; } ptr = elptr(*idptr); do { if ( equal_id(*idptr,*sentinel) ) return 0; *idptr = ptr->forechain; if ( !valid_id(*idptr) ) return 0; ptr = elptr(*idptr); } while ( !(ptr->attr & ALLOCATED) #ifdef MPI_EVOLVER || (id_task(ptr->self_id) != this_task) #endif ); return 1; } /*************************************************************************** * * function: memory_report() * * purpose: implement 'c' command memory usage report */ void memory_report() { long mem; int k; #ifdef MPI_EVOLVER if ( this_task == 0 ) { mpi_count_report(); } else #endif { mem = 0; for ( k = 0 ; k < NUMELEMENTS ; k++ ) mem += web.skel[k].count*web.sizes[k]; sprintf(errmsg, "Vertices: %ld Edges: %ld Facets: %ld Bodies: %ld Facetedges: %ld\nElement memory: %ld\n", web.skel[0].count,web.skel[1].count,web.skel[2].count, web.skel[3].count,web.skel[4].count, mem); outstring(errmsg); } if ( verbose_flag ) { /* report element sizes */ for ( k = 0 ; k < NUMELEMENTS ; k++ ) { sprintf(msg,"%10.10s size: %4d bytes; number allocated: %d\n", typenames[k],web.sizes[k],web.skel[k].maxcount); outstring(msg); } sprintf(msg,"quantity size: %4d bytes; number allocated: %4d\n", sizeof(struct gen_quant),gen_quant_list_max); outstring(msg); sprintf(msg,"instance size: %4d bytes; number allocated: %4d\n", sizeof(struct method_instance),meth_inst_list_max); outstring(msg); } #if defined(_WIN32) && defined(_HEAPOK) #ifndef _HEAPINFO #define _HEAPINFO _heapinfo #endif if ( _heapchk() != _HEAPOK ) kb_error(1317,"Internal error: Corrupt heap! Memory is trashed.\n",WARNING); else if ( verbose_flag ) { struct _HEAPINFO hinfo; int b_use=0,b_free=0; size_t mem_use=0,mem_free=0; char * heaptop = NULL; char * heapstart = NULL; MEMORYSTATUS memstat; hinfo._pentry = NULL; while ( _heapwalk(&hinfo) == _HEAPOK ) { if ( heapstart == NULL ) heapstart = (char*)hinfo._pentry; #ifdef HEAPLIST sprintf(errmsg,"%p %10ld %s end %p\n",hinfo._pentry,hinfo._size, hinfo._useflag ? "used" : "free", (char*)hinfo._pentry+hinfo._size); outstring(errmsg); #endif if (hinfo._useflag) { b_use++; mem_use+= hinfo._size; } else { b_free++; mem_free += hinfo._size; } if ( (char*)hinfo._pentry + hinfo._size > heaptop ) heaptop = (char*)hinfo._pentry + hinfo._size; } outstring("\n"); sprintf(errmsg,"blocks in use: %d memory in use: %ld \n", b_use,mem_use); outstring(errmsg); sprintf(errmsg,"blocks free: %d memory free: %ld \n", b_free,mem_free); outstring(errmsg); sprintf(errmsg,"Heap top: %p\n",heaptop); outstring(errmsg); sprintf(errmsg,"Heap size: %4.2f MB\n", (heaptop-heapstart)/1024./1024.); outstring(errmsg); memstat.dwLength = sizeof(memstat); GlobalMemoryStatus(&memstat); sprintf(errmsg,"Physical memory size: %d bytes Virtual memory top: %X\n", memstat.dwTotalPhys,memstat.dwTotalVirtual); outstring(errmsg); } #endif #if defined(_UNISTD_H) if ( verbose_flag ) { /* do this only on unix systems with unistd.h */ sprintf(msg,"\nTotal data memory arena %d\n", (char*)sbrk(0)-(char*)&evolver_version); outstring(msg); } #endif #if defined(M_MXFAST) && defined(IRIS) if ( verbose_flag ) { char *ptr; struct mallinfo m; /* using libmalloc.a for debugging */ ptr = malloc(10); if ( ptr == NULL ) erroutstring("Bad heap.\n"); else myfree(ptr); m = mallinfo(); sprintf(msg,"Arena %d Ordblocks: %d Orduse: %d Ordfree: %d\n", m.arena,m.ordblks,m.uordblks,m.fordblks); outstring(msg); sprintf(msg,"Small blocks: %d Small use: %d Small free: %d\n", m.smblks,m.usmblks,m.fsmblks); outstring(msg); } #endif #if defined(SUNXX) if ( memdebug ) if ( malloc_verify() != 1 ) kb_error(1318,"Internal error: Malloc_verify() failed.\n.",RECOVERABLE); #endif mem_list_summary(); dy_check(); } /************************************************************************** * * function: reset_skeleton() * * purpose: Clean out old surface and initialize empty web. * Note all permanently allocated memory cleaned out en masse * previously. * */ void reset_skeleton() { int i; int three = FACET_VERTS; int permcount = web.perm_global_count; int maxperm = web.max_perm_globals; struct global *perm = perm_globals(0); ENTER_GRAPH_MUTEX gauss1Dpt = NULL; gauss1Dwt = NULL; memset((char *)&web,0,sizeof(web)); /* total clean out */ web.perm_global_count = permcount; web.max_perm_globals = maxperm; dy_perm_globals = perm; web.sizes[VERTEX] = sizeof(struct vertex); web.sizes[EDGE] = sizeof(struct edge); web.sizes[FACET] = sizeof(struct facet); web.sizes[BODY] = sizeof(struct body); web.sizes[FACETEDGE] = sizeof(struct facetedge); web.usedsizes[VERTEX] = sizeof(struct vertex); web.usedsizes[EDGE] = sizeof(struct edge); web.usedsizes[FACET] = sizeof(struct facet); web.usedsizes[BODY] = sizeof(struct body); web.usedsizes[FACETEDGE] = sizeof(struct facetedge); for ( i = 0 ; i < NUMELEMENTS ; i++ ) { web.skel[i].max_ord = -1; blocklist[i] = NULL; blockmax[i] = blockcount[i] = 0; } vibase = NULL; eibase = NULL; fibase = NULL; bibase = NULL; feibase = NULL; web.skel[EDGE].dimension = 1; web.skel[FACET].dimension = 2; web.skel[BODY].dimension = 3; LEAVE_GRAPH_MUTEX /* set up permanent attributes, empty to start with */ /* have REAL attributes first, so can align on 8-byte */ /* Be sure the order given here is same as order in skeleton.h */ /* following each element structure definition. */ /* vertex */ add_attribute(VERTEX,"__x",REAL_TYPE,1,NULL,0,NULL); EXTRAS(VERTEX)[V_COORD_ATTR].flags |= RECALC_ATTR; add_attribute(VERTEX,"__oldx",REAL_TYPE,1,NULL,0,NULL); add_attribute(VERTEX,"__p",REAL_TYPE,1,NULL,0,NULL); EXTRAS(VERTEX)[V_PARAM_ATTR].flags |= RECALC_ATTR; add_attribute(VERTEX,"__force",REAL_TYPE,1,NULL,0,NULL); add_attribute(VERTEX,"__velocity",REAL_TYPE,1,NULL,0,NULL); add_attribute(VERTEX,"__v_constraint_list",INTEGER_TYPE,1,NULL,0,NULL); web.meth_attr[VERTEX] = add_attribute(VERTEX,"__v_method_list",INTEGER_TYPE,1,NULL,0,NULL); add_attribute(VERTEX,"__vertex_normal",REAL_TYPE,1,NULL,0,NULL); /* edge */ add_attribute(EDGE,"density",REAL_TYPE,0,NULL,0,NULL); add_attribute(EDGE,"__e_vertices",ELEMENTID_TYPE,1,NULL,0,NULL); add_attribute(EDGE,"__edge_vector",REAL_TYPE,1,NULL,0,NULL);/*dummy*/ add_attribute(EDGE,"__wrap_list",INTEGER_TYPE,1,NULL,0,NULL); add_attribute(EDGE,"__e_constraint_list",INTEGER_TYPE,1,NULL,0,NULL); web.meth_attr[EDGE] = add_attribute(EDGE,"__e_method_list",INTEGER_TYPE,1,NULL,0,NULL); /* facet */ add_attribute(FACET,"__f_constraint_list",INTEGER_TYPE,1,NULL,0,NULL); add_attribute(FACET,"__f_vertices",ELEMENTID_TYPE,1,&three,0,NULL); add_attribute(FACET,"__facet_normal",REAL_TYPE,1,NULL,0,NULL); add_attribute(FACET,"__body_list",ELEMENTID_TYPE,1,NULL,0,NULL); add_attribute(FACET,"__next_vfacet_list",ELEMENTID_TYPE,0,NULL,0,NULL); add_attribute(FACET,"__next_bfacet_list",ELEMENTID_TYPE,1,NULL,0,NULL); web.meth_attr[FACET] = add_attribute(FACET,"__f_method_list",INTEGER_TYPE,1,NULL,0,NULL); /* body */ web.meth_attr[BODY] = add_attribute(BODY,"__b_method_list",INTEGER_TYPE,1,NULL,0,NULL); F_TAG_ATTR = F_PHASE_ATTR = V_BOUNDARY_ATTR = E_BOUNDARY_ATTR = F_BOUNDARY_ATTR = B_PHASE_ATTR = 0; #ifdef MPI_EVOLVER { int m = MPI_EXPORT_MAX; web.mpi_export_attr[VERTEX] = add_attribute(VERTEX,"__mpi_export_v",USHORT_TYPE,1,&m,0,NULL); web.mpi_export_attr[EDGE] = add_attribute(EDGE,"__mpi_export_e",USHORT_TYPE,1,&m,0,NULL); web.mpi_export_attr[FACET] = add_attribute(FACET,"__mpi_export_f",USHORT_TYPE,1,&m,0,NULL); web.mpi_export_attr[BODY] = add_attribute(BODY,"__mpi_export_b",USHORT_TYPE,1,&m,0,NULL); web.mpi_export_attr[FACETEDGE] = add_attribute(FACETEDGE,"__mpi_export_fe",USHORT_TYPE,1,&m,0,NULL); mpi_export_voffset = EXTRAS(VERTEX)[web.mpi_export_attr[VERTEX]].offset; mpi_export_eoffset = EXTRAS(EDGE)[web.mpi_export_attr[EDGE]].offset; mpi_export_foffset = EXTRAS(FACET)[web.mpi_export_attr[FACET]].offset; mpi_export_boffset = EXTRAS(BODY)[web.mpi_export_attr[BODY]].offset; mpi_export_feoffset = EXTRAS(FACETEDGE)[web.mpi_export_attr[FACETEDGE]].offset; } #endif } /*********************************************************************** * * function: free_discards() * * purpose: Totally free up discard list. To be called only when * no lists are being used. */ void free_discards(mode) int mode; /* DISCARDS_ALL or DISCARDS_SOME */ { int type; for ( type = 0 ; type < NUMELEMENTS ; type++ ) { element_id id,next_id; int small_potatoes = (mode==DISCARDS_ALL) ? 0 : web.skel[type].count/10; if ( web.skel[type].discard_count <= small_potatoes ) continue; /* don't bother with small potatoes */ id = web.skel[type].used; while ( valid_id(id) ) { struct element *ptr = elptr(id); next_id = ptr->forechain; if ( !(ptr->attr & ALLOCATED) ) { /* move to free list */ #ifdef MPI_EVOLVER element_id loc_id = ptr->local_id; #else element_id loc_id = id; #endif if ( valid_id(ptr->forechain) ) elptr(ptr->forechain)->backchain = ptr->backchain; else web.skel[type].last = ptr->backchain; if ( valid_id(ptr->backchain) ) elptr(ptr->backchain)->forechain = ptr->forechain; else web.skel[type].used = ptr->forechain; #ifdef HASH_ID *(struct element **)&(ptr->forechain) = web.skel[type].freehead; web.skel[type].freehead = ptr; elhash_delete(id); ptr->self_id = NULLID; web.skel[type].alloc--; #else if ( valid_id(web.skel[type].free) ) elptr(web.skel[type].free)->backchain = loc_id; else web.skel[type].freelast = loc_id; ptr->forechain = web.skel[type].free; ptr->backchain = NULLID; web.skel[type].free = loc_id; #endif } id = next_id; } web.skel[type].discard_count = 0; } } /************************************************************************** * * function: move_to_free_front() * * purpose: get particular id element to front of free list so id will * match datafile number. Called only during datafile. */ void move_to_free_front(type,id) int type; /* element type */ int id; /* element number */ { int ord = id - 1; #ifdef HASH_ID if ( !match_id_flag || addload_flag ) return; /* for old way */ web.skel[type].free_spot = ord; #else element_id eid; struct element *eptr,*fptr,*bptr; if ( !match_id_flag || addload_flag ) return; /* for old way */ if ( sparse_ibase_flag ) { /* extend ibase with empty slots far enough */ if ( id > web.skel[type].ialloc ) { int imax = web.skel[type].ialloc > 1024 ? 2*web.skel[type].ialloc : 1024; while ( imax < id ) imax *= 2; web.skel[type].ibase = (INDIRECT_TYPE*)kb_realloc((char*)web.skel[type].ibase, imax*sizeof(INDIRECT_TYPE)); web.skel[type].ialloc = imax; } if ( !valid_id(web.skel[type].free) ) extend(type,EXTEND_BATCH); /* refill freelist */ if ( web.skel[type].ibase[ord] == NULL ) { /* move first of freelist to desired spot */ element_id free_id = web.skel[type].free; int free_ord; free_ord = ordinal(free_id); eptr = web.skel[type].ibase[ord] = web.skel[type].ibase[free_ord]; web.skel[type].ibase[free_ord] = NULL; eptr->self_id = ((element_id)type << TYPESHIFT) | VALIDMASK | ord; #ifdef MPI_EVOLVER eptr->self_id |= (element_id)this_task << TASK_ID_SHIFT; #endif web.skel[type].free = eptr->self_id; if ( !valid_id(eptr->forechain) ) web.skel[type].freelast = eptr->self_id; return; } } else { /* dense ibase, extend and allocate empty structures */ while ( id > web.skel[type].maxcount ) extend(type,EXTEND_BATCH); } eid = ((element_id)type << TYPESHIFT) | VALIDMASK | abs(ord); #ifdef MPI_EVOLVER eid |= (element_id)this_task << TASK_ID_SHIFT; #endif eptr = elptr(eid); if ( valid_element(eid) ) { kb_error(2187,"Internal error: Cannot find element in free list.\n", DATAFILE_ERROR); return; } if ( eid == web.skel[type].free ) return; /* already in place */ if ( eid == web.skel[type].freelast ) web.skel[type].freelast = eptr->backchain; /* now cut out of free list */ if ( valid_id(eptr->backchain) ) { bptr = elptr(eptr->backchain); bptr->forechain = eptr->forechain; /* cut */ } if ( valid_id(eptr->forechain) ) { fptr = elptr(eptr->forechain); fptr->backchain = eptr->backchain; } /* paste at front of free list */ if ( valid_id(web.skel[type].free) ) { fptr = elptr(web.skel[type].free); fptr->backchain = eid; } eptr->forechain = web.skel[type].free; web.skel[type].free = eid; #endif } /********************************************************************* * * Function: reorder_storage() * * Purpose: order storage of element structures according to value * of element extra attribute order_key (real). Meant to order storage * in cache and swap friendly way. Invoked by command reorder_storage. * * Elements generators work through forechain pointer, so we must * re-order storage and re-link forechain pointers. **********************************************************************/ static int key_offset; /* offset of key in element structure */ static char * keynames[NUMELEMENTS] = {"vertex_order_key", "edge_order_key","facet_order_key","body_order_key", "facetedge_order_key"}; int esort ARGS((char*,char*)); int esort (a,b) /* sort routine */ char *a, *b; { if ( *(REAL*)(a+key_offset) < *(REAL*)(b+key_offset) ) return -1; if ( *(REAL*)(a+key_offset) > *(REAL*)(b+key_offset) ) return 1; return 0; } void reorder_storage() { struct element *newblock[NUMELEMENTS]; int i,j,n; free_discards(DISCARDS_ALL); /* allocate single-block space for each type */ for ( n = 0 ; n < NUMELEMENTS ; n++ ) newblock[n] = (struct element *)mycalloc(web.skel[n].maxcount+1, web.sizes[n]); /* copy element structures. */ for ( n = 0 ; n < NUMELEMENTS ; n++ ) { element_id id = web.skel[n].used; char *spot = (char*)(newblock[n]); while ( valid_id(id) ) { struct element *ep = elptr(id); memcpy(spot,(char*)ep,web.sizes[n]); id = ep->forechain; spot += web.sizes[n]; } #ifndef HASH_ID /* free list at end */ id = web.skel[n].free; while ( valid_id(id) ) { struct element *ep = elptr(id); memcpy(spot,(char*)ep,web.sizes[n]); id = ep->forechain; spot += web.sizes[n]; } #endif } /* sort elements in use */ for ( i = 0 ; i < NUMELEMENTS ; i++ ) { struct extra *ex; int k; if ( web.skel[i].count == 0 ) continue; key_offset = -1; /* sentinel value */ for ( k = 0, ex = EXTRAS(i) ; k < web.skel[i].extra_count ; k++ , ex++ ) if ( stricmp(keynames[i], ex->name) == 0 ) { key_offset = ex->offset; break; } if ( key_offset < 0 ) { for ( n = 0 ; n < NUMELEMENTS ; n++ ) myfree((char*)newblock[n]); sprintf(errmsg,"reorder_storage: %s attribute not defined.\n",keynames[i]); kb_error(2188,errmsg, RECOVERABLE); } qsort((char*)newblock[i],web.skel[i].count,web.sizes[i],FCAST esort); } /* reset ibase array of pointers and list links */ for ( i = 0 ; i < NUMELEMENTS ; i++ ) { struct element *ep,*nextep; int k; if ( web.skel[i].maxcount == 0 ) continue; if ( web.skel[i].count ) { web.skel[i].used = newblock[i]->self_id; newblock[i]->backchain = NULLID; for ( k = 0, ep = newblock[i] ; k < web.skel[i].count-1 ; k++ ) { nextep = (struct element *)((char*)ep + web.sizes[i]); ep->forechain = nextep->self_id; nextep->backchain = ep->self_id; web.skel[i].ibase[ordinal(ep->self_id)] = ep; ep = nextep; } web.skel[i].ibase[ordinal(ep->self_id)] = ep; ep->forechain = NULLID; web.skel[i].last = ep->self_id; } /* and free list */ for ( k = web.skel[i].count ; k < web.skel[i].maxcount ; k++ ) { ep = (struct element *)((char*)(newblock[i]) + k*web.sizes[i]); web.skel[i].ibase[ordinal(ep->self_id)] = ep; } } /* free old storage */ for ( i = 0 ; i < NUMELEMENTS ; i++ ) { for ( j = 0 ; j < blockcount[i] ; j++ ) myfree((char *)blocklist[i][j].blockptr); blockmax[i] = blockcount[i] = 0; } /* establish new */ for ( i = 0 ; i < NUMELEMENTS ; i++ ) { if ( web.skel[i].count == 0 ) continue; blocklist[i][0].blockptr = newblock[i]; blocklist[i][0].count = web.skel[i].maxcount; blockmax[i] = blockcount[i] = 1; } global_timestamp++; top_timestamp = global_timestamp; } /************************************************************************* * * Function: renumber_all() * * Purpose: Renumber elements according to linked list order. * */ void renumber_all() { int type; struct element *ep=NULL; element_id id; int k; int dim,off,dima,offa,dimb,offb,dimc,offc; struct element **newibase[NUMELEMENTS]; free_discards(DISCARDS_ALL); for ( type = VERTEX ; type <= FACETEDGE ; type++ ) newibase[type] = (struct element **)mycalloc(web.skel[type].ialloc, sizeof(element_id *)); for ( type = VERTEX ; type <= FACETEDGE ; type++ ) { element_id count; /* reset self-id of used elements */ count = 0; id = web.skel[type].used; while ( valid_id(id) ) { ep = elptr(id); ep->self_id = (ep->self_id & (~OFFSETMASK)) | count; count++; id = ep->forechain; } } /* reset mutual links within structures */ FOR_ALL_FACETEDGES(id) { struct facetedge *ep = feptr(id); ep->fe_edge_id = copy_sign(elptr(ep->fe_edge_id)->self_id,ep->fe_edge_id); ep->fe_facet_id = valid_id(ep->fe_facet_id) ? copy_sign(elptr(ep->fe_facet_id)->self_id,ep->fe_facet_id) : ep->fe_facet_id; ep->nextedge[0] = copy_sign(elptr(ep->nextedge[0])->self_id,ep->nextedge[0]); ep->nextedge[1] = copy_sign(elptr(ep->nextedge[1])->self_id,ep->nextedge[1]); ep->nextfacet[0] = copy_sign(elptr(ep->nextfacet[0])->self_id,ep->nextfacet[0]); ep->nextfacet[1] = copy_sign(elptr(ep->nextfacet[1])->self_id,ep->nextfacet[1]); } FOR_ALL_VERTICES(id) { struct vertex *ep = vptr(id); ep->e_id = copy_sign(elptr(ep->e_id)->self_id,ep->e_id); } off = EXTRAS(EDGE)[E_VERTICES_ATTR].offset; /* endpoints */ dim = EXTRAS(EDGE)[E_VERTICES_ATTR].array_spec.datacount; FOR_ALL_EDGES(id) { struct edge *ep = eptr(id); vertex_id *vp; ep->fe_id = copy_sign(elptr(ep->fe_id)->self_id,ep->fe_id); ep->next_vedge[0] = copy_sign(elptr(ep->next_vedge[0])->self_id, ep->next_vedge[0]); ep->next_vedge[1] = copy_sign(elptr(ep->next_vedge[1])->self_id, ep->next_vedge[1]); vp = (vertex_id*)((char*)ep + off); for ( k = 0 ; k < dim ; k++ ) vp[k] = vptr(vp[k])->self_id; } off = EXTRAS(FACET)[F_VERTICES_ATTR].offset; /* endpoints */ dim = EXTRAS(FACET)[F_VERTICES_ATTR].array_spec.datacount; offa = EXTRAS(FACET)[F_BODY_LIST_ATTR].offset; /* bodies */ dima = EXTRAS(FACET)[F_BODY_LIST_ATTR].array_spec.datacount; offb = EXTRAS(FACET)[F_NEXT_VFACET_ATTR].offset; /* links */ dimb = EXTRAS(FACET)[F_NEXT_VFACET_ATTR].array_spec.datacount; offc = EXTRAS(FACET)[F_NEXT_BFACET_ATTR].offset; /* links */ dimc = EXTRAS(FACET)[F_NEXT_BFACET_ATTR].array_spec.datacount; FOR_ALL_FACETS(id) { struct facet *ep = fptr(id); element_id *p; ep->fe_id = copy_sign(elptr(ep->fe_id)->self_id,ep->fe_id); p = (element_id*)((char*)ep + off); for ( k = 0 ; k < dim ; k++ ) if ( valid_id(p[k]) ) p[k] = copy_sign(elptr(p[k])->self_id,p[k]); p = (element_id*)((char*)ep + offa); for ( k = 0 ; k < dima ; k++ ) if ( valid_id(p[k]) ) p[k] = copy_sign(elptr(p[k])->self_id,p[k]); p = (element_id*)((char*)ep + offb); for ( k = 0 ; k < dimb ; k++ ) if ( valid_id(p[k]) ) p[k] = copy_sign(elptr(p[k])->self_id,p[k]); p = (element_id*)((char*)ep + offc); for ( k = 0 ; k < dimc ; k++ ) if ( valid_id(p[k]) ) p[k] = copy_sign(elptr(p[k])->self_id,p[k]); } FOR_ALL_BODIES(id) { struct body * ep = bptr(id); ep->f_id = copy_sign(elptr(ep->f_id)->self_id,ep->f_id); } /* now new pointers in newibase */ for ( type = VERTEX ; type <= FACETEDGE ; type++ ) { int count = 0; if ( web.skel[type].count == 0 ) continue; FOR_ALL_ELEMENTS(type,id) newibase[type][count++] = elptr(id); } /* fix up links */ for ( type = VERTEX ; type <= FACETEDGE ; type++ ) { if ( web.skel[type].count == 0 ) continue; if ( web.skel[type].count == 1 ) { newibase[type][0]->forechain = NULLID; newibase[type][0]->backchain = NULLID; continue; } newibase[type][0]->forechain = newibase[type][0]->self_id+1; newibase[type][0]->backchain = NULLID; for ( k = 1 ; k < web.skel[type].count-1; k++ ) { newibase[type][k]->forechain = newibase[type][k]->self_id+1; newibase[type][k]->backchain = newibase[type][k]->self_id-1; } newibase[type][k]->forechain = NULLID; newibase[type][k]->backchain = newibase[type][k]->self_id - 1; web.skel[type].used = newibase[type][0]->self_id; web.skel[type].last = newibase[type][k]->self_id; } #ifndef HASH_ID /* fix up freelists */ for ( type = VERTEX ; type <= FACETEDGE ; type++ ) { int count; element_id backid = NULLID,newid; count = web.skel[type].count; id = web.skel[type].free; if ( !valid_id(id) ) continue; newid = (id & (~OFFSETMASK)) | count; web.skel[type].free = newid; while ( valid_id(id) ) { ep = elptr(id); id = ep->forechain; ep->forechain = ++newid; ep->backchain = backid; backid = id; newibase[type][count] = ep; ep->self_id = (ep->self_id & (~OFFSETMASK)) | count; count++; } ep->forechain = NULLID; web.skel[type].freelast = backid; } #endif /* miscellaneous elements */ if ( valid_id(web.zoom_v) ) web.zoom_v = vptr(web.zoom_v)->self_id; if ( pickvnum ) pickvnum = loc_ordinal(vibase[pickvnum-1]->self_id)+1; if ( pickenum ) pickenum = loc_ordinal(eibase[pickenum-1]->self_id)+1; if ( pickfnum ) pickfnum = loc_ordinal(fibase[pickfnum-1]->self_id)+1; /* swap ibase to new */ for ( type = VERTEX ; type <= FACETEDGE ; type++ ) { myfree((char*)(web.skel[type].ibase)); web.skel[type].ibase = newibase[type]; } vibase = web.skel[VERTEX].ibase; eibase = web.skel[EDGE].ibase; fibase = web.skel[FACET].ibase; bibase = web.skel[BODY].ibase; feibase = web.skel[FACETEDGE].ibase; global_timestamp++; top_timestamp = global_timestamp; } /************************************************************************* * * function: interp_edge_attribute() * * purpose: Find interpolated value of a vertex attribute at a Gauss point * on an edge. */ REAL interp_edge_attribute(eid,ext,inx,ptnum) edge_id eid; struct extra *ext; /* extra attribute involved */ int inx; /* index within attribute */ int ptnum; /* which gauss point */ { int ctrl = web.skel[EDGE].ctrlpts; REAL sum = 0.0; int i; vertex_id *v = get_edge_vertices(eid); struct gauss_lag *gl = &gauss_lagrange[EDGE][web.gauss1D_order]; if ( web.modeltype == QUADRATIC ) { sum += gl->gpoly[ptnum][0]*get_extra_attrib_value(v[1],ext,inx); sum += gl->gpoly[ptnum][1]*get_extra_attrib_value(v[2],ext,inx); sum += gl->gpoly[ptnum][2]*get_extra_attrib_value(v[0],ext,inx); } else for ( i = 0 ; i < ctrl ; i++ ) sum += gl->gpoly[ptnum][i]*get_extra_attrib_value(v[i],ext,inx); return sum; } /************************************************************************* * * function: interp_facet_attribute() * * purpose: Find interpolated value of a vertex attribute at a Gauss point * on a facet. */ REAL interp_facet_attribute(fid,ext,inx,ptnum) facet_id fid; struct extra *ext; /* extra attribute involved */ int inx; /* index within attribute */ int ptnum; /* which gauss point */ { int ctrl = web.skel[FACET].ctrlpts; REAL sum = 0.0; int i; vertex_id *v; vertex_id vv[2*FACET_VERTS]; struct gauss_lag *gl = &gauss_lagrange[FACET][web.gauss2D_order]; if ( web.modeltype == LINEAR ) { facetedge_id fe = get_facet_fe(fid); for ( i = 0 ; i < FACET_VERTS ; i++ ) { vv[i] = get_fe_tailv(fe); fe = get_next_edge(fe); } v = vv; } else if ( web.modeltype == QUADRATIC ) { facetedge_id fe = get_facet_fe(fid); vv[0] = get_fe_tailv(fe); vv[1] = get_fe_midv(fe); vv[2] = get_fe_headv(fe); fe = get_next_edge(fe); vv[4] = get_fe_midv(fe); vv[5] = get_fe_headv(fe); fe = get_next_edge(fe); vv[3] = get_fe_midv(fe); v = vv; } else /* LAGRANGE */ v = get_facet_vertices(fid); for ( i = 0 ; i < ctrl ; i++ ) sum += gl->gpoly[ptnum][i]*get_extra_attrib_value(v[i],ext,inx); return sum; } /************************************************************************* * * function: get_extra_attrib_value() * * purpose: Return an extra attribute value as a real. * */ REAL get_extra_attrib_value(id,ext,inx) element_id id; struct extra *ext; int inx; { if ( inx >= ext->array_spec.datacount ) { sprintf(errmsg,"Attribute %s total index is %d; maximum is %d.\n", ext->name,inx+1,ext->array_spec.datacount); kb_error(1151,errmsg,RECOVERABLE); } if ( inx < 0 ) { sprintf(errmsg,"Attribute %s index zero or negative: %d.\n", ext->name,inx+1); kb_error(2523,errmsg,RECOVERABLE); } switch ( ext->type ) { case REAL_TYPE: return ((REAL*)get_extra_ptr(id,ext))[inx]; case INTEGER_TYPE: return (REAL)(((int*)get_extra_ptr(id,ext))[inx]); case UINT_TYPE: return (REAL)(((unsigned int*)get_extra_ptr(id,ext))[inx]); case UCHAR_TYPE: return (REAL)(((unsigned char*)get_extra_ptr(id,ext))[inx]); case CHAR_TYPE: return (REAL)(((char*)get_extra_ptr(id,ext))[inx]); case USHORT_TYPE: return (REAL)(((unsigned short int*)get_extra_ptr(id,ext))[inx]); case SHORT_TYPE: return (REAL)(((short int*)get_extra_ptr(id,ext))[inx]); case ULONG_TYPE: return (REAL)(((unsigned long int*)get_extra_ptr(id,ext))[inx]); case LONG_TYPE: return (REAL)(((long int*)get_extra_ptr(id,ext))[inx]); case PTR_TYPE: return (REAL)(unsigned long int)(((char**)get_extra_ptr(id,ext))[inx]); default: kb_error(3101, "get_extra_attribute_value() trying to get nonumeric type.\n", RECOVERABLE); break; } return 0.0; /* shouldn't get here. */ } /***********************************************************************/ /*********************************************************************** Routines for locating element structures through hash of element ID. Meant for handling sparse distributions of IDs, for example in large parallel distributed surfaces. ID is hashed on task, element type, and ordinal; not on valid bit or orientation. Unified list for all elements. *************************************************************************/ /************************************************************************ * * Function: int32hash() * * Purpose: hash 32 bits to 32 bits */ unsigned int int32hash(key) unsigned int key; { key += ~(key << 15); key ^= (key >> 10); key += (key << 3); key ^= (key >> 6); key += ~(key << 11); key ^= (key >> 16); return key; } /************************************************************************ * * Function: int64hash() * * Purpose: hash 64 bits to 64 bits * Integer hashing functions from Thomas Wang, * http://www.concentric.net/~Ttwang/tech/inthash.htm */ #ifdef NOLONGLONG unsigned long int64hash(key) unsigned long key; #else unsigned long long int64hash(key) unsigned long long key; #endif { key += ~(key << 32); key ^= (key >> 22); key += ~(key << 13); key ^= (key >> 8); key += (key << 3); key ^= (key >> 15); key += ~(key << 27); key ^= (key >> 31); return key; } /************************************************************************* * * Function: elhash_lookup() * * Purpose: Retrieve element structure pointer from hash table * */ struct element * elhash_lookup(id) element_id id; { element_id key; int inx; key = id_hash(id); inx = ((int)key) & web.elhashmask; for (;;) { if ( web.elhashtable[inx] == NULL ) return NULL; if ( web.elhashtable[inx] != ELHASH_FREE ) if ( equal_element(web.elhashtable[inx]->self_id,id) ) return web.elhashtable[inx]; inx++; inx &= web.elhashmask; /* wraparound, maybe */ } } /************************************************************************* * * Function: elhash_bigger() * * Purpose: Double size of element hash table. * */ void elhash_bigger() { int oldsize = web.elhashsize; struct element ** oldtable; int n; if ( web.elhashsize == 0 ) { web.elhashsize = 4096; web.elhashmask = 0xFFF; } else { web.elhashmask |= web.elhashsize; /* depending on power of 2 */ web.elhashsize *= 2; } oldtable = web.elhashtable; web.elhashtable = (struct element **)mycalloc(web.elhashsize,sizeof(struct element*)); /* rehash */ web.elhashcount = 0; for ( n = 0 ; n < oldsize ; n++ ) if ( oldtable[n] && (oldtable[n] != ELHASH_FREE) ) elhash_insert(oldtable[n]->self_id,oldtable[n]); } /*********************************************************************** * * function: elhash_extend() * * purpose: allocate more empty element structures using pointer freelist */ void elhash_extend(type,mode) int type; int mode; /* EXTEND_BATCH or EXTEND_FOR_REFINE */ { int number=0; /* of new items to allocate */ char *newblock; INDIRECT_TYPE *newiblock; struct element *newptr = NULL, *oldptr; int k; int allocsize; long oldnum = web.skel[type].maxcount; long newnum=0; ENTER_GRAPH_MUTEX; /* Don't mess with structures while graph thread using them */ if ( blockcount[type] >= blockmax[type] ) { blocklist[type] = (struct blocklist_struct*) kb_realloc((char*)(blocklist[type]), (blockcount[type]+BATCHSIZE)*sizeof(struct blocklist_struct)); blockmax[type] += BATCHSIZE; } if ( mode == EXTEND_BATCH ) { /* calculate number of structures to fit in block size just under 2^n */ allocsize = BATCHSIZE*web.sizes[type]; k = 0x100 ; while ( k < allocsize ) k <<= 1 ; number = (k-16)/web.sizes[type]; /* maybe room for block header */ newnum = web.skel[type].maxcount + number; } else if ( mode == EXTEND_FOR_REFINE ) { /* increase by 2^surface_dimension factor */ number = web.skel[type].count*(1< OFFSETMASK ) { sprintf(errmsg, "Trying to allocate more %s than ID format allows, %Ld\n", typenames[type],OFFSETMASK); kb_error(3214,errmsg,RECOVERABLE); } newblock = mycalloc(number,web.sizes[type]); blocklist[type][blockcount[type]].start_ord = oldnum ; blocklist[type][blockcount[type]].count = number ; blocklist[type][blockcount[type]++].blockptr = (struct element *)newblock; while ( newnum > web.skel[type].ialloc ) { if ( web.skel[type].ibase == NULL ) { newiblock = (INDIRECT_TYPE*)mycalloc(number,sizeof(INDIRECT_TYPE)); web.skel[type].ialloc = number; } else { newiblock = (INDIRECT_TYPE*)kb_realloc((char*)(web.skel[type].ibase), 2*web.skel[type].ialloc*sizeof(INDIRECT_TYPE)); web.skel[type].ialloc *= 2; } web.skel[type].ibase = newiblock; switch(type) { case VERTEX: vibase = newiblock; break; case EDGE: eibase = newiblock; break; case FACET: fibase = newiblock; break; case BODY: bibase = newiblock; break; case FACETEDGE: feibase = newiblock; break; } } /* add to freelist */ oldptr = (struct element *)newblock; for ( k = 0 ; k < number ; k++ ) { newptr = (struct element *)(newblock + k*web.sizes[type]); *(struct element**)&(oldptr->forechain) = newptr; oldptr = newptr; } *(struct element**)&(oldptr->forechain) = web.skel[type].freehead; web.skel[type].freehead = (struct element *)newblock; elhash_extend_exit: LEAVE_GRAPH_MUTEX; } /************************************************************************* * * Function: elhash_new_element() * * Purpose: Allocate new element from freelist. If desired_id is non-null, * it will allocate that id. */ struct element *elhash_new_element(type,desired_id) int type; /* of element */ element_id desired_id; { struct element *newptr,*oldptr; element_id newid; int spot; if ( web.skel[type].freehead == NULL ) /* allocate another batch */ elhash_extend(type,EXTEND_BATCH); newptr = web.skel[type].freehead; web.skel[type].freehead = *(struct element**)&(newptr->forechain); if ( web.skel[type].maxcount == 0 ) web.skel[type].maxcount = 1024; if ( web.skel[type].alloc > 0.8*web.skel[type].maxcount ) web.skel[type].maxcount *= 2; /* find next available element number, using persistant scan */ if ( desired_id ) { spot = (int)(desired_id & OFFSETMASK); newid = ((element_id)type << TYPESHIFT) | VALIDMASK | spot; oldptr = elhash_lookup(newid); if ( oldptr ) kb_error(5433,"Duplicate element number.\n",RECOVERABLE); elhash_insert(newid,newptr); } else { spot = web.skel[type].free_spot; do { newid = ((element_id)type << TYPESHIFT) | VALIDMASK | spot; oldptr = elhash_lookup(newid); spot++; if ( spot >= web.skel[type].maxcount ) spot = 0; } while (oldptr != NULL); elhash_insert(newid,newptr); web.skel[type].free_spot = spot; } newptr->self_id = newid; web.skel[type].alloc++; return newptr; } /************************************************************************* * * Function: elhash_insert() * * Purpose: Add one id to element hash table. Not an error if already * there. */ void elhash_insert(id,eptr) element_id id; struct element * eptr; { element_id key; int inx; if ( web.elhashcount >= web.elhashsize/2 ) elhash_bigger(); key = id_hash(id); inx = ((int)key) & web.elhashmask; for (;;) { if ( (web.elhashtable[inx] == NULL) || (web.elhashtable[inx] == ELHASH_FREE) ) { web.elhashtable[inx] = eptr; web.elhashcount++; return; } if ( equal_element(web.elhashtable[inx]->self_id,id)) { web.elhashtable[inx] = eptr; /* maybe change due to expand() */ return; } inx++; inx &= web.elhashmask; /* wraparound, maybe */ } } /************************************************************************* * * Function: elhash_insert() * * Purpose: Replace pointer for one id to element hash table. * NOTE: Old pointer must still be valid! */ void elhash_replace(id,eptr) element_id id; struct element * eptr; { element_id key; int inx; key = id_hash(id); inx = ((int)key) & web.elhashmask; for (;;) { if ( web.elhashtable[inx] == NULL) kb_error(4382,"INTERNAL ERROR - elhash_replace(): entry not found.\n", RECOVERABLE); if ( web.elhashtable[inx] != ELHASH_FREE ) if ( equal_element(web.elhashtable[inx]->self_id,id)) { web.elhashtable[inx] = eptr; /* maybe change due to expand() */ return; } inx++; inx &= web.elhashmask; /* wraparound, maybe */ } } /************************************************************************* * * Function: elhash_delete() * * Purpose: Delete an entry from the element hash table. */ void elhash_delete(id) element_id id; { element_id key; int inx; /* First, find it */ key = id_hash(id); inx = ((int)key) & web.elhashmask; for (;;) { if ( web.elhashtable[inx] == NULL ) return; /* wasn't there */ if ( (web.elhashtable[inx] != ELHASH_FREE) && (equal_element(web.elhashtable[inx]->self_id,id)) ) break; inx++; inx &= web.elhashmask; /* wraparound, maybe */ } /* mark as freed, not empty, so searches will continue past */ web.elhashtable[inx] = ELHASH_FREE; web.elhashcount--; } /* end element id hashing ********************************************/ #ifdef MPI_EVOLVER /************************************************************************* * * Function: mpi_remote_present() * * purpose: test if a particular remote element id exists locally. */ int mpi_remote_present(id) element_id id; { struct element *eptr = mpi_remote_elptr(id); if ( eptr == NULL ) return 0; if ( eptr->attr & ALLOCATED ) return 1; return 0; } #endif /************************************************************************ * * function: valid_element() * * purpose: To see if the element referred to by an element id is * actually present and valid. */ int valid_element(id) element_id id; { struct element *ep; if ( !valid_id(id) ) return 0; ep = elptr(id); if ( ep == NULL ) return 0; if ( ep->attr & ALLOCATED ) return 1; return 0; } /************************************************************************ * * function: binary_tree_add() * * purpose: implements binary tree addition for accurate sum of large * number of addends. */ void binary_tree_add(addends,term) REAL *addends; REAL term; { int i; for ( i = 0 ; addends[i] != 0.0 ; i++ ) { term += addends[i]; addends[i] = 0.0; } addends[i] = term; } evolver-2.30c.dfsg/src/knot3.c0000644000175300017530000006446011410765113016431 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ #include "include.h" /* for accessing knot energy exponent as adjustable parameter */ extern int exponent_param; /* parameter number */ int cos_exponent_param; /* parameter number */ #define KNOTPOWER_NAME "knot_power" /* name in datafile */ /****************************************************************** edge edge knot energy Suggested and programmed by John Sullivan Between pairs of edges, energy is inverse square power of distance between midpoints of edges. E = 1/d^2 * |e1||e2| This should be roughly the same as uniform_knot_energy, but distances are calculated from edge midpoints. Also, this automatically handles vertices of degree != 2. It is the same as circle_knot_energy except for the factor of (1-cos). ******************************************************************/ /************************************************************** * * function: edge_edge_knot_energy() * * purpose: calculates energy of one pair of edges. * * input: info about edge is in qinfo structure. * */ REAL edge_edge_knot_energy(e_info) struct qinfo *e_info; { edge_id e1 = e_info->id,e2; REAL *x1,*x2,*yy1,*y2; /* end coordinates */ REAL energy = 0.0; REAL dx1[MAXCOORD]; REAL dx2[MAXCOORD]; REAL LL1,L1,LL2,L2,dd; int j; x1 = get_coord(get_edge_tailv(e1)); x2 = get_coord(get_edge_headv(e1)); for ( j = 0 ; j < SDIM ; j++ ) dx1[j] = x2[j] - x1[j]; LL1 = SDIM_dot(dx1,dx1); L1 = sqrt(LL1); FOR_ALL_EDGES(e2) { if ( e2 == e1 ) continue; /* skip self */ yy1 = get_coord(get_edge_tailv(e2)); y2 = get_coord(get_edge_headv(e2)); LL2 = dd = 0.0; for ( j = 0 ; j < SDIM ; j++ ) { REAL rj; dx2[j] = y2[j] - yy1[j]; LL2 += dx2[j]*dx2[j]; rj = (yy1[j] + y2[j] - x1[j] - x2[j])/2; dd += rj*rj; } L2 = sqrt(LL2); energy += L1*L2/dd; } return energy; } /************************************************************** * * function: edge_edge_knot_energy_gradient() * * purpose: calculates energy gradient of one edge due to potential * with all others. * * input: info about edge is in qinfo structure. * */ REAL edge_edge_knot_energy_gradient(e_info) struct qinfo *e_info; { edge_id e1 = e_info->id,e2; REAL *x1,*x2,*yy1,*y2; /* end coordinates */ REAL energy = 0.0; REAL dx1[MAXCOORD]; REAL dx2[MAXCOORD]; REAL r[MAXCOORD]; REAL LL1,L1,LL2,L2,dd; REAL en1; int i,j; for ( i = 0 ; i < 2 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) e_info->grad[i][j] = 0.0; x1 = get_coord(get_edge_tailv(e1)); x2 = get_coord(get_edge_headv(e1)); for ( j = 0 ; j < SDIM ; j++ ) dx1[j] = x2[j] - x1[j]; LL1 = SDIM_dot(dx1,dx1); L1 = sqrt(LL1); FOR_ALL_EDGES(e2) { if ( e2 == e1 ) continue; /* skip self */ yy1 = get_coord(get_edge_tailv(e2)); y2 = get_coord(get_edge_headv(e2)); LL2 = dd = 0.0; for ( j = 0 ; j < SDIM ; j++ ) { dx2[j] = y2[j] - yy1[j]; LL2 += dx2[j]*dx2[j]; r[j] = (yy1[j] + y2[j] - x1[j] - x2[j])/2; dd += r[j]*r[j]; } L2 = sqrt(LL2); en1 = L1*L2/dd; energy += en1; for ( j = 0 ; j < SDIM ; j++ ) { register REAL common = 2*en1/dd*r[j]; register REAL oppose = 2*L2/L1/dd*dx1[j]; e_info->grad[0][j] += common - oppose; e_info->grad[1][j] += common + oppose; } } return energy; /* since doing all pairs */ } /****************************************************************** * * function: edge_normalization() * * purpose: calculates internal knot energy to normalize * singular divergence of integral. */ #define do_nextedge(ee) (ee = inverse_id(get_next_head_edge(ee))) REAL edge_normalization(e_info) struct qinfo *e_info; { edge_id e_id; REAL ti,tj; REAL dist=0., energy=0., comp_len; REAL power; e_id = e_info->id; comp_len = tj = ti = get_edge_length(e_id); power = globals(exponent_param)->value.real; for ( do_nextedge(e_id); e_id != e_info->id ; do_nextedge(e_id) ) comp_len += get_edge_length(e_id); for ( do_nextedge(e_id); e_id != e_info->id ; do_nextedge(e_id) ) { REAL shortdist; dist += tj/2; tj = get_edge_length(e_id); dist += tj/2; shortdist = (2*dist1.? 1. : x)) ) REAL edge_min_knot_energy(e_info) struct qinfo *e_info; { edge_id e1 = e_info->id,e2; REAL *x1,*x2,*yy1,*y2; /* end coordinates */ REAL energy = 0.0; REAL v[MAXCOORD], w[MAXCOORD], d[MAXCOORD]; REAL vv,ww,vw,dv,dw,dd, mind; REAL vwvw, s,t; int j; x1 = get_coord(get_edge_tailv(e1)); x2 = get_coord(get_edge_headv(e1)); for ( j = 0 ; j < SDIM ; j++ ) v[j] = x2[j] - x1[j]; vv = SDIM_dot(v,v); FOR_ALL_EDGES(e2) { if ( e2 == e1 ) continue; /* skip self */ if (get_edge_tailv(e2) == get_edge_headv(e1) || get_edge_tailv(e1) == get_edge_headv(e2)) continue; /* skip nhbrs */ yy1 = get_coord(get_edge_tailv(e2)); y2 = get_coord(get_edge_headv(e2)); ww = vw = dv = dw = dd = 0.0; for ( j = 0 ; j < SDIM ; j++ ) { w[j] = y2[j] - yy1[j]; d[j] = x1[j] - yy1[j]; ww += w[j]*w[j]; vw += v[j]*w[j]; dv += d[j]*v[j]; dw += d[j]*w[j]; dd += d[j]*d[j]; } vwvw = vv*ww-vw*vw; if (vwvw > 0.) { s = (vw*dw-ww*dv)/vwvw; t = (-vw*dv+vv*dw)/vwvw; if (s>=0. && s<=1.) { if (t<0.) { t = 0.; s = -dv/vv; clip(s); } else if (t>1.) { t = 1.; s = (vw-dv)/vv; clip(s); } } else if (t>=0. && t<=1.) { if (s<0.) { s = 0.; t = dw/ww; clip(t); } else if (s>1.) { s = 1.; t = (vw+dw)/ww; clip(t); } } else if (s<0. && t<0.) { /* s=0 and t=dw/ww clipped; or t=0 and s=-dv/vv clipped */ if (dw/ww > 0.) { s = 0.; t = dw/ww; clip(t); } else { t = 0.; s =-dv/vv; clip(s); } } else if (s<0. && t>1.) { /* s=0 and t=dw/ww clipped; or t=1 and s=(vw-dv)/vv clipped */ if (dw/ww < 1.) { s = 0.; t = dw/ww; clip(t); } else { t = 1.; s =(vw-dv)/vv; clip(s); } } else if (s>1. && t<0.) { /* s=1 and t=(vw+dw)/ww clipped; or t=0 and s=-dv/vv clipped */ if ((vw+dw)/ww > 0.) { s = 1.; t = (vw+dw)/ww; clip(t); } else { t = 0.; s =-dv/vv; clip(s); } } else if (s>1. && t>1.) { /* s=0 and t=(vw+dw)/ww clipped; or t=1 and s=(vw-dv)/vv clipped */ if ((vw+dw)/ww < 1.) { s = 1.; t = (vw+dw)/ww; clip(t); } else { t = 1.; s =(vw-dv)/vv; clip(s); } } mind = dd+2*s*dv-2*t*dw+s*s*vv+t*t*ww-2*s*t*vw; } else /* v,w are parallel */ { mind = dd - dv*dv/vv; /* the dist between || lines */ if (vv>ww) /* w is shorter edge, look at its endpoints */ { s = (dv-vw)/vv; if (s>=0. && s<=1.) goto ok; s = dv/vv; if (s>=0. && s<=1.) goto ok; if (vw<0.) mind = (s<0.? dd : dd+2*dv-2*dw+vv+ww-2*vw); else mind = (s<0.? dd-2*dw+ww : dd+2*dv+vv); } else /* v is shorter edge, look at its endpoints */ { t = (-dw-vw)/ww; if (t>=0. && t<=1.) goto ok; t = -dw/ww; if (t>=0. && t<=1.) goto ok; if (vw<0.) mind = (t<0.? dd : dd+2*dv-2*dw+vv+ww-2*vw); else mind = (t<0.? dd+2*dv+vv : dd-2*dw+ww); } } ok: energy += sqrt(vv*ww)/mind; } return energy; } /****************************************************************** * * function: simon_normalization() * * purpose: calculates internal knot energy to normalize * singular divergence of integral. */ REAL simon_normalization(e_info) struct qinfo *e_info; { edge_id e_id; REAL ti; REAL dist, energy=0.; int comp_nedge=1; int j; REAL power; e_id = e_info->id; power = globals(exponent_param)->value.real; for ( e_id = inverse_id(get_next_head_edge(e_id)) ; e_id != e_info->id ; e_id = inverse_id(get_next_head_edge(e_id)) ) comp_nedge++; ti = 2*sin(M_PI/comp_nedge); for (j=2; 2*j<=comp_nedge; j++) { dist = 2*sin((j-1)*M_PI/comp_nedge); energy += ti*ti/pow(dist,power); if (2*j==comp_nedge) energy -= ti*ti/pow(dist,power)/2; } return 2*energy; } /****************************************************************** circle knot energy Suggested by Peter Doyle Between pairs of edges, energy is inverse square power of distance between midpoints of edges, times (1 - cos theta) where theta is between one edge and circle thru midpoint tangent to midpoint of other edge. E = 1/d^2 * (|e1||e2| - e1.e2 +2*e1.d*e2.d/d^2) */ /************************************************************** * * function: circle_knot_energy() * * purpose: calculates energy of one pair of edges. * * input: info about edge is in qinfo structure. * */ REAL circle_knot_energy(e_info) struct qinfo *e_info; { edge_id e1 = e_info->id,e2; REAL *x1,*x2,*yy1,*y2; /* end coordinates */ REAL energy = 0.0; REAL dx1[MAXCOORD]; REAL dx2[MAXCOORD]; REAL LL1,L1,LL2,L2,dd,de1,de2; REAL e1e2; int j; x1 = get_coord(get_edge_tailv(e1)); x2 = get_coord(get_edge_headv(e1)); for ( j = 0 ; j < SDIM ; j++ ) dx1[j] = x2[j] - x1[j]; LL1 = SDIM_dot(dx1,dx1); L1 = sqrt(LL1); FOR_ALL_EDGES(e2) { if ( e2 == e1 ) continue; yy1 = get_coord(get_edge_tailv(e2)); y2 = get_coord(get_edge_headv(e2)); LL2 = dd = de1 = de2 = e1e2 = 0.0; for ( j = 0 ; j < SDIM ; j++ ) { REAL rj; dx2[j] = y2[j] - yy1[j]; LL2 += dx2[j]*dx2[j]; rj = (yy1[j] + y2[j] - x1[j] - x2[j])/2; dd += rj*rj; de1 += rj*dx1[j]; de2 += rj*dx2[j]; e1e2 += dx1[j]*dx2[j]; } L2 = sqrt(LL2); energy += (L1*L2 + e1e2 - 2*de1*de2/dd)/dd; /* 0 for cocircular edges */ } return energy; } /************************************************************** * * function: circle_knot_energy_gradient() * * purpose: calculates energy gradient of one edge due to potential * with all others. * * input: info about edge is in qinfo structure. * */ REAL circle_knot_energy_gradient(e_info) struct qinfo *e_info; { edge_id e1 = e_info->id,e2; REAL *x1,*x2,*yy1,*y2; /* end coordinates */ REAL energy = 0.0; REAL dx1[MAXCOORD]; REAL dx2[MAXCOORD]; REAL r[MAXCOORD]; REAL LL1,L1,LL2,L2,dd,de1,de2; REAL e1e2; REAL en1,en2; int i,j; for ( i = 0 ; i < 2 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) e_info->grad[i][j] = 0.0; x1 = get_coord(get_edge_tailv(e1)); x2 = get_coord(get_edge_headv(e1)); for ( j = 0 ; j < SDIM ; j++ ) dx1[j] = x2[j] - x1[j]; LL1 = SDIM_dot(dx1,dx1); L1 = sqrt(LL1); FOR_ALL_EDGES(e2) { if ( e2 == e1 ) continue; /* each pair once */ yy1 = get_coord(get_edge_tailv(e2)); y2 = get_coord(get_edge_headv(e2)); LL2 = dd = de1 = de2 = e1e2 = 0.0; for ( j = 0 ; j < SDIM ; j++ ) { dx2[j] = y2[j] - yy1[j]; LL2 += dx2[j]*dx2[j]; r[j] = (yy1[j] + y2[j] - x1[j] - x2[j])/2; dd += r[j]*r[j]; de1 += r[j]*dx1[j]; de2 += r[j]*dx2[j]; e1e2 += dx1[j]*dx2[j]; } L2 = sqrt(LL2); de1 /= dd; de2 /= dd; en1 = (L1*L2 + e1e2)/dd; en2 = -2*de1*de2; energy += en1+en2; for ( j = 0 ; j < SDIM ; j++ ) { register REAL common = 2*((en1+2*en2)*r[j] + de1*dx2[j]+de2*dx1[j])/dd; register REAL oppose = 2*(L2/L1*dx1[j] + dx2[j] - 2*r[j]*de2)/dd; e_info->grad[0][j] += common - oppose; e_info->grad[1][j] += common + oppose; } } return energy; /* since doing all pairs */ } /****************************************************************** sin knot energy Suggested by Oded Schramm Programmed by John Sullivan Between pairs of edges, energy is inverse square power of distance between midpoints of edges, times (sin theta) where theta is between one edge and circle thru midpoint tangent to midpoint of other edge. E = |e1||e2|/d^2 * sqrt(1 - e1.e2/|e1||e2| +2*e1.d*e2.d/d^2/|e1||e2|) = L1 L2/d^2 * sqrt(s) */ /************************************************************** * * function: sin_knot_energy() * * purpose: calculates energy of one pair of edges. * * input: info about edge is in qinfo structure. * */ REAL sin_knot_energy(e_info) struct qinfo *e_info; { edge_id e1 = e_info->id,e2; REAL *x1,*x2,*yy1,*y2; /* end coordinates */ REAL energy = 0.0; REAL dx1[MAXCOORD]; REAL dx2[MAXCOORD]; REAL LL1,L1,LL2,L2,dd,de1,de2; REAL e1e2, L1L2, s, t; int j; x1 = get_coord(get_edge_tailv(e1)); x2 = get_coord(get_edge_headv(e1)); for ( j = 0 ; j < SDIM ; j++ ) dx1[j] = x2[j] - x1[j]; LL1 = SDIM_dot(dx1,dx1); L1 = sqrt(LL1); FOR_ALL_EDGES(e2) { if ( e2 <= e1 ) continue; /* each pair once */ yy1 = get_coord(get_edge_tailv(e2)); y2 = get_coord(get_edge_headv(e2)); LL2 = dd = de1 = de2 = e1e2 = 0.0; for ( j = 0 ; j < SDIM ; j++ ) { REAL rj; dx2[j] = y2[j] - yy1[j]; LL2 += dx2[j]*dx2[j]; rj = (yy1[j] + y2[j] - x1[j] - x2[j])/2; dd += rj*rj; de1 += rj*dx1[j]; de2 += rj*dx2[j]; e1e2 += dx1[j]*dx2[j]; } L2 = sqrt(LL2); L1L2 = L1*L2; t = (2*de1*de2/dd - e1e2)/L1L2; s = 1-t*t; if (s<=0.) continue; energy += L1L2/dd * sqrt(s); /* 0 for cocircular edges */ } return 2*energy; } /************************************************************** * * function: sin_knot_energy_gradient() * * purpose: calculates energy gradient of one edge due to potential * with all others. * * input: info about edge is in qinfo structure. * */ REAL sin_knot_energy_gradient(e_info) struct qinfo *e_info; { edge_id e1 = e_info->id,e2; REAL *x1,*x2,*yy1,*y2; /* end coordinates */ REAL energy = 0.0; REAL dx1[MAXCOORD]; REAL dx2[MAXCOORD]; REAL r[MAXCOORD]; REAL LL1,L1,LL2,L2,dd,de1,de2; REAL e1e2, L1L2, s, t; REAL en, fac; int i,j; for ( i = 0 ; i < 2 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) e_info->grad[i][j] = 0.0; x1 = get_coord(get_edge_tailv(e1)); x2 = get_coord(get_edge_headv(e1)); for ( j = 0 ; j < SDIM ; j++ ) dx1[j] = x2[j] - x1[j]; LL1 = SDIM_dot(dx1,dx1); L1 = sqrt(LL1); FOR_ALL_EDGES(e2) { if ( e2 == e1 ) continue; /* each ordered pair once */ yy1 = get_coord(get_edge_tailv(e2)); y2 = get_coord(get_edge_headv(e2)); LL2 = dd = de1 = de2 = e1e2 = 0.0; for ( j = 0 ; j < SDIM ; j++ ) { dx2[j] = y2[j] - yy1[j]; LL2 += dx2[j]*dx2[j]; r[j] = (yy1[j] + y2[j] - x1[j] - x2[j])/2; dd += r[j]*r[j]; de1 += r[j]*dx1[j]; de2 += r[j]*dx2[j]; e1e2 += dx1[j]*dx2[j]; } L2 = sqrt(LL2); L1L2 = L1*L2; t = (2*de1*de2/dd - e1e2)/L1L2; s = 1-t*t; if (s<=0.) continue; en = sqrt(s)/dd; fac = t*en/s; energy += (en = en*L1L2); /* 0 for cocircular edges */ de1 /= dd; de2 /= dd; for ( j = 0 ; j < SDIM ; j++ ) { register REAL common = en/dd*r[j] + fac*(de2*dx1[j] + de1*dx2[j] - 2*de1*de2*r[j]); register REAL oppose = en/LL1*dx1[j] + fac*(dx2[j] - 2*de2*r[j] + t*L2/L1*dx1[j]); e_info->grad[0][j] += 2*(common - oppose); e_info->grad[1][j] += 2*(common + oppose); } } return energy; /* since doing all pairs */ } /****************************************************************** sphere knot energy Suggested by John Sullivan and Rob Kusner Between pairs of facets, energy is inverse square power of distance between midpoints of facets, times (1 - cos theta) where theta is between one facet and sphere thru midpoint tangent to midpoint of other facet. | r.r 2r.s1 2r.s2| E = (1/4)1/r^4 * (|a1||a2| - (det|t1.r t1.s1 t1.s2| )/r^2) |t2.r t2.s1 t2.s2| |r.r/2 r.s1 r.s2| E = 1/r^4 * (|a1||a2|/4) ( 1 - 2*det|t1.r t1.s1 t1.s2| / (r^2 |a1||a2|) )^2 |t2.r t2.s1 t2.s2| where r is vector between midpoints, s1,s2 are sides of one facet, and t1,t2 are sides of the other. ******************************************************************/ #define SURF_KNOTPOW_NAME "surface_knot_power" #define SURF_COSPOW_NAME "surface_cos_power" static REAL spower,cpower; void sphere_knot_energy_init(mode,mi) int mode; struct method_instance *mi; { exponent_param = lookup_global(SURF_KNOTPOW_NAME); if ( exponent_param < 0 ) /* missing, so add */ { exponent_param = add_global(SURF_KNOTPOW_NAME); globals(exponent_param)->value.real = 4.0; /* default */ globals(exponent_param)->flags |= ORDINARY_PARAM | RECALC_PARAMETER | ALWAYS_RECALC; } spower = globals(exponent_param)->value.real/2; cos_exponent_param = lookup_global(SURF_COSPOW_NAME); if ( cos_exponent_param < 0 ) /* missing, so add */ { cos_exponent_param = add_global(SURF_COSPOW_NAME); globals(cos_exponent_param)->value.real = 1.0; /* default */ globals(cos_exponent_param)->flags |= ORDINARY_PARAM|RECALC_PARAMETER | ALWAYS_RECALC; } cpower = globals(cos_exponent_param)->value.real; } /************************************************************** * * function: sphere_knot_energy() * * purpose: calculates energy of one pair of facets. * * input: info about facet is in qinfo structure. * */ REAL sphere_knot_energy(f_info) struct qinfo *f_info; { facet_id f1 = f_info->id,f2; REAL *x[FACET_VERTS],*y[FACET_VERTS]; /* vertex coordinates */ REAL energy = 0.0; REAL s1[MAXCOORD],s2[MAXCOORD]; REAL t1[MAXCOORD],t2[MAXCOORD]; REAL rr2,rs1,rs2,t1r,t1s1,t1s2,t2r,t2s1,t2s2; REAL s1s1,s1s2,s2s2,t1t1,t1t2,t2t2; REAL As,AsAt,det,angfac; int j; facetedge_id fe; vertex_id v[FACET_VERTS],w[FACET_VERTS]; REAL pp; REAL xmid[MAXCOORD]; REAL rj[MAXCOORD]; fe = get_facet_fe(f1); for ( j = 0 ; j < FACET_VERTS ; j++ ) { v[j] = get_fe_tailv(fe); x[j] = get_coord(v[j]); fe = get_next_edge(fe); } for ( j = 0 ; j < SDIM ; j++ ) { s1[j] = x[1][j] - x[0][j]; s2[j] = x[2][j] - x[0][j]; xmid[j] = (x[0][j]+x[1][j]+x[2][j])/3; } s1s1 = SDIM_dot(s1,s1); s1s2 = SDIM_dot(s1,s2); s2s2 = SDIM_dot(s2,s2); As = sqrt(s1s1*s2s2 - s1s2*s1s2); FOR_ALL_FACETS(f2) { if ( f2 <= f1 ) continue; /* each pair once */ fe = get_facet_fe(f2); for ( j = 0 ; j < FACET_VERTS ; j++ ) { w[j] = get_fe_tailv(fe); y[j] = get_coord(w[j]); fe = get_next_edge(fe); } t1t1=t1t2=t2t2=rr2=rs1=rs2=t1r=t1s1=t1s2=t2r=t2s1=t2s2=0.0; for (j=0; jid,f2; REAL *x[FACET_VERTS],*y[FACET_VERTS]; /* vertex coordinates */ REAL energy = 0.0; REAL s1[MAXCOORD],s2[MAXCOORD]; REAL t1[MAXCOORD],t2[MAXCOORD]; REAL r[MAXCOORD]; REAL dAs1[MAXCOORD],dAs2[MAXCOORD]; REAL rr2,rs1,rs2,t1r,t1s1,t1s2,t2r,t2s1,t2s2; REAL s1s1,s1s2,s2s2,t1t1,t1t2,t2t2; REAL dEdrr2,dEdrs1,dEdrs2,dEdt1r,dEdt1s1,dEdt1s2,dEdt2r,dEdt2s1,dEdt2s2; REAL angfac,mult,qq; REAL As,AsAt,detr; facetedge_id fe; vertex_id v[FACET_VERTS],w[FACET_VERTS]; int i,j; for ( i = 0 ; i < FACET_VERTS ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) f_info->grad[i][j] = 0.0; fe = get_facet_fe(f1); for ( j = 0 ; j < FACET_VERTS ; j++ ) { v[j] = get_fe_tailv(fe); x[j] = get_coord(v[j]); fe = get_next_edge(fe); } for ( j = 0 ; j < SDIM ; j++ ) { s1[j] = x[1][j] - x[0][j]; s2[j] = x[2][j] - x[0][j]; } s1s1 = SDIM_dot(s1,s1); s1s2 = SDIM_dot(s1,s2); s2s2 = SDIM_dot(s2,s2); As = sqrt(s1s1*s2s2 - s1s2*s1s2); for ( j = 0 ; j < SDIM ; j++ ) { dAs1[j] = (s2s2*s1[j] - s1s2*s2[j])/As; dAs2[j] = (s1s1*s2[j] - s1s2*s1[j])/As; } FOR_ALL_FACETS(f2) { if ( f1 == f2 ) continue; /* don't do self */ fe = get_facet_fe(f2); for ( j = 0 ; j < FACET_VERTS ; j++ ) { w[j] = get_fe_tailv(fe); y[j] = get_coord(w[j]); fe = get_next_edge(fe); } t1t1=t1t2=t2t2=rr2=rs1=rs2=t1r=t1s1=t1s2=t2r=t2s1=t2s2=0.0; for (j=0; jgrad[0][j] -= ff1+ff2; f_info->grad[1][j] += ff1; f_info->grad[2][j] += ff2; } dEdrr2 = - spower*q/rr2/3; for ( j = 0 ; j < SDIM ; j++ ) { register REAL common; common = dEdrr2*r[j]; f_info->grad[0][j] -= common; f_info->grad[1][j] -= common; f_info->grad[2][j] -= common; } continue; } detr = (rr2*t1s1*t2s2 + rs1*t1s2*t2r + rs2*t1r*t2s1 - rr2*t1s2*t2s1 - rs1*t1r*t2s2 - rs2*t1s1*t2r)/rr2; angfac = 1 + detr/AsAt; if ( cpower == 1.0 ) qq = 1.0 / pow(2*rr2,spower+1); else qq = pow(angfac,cpower-1) / pow(2*rr2,spower+1); energy += qq*angfac*rr2*AsAt/2; /* mult = enf*(AsAt-detr)/As; */ mult = qq*rr2 * (angfac - cpower*detr/AsAt) * AsAt/As; for ( j = 0 ; j < SDIM ; j++ ) { register REAL ff1, ff2; ff1 = mult*dAs1[j]; ff2 = mult*dAs2[j]; f_info->grad[0][j] -= ff1+ff2; f_info->grad[1][j] += ff1; f_info->grad[2][j] += ff2; } mult = cpower*qq/3; dEdrr2 = mult*(t1s1*t2s2 - t1s2*t2s1 - detr) - spower*qq*angfac*AsAt/3; dEdrs1 = mult*(t1s2*t2r - t1r*t2s2); dEdrs2 = mult*(t1r*t2s1 - t1s1*t2r); for ( j = 0 ; j < SDIM ; j++ ) { register REAL rs1r, rs2r, common; rs1r = dEdrs1*r[j]*3; rs2r = dEdrs2*r[j]*3; common = dEdrr2*r[j] + dEdrs1*s1[j] + dEdrs2*s2[j]; f_info->grad[0][j] -= (common + rs1r + rs2r); f_info->grad[1][j] -= (common - rs1r); f_info->grad[2][j] -= (common - rs2r); } dEdt1r = mult*(rs2*t2s1 - rs1*t2s2); dEdt2r = mult*(rs1*t1s2 - rs2*t1s1); mult *= 3; dEdt1s1 = mult*(rr2*t2s2 - rs2*t2r); dEdt1s2 = mult*(rs1*t2r - rr2*t2s1); dEdt2s1 = mult*(rs2*t1r - rr2*t1s2); dEdt2s2 = mult*(rr2*t1s1 - rs1*t1r); for ( j = 0 ; j < SDIM ; j++ ) { register REAL tr, ts1, ts2; tr = dEdt1r*t1[j] + dEdt2r*t2[j]; ts1 = dEdt1s1*t1[j] + dEdt2s1*t2[j]; ts2 = dEdt1s2*t1[j] + dEdt2s2*t2[j]; f_info->grad[0][j] -= (tr+ts1+ts2); f_info->grad[1][j] -= (tr-ts1); f_info->grad[2][j] -= (tr-ts2); } } return energy; /* since doing all pairs */ } evolver-2.30c.dfsg/src/torvol.c0000644000175300017530000014320111410765113016707 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /****************************************************************** * * File: torvol.c * * Purpose: find volumes of bodies in toroidal domain * and volume gradients. * */ #include "include.h" #define NEWTORVOL /* invokes named method */ void torvol() { facet_id f_id; /* main facet iterator */ body_id b_id; body_id b0_id,b1_id; /* facet adjacent bodies */ #ifdef NEWTORVOL struct qinfo f_info; /* for calling q_facet_torus_volume */ q_info_init(&f_info,METHOD_VALUE); #endif /* adjust body volumes to the invariant constant for each */ FOR_ALL_BODIES(b_id) set_body_volume(b_id,get_body_volconst(b_id),NOSETSTAMP); if ( web.representation == STRING ) FOR_ALL_FACETS(f_id) set_facet_area(f_id,0.0); FOR_ALL_FACETS(f_id) { REAL t; /* accumulator for this facet */ if ( get_fattr(f_id) & NONCONTENT ) continue; /* find adjacent bodies */ b0_id = get_facet_body(f_id); b1_id = get_facet_body(facet_inverse(f_id)); if ( !valid_id(b0_id) && !valid_id(b1_id) ) continue; #ifdef NEWTORVOL f_info.id = f_id; q_facet_setup(NULL,&f_info,NEED_SIDE|TORUS_MODULO_MUNGE|ORIENTABLE_METHOD); t = q_facet_torus_volume(&f_info); if ( valid_id(b0_id) ) add_body_volume(b0_id,t); if ( valid_id(b1_id) ) add_body_volume(b1_id,-t); #else REAL *v[FACET_VERTS]; /* pointers to three vertex coordinates */ facetedge_id fe; int i; REAL adj[FACET_EDGES][MAXCOORD]; /* torus wrap adjustments for edge */ /* get basic info */ fe = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i ++ ) { v[i] = get_coord(get_fe_tailv(fe)); get_edge_adjust(get_fe_edge(fe),adj[i]); fe = get_next_edge(fe); } /* basic tetrahedron */ t = triple_prod(v[0],v[1],v[2]); /* torus wrap corrections */ for ( i = 0 ; i < FACET_EDGES ; i++ ) { /* two-vertex term */ t += triple_prod(adj[(i+1)%FACET_EDGES],v[i],v[(i+1)%FACET_EDGES])/2; t -= triple_prod(adj[(i+2)%FACET_EDGES],v[i],v[(i+1)%FACET_EDGES])/2; /* one-vertex term */ t += triple_prod(v[i],adj[(i+2)%FACET_EDGES],adj[i]); } if ( valid_id(b0_id) ) add_body_volume(b0_id,t/6); if ( valid_id(b1_id) ) add_body_volume(b1_id,-t/6); #endif } #ifdef NEWTORVOL q_info_free(&f_info); #endif } /********************************************************************** Linear torus volume quantity **********************************************************************/ /********************************************************************** * * function: q_facet_torus_volume() * * purpose: value of volume integral on facet */ REAL q_facet_torus_volume(f_info) struct qinfo *f_info; { REAL **x; REAL vol; int i; REAL **dx = web.inverse_periods; unsigned long allwrap; MAT2D(u,FACET_VERTS,MAXCOORD); /* affine coordinates of vertices */ facetedge_id fe; WRAPTYPE w[FACET_EDGES]; if ( !dx ) kb_error(3370,"Method facet_torus_volume requires torus model.\n", RECOVERABLE); if ( web.modeltype == QUADRATIC ) return q_facet_torus_volume_q(f_info); if ( web.modeltype == LAGRANGE )return q_facet_torus_volume_lagr(f_info); x = f_info->x; /* get affine coordinates of vertices */ mat_mul_tr(x,dx,u,FACET_VERTS,SDIM,SDIM); /* main integral over facet */ vol = ((u[1][0]-u[0][0])*(u[2][1]-u[0][1]) - (u[1][1]-u[0][1])*(u[2][0]-u[0][0])); vol *= (u[0][2]+u[1][2]+u[2][2])/6; /* add corrections due to wraps */ fe = get_facet_fe(f_info->id); for ( i = 0 ; i < FACET_EDGES ; i++, fe = get_next_edge(fe) ) w[i] = get_fe_wrap(fe); if ( f_info->wraps[0] ) { sprintf(errmsg,"Base vertex of facet %s has nonzero wrap in facet_torus_volume.\n", ELNAME(f_info->id)); kb_error(1326,errmsg,WARNING); } #define WR(w,i) (w>>((i)*TWRAPBITS) & WRAPMASK) allwrap = WR(w[0],2)+(WR(w[1],2)<wraps[1],0) ) { case POSWRAP: vol -= u[1][1]; break; case NEGWRAP: vol += u[1][1]; break; } switch ( WR(f_info->wraps[2],0) ) { case POSWRAP: vol += u[2][1]; break; case NEGWRAP: vol -= u[2][1]; break; } break; case (NEGWRAP) + (POSWRAP << TWRAPBITS): vol += ((u[1][0]+u[0][0])*(u[1][1]-u[0][1]) +(u[2][0]+u[1][0])*(u[2][1]-u[1][1]))/2; switch ( WR(f_info->wraps[2],0) ) { case POSWRAP: vol -= u[2][1]; break; case NEGWRAP: vol += u[2][1]; break; } break; case (NEGWRAP << TWRAPBITS) + (POSWRAP << (2*TWRAPBITS)): vol += ((u[2][0]+u[1][0])*(u[2][1]-u[1][1]) +(u[0][0]+u[2][0])*(u[0][1]-u[2][1]))/2; switch ( WR(f_info->wraps[1],0) ) { case POSWRAP: vol += u[1][1]; ; break; case NEGWRAP: vol -= u[1][1]; ; break; } break; default: sprintf(errmsg, "Internal error: Bad allwrap %08lX in facet_torus_volume facet %s.\n", allwrap,ELNAME(f_info->id)); kb_error(1327,errmsg, RECOVERABLE); } vol *= web.torusv; return vol; } /********************************************************************** * * function: q_facet_torus_volume_grad() * * purpose: gradient and value of volume integral on quadratic facet */ REAL q_facet_torus_volume_grad(f_info) struct qinfo *f_info; { REAL **x; REAL zsum,ssum; int i,j; REAL **dx = web.inverse_periods; WRAPTYPE w[FACET_EDGES]; unsigned long allwrap; MAT2D(u,FACET_VERTS,MAXCOORD); /* affine coordinates of vertices */ facetedge_id fe; MAT2D(ugrad,FACET_VERTS,MAXCOORD); REAL vol; if ( web.modeltype == QUADRATIC ) return q_facet_torus_volume_q_grad(f_info); if ( web.modeltype == LAGRANGE ) return q_facet_torus_volume_lagr_grad(f_info); x = f_info->x; /* get affine coordinates of vertices */ mat_mul_tr(x,dx,u,FACET_VERTS,SDIM,SDIM); /* main integral over facet */ zsum = (u[0][2]+u[1][2]+u[2][2])/6; ssum = (u[1][0]-u[0][0])*(u[2][1]-u[0][1]) - (u[1][1]-u[0][1])*(u[2][0]-u[0][0]); vol = zsum*ssum; ugrad[0][0] = zsum*(u[1][1]-u[2][1]); ugrad[0][1] = zsum*(u[2][0]-u[1][0]); ugrad[0][2] = ssum/6; ugrad[1][0] = zsum*(u[2][1]-u[0][1]); ugrad[1][1] = zsum*(u[0][0]-u[2][0]); ugrad[1][2] = ssum/6; ugrad[2][0] = zsum*(u[0][1]-u[1][1]); ugrad[2][1] = zsum*(u[1][0]-u[0][0]); ugrad[2][2] = ssum/6; /* add corrections due to wraps */ fe = get_facet_fe(f_info->id); for ( i = 0 ; i < FACET_EDGES ; i++, fe = get_next_edge(fe) ) w[i] = get_fe_wrap(fe); allwrap = WR(w[0],2)+(WR(w[1],2)<wraps[1],0) ) { case POSWRAP: vol -= u[1][1]; ugrad[1][1] -= 1.0; break; case NEGWRAP: vol += u[1][1]; ugrad[1][1] += 1.0; break; } switch ( WR(f_info->wraps[2],0) ) { case POSWRAP: vol += u[2][1]; ugrad[2][1] += 1.0; break; case NEGWRAP: vol -= u[2][1]; ugrad[2][1] -= 1.0; break; } break; case (NEGWRAP) + (POSWRAP << TWRAPBITS): vol += ((u[1][0]+u[0][0])*(u[1][1]-u[0][1]) +(u[2][0]+u[1][0])*(u[2][1]-u[1][1]))/2; ugrad[0][0] += (u[1][1]-u[0][1])/2; ugrad[0][1] -= (u[1][0]+u[0][0])/2; ugrad[1][0] += (u[2][1]-u[0][1])/2; ugrad[1][1] += (u[0][0]-u[2][0])/2; ugrad[2][0] += (u[2][1]-u[1][1])/2; ugrad[2][1] += (u[2][0]+u[1][0])/2; switch ( WR(f_info->wraps[2],0) ) { case POSWRAP: vol -= u[2][1]; ugrad[2][1] -= 1.0; break; case NEGWRAP: vol += u[2][1]; ugrad[2][1] += 1.0; break; } switch ( WR(f_info->wraps[0],0) ) { case POSWRAP: vol += u[0][1]; ugrad[0][1] += 1.0;break; case NEGWRAP: vol -= u[0][1]; ugrad[0][1] -= 1.0;break; } break; case (NEGWRAP << TWRAPBITS) + (POSWRAP << (2*TWRAPBITS)): vol += ((u[2][0]+u[1][0])*(u[2][1]-u[1][1]) +(u[0][0]+u[2][0])*(u[0][1]-u[2][1]))/2; ugrad[0][0] += (u[0][1]-u[2][1])/2; ugrad[0][1] += (u[0][0]+u[2][0])/2; ugrad[1][0] += (u[2][1]-u[1][1])/2; ugrad[1][1] -= (u[2][0]+u[1][0])/2; ugrad[2][0] += (u[0][1]-u[1][1])/2; ugrad[2][1] += (u[1][0]-u[0][0])/2; switch ( WR(f_info->wraps[1],0) ) { case POSWRAP: vol += u[1][1]; ugrad[1][1] += 1.0; break; case NEGWRAP: vol -= u[1][1]; ugrad[1][1] -= 1.0; break; } break; default: kb_error(1328,"Internal error: Bad allwrap in facet_volume.\n",RECOVERABLE); } vol *= web.torusv; for ( i = 0 ; i < FACET_VERTS ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) ugrad[i][j] *= web.torusv; mat_mult(ugrad,dx,f_info->grad,FACET_VERTS,SDIM,SDIM); return vol; } /********************************************************************** * * function: q_facet_torus_volume_hess() * * purpose: hessian, gradient and value of volume integral on quadratic facet */ REAL q_facet_torus_volume_hess(f_info) struct qinfo *f_info; { REAL **x; REAL zsum,ssum; int i,j,ii,jj; REAL **dx = web.inverse_periods; WRAPTYPE w[FACET_EDGES]; unsigned long allwrap; MAT2D(u,FACET_VERTS,MAXCOORD); /* affine coordinates of vertices */ facetedge_id fe; MAT2D(ugrad,FACET_VERTS,MAXCOORD); MAT4D(h,FACET_VERTS,FACET_VERTS,MAXCOORD,MAXCOORD); MAT2D(temph,MAXCOORD,MAXCOORD); REAL vol; if ( !dx ) kb_error(2194,"Need torus model to do facet_torus_volume.\n",RECOVERABLE); if ( web.modeltype == QUADRATIC ) return q_facet_torus_volume_q_hess(f_info); if ( web.modeltype == LAGRANGE ) return q_facet_torus_volume_lagr_hess(f_info); x = f_info->x; /* get affine coordinates of vertices */ mat_mul_tr(x,dx,u,FACET_VERTS,SDIM,SDIM); /* main integral over facet */ zsum = (u[0][2]+u[1][2]+u[2][2])/6; ssum = (u[1][0]-u[0][0])*(u[2][1]-u[0][1]) - (u[1][1]-u[0][1])*(u[2][0]-u[0][0]); vol = zsum*ssum; ugrad[0][0] = zsum*(u[1][1]-u[2][1]); ugrad[0][1] = zsum*(u[2][0]-u[1][0]); ugrad[0][2] = ssum/6; ugrad[1][0] = zsum*(u[2][1]-u[0][1]); ugrad[1][1] = zsum*(u[0][0]-u[2][0]); ugrad[1][2] = ssum/6; ugrad[2][0] = zsum*(u[0][1]-u[1][1]); ugrad[2][1] = zsum*(u[1][0]-u[0][0]); ugrad[2][2] = ssum/6; for ( i = 0 ; i < FACET_VERTS ; i++ ) for ( ii = 0 ; ii < FACET_VERTS ; ii++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) h[i][ii][j][jj] = 0.0; h[1][2][0][1] = h[1][0][1][0] = h[0][2][1][0] = zsum; h[2][1][1][0] = h[0][1][0][1] = h[2][0][0][1] = zsum; h[1][0][0][1] = h[0][2][0][1] = h[1][2][1][0] = -zsum; h[0][1][1][0] = h[2][0][1][0] = h[2][1][0][1] = -zsum; for ( i = 0 ; i < 3 ; i++ ) { h[i][0][2][0] = h[0][i][0][2] = (u[1][1]-u[2][1])/6; h[i][0][2][1] = h[0][i][1][2] = (u[2][0]-u[1][0])/6; h[i][1][2][0] = h[1][i][0][2] = (u[2][1]-u[0][1])/6; h[i][1][2][1] = h[1][i][1][2] = (u[0][0]-u[2][0])/6; h[i][2][2][0] = h[2][i][0][2] = (u[0][1]-u[1][1])/6; h[i][2][2][1] = h[2][i][1][2] = (u[1][0]-u[0][0])/6; } /* add corrections due to wraps */ fe = get_facet_fe(f_info->id); for ( i = 0 ; i < FACET_EDGES ; i++, fe = get_next_edge(fe) ) w[i] = get_fe_wrap(fe); allwrap = WR(w[0],2)+(WR(w[1],2)<wraps[1],0) ) { case POSWRAP: vol -= u[1][1]; ugrad[1][1] -= 1.0; break; case NEGWRAP: vol += u[1][1]; ugrad[1][1] += 1.0; break; } switch ( WR(f_info->wraps[2],0) ) { case POSWRAP: vol += u[2][1]; ugrad[2][1] += 1.0; break; case NEGWRAP: vol -= u[2][1]; ugrad[2][1] -= 1.0; break; } break; case (NEGWRAP) + (POSWRAP << TWRAPBITS): vol += ((u[1][0]+u[0][0])*(u[1][1]-u[0][1]) +(u[2][0]+u[1][0])*(u[2][1]-u[1][1]))/2; ugrad[0][0] += (u[1][1]-u[0][1])/2; ugrad[0][1] -= (u[1][0]+u[0][0])/2; ugrad[1][0] += (u[2][1]-u[0][1])/2; ugrad[1][1] += (u[0][0]-u[2][0])/2; ugrad[2][0] += (u[2][1]-u[1][1])/2; ugrad[2][1] += (u[2][0]+u[1][0])/2; h[0][1][0][1] += 0.5; h[0][0][0][1] -= 0.5; h[0][1][1][0] -= 0.5; h[0][0][1][0] -= 0.5; h[1][2][0][1] += 0.5; h[1][0][0][1] -= 0.5; h[1][0][1][0] += 0.5; h[1][2][1][0] -= 0.5; h[2][2][0][1] += 0.5; h[2][1][0][1] -= 0.5; h[2][2][1][0] += 0.5; h[2][1][1][0] += 0.5; switch ( WR(f_info->wraps[2],0) ) { case POSWRAP: vol -= u[2][1]; ugrad[2][1] -= 1.0; break; case NEGWRAP: vol += u[2][1]; ugrad[2][1] += 1.0; break; } switch ( WR(f_info->wraps[0],0) ) { case POSWRAP: vol += u[0][1]; ugrad[0][1] += 1.0;break; case NEGWRAP: vol -= u[0][1]; ugrad[0][1] -= 1.0;break; } break; case (NEGWRAP << TWRAPBITS) + (POSWRAP << (2*TWRAPBITS)): vol += ((u[2][0]+u[1][0])*(u[2][1]-u[1][1]) +(u[0][0]+u[2][0])*(u[0][1]-u[2][1]))/2; ugrad[0][0] += (u[0][1]-u[2][1])/2; ugrad[0][1] += (u[0][0]+u[2][0])/2; ugrad[1][0] += (u[2][1]-u[1][1])/2; ugrad[1][1] -= (u[2][0]+u[1][0])/2; ugrad[2][0] += (u[0][1]-u[1][1])/2; ugrad[2][1] += (u[1][0]-u[0][0])/2; h[0][0][0][1] += 0.5; h[0][2][0][1] -= 0.5; h[0][0][1][0] += 0.5; h[0][2][1][0] += 0.5; h[1][2][0][1] += 0.5; h[1][1][0][1] -= 0.5; h[1][2][1][0] -= 0.5; h[1][1][1][0] -= 0.5; h[2][0][0][1] += 0.5; h[2][1][0][1] -= 0.5; h[2][1][1][0] += 0.5; h[2][0][1][0] -= 0.5; switch ( WR(f_info->wraps[1],0) ) { case POSWRAP: vol += u[1][1]; ugrad[1][1] += 1.0; break; case NEGWRAP: vol -= u[1][1]; ugrad[1][1] -= 1.0; break; } break; default: kb_error(1330,"Internal error: Bad allwrap in facet_volume.\n",RECOVERABLE); } vol *= web.torusv; for ( i = 0 ; i < FACET_VERTS ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) ugrad[i][j] *= web.torusv; mat_mult(ugrad,dx,f_info->grad,FACET_VERTS,SDIM,SDIM); for ( i = 0 ; i < FACET_VERTS ; i++ ) for ( ii = 0 ; ii < FACET_VERTS ; ii++ ) { for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) { h[i][ii][j][jj] *= web.torusv; } mat_mult(h[i][ii],dx,temph,SDIM,SDIM,SDIM); tr_mat_mul(dx,temph,f_info->hess[i][ii],SDIM,SDIM,SDIM); } return vol; } /********************************************************************** Quadratic torus volume quantity **********************************************************************/ REAL cut_int ARGS((REAL**)); void cut_grad ARGS((REAL**,REAL**,int)); void cut_hess ARGS((REAL**,REAL**,REAL****,int)); REAL cut_int(u) REAL **u; /* coefficients of the 3 vertices along edge */ { REAL area = 0.0; int i,j; for ( i = 0 ; i < EDGE_CTRL ; i++ ) for ( j = 0 ; j < EDGE_CTRL ; j ++ ) area += scoeff[i][j]*u[i][0]*u[j][1]; return area; } void cut_grad(u,ugrad,sign) REAL **u; REAL **ugrad; /* gradients to increment */ int sign; /* +1 or -1 */ { int i,j; for ( i = 0 ; i < EDGE_CTRL ; i++ ) for ( j = 0 ; j < EDGE_CTRL ; j ++ ) { ugrad[i][0] += sign*scoeff[i][j]*u[j][1]; ugrad[j][1] += sign*scoeff[i][j]*u[i][0]; } } void cut_hess(u,ugrad,uhess,sign) REAL **u; REAL **ugrad; /* gradients to increment */ REAL ****uhess; /* hessians to increment */ int sign; /* +1 or -1 */ { int i,j; for ( i = 0 ; i < EDGE_CTRL ; i++ ) for ( j = 0 ; j < EDGE_CTRL ; j ++ ) { ugrad[i][0] += sign*scoeff[i][j]*u[j][1]; ugrad[j][1] += sign*scoeff[i][j]*u[i][0]; uhess[i][j][0][1] += sign*scoeff[i][j]; uhess[j][i][1][0] += sign*scoeff[i][j]; } } /********************************************************************** * * function: q_facet_torus_volume_q() * * purpose: value of volume integral on facet, quadratic */ REAL q_facet_torus_volume_q(f_info) struct qinfo *f_info; { REAL **x; REAL vol; int i,j,k; REAL **dx = web.inverse_periods; WRAPTYPE w[FACET_EDGES]; unsigned long allwrap; MAT2D(u,FACET_CTRL+1,MAXCOORD); /* affine coordinates of vertices */ facetedge_id fe; x = f_info->x; /* get affine coordinates of vertices */ mat_mul_tr(x,dx,u,FACET_CTRL,SDIM,SDIM); u[FACET_CTRL] = u[0]; /* handy wrap around */ /* main integral over facet */ /* volume, integral of z dx dy */ vol = 0.0; for ( i = 0 ; i < FACET_CTRL ; i++ ) for ( j = 0 ; j < FACET_CTRL ; j++ ) for ( k = 0 ; k < FACET_CTRL ; k++ ) { REAL v = vcoeff[i][j][k]; if ( v == 0.0 ) continue; vol += v*u[i][2]*(u[j][0]*u[k][1]-u[j][1]*u[k][0]); } /* add corrections due to wraps */ fe = get_facet_fe(f_info->id); for ( i = 0 ; i < FACET_EDGES ; i++, fe = get_next_edge(fe) ) w[i] = get_fe_wrap(fe); allwrap = WR(w[0],2)+(WR(w[1],2)<wraps[2],0) ) { case POSWRAP: vol -= u[2][1];break; case NEGWRAP: vol += u[2][1];break; } switch ( WR(f_info->wraps[4],0) ) { case POSWRAP: vol += u[4][1]; break; case NEGWRAP: vol -= u[4][1]; break; } break; case (NEGWRAP) + (POSWRAP << TWRAPBITS): vol -= cut_int(u) + cut_int(u+2); switch ( WR(f_info->wraps[4],0) ) { case POSWRAP: vol -= u[4][1]; break; case NEGWRAP: vol += u[4][1]; break; } switch ( WR(f_info->wraps[0],0) ) { case POSWRAP: vol += u[0][1]; break; case NEGWRAP: vol -= u[0][1]; break; } break; case (NEGWRAP << TWRAPBITS) + (POSWRAP << (2*TWRAPBITS)): vol -= cut_int(u+2) + cut_int(u+4); switch ( WR(f_info->wraps[2],0) ) { case POSWRAP: vol += u[2][1]; break; case NEGWRAP: vol -= u[2][1]; break; } break; default: kb_error(1331,"Internal error: Bad allwrap in facet_volume.\n",RECOVERABLE); } vol *= web.torusv; return vol; } /********************************************************************** * * function: q_facet_torus_volume_q_grad() * * purpose: gradient and value of volume integral on quadratic facet */ REAL q_facet_torus_volume_q_grad(f_info) struct qinfo *f_info; { REAL **x; REAL vol; int i,j,k; REAL **dx = web.inverse_periods; WRAPTYPE w[FACET_EDGES]; unsigned long allwrap; MAT2D(u,FACET_CTRL+1,MAXCOORD); /* affine coordinates of vertices */ MAT2D(ugrad,FACET_CTRL+1,MAXCOORD); facetedge_id fe; x = f_info->x; /* get affine coordinates of vertices */ mat_mul_tr(x,dx,u,FACET_CTRL,SDIM,SDIM); u[FACET_CTRL] = u[0]; /* handy wrap around */ ugrad[FACET_CTRL] = ugrad[0]; /* handy wrap around */ /* main integral over facet */ /* volume, integral of z dx dy */ vol = 0.0; for ( i = 0 ; i < FACET_CTRL ; i++ ) for ( j = 0 ; j < FACET_CTRL ; j++ ) for ( k = 0 ; k < FACET_CTRL ; k++ ) { REAL v = vcoeff[i][j][k]; if ( v == 0.0 ) continue; vol += v*u[i][2]*(u[j][0]*u[k][1]-u[j][1]*u[k][0]); } /* gradients */ for ( k = 0 ; k < FACET_CTRL ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) ugrad[k][j] = 0.0; for ( i = 0 ; i < FACET_CTRL ; i++ ) for ( j = 0 ; j < FACET_CTRL ; j++ ) for ( k = 0 ; k < FACET_CTRL ; k++ ) { REAL v = vcoeff[i][j][k]; if ( v == 0.0 ) continue; ugrad[i][2] += v*(u[j][0]*u[k][1]-u[j][1]*u[k][0]); ugrad[j][0] += v*u[i][2]*u[k][1]; ugrad[k][1] += v*u[i][2]*u[j][0]; ugrad[j][1] -= v*u[i][2]*u[k][0]; ugrad[k][0] -= v*u[i][2]*u[j][1]; } /* add corrections due to wraps */ fe = get_facet_fe(f_info->id); for ( i = 0 ; i < FACET_EDGES ; i++, fe = get_next_edge(fe) ) w[i] = get_fe_wrap(fe); allwrap = WR(w[0],2)+(WR(w[1],2)<wraps[2],0) ) { case POSWRAP: vol -= u[2][1]; ugrad[2][1] -= 1.0; break; case NEGWRAP: vol += u[2][1]; ugrad[2][1] += 1.0; break; } switch ( WR(f_info->wraps[4],0) ) { case POSWRAP: vol += u[4][1]; ugrad[4][1] += 1.0; break; case NEGWRAP: vol -= u[4][1]; ugrad[4][1] -= 1.0; break; } break; case (NEGWRAP) + (POSWRAP << TWRAPBITS): vol -= cut_int(u) + cut_int(u+2); cut_grad(u,ugrad,-1); cut_grad(u+2,ugrad+2,-1); switch ( WR(f_info->wraps[4],0) ) { case POSWRAP: vol -= u[4][1]; ugrad[4][1] -= 1.0; break; case NEGWRAP: vol += u[4][1]; ugrad[4][1] += 1.0; break; } switch ( WR(f_info->wraps[0],0) ) { case POSWRAP: vol += u[0][1]; ugrad[0][1] += 1.0;break; case NEGWRAP: vol -= u[0][1]; ugrad[0][1] -= 1.0;break; } break; case (NEGWRAP << TWRAPBITS) + (POSWRAP << (2*TWRAPBITS)): vol -= cut_int(u+2) + cut_int(u+4); cut_grad(u+2,ugrad+2,-1); cut_grad(u+4,ugrad+4,-1); switch ( WR(f_info->wraps[2],0) ) { case POSWRAP: vol += u[2][1]; ugrad[2][1] += 1.0; break; case NEGWRAP: vol -= u[2][1]; ugrad[2][1] -= 1.0; break; } break; default: kb_error(1332,"Internal error: Bad allwrap in facet_volume.\n",RECOVERABLE); } vol *= web.torusv; for ( i = 0 ; i < FACET_CTRL ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) ugrad[i][j] *= web.torusv; mat_mult(ugrad,dx,f_info->grad,FACET_CTRL,SDIM,SDIM); return vol; } /********************************************************************** * * function: q_facet_torus_volume_q_hess() * * purpose: gradient and value of volume integral on quadratic facet */ REAL q_facet_torus_volume_q_hess(f_info) struct qinfo *f_info; { REAL **x; REAL vol; int i,j,k; REAL **dx = web.inverse_periods; WRAPTYPE w[FACET_EDGES]; unsigned long allwrap; MAT2D(u,FACET_CTRL+1,MAXCOORD); /* affine coordinates of vertices */ MAT2D(ugrad,FACET_CTRL+1,MAXCOORD); facetedge_id fe; int ii,jj; MAT4D(h,FACET_CTRL+1,FACET_CTRL+1,MAXCOORD,MAXCOORD); MAT2D(temph,MAXCOORD,MAXCOORD); REAL ***hh[FACET_CTRL+1]; x = f_info->x; /* get affine coordinates of vertices */ mat_mul_tr(x,dx,u,FACET_CTRL,SDIM,SDIM); u[FACET_CTRL] = u[0]; /* handy wrap around */ ugrad[FACET_CTRL] = ugrad[0]; /* handy wrap around */ h[FACET_CTRL] = h[0]; for ( i = 0 ; i < FACET_CTRL ; i++ ) h[i][FACET_CTRL] = h[i][0]; /* main integral over facet */ /* volume, integral of z dx dy */ vol = 0.0; for ( i = 0 ; i < FACET_CTRL ; i++ ) for ( j = 0 ; j < FACET_CTRL ; j++ ) for ( k = 0 ; k < FACET_CTRL ; k++ ) { REAL v = vcoeff[i][j][k]; if ( v == 0.0 ) continue; vol += v*u[i][2]*(u[j][0]*u[k][1]-u[j][1]*u[k][0]); } for ( i = 0 ; i < FACET_CTRL ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) ugrad[i][j] = 0.0; for ( i = 0 ; i < FACET_CTRL ; i++ ) for ( ii = 0 ; ii < FACET_CTRL ; ii++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) h[i][ii][j][jj] = 0.0; for ( i = 0 ; i < FACET_CTRL ; i++ ) for ( j = 0 ; j < FACET_CTRL ; j++ ) for ( k = 0 ; k < FACET_CTRL ; k++ ) { REAL v = vcoeff[i][j][k]; if ( v == 0.0 ) continue; ugrad[i][2] += v*(u[j][0]*u[k][1]-u[j][1]*u[k][0]); ugrad[j][0] += v*u[i][2]*u[k][1]; ugrad[k][1] += v*u[i][2]*u[j][0]; ugrad[j][1] -= v*u[i][2]*u[k][0]; ugrad[k][0] -= v*u[i][2]*u[j][1]; h[i][j][2][0] += v*u[k][1]; h[i][k][2][1] += v*u[j][0]; h[i][j][2][1] -= v*u[k][0]; h[i][k][2][0] -= v*u[j][1]; h[j][i][0][2] += v*u[k][1]; h[j][k][0][1] += v*u[i][2]; h[k][i][1][2] += v*u[j][0]; h[k][j][1][0] += v*u[i][2]; h[j][i][1][2] -= v*u[k][0]; h[j][k][1][0] -= v*u[i][2]; h[k][i][0][2] -= v*u[j][1]; h[k][j][0][1] -= v*u[i][2]; } /* add corrections due to wraps */ fe = get_facet_fe(f_info->id); for ( i = 0 ; i < FACET_EDGES ; i++, fe = get_next_edge(fe) ) w[i] = get_fe_wrap(fe); allwrap = WR(w[0],2)+(WR(w[1],2)<wraps[2],0) ) { case POSWRAP: vol -= u[2][1]; ugrad[2][1] -= 1.0; break; case NEGWRAP: vol += u[2][1]; ugrad[2][1] += 1.0; break; } switch ( WR(f_info->wraps[4],0) ) { case POSWRAP: vol += u[4][1]; ugrad[4][1] += 1.0; break; case NEGWRAP: vol -= u[4][1]; ugrad[4][1] -= 1.0; break; } break; case (NEGWRAP) + (POSWRAP << TWRAPBITS): vol -= cut_int(u) + cut_int(u+2); for ( i = 0 ; i < EDGE_CTRL ; i++ ) hh[i] = h[i]; cut_hess(u,ugrad,hh,-1); for ( i = 0 ; i < EDGE_CTRL ; i++ ) hh[i] = h[i+2]+2; cut_hess(u+2,ugrad+2,hh,-1); switch ( WR(f_info->wraps[4],0) ) { case POSWRAP: vol -= u[4][1]; ugrad[4][1] -= 1.0; break; case NEGWRAP: vol += u[4][1]; ugrad[4][1] += 1.0; break; } switch ( WR(f_info->wraps[0],0) ) { case POSWRAP: vol += u[0][1]; ugrad[0][1] += 1.0;break; case NEGWRAP: vol -= u[0][1]; ugrad[0][1] -= 1.0;break; } break; case (NEGWRAP << TWRAPBITS) + (POSWRAP << (2*TWRAPBITS)): vol -= cut_int(u+2) + cut_int(u+4); for ( i = 0 ; i < EDGE_CTRL ; i++ ) hh[i] = h[i+2]+2; cut_hess(u+2,ugrad+2,hh,-1); for ( i = 0 ; i < EDGE_CTRL ; i++ ) hh[i] = h[i+4]+4; cut_hess(u+4,ugrad+4,hh,-1); switch ( WR(f_info->wraps[2],0) ) { case POSWRAP: vol += u[2][1]; ugrad[2][1] += 1.0; break; case NEGWRAP: vol -= u[2][1]; ugrad[2][1] -= 1.0; break; } break; default: kb_error(1333, "Internal error: Bad allwrap in facet_volume.\n",RECOVERABLE); } vol *= web.torusv; for ( i = 0 ; i < FACET_CTRL ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) ugrad[i][j] *= web.torusv; mat_mult(ugrad,dx,f_info->grad,FACET_CTRL,SDIM,SDIM); for ( i = 0 ; i < FACET_CTRL ; i++ ) for ( ii = 0 ; ii < FACET_CTRL ; ii++ ) { for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) h[i][ii][j][jj] *= web.torusv; mat_mult(h[i][ii],dx,temph,SDIM,SDIM,SDIM); tr_mat_mul(dx,temph,f_info->hess[i][ii],SDIM,SDIM,SDIM); } return vol; } /********************************************************************** Lagrange torus volume quantity **********************************************************************/ REAL lagr_cut_int ARGS((REAL**)); void lagr_cut_grad ARGS((REAL**,REAL**,int)); void lagr_cut_hess ARGS((REAL**,REAL**,REAL****,int)); REAL lagr_cut_int(u) REAL **u; /* coefficients of the vertices along edge */ { REAL area; struct gauss_lag *gl = &gauss_lagrange[1][web.gauss1D_order]; int ctrl = web.skel[EDGE].ctrlpts; int m,k; /* main integral over edge */ area = 0.0; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL dy,x; for ( x = 0.0, dy = 0.0, k = 0 ; k < ctrl ; k++ ) { x += gl->gpoly[m][k]*u[k][0]; dy += gl->gpolypart[m][0][k]*u[k][1]; } area -= gl->gausswt[m]*x*dy; } return area; } void lagr_cut_grad(u,ugrad,sign) REAL **u; REAL **ugrad; /* gradients to increment */ int sign; /* +1 or -1 */ { int m,k; struct gauss_lag *gl = &gauss_lagrange[1][web.gauss1D_order]; int ctrl = web.skel[EDGE].ctrlpts; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL x,dy; for ( x = 0.0, dy = 0.0, k = 0 ; k < ctrl ; k++ ) { x += gl->gpoly[m][k]*u[k][0]; dy += gl->gpolypart[m][0][k]*u[k][1]; } for ( k = 0 ; k < ctrl ; k++ ) { ugrad[k][1] -= sign*gl->gausswt[m]*x*gl->gpolypart[m][0][k]; ugrad[k][0] -= sign*gl->gausswt[m]*dy*gl->gpoly[m][k]; } } } void lagr_cut_hess(u,ugrad,uhess,sign) REAL **u; REAL **ugrad; /* gradients to increment */ REAL ****uhess; /* hessians to increment */ int sign; /* +1 or -1 */ { int m,k,kk; struct gauss_lag *gl = &gauss_lagrange[1][web.gauss1D_order]; int ctrl = web.skel[EDGE].ctrlpts; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL x,dy; REAL wt = sign*gl->gausswt[m]; for ( x = 0.0, dy = 0.0, k = 0 ; k < ctrl ; k++ ) { x += gl->gpoly[m][k]*u[k][0]; dy += gl->gpolypart[m][0][k]*u[k][1]; } for ( k = 0 ; k < ctrl ; k++ ) { ugrad[k][1] -= wt*x*gl->gpolypart[m][0][k]; ugrad[k][0] -= wt*dy*gl->gpoly[m][k]; for ( kk = 0 ; kk < ctrl ; kk++ ) { uhess[k][kk][1][0] -= wt*gl->gpoly[m][kk]*gl->gpolypart[m][0][k]; uhess[k][kk][0][1] -= wt*gl->gpolypart[m][0][kk]*gl->gpoly[m][k]; } } } } /********************************************************************** * * function: q_facet_torus_volume_lagr() * * purpose: value of volume integral on facet, quadratic */ REAL q_facet_torus_volume_lagr(f_info) struct qinfo *f_info; { REAL **x; REAL vol; int i,m; int ctrl = web.skel[FACET].ctrlpts; REAL **dx = web.inverse_periods; WRAPTYPE w[FACET_EDGES]; unsigned long allwrap; REAL **u = f_info->u; facetedge_id fe; MAT2D(mat,MAXCOORD,MAXCOORD); int dim = web.dimension; REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; REAL ***uu = f_info->uu; /* side vertex coords */ int order = web.lagrange_order; x = f_info->x; /* get affine coordinates of vertices */ mat_mul_tr(x,dx,u,ctrl,SDIM,SDIM); for ( i = 0 ; i <= web.lagrange_order ; i++ ) { uu[0][i] = u[i]; uu[1][i] = u[(i+1)*order - (i-1)*i/2]; uu[2][web.lagrange_order-i] = u[i*(order+1) - i*(i-1)/2]; } /* main integral over facet */ /* volume, integral of z dx dy */ vol = 0.0; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL z; mat_mult(gl->gpolypart[m],u,mat,dim,ctrl,SDIM); for ( z = 0.0, i = 0 ; i < ctrl ; i++ ) z += gl->gpoly[m][i]*u[i][dim]; vol += gl->gausswt[m]*det_adjoint(mat,dim)*z; } vol *= sign/factorial[dim]; /* add corrections due to wraps */ fe = get_facet_fe(f_info->id); for ( i = 0 ; i < FACET_EDGES ; i++, fe = get_next_edge(fe) ) w[i] = get_fe_wrap(fe); allwrap = WR(w[0],2)+(WR(w[1],2)<wraps[web.lagrange_order],0) ) { case POSWRAP: vol -= uu[1][0][1];break; case NEGWRAP: vol += uu[1][0][1];break; } switch ( WR(f_info->wraps[ctrl-1],0) ) { case POSWRAP: vol += uu[2][0][1]; break; case NEGWRAP: vol -= uu[2][0][1]; break; } break; case (NEGWRAP) + (POSWRAP << TWRAPBITS): vol -= lagr_cut_int(uu[0]); vol -= lagr_cut_int(uu[1]); switch ( WR(f_info->wraps[ctrl-1],0) ) { case POSWRAP: vol -= uu[2][0][1]; break; case NEGWRAP: vol += uu[2][0][1]; break; } switch ( WR(f_info->wraps[0],0) ) { case POSWRAP: vol += uu[0][0][1]; break; case NEGWRAP: vol -= uu[0][0][1]; break; } break; case (NEGWRAP << TWRAPBITS) + (POSWRAP << (2*TWRAPBITS)): vol -= lagr_cut_int(uu[1]); vol -= lagr_cut_int(uu[2]); switch ( WR(f_info->wraps[web.lagrange_order],0) ) { case POSWRAP: vol += uu[1][0][1]; break; case NEGWRAP: vol -= uu[1][0][1]; break; } break; default: kb_error(1334,"Internal error: Bad allwrap in facet_volume.\n",RECOVERABLE); } vol *= web.torusv; return vol; } /********************************************************************** * * function: q_facet_torus_volume_lagr_grad() * * purpose: gradient and value of volume integral on quadratic facet */ REAL q_facet_torus_volume_lagr_grad(f_info) struct qinfo *f_info; { REAL **x; REAL vol; int i,j,k; REAL **dx = web.inverse_periods; WRAPTYPE w[FACET_EDGES]; unsigned long allwrap; facetedge_id fe; REAL **ugrad = f_info->ugrad; REAL ***uu = f_info->uu; /* side vertex coords */ REAL ***uugrad = f_info->uugrad; /* side vertex coords */ REAL **u = f_info->u; REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; int order = web.lagrange_order; int m; int dim = web.dimension; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; MAT2D(mat,MAXCOORD,MAXCOORD); REAL z; int ctrl = web.skel[FACET].ctrlpts; x = f_info->x; for ( i = 0 ; i < ctrl ; i++ ) for ( j = 0 ; j < SDIM; j++ ) ugrad[i][j] = 0.0; /* get affine coordinates of vertices */ mat_mul_tr(x,dx,u,ctrl,SDIM,SDIM); for ( i = 0 ; i <= web.lagrange_order ; i++ ) { uu[0][i] = u[i]; uugrad[0][i] = ugrad[i]; uu[1][i] = u[(i+1)*order - (i-1)*i/2]; uugrad[1][i] = ugrad[(i+1)*order - (i-1)*i/2]; uu[2][web.lagrange_order-i] = u[i*(order+1) - i*(i-1)/2]; uugrad[2][web.lagrange_order-i] = ugrad[i*(order+1) - i*(i-1)/2]; } /* main integral over facet */ /* volume, integral of z dx dy */ vol = 0.0; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL weight = sign* gl->gausswt[m]/factorial[dim]; REAL det; mat_mult(gl->gpolypart[m],u,mat,dim,ctrl,SDIM); for ( z = 0.0, i = 0 ; i < ctrl ; i++ ) z += gl->gpoly[m][i]*u[i][dim]; det = det_adjoint(mat,dim); vol += weight*det*z; for ( k = 0 ; k < gl->lagpts; k++ ) for ( j = 0 ; j < dim ; j++ ) for ( i = 0 ; i < dim ; i++ ) ugrad[k][j] += weight*z*gl->gpolypart[m][i][k]*mat[j][i]; for ( k = 0 ; k < gl->lagpts ; k++ ) ugrad[k][dim] += weight*gl->gpoly[m][k]*det; } /* add corrections due to wraps */ fe = get_facet_fe(f_info->id); for ( i = 0 ; i < FACET_EDGES ; i++, fe = get_next_edge(fe) ) w[i] = get_fe_wrap(fe); allwrap = WR(w[0],2)+(WR(w[1],2)<wraps[order],0) ) { case POSWRAP: vol -= uu[1][0][1]; uugrad[1][0][1] -= 1.0; break; case NEGWRAP: vol += uu[1][0][1]; uugrad[1][0][1] += 1.0; break; } switch ( WR(f_info->wraps[ctrl-1],0) ) { case POSWRAP: vol += uu[2][0][1]; uugrad[2][0][1] += 1.0; break; case NEGWRAP: vol -= uu[2][0][1]; uugrad[2][0][1] -= 1.0; break; } break; case (NEGWRAP) + (POSWRAP << TWRAPBITS): vol -= lagr_cut_int(uu[0]) + lagr_cut_int(uu[1]); lagr_cut_grad(uu[0],uugrad[0],-1); lagr_cut_grad(uu[1],uugrad[1],-1); switch ( WR(f_info->wraps[ctrl-1],0) ) { case POSWRAP: vol -= uu[2][0][1]; uugrad[2][0][1] -= 1.0; break; case NEGWRAP: vol += uu[2][0][1]; uugrad[2][0][1] += 1.0; break; } switch ( WR(f_info->wraps[0],0) ) { case POSWRAP: vol += uu[0][0][1]; uugrad[0][0][1] += 1.0;break; case NEGWRAP: vol -= uu[0][0][1]; uugrad[0][0][1] -= 1.0;break; } break; case (NEGWRAP << TWRAPBITS) + (POSWRAP << (2*TWRAPBITS)): vol -= lagr_cut_int(uu[1]) + lagr_cut_int(uu[2]); lagr_cut_grad(uu[1],uugrad[1],-1); lagr_cut_grad(uu[2],uugrad[2],-1); switch ( WR(f_info->wraps[order],0) ) { case POSWRAP: vol += uu[1][0][1]; uugrad[1][0][1] += 1.0; break; case NEGWRAP: vol -= uu[1][0][1]; uugrad[1][0][1] -= 1.0; break; } break; default: kb_error(1202,"Internal error: Bad allwrap in facet_volume.\n",RECOVERABLE); } vol *= web.torusv; for ( i = 0 ; i < ctrl ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) ugrad[i][j] *= web.torusv; mat_mult(ugrad,dx,f_info->grad,ctrl,SDIM,SDIM); return vol; } /********************************************************************** * * function: q_facet_torus_volume_lagr_hess() * * purpose: gradient and value of volume integral on quadratic facet */ REAL q_facet_torus_volume_lagr_hess(f_info) struct qinfo *f_info; { REAL **x; REAL vol; int i,j,k,kk,m; REAL **dx = web.inverse_periods; WRAPTYPE w[FACET_EDGES]; unsigned long allwrap; facetedge_id fe; int ii,jj; MAT2D(temph,MAXCOORD,MAXCOORD); int ctrl = web.skel[FACET].ctrlpts; REAL **ugrad = f_info->ugrad; REAL ***uu = f_info->uu; /* side vertex coords */ REAL ***uugrad = f_info->uugrad; /* side vertex grads */ REAL *****uuhess = f_info->uuhess; /* side vertex hessians */ REAL ****uhess = f_info->uhess; /* vertex hessians */ int order = web.lagrange_order; REAL **u = f_info->u; MAT2D(mat,MAXCOORD,MAXCOORD); int dim = web.dimension; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; MAT4D(dethess,MAXCOORD,MAXCOORD,MAXCOORD,MAXCOORD); REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; x = f_info->x; for ( i = 0 ; i < ctrl ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) ugrad[i][j] = 0.0; for ( i = 0 ; i < ctrl ; i++ ) for ( ii = 0 ; ii < ctrl ; ii++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) uhess[i][ii][j][jj] = 0.0; /* get affine coordinates of vertices */ mat_mul_tr(x,dx,u,ctrl,SDIM,SDIM); /* set up edge pointers */ for ( i = 0 ; i <= web.lagrange_order ; i++ ) { uu[0][i] = u[i]; uugrad[0][i] = ugrad[i]; uu[1][i] = u[(i+1)*order - (i-1)*i/2]; uugrad[1][i] = ugrad[(i+1)*order - (i-1)*i/2]; uu[2][web.lagrange_order-i] = u[i*(order+1) - i*(i-1)/2]; uugrad[2][web.lagrange_order-i] = ugrad[i*(order+1) - i*(i-1)/2]; for ( j = 0 ; j <= web.lagrange_order ; j++ ) { uuhess[0][i][j] = uhess[i][j]; uuhess[1][i][j] = uhess[(i+1)*order-(i-1)*i/2][(j+1)*order-(j-1)*j/2]; uuhess[2][order-i][order-j] = uhess[i*(order+1) - i*(i-1)/2][j*(order+1) - j*(j-1)/2]; } } /* main integral over facet, without wraps */ for ( m = 0, vol = 0.0 ; m < gl->gnumpts ; m++ ) { REAL det; REAL z,sum; REAL weight = sign*gl->gausswt[m]/factorial[dim]; mat_mult(gl->gpolypart[m],u,mat,dim,ctrl,SDIM); det_hess(mat,dethess,dim); det = det_adjoint(mat,dim); for ( z = 0.0, i = 0 ; i < ctrl ; i++ ) z += gl->gpoly[m][i]*u[i][dim]; vol += weight*det*z; /* gradient */ for ( k = 0 ; k < gl->lagpts; k++ ) for ( j = 0 ; j < dim ; j++ ) for ( i = 0 ; i < dim ; i++ ) ugrad[k][j] += weight*z*gl->gpolypart[m][i][k]*mat[j][i]; for ( k = 0 ; k < gl->lagpts ; k++ ) ugrad[k][dim] += weight*gl->gpoly[m][k]*det; /* hessian */ for ( k = 0 ; k < gl->lagpts ; k++ ) for ( kk = 0 ; kk < gl->lagpts ; kk++ ) { if ( dim == 2 ) { uhess[k][kk][0][1] += weight*z* (gl->gpolypart[m][0][k]*gl->gpolypart[m][1][kk] - gl->gpolypart[m][1][k]*gl->gpolypart[m][0][kk]); uhess[k][kk][1][0] += weight*z* (gl->gpolypart[m][1][k]*gl->gpolypart[m][0][kk] - gl->gpolypart[m][0][k]*gl->gpolypart[m][1][kk]); } else if ( dim > 2 ) { for ( j = 0 ; j < dim ; j++ ) { for ( jj = 0 ; jj < dim ; jj++ ) for ( i = 0 ; i < dim ; i++ ) for ( ii = 0 ; ii < dim ; ii++ ) { uhess[k][kk][j][jj] += weight*z*dethess[i][j][ii][jj] *gl->gpolypart[m][i][k]*gl->gpolypart[m][ii][kk]; } } } for ( j = 0 ; j < dim ; j++ ) { for ( i = 0, sum = 0.0 ; i < dim ; i++ ) sum += gl->gpolypart[m][i][k]*mat[j][i]; uhess[k][kk][j][dim] += weight*gl->gpoly[m][kk]*sum; } for ( jj = 0 ; jj < dim ; jj++ ) { for ( ii = 0,sum = 0.0 ; ii < dim ; ii++ ) sum += gl->gpolypart[m][ii][kk]*mat[jj][ii]; uhess[k][kk][dim][jj] += weight*gl->gpoly[m][k]*sum; } } } /* add corrections due to wraps */ fe = get_facet_fe(f_info->id); for ( i = 0 ; i < FACET_EDGES ; i++, fe = get_next_edge(fe) ) w[i] = get_fe_wrap(fe); allwrap = WR(w[0],2)+(WR(w[1],2)<wraps[order],0) ) { case POSWRAP: vol -= uu[1][0][1]; uugrad[1][0][1] -= 1.0; break; case NEGWRAP: vol += uu[1][0][1]; uugrad[1][0][1] += 1.0; break; } switch ( WR(f_info->wraps[ctrl-1],0) ) { case POSWRAP: vol += uu[2][0][1]; uugrad[2][0][1] += 1.0; break; case NEGWRAP: vol -= uu[2][0][1]; uugrad[2][0][1] -= 1.0; break; } break; case (NEGWRAP) + (POSWRAP << TWRAPBITS): vol -= lagr_cut_int(uu[0]) + lagr_cut_int(uu[1]); lagr_cut_hess(uu[0],uugrad[0],uuhess[0],-1); lagr_cut_hess(uu[1],uugrad[1],uuhess[1],-1); switch ( WR(f_info->wraps[ctrl-1],0) ) { case POSWRAP: vol -= uu[2][0][1]; uugrad[2][0][1] -= 1.0; break; case NEGWRAP: vol += uu[2][0][1]; uugrad[2][0][1] += 1.0; break; } switch ( WR(f_info->wraps[0],0) ) { case POSWRAP: vol += uu[0][0][1]; uugrad[0][0][1] += 1.0;break; case NEGWRAP: vol -= uu[0][0][1]; uugrad[0][0][1] -= 1.0;break; } break; case (NEGWRAP << TWRAPBITS) + (POSWRAP << (2*TWRAPBITS)): vol -= lagr_cut_int(uu[1]) + lagr_cut_int(uu[2]); lagr_cut_hess(uu[1],uugrad[1],uuhess[1],-1); lagr_cut_hess(uu[2],uugrad[2],uuhess[2],-1); switch ( WR(f_info->wraps[order],0) ) { case POSWRAP: vol += uu[1][0][1]; uugrad[1][0][1] += 1.0; break; case NEGWRAP: vol -= uu[1][0][1]; uugrad[1][0][1] -= 1.0; break; } break; default: kb_error(1335,"Internal error: Bad allwrap in facet_volume.\n", RECOVERABLE); } /* pull back gradient and hessian to Euclidean coords */ vol *= web.torusv; for ( i = 0 ; i < ctrl ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) ugrad[i][j] *= web.torusv; mat_mult(ugrad,dx,f_info->grad,ctrl,SDIM,SDIM); for ( i = 0 ; i < ctrl ; i++ ) for ( ii = 0 ; ii < ctrl ; ii++ ) { for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) { uhess[i][ii][j][jj] *= web.torusv; } mat_mult(uhess[i][ii],dx,temph,SDIM,SDIM,SDIM); tr_mat_mul(dx,temph,f_info->hess[i][ii],SDIM,SDIM,SDIM); } return vol; } evolver-2.30c.dfsg/src/lagrange.c0000644000175300017530000011021311410765113017137 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************* * * file: lagrange.c * */ #include "include.h" /********************************************************************* ********************************************************************** Lagrange model quantities ********************************************************************** **********************************************************************/ /********************************************************************* Edge length quantity Uses Gaussian integration. Not known to be upper bound on area. **********************************************************************/ /********************************************************************* * * Returns energy due to edge. * Lagrange version. */ REAL lagrange_edge_tension_value(e_info) struct qinfo *e_info; { REAL value = 0.0; int m,i,j; REAL det; REAL **tang; int dim = (web.representation==STRING) ? 1 : web.dimension-1; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss1D_order]; MAT2D(mat,MAXCOORD,MAXCOORD); for ( m = 0 ; m < gl->gnumpts ; m++ ) /* integration point number */ { tang = e_info->sides[m]; for ( i = 0 ; i < dim ; i++ ) for ( j = 0 ; j <= i ; j++ ) mat[i][j] = mat[j][i] = SDIM_dot(tang[i],tang[j]); det = det_adjoint(mat,dim); value += gl->gausswt[m]*sqrt(det); } value /= factorial[dim]; /* triangle factor */ if ( METH_INSTANCE(e_info->method)->flags & DEFAULT_INSTANCE ) { set_edge_length(e_info->id,value); #ifdef SHARED_MEMORY if ( nprocs > 1 ) proc_total_area[GET_THREAD_ID] += value; else #endif binary_tree_add(web.total_area_addends,value); } if ( METH_INSTANCE(e_info->method)->flags & USE_DENSITY ) value *= get_edge_density(e_info->id); return value; } /********************************************************************* * * Returns gradient and energy due to edge. * Lagrange version. */ REAL lagrange_edge_tension_grad(e_info) struct qinfo *e_info; { REAL value = 0.0; int i,ii,m,j,k; REAL det; REAL density,fudge; int dim = (web.representation==STRING) ? 1 : web.dimension-1; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss1D_order]; MAT2D(mat,MAXCOORD,MAXCOORD); if ( METH_INSTANCE(e_info->method)->flags & USE_DENSITY ) density = get_edge_density(e_info->id); else density = 1.0; for ( m = 0 ; m < gl->gnumpts ; m++ ) /* integration point number */ { REAL **tang = e_info->sides[m]; /* calculate tangents and det */ for ( i = 0 ; i < dim ; i++ ) for ( j = 0 ; j <= i ; j++ ) mat[i][j] = mat[j][i] = SDIM_dot(tang[i],tang[j]); det = det_adjoint(mat,dim); value += gl->gausswt[m]*sqrt(det); /* gradients */ fudge = density*gl->gausswt[m]/sqrt(det)/factorial[dim]; for ( k = 0 ; k < gl->lagpts ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) { REAL sum = 0.0; for ( i = 0 ; i < dim ; i++ ) for ( ii = 0 ; ii < dim ; ii++ ) sum += tang[i][j]*gl->gpolypart[m][ii][k]*mat[i][ii]; e_info->grad[k][j] += fudge*sum; } } return density*value/factorial[dim]; } /********************************************************************* * * Returns hessian, gradient and energy due to edge. * Lagrange version. */ REAL lagrange_edge_tension_hess(e_info) struct qinfo *e_info; { REAL value = 0.0; int i,ii,m,j,k,jj,kk; REAL det; REAL density,fudge; int dim = (web.representation==STRING) ? 1 : web.dimension-1; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss1D_order]; MAT2D(mat,MAXCOORD,MAXCOORD); REAL **sums; REAL ****dethess=NULL; REAL detinv; if ( METH_INSTANCE(e_info->method)->flags & USE_DENSITY ) density = get_edge_density(e_info->id); else density = 1.0; sums = dmatrix(0,gl->lagpts-1,0,SDIM-1); if ( dim > 2 ) dethess = dmatrix4(dim,dim,dim,dim); for ( m = 0 ; m < gl->gnumpts ; m++ ) /* integration point number */ { REAL **tang = e_info->sides[m]; REAL **gp = gl->gpolypart[m]; /* calculate tangents and det */ for ( i = 0 ; i < dim ; i++ ) for ( j = 0 ; j <= i ; j++ ) mat[i][j] = mat[j][i] = SDIM_dot(tang[i],tang[j]); if ( dim > 2 ) det_hess(mat,dethess,dim); /* for hessian */ det = det_adjoint(mat,dim); detinv = (det == 0.0) ? 0.0 : 1/det; value += gl->gausswt[m]*sqrt(det); /* gradients */ fudge = density*gl->gausswt[m]*sqrt(detinv)/factorial[dim]; for ( k = 0 ; k < gl->lagpts ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) { REAL sum = 0.0; for ( i = 0 ; i < dim ; i++ ) for ( ii = 0 ; ii < dim ; ii++ ) sum += tang[i][j]*gp[ii][k]*mat[i][ii]; sums[k][j] = sum; e_info->grad[k][j] += fudge*sum; } /* hessians */ for ( k = 0 ; k < gl->lagpts ; k++ ) for ( kk = 0 ; kk <= k ; kk++ ) { REAL jjjsum; REAL gp01; REAL gp10; REAL gp00=0.0; REAL gp11=0.0; REAL coeff1=0.0; REAL coeff2=0.0; if ( dim == 2 ) { gp01 = gp[0][k]*gp[1][kk]; gp10 = gp[1][k]*gp[0][kk]; gp00 = gp[0][k]*gp[0][kk]; gp11 = gp[1][k]*gp[1][kk]; coeff1 = 2*gp01-gp10; coeff2 = 2*gp10-gp01; } for ( i = 0,jjjsum=0.0 ; i < dim ; i++ ) for ( ii = 0 ; ii < dim ; ii++ ) jjjsum += gp[i][k]*gp[ii][kk]*mat[i][ii]; for ( j = 0 ; j < SDIM ; j++ ) { int jjend = (k==kk) ? j+1 : SDIM; for ( jj = 0 ; jj < jjend ; jj++ ) { REAL sum,h; int i1,i2,jj1,jj2; h = -sums[k][j]*sums[kk][jj]*detinv; if ( dim == 2 ) { h += (coeff1*tang[0][j] - gp00*tang[1][j])*tang[1][jj] + (coeff2*tang[1][j] - gp11*tang[0][j])*tang[0][jj]; } else if ( dim > 2 ) { sum = 0.0; for (i1 = 0 ; i1 < dim ; i1++ ) { REAL suma = 0.0; for ( jj1 = 0 ; jj1 < dim ; jj1++ ) { REAL sumb = 0.0; for ( i2 = 0 ; i2 < dim ; i2++ ) { /* note: inner loops here pretty well optimized */ for ( jj2 = 0 ; jj2 < dim ; jj2++ ) { REAL dh = dethess[i1][jj1][i2][jj2]; if ( dh == 0.0 ) continue; /* using symmetry of dethess */ sumb += dh*(tang[i2][jj]*gp[jj2][kk]+tang[jj2][jj]*gp[i2][kk]); } } suma += gp[jj1][k]*sumb; } sum += tang[i1][j]*suma; } h += sum; } if ( j==jj ) h += jjjsum; h *= fudge; e_info->hess[k][kk][j][jj] += h; if ( (kk != k) || (jj != j) ) e_info->hess[kk][k][jj][j] += h; } } } } free_matrix(sums); if ( dim > 2 ) free_matrix4(dethess); return density*value/factorial[dim]; } /********************************************************************* Film area quantity Uses Gaussian integration. Not known to be upper bound on area. **********************************************************************/ /********************************************************************* * * Returns energy due to facet. * Lagrange version. */ REAL lagrange_facet_tension_value(f_info) struct qinfo *f_info; { REAL value = 0.0; int m,i,j; REAL det; REAL **tang; int dim = web.dimension; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; MAT2D(mat,MAXCOORD,MAXCOORD); for ( m = 0 ; m < gl->gnumpts ; m++ ) /* integration point number */ { tang = f_info->sides[m]; for ( i = 0 ; i < dim ; i++ ) for ( j = 0 ; j <= i ; j++ ) mat[i][j] = mat[j][i] = SDIM_dot(tang[i],tang[j]); det = det_adjoint(mat,dim); if ( det <= 0.0 ) continue; value += gl->gausswt[m]*sqrt(det); } value /= factorial[dim]; /* triangle factor */ if ( METH_INSTANCE(f_info->method)->flags & DEFAULT_INSTANCE ) { set_facet_area(f_info->id,value); #ifdef SHARED_MEMORY if ( nprocs > 1 ) proc_total_area[GET_THREAD_ID] += value; else #endif binary_tree_add(web.total_area_addends,value); } if ( METH_INSTANCE(f_info->method)->flags & USE_DENSITY ) value *= get_facet_density(f_info->id); return value; } /********************************************************************* * * Returns gradient and energy due to facet. * Lagrange version. */ REAL lagrange_facet_tension_grad(f_info) struct qinfo *f_info; { REAL value = 0.0; int i,ii,m,j,k; REAL det; REAL density,fudge; int dim = web.dimension; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; MAT2D(mat,MAXCOORD,MAXCOORD); if ( METH_INSTANCE(f_info->method)->flags & USE_DENSITY ) density = get_facet_density(f_info->id); else density = 1.0; for ( m = 0 ; m < gl->gnumpts ; m++ ) /* integration point number */ { REAL **tang = f_info->sides[m]; /* calculate tangents and det */ for ( i = 0 ; i < dim ; i++ ) for ( j = 0 ; j <= i ; j++ ) mat[i][j] = mat[j][i] = SDIM_dot(tang[i],tang[j]); det = det_adjoint(mat,dim); if ( det <= 0.0 ) continue; value += gl->gausswt[m]*sqrt(det); /* gradients */ fudge = density*gl->gausswt[m]/sqrt(det)/factorial[dim]; for ( k = 0 ; k < gl->lagpts ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) { REAL sum = 0.0; for ( i = 0 ; i < dim ; i++ ) for ( ii = 0 ; ii < dim ; ii++ ) sum += tang[i][j]*gl->gpolypart[m][ii][k]*mat[i][ii]; f_info->grad[k][j] += fudge*sum; } } return density*value/factorial[dim]; } /********************************************************************* * * Returns hessian, gradient and energy due to facet. * Lagrange version. */ REAL lagrange_facet_tension_hess(f_info) struct qinfo *f_info; { REAL value = 0.0; int i,ii,m,j,k,jj,kk; REAL det; REAL density,fudge; int dim = web.dimension; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; MAT2D(mat,MAXCOORD,MAXCOORD); REAL **sums; REAL ****dethess=NULL; REAL detinv; if ( METH_INSTANCE(f_info->method)->flags & USE_DENSITY ) density = get_facet_density(f_info->id); else density = 1.0; sums = dmatrix(0,gl->lagpts-1,0,SDIM-1); if ( dim > 2 ) dethess = dmatrix4(dim,dim,dim,dim); for ( m = 0 ; m < gl->gnumpts ; m++ ) /* integration point number */ { REAL **tang = f_info->sides[m]; REAL **gp = gl->gpolypart[m]; /* calculate tangents and det */ for ( i = 0 ; i < dim ; i++ ) for ( j = 0 ; j <= i ; j++ ) mat[i][j] = mat[j][i] = SDIM_dot(tang[i],tang[j]); if ( dim > 2 ) det_hess(mat,dethess,dim); /* for hessian */ det = det_adjoint(mat,dim); if ( det <= 0.0 ) continue; detinv = (det == 0.0) ? 0.0 : 1/det; value += gl->gausswt[m]*sqrt(det); /* gradients */ fudge = density*gl->gausswt[m]*sqrt(detinv)/factorial[dim]; for ( k = 0 ; k < gl->lagpts ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) { REAL sum = 0.0; for ( i = 0 ; i < dim ; i++ ) for ( ii = 0 ; ii < dim ; ii++ ) sum += tang[i][j]*gp[ii][k]*mat[i][ii]; sums[k][j] = sum; f_info->grad[k][j] += fudge*sum; } /* hessians */ for ( k = 0 ; k < gl->lagpts ; k++ ) for ( kk = 0 ; kk <= k ; kk++ ) { REAL jjjsum; REAL gp01; REAL gp10; REAL gp00=0.0; REAL gp11=0.0; REAL coeff1=0.0; REAL coeff2=0.0; if ( dim == 2 ) { gp01 = gp[0][k]*gp[1][kk]; gp10 = gp[1][k]*gp[0][kk]; gp00 = gp[0][k]*gp[0][kk]; gp11 = gp[1][k]*gp[1][kk]; coeff1 = 2*gp01-gp10; coeff2 = 2*gp10-gp01; } for ( i = 0,jjjsum=0.0 ; i < dim ; i++ ) for ( ii = 0 ; ii < dim ; ii++ ) jjjsum += gp[i][k]*gp[ii][kk]*mat[i][ii]; for ( j = 0 ; j < SDIM ; j++ ) { int jjend = (k==kk) ? j+1 : SDIM; REAL term1 = (coeff1*tang[0][j] - gp00*tang[1][j]); REAL term2 = (coeff2*tang[1][j] - gp11*tang[0][j]); REAL hterm = -sums[k][j]*detinv; for ( jj = 0 ; jj < jjend ; jj++ ) { REAL sum,h; int i1,i2,jj1,j2; h = hterm*sums[kk][jj]; if ( dim == 2 ) { h += term1*tang[1][jj] + term2*tang[0][jj]; } else if ( dim > 2 ) { sum = 0.0; for (i1 = 0 ; i1 < dim ; i1++ ) { REAL suma = 0.0; for ( jj1 = 0 ; jj1 < dim ; jj1++ ) { REAL sumb = 0.0; for ( i2 = 0 ; i2 < dim ; i2++ ) { /* note: inner loops here pretty well optimized */ for ( j2 = 0 ; j2 < dim ; j2++ ) { REAL dh = dethess[i1][jj1][i2][j2]; if ( dh == 0.0 ) continue; /* using symmetry of dethess */ sumb += dh*(tang[i2][jj]*gp[j2][kk]+tang[j2][jj]*gp[i2][kk]); } } suma += gp[jj1][k]*sumb; } sum += tang[i1][j]*suma; } h += sum; } if ( j==jj ) h += jjjsum; h *= fudge; f_info->hess[k][kk][j][jj] += h; /* do outside of gauss point loop if ( (kk != k) || (jj != j) ) f_info->hess[kk][k][jj][j] += h; */ } } } } /* transpose part */ for ( k = 0 ; k < gl->lagpts ; k++ ) for ( kk = 0 ; kk <= k ; kk++ ) for ( j = 0 ; j < SDIM ; j++ ) { int jjend = (k==kk) ? j+1 : SDIM; for ( jj = 0 ; jj < jjend ; jj++ ) { if ( (kk != k) || (jj != j) ) f_info->hess[kk][k][jj][j] += f_info->hess[k][kk][j][jj]; } } free_matrix(sums); if ( dim > 2 ) free_matrix4(dethess); return density*value/factorial[dim]; } /********************************************************************* Lagrange edge_vector_integral method For 1D edges only. *********************************************************************/ /********************************************************************* * * function: edge_vector_integral_lagrange() * * purpose: method value * */ REAL edge_vector_integral_lagrange(e_info) struct qinfo *e_info; { int m,j,k; REAL value=0.0; REAL tang[MAXCOORD]; REAL sign = (get_eattr(e_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; struct gauss_lag *gl = &gauss_lagrange[1][web.gauss1D_order]; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL weight = sign*gl->gausswt[m]; e_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( j = 0 ; j < SDIM ; j++ ) { tang[j] = 0.0; for ( k = 0 ; k < gl->lagpts ; k++ ) tang[j] += gl->gpolypart[m][0][k]*e_info->x[k][j]; value += weight*tang[j]*eval(METH_INSTANCE(abs(e_info->method))->expr[j], e_info->gauss_pt[m],e_info->id,NULL); } } return value; } /********************************************************************* * * function: edge_vector_integral_lagrange_grad() * * purpose: method gradient * */ REAL edge_vector_integral_lagrange_grad(e_info) struct qinfo *e_info; { int m,j,k,i; REAL value = 0.0; REAL val[MAXCOORD]; REAL derivs[MAXCOORD][MAXCOORD]; REAL sum; REAL tang[MAXCOORD]; REAL sign = (get_eattr(e_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; struct gauss_lag *gl = &gauss_lagrange[1][web.gauss1D_order]; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL weight = sign*gl->gausswt[m]; e_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( j = 0 ; j < SDIM ; j++ ) { tang[j] = 0.0; for ( k = 0 ; k < gl->lagpts ; k++ ) tang[j] += gl->gpolypart[m][0][k]*e_info->x[k][j]; } for ( j = 0 ; j < SDIM ; j++ ) eval_all(METH_INSTANCE(abs(e_info->method))->expr[j], e_info->gauss_pt[m],SDIM,val+j,derivs[j],e_info->id); value += weight*SDIM_dot(val,tang); for ( k = 0 ; k < SDIM ; k++ ) { for ( sum = 0.0, j = 0 ; j < SDIM ; j++ ) sum += derivs[j][k]*tang[j]; for ( i = 0 ; i < gl->lagpts ; i++ ) e_info->grad[i][k] += weight*(gl->gpolypart[m][0][i]*val[k] + gl->gpoly[m][i]*sum); } } return value; } /********************************************************************* * * function: edge_vector_integral_lagrange_hess() * * purpose: method gradient and hessian * */ REAL edge_vector_integral_lagrange_hess(e_info) struct qinfo *e_info; { int m,i,j,k,ii,kk; REAL value = 0.0; REAL val[MAXCOORD]; REAL derivs[MAXCOORD][MAXCOORD]; REAL sum; MAT3D(second,MAXCOORD,MAXCOORD,MAXCOORD); REAL tang[MAXCOORD]; REAL sign = (get_eattr(e_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; struct gauss_lag *gl = &gauss_lagrange[1][web.gauss1D_order]; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL weight = sign*gl->gausswt[m]; e_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( j = 0 ; j < SDIM ; j++ ) { tang[j] = 0.0; for ( k = 0 ; k < gl->lagpts ; k++ ) tang[j] += gl->gpolypart[m][0][k]*e_info->x[k][j]; } for ( j = 0 ; j < SDIM ; j++ ) eval_second(METH_INSTANCE(abs(e_info->method))->expr[j], e_info->gauss_pt[m],SDIM,val+j,derivs[j],second[j],e_info->id); value += weight*SDIM_dot(val,tang); for ( k = 0 ; k < SDIM ; k++ ) { for ( sum = 0.0, j = 0 ; j < SDIM ; j++ ) sum += derivs[j][k]*tang[j]; for ( i = 0 ; i < gl->lagpts ; i++ ) e_info->grad[i][k] += weight*(gl->gpolypart[m][0][i]*val[k] + gl->gpoly[m][i]*sum); } for ( ii = 0 ; ii < SDIM ; ii++ ) for ( i = 0 ; i < SDIM ; i++ ) { for ( sum = 0.0, j = 0 ; j < SDIM ; j++ ) sum += second[j][ii][i]*tang[j]; for ( k = 0 ; k < gl->lagpts ; k++ ) for ( kk = 0 ; kk < gl->lagpts ; kk++ ) e_info->hess[k][kk][i][ii] += weight* ( sum*gl->gpoly[m][k]*gl->gpoly[m][kk] + gl->gpolypart[m][0][k]*derivs[i][ii]*gl->gpoly[m][kk] + gl->gpolypart[m][0][kk]*derivs[ii][i]*gl->gpoly[m][k] ); } } return value; } /********************************************************************* facet_vector_integral method Integral of vectorfield over facet. nD facet in (n+1)D only. Lagrange model *********************************************************************/ REAL lagrange_vector_integral_all ARGS((struct qinfo*,int)); /********************************************************************* * * function: lagrange_vector_integral() * * purpose: method value * */ REAL lagrange_vector_integral(f_info) struct qinfo *f_info; { int i,m,j; REAL value=0.0; MAT2D(mat,MAXCOORD,MAXCOORD); int dim = web.dimension; REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; for ( m = 0 ; m < gl->gnumpts ; m++ ) { for ( i = 0 ; i < dim ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) mat[i][j] = f_info->sides[m][i][j]; f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( j = 0 ; j < SDIM ; j++ ) mat[dim][j] = eval(METH_INSTANCE(abs(f_info->method))->expr[j], f_info->gauss_pt[m],f_info->id,NULL); value += gl->gausswt[m]*det_adjoint(mat,SDIM); } return sign*value/factorial[dim]; } /********************************************************************* * * function: lagrange_vector_integral_grad() * * purpose: method gradient * */ REAL lagrange_vector_integral_grad(f_info) struct qinfo *f_info; { int i,m,j,k,jj; REAL value = 0.0; REAL val[MAXCOORD]; REAL derivs[MAXCOORD][MAXCOORD]; MAT2D(mat,MAXCOORD,MAXCOORD); REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; int dim = web.dimension; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL weight = sign*gl->gausswt[m]/factorial[dim]; f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( i = 0 ; i < dim ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) mat[i][j] = f_info->sides[m][i][j]; /* mat destroyed by det */ for ( j = 0 ; j < SDIM ; j++ ) { eval_all(METH_INSTANCE(abs(f_info->method))->expr[j],f_info->gauss_pt[m],SDIM, val+j, derivs[j],f_info->id); mat[web.dimension][j] = val[j]; } value += weight*det_adjoint(mat,SDIM); for ( k = 0 ; k < gl->lagpts; k++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( i = 0 ; i < dim ; i++ ) { f_info->grad[k][j] += weight*gl->gpolypart[m][i][k]*mat[j][i]; } for ( k = 0 ; k < gl->lagpts ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) { f_info->grad[k][j] += weight *gl->gpoly[m][k]*derivs[jj][j]*mat[jj][dim]; } } return value; } /********************************************************************* * * function: lagrange_vector_integral_hess() * * purpose: method hessian * */ REAL lagrange_vector_integral_hess(f_info) struct qinfo *f_info; { return lagrange_vector_integral_all(f_info,METHOD_HESSIAN); } REAL lagrange_vector_integral_all(f_info,mode) struct qinfo *f_info; int mode; { int i,m,j,k,jj,ii,kk; REAL value = 0.0; REAL val[MAXCOORD]; REAL derivs[MAXCOORD][MAXCOORD]; MAT3D(seconds,MAXCOORD,MAXCOORD,MAXCOORD); MAT2D(mat,MAXCOORD,MAXCOORD); REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; int dim = web.dimension; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; MAT4D(dethess,MAXCOORD,MAXCOORD,MAXCOORD,MAXCOORD); for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL det; REAL weight = sign*gl->gausswt[m]/factorial[dim]; f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) mat[i][j] = f_info->sides[m][i][j]; /* mat destroyed by det */ for ( j = 0 ; j < SDIM ; j++ ) { eval_second(METH_INSTANCE(abs(f_info->method))->expr[j], f_info->gauss_pt[m],SDIM,val+j, derivs[j],seconds[j],f_info->id); mat[web.dimension][j] = val[j]; } det_hess(mat,dethess,SDIM); det = det_adjoint(mat,SDIM); value += weight*det; if ( mode == METHOD_VALUE ) continue; /* gradient */ for ( k = 0 ; k < gl->lagpts; k++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( i = 0 ; i < dim ; i++ ) { f_info->grad[k][j] += weight*gl->gpolypart[m][i][k]*mat[j][i]; } for ( k = 0 ; k < gl->lagpts ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) { f_info->grad[k][j] += weight *gl->gpoly[m][k]*derivs[jj][j]*mat[jj][dim]; } if ( mode == METHOD_GRADIENT ) continue; /* hessian */ for ( k = 0 ; k < gl->lagpts ; k++ ) for ( kk = 0 ; kk < gl->lagpts ; kk++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) { REAL h = 0.0; int jjj; for ( i = 0 ; i < dim ; i++ ) for ( ii = 0 ; ii < dim ; ii++ ) { h += dethess[i][j][ii][jj] *gl->gpolypart[m][i][k]*gl->gpolypart[m][ii][kk]; } for ( i = 0 ; i < dim ; i++ ) for ( jjj = 0 ; jjj < SDIM ; jjj++ ) h += dethess[i][j][dim][jjj]*gl->gpolypart[m][i][k] *gl->gpoly[m][kk]*derivs[jjj][jj]; for ( ii = 0 ; ii < dim ; ii++ ) for ( jjj = 0 ; jjj < SDIM ; jjj++ ) h += dethess[dim][jjj][ii][jj]*gl->gpolypart[m][ii][kk] *gl->gpoly[m][k]*derivs[jjj][j]; for ( i = 0 ; i < SDIM ; i++ ) h += gl->gpoly[m][k]*gl->gpoly[m][kk]*seconds[i][j][jj] *mat[i][web.dimension]; f_info->hess[k][kk][j][jj] += weight*h; } } return value; } /********************************************************************* lagrange_k_vector_integral method Integral of simple k-vectorfield over element. Edges and facets. Lagrange model *********************************************************************/ REAL lagrange_k_vector_integral_all ARGS((struct qinfo*,int)); /********************************************************************* * * function: lagrange_k_vector_integral() * * purpose: method value * */ REAL lagrange_k_vector_integral(f_info) struct qinfo *f_info; { int i,m,j,k; REAL value=0.0; MAT2D(mat,MAXCOORD,MAXCOORD); int dim = web.dimension; REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; if ( id_type(f_info->id) == EDGE ) { dim = (web.representation==STRING)?1:web.dimension-1; gl = &gauss_lagrange[dim][web.gauss1D_order]; } for ( m = 0 ; m < gl->gnumpts ; m++ ) { for ( i = 0 ; i < dim ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) mat[i][j] = f_info->sides[m][i][j]; for ( k = 0 ; k+dim < SDIM ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) mat[k+dim][j] = eval(METH_INSTANCE(abs(f_info->method))->expr[j+k*SDIM], f_info->gauss_pt[m],NULLID,NULL); value += gl->gausswt[m]*det_adjoint(mat,SDIM); } return sign*value/factorial[dim]; } /********************************************************************* * * function: lagrange_k_vector_integral_grad() * * purpose: method gradient * */ REAL lagrange_k_vector_integral_grad(f_info) struct qinfo *f_info; { int i,m,j,k,jj; REAL value = 0.0; REAL val[MAXCOORD]; REAL derivs[MAXCOORD][MAXCOORD][MAXCOORD]; MAT2D(mat,MAXCOORD,MAXCOORD); REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; int dim = web.dimension; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; if ( id_type(f_info->id) == EDGE ) { dim = (web.representation==STRING)?1:web.dimension-1; gl = &gauss_lagrange[dim][web.gauss1D_order]; } for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL weight = sign*gl->gausswt[m]/factorial[dim]; for ( i = 0 ; i < dim ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) mat[i][j] = f_info->sides[m][i][j]; /* mat destroyed by det */ for ( k = 0 ; k+dim < SDIM ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) { eval_all(METH_INSTANCE(abs(f_info->method))->expr[j+k*SDIM], f_info->gauss_pt[m],SDIM,val+j,derivs[k][j],f_info->id); mat[k+dim][j] = val[j]; } value += weight*det_adjoint(mat,SDIM); for ( k = 0 ; k < gl->lagpts; k++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( i = 0 ; i < dim ; i++ ) { f_info->grad[k][j] += weight*gl->gpolypart[m][i][k]*mat[j][i]; } for ( k = 0 ; k < gl->lagpts ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) for ( i = dim ; i < SDIM ; i++ ) { f_info->grad[k][j] += weight *gl->gpoly[m][k]*derivs[i-dim][jj][j]*mat[jj][i]; } } return value; } /********************************************************************* * * function: lagrange_k_vector_integral_hess() * * purpose: method hessian * */ REAL lagrange_k_vector_integral_hess(f_info) struct qinfo *f_info; { return lagrange_k_vector_integral_all(f_info,METHOD_HESSIAN); } REAL lagrange_k_vector_integral_all(f_info,mode) struct qinfo *f_info; int mode; { int i,m,j,k,jj,ii,kk; REAL value = 0.0; REAL val[MAXCOORD]; REAL derivs[MAXCOORD][MAXCOORD][MAXCOORD]; MAT4D(seconds,MAXCOORD,MAXCOORD,MAXCOORD,MAXCOORD); MAT2D(mat,MAXCOORD,MAXCOORD); REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; int dim = web.dimension; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; MAT4D(dethess,MAXCOORD,MAXCOORD,MAXCOORD,MAXCOORD); int order = METH_INSTANCE(f_info->method)->vec_order; if ( id_type(f_info->id) == EDGE ) { dim = (web.representation==STRING)?1:web.dimension-1; gl = &gauss_lagrange[dim][web.gauss1D_order]; } for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL det; REAL weight = sign*gl->gausswt[m]/factorial[dim]; for ( i = 0 ; i < order ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) mat[i][j] = f_info->sides[m][i][j]; /* mat destroyed by det */ for ( k = 0 ; k+order < SDIM ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) { eval_second(METH_INSTANCE(abs(f_info->method))->expr[j+k*SDIM], f_info->gauss_pt[m],SDIM,val+j,derivs[k][j],seconds[k][j],f_info->id); mat[k+order][j] = val[j]; } det_hess(mat,dethess,SDIM); det = det_adjoint(mat,SDIM); value += weight*det; if ( mode == METHOD_VALUE ) continue; /* gradient */ for ( k = 0 ; k < gl->lagpts; k++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( i = 0 ; i < order ; i++ ) { f_info->grad[k][j] += weight*gl->gpolypart[m][i][k]*mat[j][i]; } for ( k = 0 ; k < gl->lagpts ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) for ( i = order ; i < SDIM ; i++ ) { f_info->grad[k][j] += weight *gl->gpoly[m][k]*derivs[i-order][jj][j]*mat[jj][i]; } if ( mode == METHOD_GRADIENT ) continue; /* hessian */ for ( k = 0 ; k < gl->lagpts ; k++ ) for ( kk = 0 ; kk < gl->lagpts ; kk++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) { REAL h = 0.0; int jjj; for ( i = 0 ; i < order ; i++ ) for ( ii = 0 ; ii < order ; ii++ ) { h += dethess[i][j][ii][jj] *gl->gpolypart[m][i][k]*gl->gpolypart[m][ii][kk]; } for ( i = 0 ; i < order ; i++ ) for ( ii = order ; ii < SDIM ; ii++ ) for ( jjj = 0 ; jjj < SDIM ; jjj++ ) h += dethess[i][j][ii][jjj]*gl->gpolypart[m][i][k] *gl->gpoly[m][kk]*derivs[ii-dim][jjj][jj]; for ( i = order ; i < SDIM ; i++ ) for ( ii = 0 ; ii < order ; ii++ ) for ( jjj = 0 ; jjj < SDIM ; jjj++ ) h += dethess[i][jjj][ii][jj]*gl->gpolypart[m][ii][kk] *gl->gpoly[m][k]*derivs[i-order][jjj][j]; for ( i = 0 ; i < SDIM ; i++ ) for ( ii = order ; ii < SDIM ; ii++ ) h += gl->gpoly[m][k]*gl->gpoly[m][kk]*seconds[ii-order][i][j][jj] *mat[i][ii]; f_info->hess[k][kk][j][jj] += weight*h; } } return value; } /********************************************************************* facet_volume method Integral of vectorfield over facet. nD facet in (n+1)D only. Lagrange model *********************************************************************/ REAL lagrange_facet_volume_all ARGS((struct qinfo*,int)); /********************************************************************* * * function: lagrange_facet_volume() * * purpose: method value * */ REAL lagrange_facet_volume(f_info) struct qinfo *f_info; { int i,m,j; REAL value = 0.0; MAT2D(mat,MAXCOORD,MAXCOORD); int dim = web.dimension; REAL sign = (dim&1) ? -1.0 : 1.0; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; for ( m = 0 ; m < gl->gnumpts ; m++ ) { for ( i = 0 ; i < dim ; i++ ) for ( j = 0 ; j < dim ; j++ ) mat[i][j] = f_info->sides[m][i][j]; value += gl->gausswt[m]*det_adjoint(mat,dim)*f_info->gauss_pt[m][dim]; } return sign*value/factorial[dim]; } /********************************************************************* * * function: lagrange_facet_volume_grad() * * purpose: method gradient * */ REAL lagrange_facet_volume_grad(f_info) struct qinfo *f_info; { int i,m,j,k; REAL value = 0.0; MAT2D(mat,MAXCOORD,MAXCOORD); int dim = web.dimension; REAL sign = (dim&1) ? -1.0 : 1.0; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; REAL z,det; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL weight = sign*gl->gausswt[m]/factorial[dim]; for ( i = 0 ; i < dim ; i++ ) for ( j = 0 ; j < dim ; j++ ) mat[i][j] = f_info->sides[m][i][j]; /* mat destroyed by det */ z = f_info->gauss_pt[m][dim]; det = det_adjoint(mat,dim); value += weight*det*z; for ( k = 0 ; k < gl->lagpts; k++ ) for ( j = 0 ; j < dim ; j++ ) for ( i = 0 ; i < dim ; i++ ) f_info->grad[k][j] += weight*z*gl->gpolypart[m][i][k]*mat[j][i]; for ( k = 0 ; k < gl->lagpts ; k++ ) f_info->grad[k][dim] += weight*gl->gpoly[m][k]*det; } return value; } /********************************************************************* * * function: lagrange_facet_volume_hess() * * purpose: method hessian * */ REAL lagrange_facet_volume_hess(f_info) struct qinfo *f_info; { return lagrange_facet_volume_all(f_info,METHOD_HESSIAN); } REAL lagrange_facet_volume_all(f_info,mode) struct qinfo *f_info; int mode; { int i,m,j,k,jj,ii,kk; REAL sum,value = 0.0; MAT2D(mat,MAXCOORD,MAXCOORD); int dim = web.dimension; REAL sign = (dim&1) ? -1.0 : 1.0; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; MAT4D(dethess,MAXCOORD,MAXCOORD,MAXCOORD,MAXCOORD); for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL det; REAL z = f_info->gauss_pt[m][dim]; REAL weight = sign*gl->gausswt[m]/factorial[dim]; for ( i = 0 ; i < dim ; i++ ) for ( j = 0 ; j < dim ; j++ ) mat[i][j] = f_info->sides[m][i][j]; /* mat destroyed by det */ det_hess(mat,dethess,dim); det = det_adjoint(mat,dim); value += weight*det*z; if ( mode == METHOD_VALUE ) continue; /* gradient */ for ( k = 0 ; k < gl->lagpts; k++ ) for ( j = 0 ; j < dim ; j++ ) for ( i = 0 ; i < dim ; i++ ) f_info->grad[k][j] += weight*z*gl->gpolypart[m][i][k]*mat[j][i]; for ( k = 0 ; k < gl->lagpts ; k++ ) f_info->grad[k][dim] += weight*gl->gpoly[m][k]*det; if ( mode == METHOD_GRADIENT ) continue; /* hessian */ for ( k = 0 ; k < gl->lagpts ; k++ ) for ( kk = 0 ; kk < gl->lagpts ; kk++ ) { if ( dim == 2 ) { f_info->hess[k][kk][0][1] += weight*z* (gl->gpolypart[m][0][k]*gl->gpolypart[m][1][kk] - gl->gpolypart[m][1][k]*gl->gpolypart[m][0][kk]); f_info->hess[k][kk][1][0] += weight*z* (gl->gpolypart[m][1][k]*gl->gpolypart[m][0][kk] - gl->gpolypart[m][0][k]*gl->gpolypart[m][1][kk]); } else if ( dim > 2 ) { for ( j = 0 ; j < dim ; j++ ) { for ( jj = 0 ; jj < dim ; jj++ ) for ( i = 0 ; i < dim ; i++ ) for ( ii = 0 ; ii < dim ; ii++ ) { f_info->hess[k][kk][j][jj] += weight*z*dethess[i][j][ii][jj] *gl->gpolypart[m][i][k]*gl->gpolypart[m][ii][kk]; } } } for ( j = 0 ; j < dim ; j++ ) { for ( i = 0, sum = 0.0 ; i < dim ; i++ ) sum += gl->gpolypart[m][i][k]*mat[j][i]; f_info->hess[k][kk][j][dim] += weight*gl->gpoly[m][kk]*sum; } for ( jj = 0 ; jj < dim ; jj++ ) { for ( ii = 0,sum = 0.0 ; ii < dim ; ii++ ) sum += gl->gpolypart[m][ii][kk]*mat[jj][ii]; f_info->hess[k][kk][dim][jj] += weight*gl->gpoly[m][k]*sum; } } } return value; } evolver-2.30c.dfsg/src/variable.c0000644000175300017530000016241211410765113017154 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakkf, brakke@susqu.edu * *************************************************************/ /*************************************************************** * * File: variable.c * * Purpose: allocate storage for global variables of evolver. * */ #include "include.h" #ifdef __cplusplus extern "C" { #endif char *evolver_version = "2.30c"; char needed_version[30]; #ifdef mpi_evolver char *version = "version 2.30c, January 15, 2008; compiled for mpi."; #else #ifdef SGI_MULTI char *VERSION = "Version 2.30c, January 15, 2008; SGI multiprocessing"; #else #ifdef _WIN64 char *VERSION = "Version 2.30c, January 15, 2008 Windows 64-bit, OpenGL"; #else #ifdef WIN32 char *VERSION = "Version 2.30c, January 15, 2008; Windows 32-bit, OpenGL"; #else char *VERSION = "Version 2.30c, January 15, 2008"; #endif #endif #endif #endif char *typenames[NUMELEMENTS] = {"vertex","edge","facet","body","facetedge"}; REAL factorial[20] = {1.,1.,2.,6.,24.,120.,720.,7*720.,8*7*720.,9*8*7*720., 9.*8*7*7200,9.*8*7*7200*11,9.*8*7*7200*11*12,9.*8*7*7200*11*12*13, 9.*8*7*7200*11*12*13*14,9.*8*7*7200*11*12*13*14*15, 9.*8*7*7200*11*12*13*14*15*16,9.*8*7*7200*11*12*13*14*15*16*17, 9.*8*7*7200*11*12*13*14*15*16*17*18,9.*8*7*7200*11*12*13*14*15*16*17*18*19}; int broken_pipe_flag; /* so output routines will know to quit */ int function_kludge_flag; /* handling errors in function def in datafile */ int this_task = 1; /* machine identifier for MPI; really task number */ /* 1 works as default when not using MPI */ int mpi_subtask_command_flag; /* whether command is running on task as subcommand from master instead of running isolated */ int mpi_local_bodies_flag; #ifdef MPI_EVOLVER int match_id_flag = 1; /* so remote references to locals work */ #else int match_id_flag = 0; /* to make id match datafile number, option -i */ #endif int echo_flag; /* whether to echo stdin; for piped input */ struct optparam_t optparam[MAXOPTPARAM]; int optparamcount; /* number thereof */ REAL **optparam_congrads; /* constraint gradients */ /* controlling edge and facet deletion options */ int star_finagling; /* extra bad configuration detection */ int force_deletion; /* even if it seems a bad idea */ /* my own ctypes */ char kb_upper_array[256]; char kb_lower_array[256]; REAL machine_eps; /* smallest machine resolution at 1.0 */ char loadfilename[PATHSIZE]; /* for LOAD command */ int addload_flag; /* if doing ADDLOAD command */ jmp_buf loadjumpbuf; /* for LOAD command */ int exit_flag; /* whether to end this command interpreter */ int hessian_subshell_flag; /* whether in hessian subshell, so can't menu */ int subshell_depth; /* nesting depth of subshells */ int file_no; /* number of current source file */ int file_no_used; /* number in list */ int file_no_max; /* number allocated */ char **file_names; /* names of source files */ char *curdir; /* current directory */ /* for binary_printf byte order */ int big_endian_flag; int little_endian_flag; struct text_s text_chunks[MAXTEXTS]; int display_text_count; char *current_prompt; /* prompt string being displayed */ char *cmdfilename; /* for saving command line read file */ int uminus_flag; /* whether to interpret " -" as UMINUS */ int facet_general_flag; int everything_quantities_flag; /* for pure quantity version */ int auto_convert_flag = 1; /* whether to automatically convert_to_quantities */ int show_all_quantities; /* to display default quantities also */ int option_q = 0; /* record command line option -q, default off */ int random_seed = 1; /* seed for random number generators */ int keep_macros_flag; /* to preserve macros after datafile. */ int macro_count; /* number of macros defined */ char *macro_subs; /* string space for substitution strings */ struct macro *macros; /* dynamically allocated */ char *warning_messages; /* for storing warning messages */ size_t warning_messages_max; /* allocated bytes for warning_messages */ int warning_messages_new; /* number of new warning messages since last looked */ int warnings_suppressed[MAXSUPPRESS]; int warnings_suppressed_count; int shadow_warn_flag; /* warn if locals shadow existing names */ int do_show_flag; /* for Mac kludge graphics command prompt */ int dont_resize_flag; /* set if datafile specified view matrix */ REAL brightness = 0.65; /* midlevel gray for screen display */ int markedgedrawflag = 0; /* for single-drawing edges */ struct constraint null_constraint; /* non-constraint placeholder */ struct vgradblock *vgradbase; /* allocated list block header start */ int vgradtop; /* number of first free structure */ long vgradmax; /* number allocated */ long vgradlastused; /* number actually used last time round */ int volgrads_every_flag; /* whether recalc volgrads every projection iteration */ int zener_drag_flag; /* whether to do zener drag */ int HOMDIM = 4; REAL **to_focus; /* used only by oglgraph.c at present */ REAL **from_focus; int lazy_transforms_flag; /* let graphics display handle symmetry */ char *dymem; /* dynamic memory region */ int dymemsize; /* size of dynamic memory region */ char *areaname; /* length or area */ int read_command_flag; /* whether commands at end of datafile */ int verb_flag; /* set if lex looking for a verb */ int cond_expr_flag; /* set if parser looking for ':' in conditional expr */ int read_wrap_flag; /* set when reading wraps in datafile */ int quiet_flag; /* whether to display normal output */ int quiet_go_flag; /* whether to display normal g output */ int quiet_load_flag; /* whether to display output while loading file */ int exit_after_error; /* auto exit flag */ int exit_after_warning; /* auto exit flag */ int break_after_warning; /* auto break flag */ int last_error; /* number of last error */ int change_flag; /* set during command that changes surface */ int assigntype; /* type of assignment operator */ char *cmdptr; /* current command or input for parsing */ int memdebug; /* flag for memory debugging in run_checks() */ int single_step_debugging; /* flag for debug stop at next line */ int check_count; /* number of errors returned by run_checks() */ int itdebug; /* flag for iteration debugging */ int nprocs = 1; /* number of parallel processors */ int procs_requested = 1; /* number of processes desired */ REAL proc_total_area[MAXPROCS]; /* for individual processes */ int logfile_flag; /* whether logging in progress */ char logfilename[PATHSIZE]; FILE *logfilefd; int keylogfile_flag; /* whether keylogging in progress */ char keylogfilename[PATHSIZE]; FILE *keylogfilefd; int web_checksum = 0; /* to see if web needs sending */ int dymem_checksum = 0; /* see if dymem needs sending */ int comp_quant_vertex; /* during calc_quant_grad */ int comp_quant_vertexi; /* during calc_quant_hess */ int comp_quant_vertexj; /* during calc_quant_hess */ int comp_quant_type; /* during calc_quant_grad */ int comp_quant_stamp; /* coordination with eval_all and eval_sec */ vertex_id comp_quant_vi; /* during calc_quant_hess */ vertex_id comp_quant_vj; /* during calc_quant_hess */ int gocount = 1; /* number of iterations left */ #if defined(MAC_APP) || defined(MAC_CW) int go_display_flag = 1; /* for displaying each change */ #else int go_display_flag = 0; /* for displaying each change */ #endif int shading_flag = 1; /* for facet shading by orientation */ int color_flag = 1; /* facet coloring by user */ int rgb_colors_flag; /* enabling rgb color scheme */ int edge_rgb_color_attr; /* number of rgb color attribute */ int facet_rgb_color_attr; /* number of rgb color attribute */ int facet_rgb_backcolor_attr; /* number of rgb color attribute */ int smooth_graph_flag; /* whether to smoothly plot in lagrange mode */ int graph_capabilities; /* bits to describe what current graphics device can do */ int edge_alpha_flag; /* whether edges have alpha channel */ int facet_alpha_flag; /* whether facets have alpha channel */ int facetback_alpha_flag; /* whether facetbacks have alpha channel */ int background_color = WHITE; /* graphics background */ int backcull_flag = 0; /* 3D graphics backculling */ int setting_backcull = 0; /* slicing view */ int slice_view_flag; REAL slice_coeff[MAXCOORD+2]; int slice_coeff_global; /* clipping view */ int clip_view_flag; int clip_coeff_global; REAL clip_coeff[MAXCLIPS][MAXCOORD+2]; #ifdef IRIS int gv_binary_flag = 1; /* whether to do geomview in binary */ #else int gv_binary_flag = 0; /* whether to do geomview in binary */ #endif int self_similar_flag; /* for self-similar motion */ REAL string_curve_tolerance; /* quadratic string model smoothness, deg */ int labelflag; /* whether ps doing labels */ /* Following PostScript line width variables relative to page size */ REAL ps_labelsize; /* default label size */ REAL ps_stringwidth; /* default edge width */ REAL ps_fixededgewidth; REAL ps_tripleedgewidth; REAL ps_conedgewidth; REAL ps_bareedgewidth; REAL ps_gridedgewidth; int full_bounding_box_flag; /* for window bounding box */ int geomview_bug_flag; /* for geomview 1.6.1 picking bug */ int gv_pipe[2]; /* for pipe for reading geomview pick commands */ vertex_id *vpicklist; /* for geomview picking */ facet_id *fpicklist; int pickvnum,pickenum,pickfnum,pickbnum; /* geomview picks */ int new_vertex_id,new_edge_id,new_facet_id,new_body_id; /* just created elements */ int gv_vect_start; /* vector vertex start in vpicklist */ int circular_arc_flag; /* whether to draw edges as arcs */ int spherical_arc_flag; /* whether to draw edges as spherical arcs */ element_id junk; /* for MSC bug */ #ifndef PARALLEL_MACHINE element_id xx_id; /* for macro temporary to prevent multiple evaluation */ element_id x1_id,x2_id,x3_id,x4_id,x5_id,x6_id,x7_id,x8_id,x9_id; element_id xa_id,xb_id,xc_id,xd_id,xe_id,xf_id,xg_id,xh_id; /*so nested macros don't tromp each other*/ #endif #ifdef __WIN32__ extern unsigned _stklen = 0x3000; /* get bigger stack */ #endif /* dynamic load library list */ struct dll dll_list[MAX_DLL]; /* global variable stuff */ int dy_global_hash_max; /* allocated entries */ int dy_global_hash_maxfill; /* entries before expanding */ int dy_global_hash_used; /* entries actually in use */ struct global *Globals; /* handy for debugging */ struct global *perm_Globals; /* handy for debugging */ int old_global_count; /* for error recovery */ int old_perm_global_count; /* for error recovery */ int proc_timestamp; /* for ordering procedure definitions */ int perm_flag; /* for whether permanent assignment parsing in place */ int reading_comp_quant_flag; int cur_quant; /* when reading compound quantity */ int compound_hess_flag; /* mode while doing compound hessian */ int quantities_only_flag; /* for using named quantities only */ int calc_quant_flag; /* set when quantity calculation underway */ int gravity_quantity_num; /* number of quantity for default gravity */ int gap_quantity_num; /* number of quantity for default gap energy */ int default_area_quant_num; /* number of quantity for default area */ char length_method_name[100]; /* for replacing default */ int length_method_number; /* for replacing default */ char area_method_name[100]; /* for replacing default */ char volume_method_name[100]; /* for replacing default */ int dirichlet_flag; /* to do area hessian with Dirichlet hessian */ int sobolev_flag; /* to do area hessian with Sobolev hessian */ FILE *logfd = NULL; /* command log file */ int read_depth; /* for nested reads */ int include_depth; /* for #include */ FILE *commandfd = NULL; /* command input file */ struct cmdfile cmdfile_stack[NESTDEPTH],datafile_stack[NESTDEPTH]; char *history_space; /* for command history */ int history_offsets[MAXHISTORY]; int history_number; /* number of current command */ int history_count; /* number in list */ char fulltext[MAXCMDSIZE+5]; /* for full text of commands */ size_t fulltextsize; /* length of command */ char datafilename[PATHSIZE]; /* current datafile name */ char filename[PATHSIZE]; /* file name in command */ int datafile_flag; /* 1 for datafile, 0 for command so expression parser knows what's up */ int datafile_input_flag; /* whether further input available from datafile */ int topflag; /* 1 while in datafile top section */ int backquote_flag; /* 1 while in backquoted command; kludge */ int lists_flag; /* set when parsing space-separated lists */ int const_expr_flag; /* 1 for const_expr, 0 for command so expression parser knows what's up */ int boundary_expr_flag; /* so parser knows when parsing boundary */ int use_given_id; /* iterator expression should not use local, for SHOW_ */ /* for redefining single letter commands */ struct expnode single_redefine[128]; extern int reading_elements_flag; /* so parser knows attributes should not be accepted in expressions */ FILE *data_fd; FILE *outfd; /* where normal output is to go */ int estimate_flag; /* for toggling estimate of energy decrease */ REAL estimated_change; /* stored result */ int autorecalc_flag; /* for toggling autorecalc after variable assign */ int verbose_flag; /* for lots of messages */ int parens; /* level of parenthesis nesting */ int brace_depth; /* level of brace nesting */ int in_quote; /* toggle for lexer */ int in_function; /* toggle for lexer */ int in_comment; /* toggle for lexer */ int in_control_structure; /* toggle for lexer */ int autopop_flag; /* whether to do autopopping */ int autopop_quartic_flag; /* scale = (length)^4, say surface diffusion */ int immediate_autopop_flag; /* set if pop before motion */ int pop_disjoin_flag; /* whether cones to be disjoined rather than merged */ int pop_enjoin_flag; /* whether cones to be enjoined rather than disjoined */ int pop_to_edge_flag; /* control which way popping goes */ int pop_to_face_flag; /* control which way popping goes */ int autochop_flag; /* whether to do autochopping */ REAL autochop_length; /* max edge length for autochop */ int autopop_count; /* number of edges found */ int autochop_count; /* number of edges found */ int kraynikpopvertex_flag; /* for special popping of a certain cone */ int kraynikpopedge_flag; /* for special popping of a certain edges */ int effective_area_flag; /* use quadratic form for area around vertex */ int old_area_flag; /* on for using old effective area */ int runge_kutta_flag; /* whether to use runge-kutta method for motion */ REAL total_time; /* total scale factor */ REAL star_fraction; /* weighting factor for star around vertices */ int area_fixed_flag; /* for fixed area constraint */ REAL area_fixed_target; /* target value for fixed area */ REAL area_fixed_pressure; /* Lagrange multiplier */ int hessian_by_diff_flag; /* for crude hessian */ int hessian_quiet_flag=1; /* 1 to suppress hessian warnings */ int hessian_normal_flag=1; /* 1 for hessian constrained to normal */ int hessian_normal_perp_flag; /* 1 for hessian metric to normal */ int hessian_special_normal_flag; /* for user spec of Hessian direction */ struct expnode hessian_special_normal_expr[MAXCOORD]; int hessian_normal_one_flag; /* 1 for hessian constrained to 1D normal */ int hessian_double_normal_flag; /* for double dimension perturbation */ REAL hessian_slant_cutoff; /* for treating constrained vertices as fixed with hessian_normal */ int hessian_linear_metric_flag; /* linear interp dot product */ REAL linear_metric_mix = .50; /* proportion of linear interp metric */ REAL quadratic_metric_mix = 1.0; /* proportion of quadratic interp metric */ int min_square_grad_flag = 0; /* what to minimize in hessian_line_seek */ int hess_move_con_flag=1; /* whether to project to global constraints in move */ /* projecting seems to be good idea */ REAL last_hessian_scale; /* from hessian_line_seek() */ REAL last_eigenvalue; /* from eigenprobe and stuff */ struct hess_entry *hashtable; /* the table */ int table_size; /* hashtable size */ int hash_per_row = 5; /* for estimating table size */ int vhead_attr; /* number of vertex attribute for vhead index */ int bhead_attr; /* number of body attribute for vhead index */ /* for controlling steps */ int rhs_flag; /* filling in right hand side */ int hess_flag; /* filling in hessian matrix */ int negdiag; /* C index of most negative entry on diagonal */ struct linsys Met; /* vector-to-form metric */ /* Flag for doing sparse rank update for function quantities, to avoid dense hessians */ int quantity_function_sparse_flag; #if defined(USEYSMP) || defined(MPI_EVOLVER) int ysmp_flag=YSMP_FACTORING; /* set if doing Yale Sparse Matrix version */ /* factor matrix */ void (*sp_factor_func)ARGS((struct linsys *)) = ysmp_factor; /* solve given rhs */ void (*sp_solve_func)ARGS((struct linsys *,REAL *,REAL *)) = ysmp_solve; /* solve multiple given rhs */ void (*sp_solve_multi_func)ARGS((struct linsys*,REAL**,REAL**,int)) = ysmp_solve_multi; /* matrix inner product with hessian inverse as metric */ void (*sp_CHinvC_func)ARGS((struct linsys *)) = sp_CHinvC; #else /* use mindeg since doing sparse_constraints */ int ysmp_flag=MINDEG_FACTORING; /* Hessian problem in Borland LONGDOUBLE version */ /* factor matrix */ void (*sp_factor_func)ARGS((struct linsys *)) = xmd_factor; /* solve given rhs */ void (*sp_solve_func)ARGS((struct linsys *,REAL *,REAL *)) = xmd_solve; /* solve multiple given rhs */ void (*sp_solve_multi_func)ARGS((struct linsys*,REAL**,REAL**,int)) = xmd_solve_multi; /* matrix inner product with hessian inverse as metric */ void (*sp_CHinvC_func)ARGS((struct linsys *)) = sp_CHinvC; #endif /* for mindeg control */ int mindeg_debug_level; int mindeg_margin = 5; int mindeg_min_region_size; int sparse_constraints_flag = 0; /* whether to store constraint gradients in sparse format */ int blas_flag; /* whether to use BLAS */ int augmented_hessian_flag = -1; /* whether to use augmented Hessian */ /* -1 for unset */ int augmented_hessian_mode; /* set during factoring */ REAL BKalpha = 0.525; /* single/REAL pivot ratio */ int BK_flag=0; /* for enabling Bunch-Kaufman version of sparse factoring */ /* sparse matrix function pointers, for easy switching among algorithms */ /* multiply vector by original sparse matrix */ void (*sp_mul_func)ARGS((struct linsys *, REAL*,REAL*)) = bk_mul; /* convert raw Hessian data to standard sparse format */ void (*sp_AIJ_setup_func)ARGS((int,struct linsys*)) = bk_AIJ_setup; /* set up matrices needed for handling constraints */ void (*sp_constraint_setup_func)ARGS((int,struct linsys *)) = bk_constraint_setup; /* set up projection to constraints using hessian metric */ void (*sp_hess_project_setup_func)ARGS((struct linsys *)) = BK_hess_project_setup; /* optional ordering of vertices */ void (*sp_ordering_func)ARGS((struct linsys *)) = NULL; int eigen_pos,eigen_neg,eigen_zero; /* inertia of shifted hessian */ int pos_def_warning_flag; /* to suppress a proliferation of warnings */ int make_pos_def_flag; /* force hessian to positive definiteness */ int mat_index; /* number of negatives on diagonal */ int mat_null; /* number of zeroes on diagonal */ REAL hessian_epsilon = 1e-10; /* cutoff for diagonal elements */ REAL hessian_epsilon_default = 1e-10; int post_project_flag; /* project to quant constr after each motion */ int normal_motion_flag; /* for motion allowed only along normals */ pt_type *vertex_normals; /* for storage of normals by ordinal */ struct expnode mobility_formula; int mobility_flag; /* whether mobility in effect */ struct expnode mobility_tensor[MAXCOORD][MAXCOORD]; int mobility_tensor_flag; /* whether tensor mobility in effect */ int check_pinning_flag; /* for vertices changing constraints */ int ackerman_flag; /* whether doing phase space motion */ int one_sided_present; /* whether any one-sided constraints exist */ int one_sided_lagrange_attr; int raw_velocity_attr; /* for use by one-sided constraints */ /* for Dennis DeTurck unit normal motion */ int unit_normal_flag; REAL deturck_factor = 1.0; /* weight for unit normal */ int int_val; REAL real_val; int attr_etype; /* for ATTRIBUTE parse */ int subtype; /* for INDEXED_SUBTYPE parse */ int coord_num; /* communication from parser to makenode */ int tok; /* current token from yylex */ int aggrtype; /* aggregate type being parsed */ int aggregate_depth; /* nesting depth of aggregate loops */ int attr_kind; /* kind of attribute being parsed */ struct sym *elsym; /* name of element during parsing */ struct sym *yysym; /* name of identifier from lex */ char *default_name = "q_id"; /* for unnamed elements */ char last_name[50]; /* name of last element generator */ char idname[35]; /* for saving yytext */ char set_extra_name[100]; /* for saving name */ /* useful for debugging */ struct extra *Extras[NUMELEMENTS]; int F_TAG_ATTR; int F_PHASE_ATTR; int B_PHASE_ATTR; int V_BOUNDARY_ATTR; int E_BOUNDARY_ATTR; int F_BOUNDARY_ATTR; struct expnode torus_period_expr[MAXCOORD][MAXCOORD]; struct expnode torus_display_period_expr[MAXCOORD][MAXCOORD]; int torus_display_mode; /* default, raw, connected, clipped */ /* squared curvature as part of energy */ struct v_curve_t *v_curve; struct e_curve_t *e_curve; int sqcurve_ignore_constr; /* set if to count fixed and constrained verts */ int square_curvature_flag; /* set if to be counted */ int square_curvature_param; /* which parameter for modulus */ int mean_curvature_param; /* which parameter for modulus */ int mean_curv_int_quantity_num; /* for everything quantities */ int sq_mean_curv_quantity_num; /* for everything quantities */ int kusner_flag; /* set for edge square curvature */ int assume_oriented_flag; /* for orientation checking */ int boundary_curvature_flag; /* whether to include boundary vertices */ int conf_edge_curv_flag; /* set for conformal edge curvature squared */ int sqgauss_flag; /* for squared gaussian curvature */ int sqgauss_param; /* which parameter for modulus */ int normal_sq_mean_curvature_mi = -1; /* which is which */ int eff_area_sq_mean_curvature_mi = -1; int sq_mean_curvature_mi = -1; int mix_sq_mean_curvature_mi = -1; int star_normal_sq_mean_curvature_mi; int star_perp_sq_mean_curvature_mi; int star_eff_area_sq_mean_curvature_mi; int star_sq_mean_curvature_mi; int h0_attr; /* attribute number for h_zero, if any */ REAL target_length; /* for string model */ int check_increase_flag; /* to detect blowups */ int approx_curve_flag; /* if approximate curvature in effect */ int mean_curv_int_flag; /* for unsquared mean curvature */ int normal_curvature_flag; /* choice of curvature formula */ int div_normal_curvature_flag; /* choice of curvature formula */ int marked_edge_attr; REAL *f_sums; /* facet_knot_energy, for sums to all other vertices */ /* homothety target value, set when homothety toggled on */ REAL homothety_target; int scrollbuffersize=25; /* output console lines */ char *msg; /* for constructing user messages */ int msgmax; /* length allocated */ char errmsg[ERRMSGSIZE]; /* for kb_error() routine */ jmp_buf jumpbuf[MAXCMDDEPTH]; /* for error recovery */ jmp_buf cmdbuf; /* for command error recovery */ jmp_buf m_jumpbuf[MAXPROCS]; /* for multiproc error recovery */ jmp_buf graphjumpbuf; /* for errors during MS graphing */ #ifdef PTHREADS pthread_t draw_thread_id; /* for graphics thread */ #else unsigned int draw_thread_id; /* for graphics thread */ #endif int this_thread; /* debugging THREADBLOCK */ int parse_error_flag; /* set when parser hits error */ int recovery_flag; /* set while recovering from parsing error */ int parse_errors; /* for counting errors */ int breakflag; /* set by user interrupt */ int iterate_flag; /* so handler knows when iteration in progress */ struct oldcoord saved; /* for old coordinates */ /* conjugate gradient stuff */ int conj_grad_flag; /* whether conjugate gradient in effect */ REAL cg_oldsum; /* total grad*grad from previous step */ REAL (*cg_hvector)[MAXCOORD] = NULL; /* saved direction vector */ REAL cg_gamma; /* direction adjustment factor */ int ribiere_flag; /* to do Polak-Ribiere version */ element_id *el_list[NUMELEMENTS]; /* for list of active elements */ long global_timestamp; /* universal clock */ long web_timestamp; /* when stuff changed */ long graph_timestamp; /* so graph routines know when surface changed */ long top_timestamp; /* timestamp for topology changes */ long vedge_timestamp = -1; /* for vertex edgelist currency */ long vfacet_timestamp = -1; /* for vertex facetlist currency */ long bfacet_timestamp = -1; /* for body facetlist currency */ long fixed_volume_timestamp; /* for volume calculation */ long info_volume_timestamp; /* for volume calculation */ long reset_timestamp; /* when surface loaded */ int need_fe_reorder_flag; /* setting causes recalc to fe_reorder */ int parallel_update_flag[NUMELEMENTS]; /* set when element info changed */ /* graphing flags */ int init_flag; /* whether graphics initialized */ int bdry_showflag = 1; /* whether to show facets on boundary */ int no_wall_flag; /* whether to suppress wall facets */ int normflag = 0; int thickenflag = 0; int innerflag = 0; int outerflag = 0; int colorflag = 0; int OOGL_flag = 0; int geomview_flag; /* whether geomview initialized */ int geompipe_flag; /* for pipe only */ int edgeshow_flag = 1; /* whether to show edges of facets */ int triple_edgeshow_flag; /* whether to show triple edges */ REAL thickness = 0.0; /* for thickening double-sided surfaces */ int user_thickness_flag = 0; /* whether user has specified thickness */ /* edge widths */ /* in order BARE_EDGE, FIXED_EDGE, CONSTRAINT_EDGE, BOUNDARY_EDGE, SINGLE_EDGE, TRIPLE_EDGE, and other */ REAL pswidths[] = {0.005,0.004,0.004,0.004,0.004,0.003,0.002}; REAL xwidths[] = {0.001,0.001,0.001,0.001,0.001,0.001,0.001}; REAL otherwidths[] = {0.005,0.005,0.005,0.005,0.005,0.005,0.005}; REAL *edgewidths=otherwidths; int bare_edge_count; REAL facet_alpha = 1.0; /* global transparency */ int view_4D_flag = 0; /* 0 for doing 3D xyz projection graphics, */ /* 1 for outputting 4D graphics */ IColor rgb_colors[16] = { /* rgba */ {0.0,0.0,0.0,1.},{0.0,0.0,1.,1.}, {0.0,1.,0.0,1.},{0.0,1.,1.,1.}, {1.,0.0,0.0,1.},{1.,0.0,1.,1.}, {1.,0.5,0.,1.},{.6,.6,.6,1.},{.3,.3,.3,1.},{.3,.8,1.,1.}, {.5,1.,.5,1.}, {.5,1.,1.,1.},{1.,.5,.5,1.},{1.,.5,1.,1.},{1.,1.,.0,1.},{1.,1.,1.,1.} }; /* query variables */ int condition_flag; /* whether query has condition expression */ struct expnode *show_expr[NUMELEMENTS]; /* for element show expressions */ struct expnode show_command[NUMELEMENTS]; /* for dump */ struct expnode show_expr_table[NUMELEMENTS]; /* actual expressions */ int celement; /* type of element for query */ int query_intval; REAL query_realval; int set_query_type; ATTR set_query_attr; int query_coord; /* graphing stuff */ char cmapname[100]; /* colormap file name */ maprow *colormap; /* rgba colormap, values 0 to 1 */ int fillcolor; /* current polygon fill color */ int box_flag = 0; /* whether or not to show outline box */ int ridge_color_flag; /* whether to differently color */ int visibility_test; /* whether to do after depth sort */ REAL overall_size; /* for anybody who wants to know how big */ /* bounding box, for PostScript */ REAL bbox_minx,bbox_miny,bbox_maxx,bbox_maxy; int need_bounding_box; /* flag to tell painter.c to calculate */ /* clip window for PostScript and painter algorithms */ REAL minclipx=-1.5,maxclipx=1.5,minclipy=-1.5,maxclipy=1.5; /* 3D bounding box */ REAL bounding_box[MAXCOORD][2]; /* vertex for zooming in on; default is first one read in */ int zoom_number = 1; /* for inner clipping for making zoom pictures */ int inner_clip_flag = 0; /* don't clip by default */ REAL inner_clip_rad = 0.0; /* show everything by default */ REAL volume_factorial = 1.0; /* simplex volume factor */ int subsimplex[1< 0) { goto L100; } /* ----SOLVE SYSTEM OF LINEAR EQUATIONS MX = B */ L2: if ((*path - 1) * (*path - 2) * (*path - 3) * (*path - 7) != 0) { goto L3; } if (umax <= 0) { goto L110; } if ( *path == 7 ) /* K.B. */ ysmp_negvector(n, &p[1], &rsp[d], &isp[iju], &isp[ju], &isp[iu], &rsp[u], &z[1], &b[1], &rsp[tmp]); else sns_(n, &p[1], &rsp[d], &isp[iju], &isp[ju], &isp[iu], &rsp[u], &z[1], &b[1], &rsp[tmp]); L3: return 0; /* ** ERROR -- ERROR DETECTED IN SSF, SNF, OR SNS */ L100: return 0; /* ** ERROR -- INSUFFICIENT STORAGE */ L110: *flag_ = *n * 10 + 1; return 0; /* ** ERROR -- ILLEGAL PATH SPECIFICATION */ L111: *flag_ = *n * 11 + 1; return 0; } /* sdrvmd_ */ /* *********************************************************************** */ /* *********************************************************************** */ /* NUMERICAL FACTORIZATION OF SYMMETRIC MATRICES */ /* *********************************************************************** */ /* ==================== change #1 (replacement) =====================1 */ /* WAS: */ /* SNF -- NUMERICAL UT-D-U FACTORIZATION OF SPARSE SYMMETRIC POSITIVE */ /* DEFINITE MATRIX */ /* SUBROUTINE SNF */ /* ===================================================================== */ /* SNFMOD -- NUMERICAL FACTORIZATION OF SPARSE SYMMETRIC MATRICES M BY */ /* THE GILL/MURRAY/WRIGHT MODIFIED CHOLESKY FACTORIZATION (GMW */ /* MCF) WITHOUT PIVOTING. THE FACTORIZATION PRODUCES U,D, AND */ /* E SO THAT M + E = UT-D-U, WHERE E AND D ARE DIAGONAL */ /* MATRICES. THIS ROUTINE IS A MODIFICATION OF THE YSMP */ /* routine SNF. ALL CHANGES ARE INDICATED. */ /* Subroutine */ int snfmod_(n, p, ip, ia, ja, a, d, iju, ju, iu, u, umax, il, jl, flag_, emax) integer *n, *p, *ip, *ia, *ja; doublereal *a, *d; integer *iju, *ju, *iu; doublereal *u; integer *umax, *il, *jl, *flag_; doublereal *emax; { /* System generated locals */ integer i__1, i__2; doublereal d__1, d__2, d__3; /* Local variables */ STATIC integer jmin, jmax; STATIC doublereal zero; STATIC integer irow, i, j, k; STATIC doublereal sgamma, ukidi, bound; STATIC integer kkmin, kkmax, nexti, jumuj; STATIC doublereal dk; STATIC integer kk, vj; STATIC doublereal xi; STATIC integer mu; STATIC doublereal eltnew, del; STATIC integer ili; STATIC doublereal elt, eps, xin, elt2, eps1; mat_index = 0 ; /* number of negatives on diagonal. K.B.*/ mat_null = 0 ; /* number of zeroes on diagonal. K.B.*/ pos_def_warning_flag = 0; /* K.B. */ /* ==================================================================end */ /* ADDITIONAL PARAMETERS */ /* IL - INTEGER ONE-DIMENSIONAL WORK ARRAY; DIMENSION = N */ /* JL - INTEGER ONE-DIMENSIONAL WORK ARRAY; DIMENSION = N */ /* DEFINITIONS OF INTERNAL PARAMETERS (DURING K-TH STAGE OF ELIMINATION) */ /* (D(I),I=K,N) CONTAINS THE K-TH ROW OF U (EXPANDED) */ /* IL(I) POINTS TO THE FIRST NONZERO ELEMENT IN COLUMNS K,...,N OF */ /* ROW I OF U */ /* JL CONTAINS LISTS OF ROWS TO BE ADDED TO UNELIMINATED ROWS -- */ /* I GE K => JL(I) IS THE FIRST ROW TO BE ADDED TO ROW I */ /* I LT K => JL(I) IS THE ROW FOLLOWING ROW I IN SOME LIST OF ROWS */ /* IN EITHER CASE, JL(I) = 0 INDICATES THE END OF A LIST */ /* EMAX is returned as max diagonal addition -- RFA, August 1991 */ /* ----------------------------------------------------------------------- */ /* REAL A(1), D(1), U(1), DK, UKIDI */ /* ==================== change #2 (insertion) =======================2 */ /* real GAMMA,XI,XIN,EPS,DEL,EPS1,ELT,ELT2,ELTNEW, */ /* * BOUND,W,EMAX,EK,ZERO */ /* Parameter adjustments */ --jl; --il; --u; --iu; --ju; --iju; --d; --a; --ja; --ia; --ip; --p; /* Function Body */ *flag_ = 0; zero = 0.; *emax = zero; /* ==================================================================end */ /* ----CHECK FOR SUFFICIENT STORAGE FOR U */ if (iu[*n + 1] - 1 > *umax) { goto L107; } if ( !hessian_quiet_flag ) printf("ysmp fill: %d\n",iu[*n+1]); /* ----INITIALIZATION */ i__1 = *n; for (k = 1; k <= i__1; ++k) { d[k] = 0.; /* L1: */ jl[k] = 0; } /* ==================== change #3 (insertion) =======================3 */ /* Calculate GAMMA and XI, the largest magnitudes of the diag. and off- */ /* diag. elements, respectively. When the diag. elts. are stored first */ /* in A in each row (PATH = 4 or 5 in ODRV), GAMMA=max(GAMMA,A(IA(i))), */ /* i=1,...,IA(n+1)-1. We assume that this IS the case. (If this were */ /* later changed, then for each row I we would have to loop through KK */ /* = KKMIN,..., KKMAX where KKMIN = IA(I), KKMAX = IA(I+1)-1, and test */ /* whether I = JA(KK), ie. row index = column index. If this equality */ /* holds, the element is a diagonal). Then calculate DEL and BOUND: */ /* DEL = max ( max(XI,GAMMA)*EPS, EPS) where EPS is a small given */ /* number, and BOUND = max ( XI/N, GAMMA, EPS). */ /* ===================================================================== */ eps = 1e-6; sgamma = zero; xi = zero; i__1 = *n; for (irow = 1; irow <= i__1; ++irow) { /* Computing MAX */ d__2 = sgamma, d__3 = (d__1 = a[ia[irow]], fabs(d__1)); sgamma = max(d__2,d__3); kkmin = ia[irow] + 1; kkmax = ia[irow + 1] - 1; if (kkmin > kkmax) { goto L21; } i__2 = kkmax; for (kk = kkmin; kk <= i__2; ++kk) { /* Computing MAX */ d__2 = xi, d__3 = (d__1 = a[kk], fabs(d__1)); xi = max(d__2,d__3); /* L20: */ } L21: ; } eps1 = max(sgamma,xi) * eps; del = max(eps,eps1); xin = (doublereal) (*n); xin = xi / xin; /* Computing MAX */ d__1 = max(sgamma,xin); bound = max(d__1,eps); /* ==================================================================end */ /* ----FOR EACH ROW K */ i__1 = *n; for (k = 1; k <= i__1; ++k) { /* ------INITIALIZE K-TH ROW WITH ELEMENTS NONZERO IN ROW P(K) OF M */ /* L3: */ jmin = ia[p[k]]; jmax = ia[p[k] + 1] - 1; if (jmin > jmax) { goto L5; } i__2 = jmax; for (j = jmin; j <= i__2; ++j) { vj = ip[ja[j]]; if (k <= vj) { d[vj] = a[j]; } /* L4: */ } /* ------MODIFY K-TH ROW BY ADDING IN THOSE ROWS I WITH U(I,K) NE 0 */ /* ------FOR EACH ROW I TO BE ADDED IN */ L5: dk = d[k]; i = jl[k]; L6: if (i == 0) { goto L9; } nexti = jl[i]; /* --------COMPUTE MULTIPLIER AND UPDATE DIAGONAL ELEMENT */ ili = il[i]; ukidi = -u[ili] * d[i]; dk += ukidi * u[ili]; u[ili] = ukidi; /* --------ADD MULTIPLE OF ROW I TO K-TH ROW ... */ jmin = ili + 1; jmax = iu[i + 1] - 1; if (jmin > jmax) { goto L8; } mu = iju[i] - iu[i]; i__2 = jmax; for (j = jmin; j <= i__2; ++j) { /* L7: */ d[ju[mu + j]] += ukidi * u[j]; } /* --------... AND ADD I TO ROW LIST FOR NEXT NONZERO ENTRY */ il[i] = jmin; j = ju[mu + jmin]; jl[i] = jl[j]; jl[j] = i; L8: i = nexti; goto L6; /* ==================== change #4 (replacement) =====================4 */ /* WAS: */ /* ------CHECK FOR ZERO PIVOT */ /* 9 IF (DK.EQ.0) GO TO 108 */ /* ===================================================================== */ /* STATEMENT 9 ABOVE WILL BE MODIFIED TO RESET Dk IN THE EVENT THE */ /* THE MATRIX IS NOT SUFF. POSITIVE-DEFINITE. NOTE THAT EVEN WHEN Dk>0, */ /* IT MAY BE MODIFIED IF THE MATRIX IS NOT POS. DEF! */ /* Dk is set as: Dk = MAX ( ABS(Dk), DEL, (ELT**2)/BOUND), where */ /* ELT is the largest magnitude among the elements in the Kth row of U. */ /* This restriction guarantees that all elts. of D are strictly positive */ /* and that the elts. of the factors satisfy a uniform bound. */ /* [ Recall that we work with the auxiliary quantities Vik = Uik * Dk. */ /* The bound we want to impose on the elts. of U, */ /* ( max(Uik)**2 ) * Dk <= BOUND, is equivalent to */ /* ( max(Vik)**2 ) / Dk <= BOUND, or */ /* Dk >= (max(Vik)**2) / BOUND.) */ /* The value for ELT = max(Vik), max over i for fixed k, is found by */ /* looping through the appropriate elements of U. These elements */ /* are currently stored in part of D. ] */ /* ===================================================================== */ L9: /* ===================================================================== */ elt = zero; jmin = iu[k]; jmax = iu[k + 1] - 1; mu = iju[k] - jmin; if (jmin > jmax) { goto L28; } i__2 = jmax; for (j = jmin; j <= i__2; ++j) { eltnew = (d__1 = d[ju[mu + j]], fabs(d__1)); elt = max(elt,eltnew); /* L26: */ } L28: elt2 = elt * elt; #ifdef OLDWAY /* Computing MAX */ d__1 = fabs(dk), d__1 = max(d__1,del), d__2 = elt2 / bound; dk = max(d__1,d__2); ek = dk - w; if (ek > *emax) { *emax = ek; *flag_ = -p[k]; } #else /* don't try to make pos def; just keep diag nonzero */ /* K.B. 8/7/93 */ if ( fabs(dk) <= hessian_epsilon ) { sprintf(msg,"Internal error: sdrv: Diag[%d] = %g; max in row: %g; adding %g",k,(DOUBLE)dk,(DOUBLE)elt,(DOUBLE)hessian_epsilon); if ( !hessian_quiet_flag ) kb_error(1647,msg,WARNING); dk = hessian_epsilon; mat_null++; } /* K.B. 12/28/93 */ if ( dk < -hessian_epsilon ) { mat_index++; sprintf(errmsg,"sdrv: Intermediate matrix not positive definite. Diag[%d] = %g;",k,(DOUBLE)dk); if ( dk < *emax ) { *emax = dk; *flag_ = -p[k]; } if ( make_pos_def_flag ) { d__1 = fabs(dk), d__1 = max(d__1,del), d__2 = elt2 / bound; dk = max(d__1,d__2); sprintf(errmsg+strlen(msg)," Forcing positive to %g.",(DOUBLE)dk); } if (!pos_def_warning_flag) if ( !hessian_quiet_flag ) kb_error(1648,errmsg,WARNING); pos_def_warning_flag = 1; } #endif /* =================================================================e nd */ /* L30: */ /* ------SAVE DIAGONAL ELEMENT */ d[k] = 1 / dk; /* ------SAVE NONZERO ENTRIES IN K-TH ROW OF U ... */ jmin = iu[k]; jmax = iu[k + 1] - 1; if (jmin > jmax) { goto L11; } mu = iju[k] - jmin; i__2 = jmax; for (j = jmin; j <= i__2; ++j) { jumuj = ju[mu + j]; u[j] = d[jumuj]; /* L10: */ d[jumuj] = 0.; } /* ------... AND ADD K TO ROW LIST FOR FIRST NONZERO ENTRY IN K-TH ROW */ il[k] = jmin; i = ju[mu + jmin]; jl[k] = jl[i]; jl[i] = k; L11: ; } /* uncomment next line to print out the largest diagonal modification */ /* IF (FLAG .LT. 0) WRITE (6,800) EMAX,-FLAG */ /* L800: */ /* ==================== change #5 (deletion) ========================5 */ /* WAS: FLAG = 0 */ /* ==================================================================end */ return 0; /* ** ERROR -- INSUFFICIENT STORAGE FOR U */ L107: *flag_ = *n * 7 + 1; return 0; } /* snfmod_ */ /* *********************************************************************** */ /* SSF -- SYMBOLIC UT-D-U FACTORIZATION OF SPARSE SYMMETRIC MATRIX */ /* *********************************************************************** */ /* Subroutine */ int ssf_(n, p, ip, ia, ja, iju, ju, iu, jumax, q, mark, jl, flag_) integer *n, *p, *ip, *ia, *ja, *iju, *ju, *iu, *jumax, *q, *mark, *jl, *flag_; { /* System generated locals */ integer i__1, i__2, i__3; /* Local variables */ STATIC integer jmin, jmax, lmax, i, j, k, m, jumin, juptr, vj, qm; STATIC logical clique; STATIC integer tag, lui, luk; /* ADDITIONAL PARAMETERS */ /* Q - INTEGER ONE-DIMENSIONAL WORK ARRAY; DIMENSION = N */ /* MARK - INTEGER ONE-DIMENSIONAL WORK ARRAY; DIMENSION = N */ /* JL - INTEGER ONE-DIMENSIONAL WORK ARRAY; DIMENSION = N */ /* DEFINITIONS OF INTERNAL PARAMETERS (DURING K-TH STAGE OF ELIMINATION) */ /* Q CONTAINS AN ORDERED LINKED LIST REPRESENTATION OF THE NONZERO */ /* STRUCTURE OF THE K-TH ROW OF U -- */ /* Q(K) IS THE FIRST COLUMN WITH A NONZERO ENTRY */ /* Q(I) IS THE NEXT COLUMN WITH A NONZERO ENTRY AFTER COLUMN I */ /* IN EITHER CASE, Q(I) = N+1 INDICATES THE END OF THE LIST */ /* JL CONTAINS LISTS OF ROWS TO BE MERGED INTO UNELIMINATED ROWS -- */ /* I GE K => JL(I) IS THE FIRST ROW TO BE MERGED INTO ROW I */ /* I LT K => JL(I) IS THE ROW FOLLOWING ROW I IN SOME LIST OF ROWS */ /* IN EITHER CASE, JL(I) = 0 INDICATES THE END OF A LIST */ /* MARK(I) IS THE LAST ROW STORED IN JU FOR WHICH U(MARK(I),I) NE 0 */ /* JUMIN AND JUPTR ARE THE INDICES IN JU OF THE FIRST AND LAST */ /* ELEMENTS IN THE LAST ROW SAVED IN JU */ /* LUK IS THE NUMBER OF NONZERO ENTRIES IN THE K-TH ROW */ /* -----------------------------------------------------------------------*/ /* ----INITIALIZATION */ /* Parameter adjustments */ --jl; --mark; --q; --iu; --ju; --iju; --ja; --ia; --ip; --p; /* Function Body */ jumin = 1; juptr = 0; iu[1] = 1; i__1 = *n; for (k = 1; k <= i__1; ++k) { mark[k] = 0; /* L1: */ jl[k] = 0; } /* ----FOR EACH ROW K */ i__1 = *n; for (k = 1; k <= i__1; ++k) { luk = 0; q[k] = *n + 1; tag = mark[k]; clique = FALSE_; if (jl[k] != 0) { clique = jl[jl[k]] == 0; } /* ------INITIALIZE NONZERO STRUCTURE OF K-TH ROW TO ROW P(K) OF M */ jmin = ia[p[k]]; jmax = ia[p[k] + 1] - 1; if (jmin > jmax) { goto L4; } i__2 = jmax; for (j = jmin; j <= i__2; ++j) { vj = ip[ja[j]]; if (vj <= k) { goto L3; } qm = k; L2: m = qm; qm = q[m]; if (qm < vj) { goto L2; } if (qm == vj) { goto L102; } ++luk; q[m] = vj; q[vj] = qm; if (mark[vj] != tag) { clique = FALSE_; } L3: ; } /* ------IF EXACTLY ONE ROW IS TO BE MERGED INTO THE K-TH ROW AND THERE IS */ /* ------A NONZERO ENTRY IN EVERY COLUMN IN THAT ROW IN WHICH THERE IS A */ /* ------NONZERO ENTRY IN ROW P(K) OF M, THEN DO NOT COMPUTE FILL-IN, JUST */ /* ------USE THE COLUMN INDICES FOR THE ROW WHICH WAS TO HAVE BEEN MERGED */ L4: if (! clique) { goto L5; } iju[k] = iju[jl[k]] + 1; luk = iu[jl[k] + 1] - (iu[jl[k]] + 1); goto L17; /* ------MODIFY NONZERO STRUCTURE OF K-TH ROW BY COMPUTING FILL-IN */ /* ------FOR EACH ROW I TO BE MERGED IN */ L5: lmax = 0; iju[k] = juptr; i = k; L6: i = jl[i]; if (i == 0) { goto L10; } /* --------MERGE ROW I INTO K-TH ROW */ lui = iu[i + 1] - (iu[i] + 1); jmin = iju[i] + 1; jmax = iju[i] + lui; qm = k; i__2 = jmax; for (j = jmin; j <= i__2; ++j) { vj = ju[j]; L7: m = qm; qm = q[m]; if (qm < vj) { goto L7; } if (qm == vj) { goto L8; } ++luk; q[m] = vj; q[vj] = qm; qm = vj; L8: ; } /* --------REMEMBER LENGTH AND POSITION IN JU OF LONGEST ROW MERGED */ if (lui <= lmax) { goto L9; } lmax = lui; iju[k] = jmin; L9: goto L6; /* ------IF THE K-TH ROW IS THE SAME LENGTH AS THE LONGEST ROW MERGED, */ /* ------THEN USE THE COLUMN INDICES FOR THAT ROW */ L10: if (luk == lmax) { goto L17; } /* ------IF THE TAIL OF THE LAST ROW SAVED IN JU IS THE SAME AS THE HEAD */ /* ------OF THE K-TH ROW, THEN OVERLAP THE TWO SETS OF COLUMN INDICES -- */ /* --------SEARCH LAST ROW SAVED FOR FIRST NONZERO ENTRY IN K-TH ROW ... */ i = q[k]; if (jumin > juptr) { goto L12; } i__2 = juptr; for (jmin = jumin; jmin <= i__2; ++jmin) { if ((i__3 = ju[jmin] - i) < 0) { goto L11; } else if (i__3 == 0) { goto L13; } else { goto L12; } L11: ; } L12: goto L15; /* --------... AND THEN TEST WHETHER TAIL MATCHES HEAD OF K-TH ROW */ L13: iju[k] = jmin; i__2 = juptr; for (j = jmin; j <= i__2; ++j) { if (ju[j] != i) { goto L15; } i = q[i]; if (i > *n) { goto L17; } /* L14: */ } juptr = jmin - 1; /* ------SAVE NONZERO STRUCTURE OF K-TH ROW IN JU */ L15: i = k; jumin = juptr + 1; juptr += luk; if (juptr > *jumax) { goto L106; } i__2 = juptr; for (j = jumin; j <= i__2; ++j) { i = q[i]; ju[j] = i; /* L16: */ mark[i] = k; } iju[k] = jumin; /* ------ADD K TO ROW LIST FOR FIRST NONZERO ELEMENT IN K-TH ROW */ L17: if (luk <= 1) { goto L18; } i = ju[iju[k]]; jl[k] = jl[i]; jl[i] = k; L18: iu[k + 1] = iu[k] + luk; } *flag_ = 0; return 0; /* ** ERROR -- DUPLICATE ENTRY IN A */ L102: *flag_ = (*n << 1) + p[k]; return 0; /* ** ERROR -- INSUFFICIENT STORAGE FOR JU */ L106: *flag_ = *n * 6 + k; return 0; } /* ssf_ */ /* *********************************************************************** */ /* SNS -- SOLUTION OF SPARSE SYMMETRIC POSITIVE DEFINITE SYSTEM OF */ /* LINEAR EQUATIONS MX = B GIVEN UT-D-U FACTORIZATION OF M */ /* *********************************************************************** */ /* Subroutine */ int sns_(n, p, d, iju, ju, iu, u, z, b, tmp) integer *n, *p; doublereal *d; integer *iju, *ju, *iu; doublereal *u, *z, *b, *tmp; { /* System generated locals */ integer i__1, i__2; /* Local variables */ STATIC integer jmin, jmax; STATIC doublereal tmpk; STATIC integer i, j, k, mu; STATIC doublereal sum; /* REAL D(1), U(1), Z(1), B(1), TMP(1), TMPK, SUM */ /* ADDITIONAL PARAMETERS */ /* TMP - REAL ONE-DIMENSIONAL WORK ARRAY; DIMENSION = N */ /* ----------------------------------------------------------------------- */ /* ----SET TMP TO PERMUTED B */ /* Parameter adjustments */ --tmp; --b; --z; --u; --iu; --ju; --iju; --d; --p; /* Function Body */ i__1 = *n; for (k = 1; k <= i__1; ++k) { /* L1: */ tmp[k] = b[p[k]]; } /* ----SOLVE UT D Y = B BY FORWARD SUBSTITUTION */ i__1 = *n; for (k = 1; k <= i__1; ++k) { tmpk = tmp[k]; jmin = iu[k]; jmax = iu[k + 1] - 1; if (jmin > jmax) { goto L3; } mu = iju[k] - jmin; i__2 = jmax; for (j = jmin; j <= i__2; ++j) { /* L2: */ tmp[ju[mu + j]] += u[j] * tmpk; } L3: tmp[k] = tmpk * d[k]; } /* ----SOLVE U X = Y BY BACK SUBSTITUTION */ k = *n; i__1 = *n; for (i = 1; i <= i__1; ++i) { sum = tmp[k]; jmin = iu[k]; jmax = iu[k + 1] - 1; if (jmin > jmax) { goto L5; } mu = iju[k] - jmin; i__2 = jmax; for (j = jmin; j <= i__2; ++j) { /* L4: */ sum += u[j] * tmp[ju[mu + j]]; } L5: tmp[k] = sum; z[p[k]] = sum; /* L6: */ --k; } return 0; } /* sns_ */ /************************************************************************* * function: ysmp_negvector() * * purpose: Find vector corresponding to diagonal element of D. * GIVEN UT-D-U FACTORIZATION OF M * Actually just solves UZ = B. Input B should have just * a 1 in spot corresponding to negative diagonal element. * Adapted from SNS_ by cutting out the UT D Y = B solution step. * ************************************************************************/ /* Subroutine */ int ysmp_negvector(n, p, d, iju, ju, iu, u, z, b, tmp) integer *n, *p; doublereal *d; integer *iju, *ju, *iu; doublereal *u, *z, *b, *tmp; { /* System generated locals */ integer i__1, i__2; /* Local variables */ STATIC integer jmin, jmax; STATIC integer i, j, k, mu; STATIC doublereal sum; /* REAL D(1), U(1), Z(1), B(1), TMP(1), TMPK, SUM */ /* ADDITIONAL PARAMETERS */ /* TMP - REAL ONE-DIMENSIONAL WORK ARRAY; DIMENSION = N */ /* ----------------------------------------------------------------------- */ /* ----SET TMP TO PERMUTED B */ /* Parameter adjustments */ --tmp; --b; --z; --u; --iu; --ju; --iju; --d; --p; /* Function Body */ i__1 = *n; for (k = 1; k <= i__1; ++k) { /* L1: */ tmp[k] = b[p[k]]; } /* ----SOLVE U X = Y BY BACK SUBSTITUTION */ k = *n; i__1 = *n; for (i = 1; i <= i__1; ++i) { sum = tmp[k]; jmin = iu[k]; jmax = iu[k + 1] - 1; if (jmin > jmax) { goto L5; } mu = iju[k] - jmin; i__2 = jmax; for (j = jmin; j <= i__2; ++j) { /* L4: */ sum += u[j] * tmp[ju[mu + j]]; } L5: tmp[k] = sum; z[p[k]] = sum; /* L6: */ --k; } return 0; } /* ysmp_negvector */ evolver-2.30c.dfsg/src/help.c0000644000175300017530000005320211410765113016313 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /****************************************************************** * * File: help.c * * Purpose: Print help screens. */ #include "include.h" #include "ytab.h" void do_line ARGS(( char *)); /******************************************************************* * * Function: main_help() * * Purpose: Help screen for main menu. */ /* strings to print 2 per line */ char *help_strings[] = { " A Set adjustable parameters.", " b Set body volumes or pressures.", " C Run consistency checks.", " c Report count of elements.", " D Toggle display every iteration.", " d Dump surface to datafile.", " G Set gravity.", " g nnn Go nnn iterations.", " H,h,? This help screen.", " i Information on status.", " l Subdivide long edges.", " m Toggle fixed motion scale.", " P Graphics display file, geomview.", " q,x Exit.", " r Refine triangulation.", " s Built-in screen graphics.", " t Remove tiny edges.", " U Toggle conjugate gradient method.", " u Equiangulate.", " V Vertex averaging.", " v Report volumes.", " w Weed out small triangles." }; char *qhelp[]={ "\n", "Operators: +,-,*,/,^n,**,==,!=,>,<,>=,<=,NOT,!,AND,&&,OR,||.\n", "Colors: black,red,green,blue,cyan,magenta,brown,darkgray,lightgray,\n", " lightblue,lightgreen,lightred,lightcyan,lightmagenta,yellow,white.\n", "Examples of the full command language:\n", " list edges | \"more\"; list topinfo >>> \"topstuff\"\n", " refine edges where not fixed and length > .23\n", " set facet color green where original == 1 or original == 2\n", " list vertices where x^2 + y^2 > 1\n", " show facets where area < .01\n", "\n", NULL}; /********************************************************************** * * function: main_help() * * purpose: handle 'h' command, print brief help page on screen. * */ void main_help() { int n = sizeof(help_strings)/sizeof(char *); int i,j; outstring("\nFor detailed help, use \"help topic\". This will also\n"); outstring("give a list of help topics containing your topic word.\n"); outstring("Use quotes around your topic if necessary.\n"); outstring("\nSome common one-letter commands: \n"); for ( i = 0, j = (n+1)/2 ; i < (n+1)/2 ; i++,j++ ) { sprintf(msg,"%-38.38s ",help_strings[i]); outstring(msg); if ( j < n ) sprintf(msg,"%-38.38s\n",help_strings[j]); else sprintf(msg,"\n"); outstring(msg); } #ifdef NOPIPE /* fake `more' */ for ( i = 0 ; qhelp[i] ; ) { outstring("Hit RETURN for more."); getstring(msg,msgmax); for ( j = 0 ; qhelp[i] && (j<23) ; i++,j++ ) outstring(qhelp[i]); } #else for ( i = 0 ; qhelp[i] ; i++ ) outstring(qhelp[i]); #endif } /********************************************************************* * * Function: graph_help() * * Purpose: Display help screen for graphics mode. */ char *ghelp_strings[] = { "View transformation commands are strings of letters. Each letter ", "causes an action and may be preceded by an integer repetition count, ", "or a real value (if appropriate; use a decimal point):", " u Rotate up 6 degrees.", " d Rotate down 6 degrees", " r Rotate right 6 degrees.", " l Rotate left 6 degrees.", " c Rotate clockwise 6 degrees.", " C Rotate counterclockwise 6 degrees.", " z Zoom by factor of 1.2.", " s Shrink by factor of 1.2.", " arrow keys - Translate image.", " m Center image.", " R Reset viewing parameters.", " T Toggle additional viewing transforms.", " e Toggle facet edge drawing.", " B Toggle display of boundary facets.", " v Toggle ridge and valley coloring.", " w Toggle facets with three vertices on constraints.", " b Draw bounding box.", " +,- Increment, decrement fill color.", " H Toggle hidden surface removal.", " t Set clipping mode for torus.", " ?,h help (this display)", " x,q Exit to main menu." }; void graph_help() { int n = sizeof(ghelp_strings)/sizeof(char *); int i; for ( i = 0 ; i < n ; i++ ) { outstring(ghelp_strings[i]); outstring("\n"); } } /********************************************************************************* * * function: printbuff() * * purpose: massage and print HTML text on screen. * */ static char buff[30000]; /* text buffer */ #define LINESIZE 75 static int pre_flag; void printbuff ARGS((void)); void printbuff() { char *spot,*s,*d; /* do HTML special characters */ if ( buff[0] == 0 ) return; spot = buff; while ( (spot = strchr(spot,'&')) != NULL ) { s = spot + 1; if ( kb_strnicmp(spot+1,"lt;",3) == 0 ) { *spot = '<'; s = spot+4; } if ( kb_strnicmp(spot+1,"gt;",3) == 0 ) { *spot = '>'; s = spot+4; } if ( kb_strnicmp(spot+1,"amp;",4) == 0 ) { *spot = '&'; s = spot+5; } if ( kb_strnicmp(spot+1,"quot;",5) == 0 ) { *spot = '"'; s = spot+6; } for ( d = spot+1 ; *s ; s++,d++ ) *d = *s; *d = 0; spot++; } if ( pre_flag ) { outstring(buff); outstring("\n"); } else { char *cur; /* cursor */ char *b; /* searching for blank */ char *src,*dest; /* compress whitespace */ if ( pre_flag == 0 ) { for ( src = buff; *src ; src++ ) if ( *src == '\t' ) *src = ' '; src = buff; while ( *src == ' ' ) src++ ; for ( dest = buff, src = buff; *src ; src++ ) { if ( (*src == ' ') && (dest[-1] == ' ') ) continue; else *(dest++) = *src; } } else dest = buff + strlen(buff); *dest = 0; dest[1] = 0; /* print lines */ for ( cur = buff; *cur ; cur = b+1 ) { /* first, scan for newline */ for ( b = cur; *b && (b < cur+LINESIZE) ; b++ ) if ( *b == '\n' ) break; if ( *b == '\n' ) { *b = 0; outstring(cur); outstring("\n"); continue; } /* now get chunk small enough to fit on a line */ b = cur + LINESIZE; if ( b < dest ) { while ( (*b != ' ') && (b > cur) ) b--; if ( b == cur ) { b = cur + LINESIZE; } } else b = dest; *b = 0; outstring(cur); outstring("\n"); } } buff[0] = 0; } /****************************************************************** * * function: do_line() * * purpose: add line to buffer, parsing and printing buffer if called for * */ static int ul_level; void do_line(line) char *line; { char *spot,*c,*b,*bb; spot = line; do { b = strchr(spot,'<'); if ( b ) { bb = strchr(b+1,'>'); if ( bb == NULL ) b = NULL; else if ( pre_flag && b[1] == ' ' ) b = NULL; /* inequality sign */ else *b = 0; } strcat(buff,spot); if ( b == NULL ) break; spot = b + 1; b = strchr(spot,'>'); if ( b ) *b = 0; c = strchr(spot,' '); if ( c ) *c = 0; if ( kb_stricmp(spot,"br") == 0 ) printbuff(); else if (kb_stricmp(spot,"sub") == 0 ) {strcat(buff,"_"); } else if (kb_stricmp(spot,"sup") == 0 ) {strcat(buff,"^"); } else if (kb_stricmp(spot,"p") == 0 ) {strcat(buff,"\n"); printbuff();} else if (kb_strnicmp(spot,"/h",2) == 0 ) {strcat(buff,"\n"); printbuff();} else if (kb_stricmp(spot,"li") == 0 ) { int n; printbuff(); strcat(buff," "); for ( n = 0 ; n < ul_level ; n++ ) strcat(buff,"> "); } else if (kb_stricmp(spot,"dt") == 0 ) {printbuff();} else if (kb_stricmp(spot,"dd") == 0 ) {strcat(buff,": ");} else if (kb_stricmp(spot,"ul") == 0 ) {ul_level++;} else if (kb_stricmp(spot,"/ul") == 0 ) {ul_level--; strcat(buff,"\n"); printbuff();} else if (kb_stricmp(spot,"/ol") == 0 ) {strcat(buff,"\n"); printbuff();} else if (kb_stricmp(spot,"/dl") == 0 ) {strcat(buff,"\n"); printbuff();} else if ( kb_stricmp(spot,"pre") == 0 ) { strcat(buff,"\n"); printbuff(); pre_flag = 1; } else if ( kb_stricmp(spot,"/pre") == 0 ) { printbuff(); pre_flag = 0; } else if ( kb_stricmp(spot,"tr") == 0 ) printbuff(); spot = b+1; } while ( *spot ); if ( pre_flag ) strcat(buff,"\n"); } /************************************************************************** * * function: old_html_help() * * Purpose: old fashioned extraction of help from html doc files, * hopefully superceded by new_text_help() * */ #define RELMAX 30 static struct relinfo { char filename[20]; char name[40]; } info[RELMAX]; static char name[100] = "name=\""; void old_html_help(keyword, /* string to look for */ found) /* flag, set if previously found, and want related */ char *keyword; int found; { int printflag = 0; int relcount = 0; int linecount = 0; char helpfilename[PATHSIZE]; FILE *fd; char line[1000]; int n; /* now check html doc files */ helpfilename[0] = 0; fd = path_open("index.htm",NOTDATAFILENAME); if ( fd == NULL ) { erroutstring( "Cannot open index.htm. Are the *.htm doc files on EVOLVERPATH?\n"); return; } /* skip header stuff */ while ( fgets(line,sizeof(line)-1,fd) ) if ( strstr(line,"") ) break; while ( fgets(line,sizeof(line)-1,fd) ) { char *sharp = strchr(line,'#'); if ( sharp == NULL ) continue; *strchr(sharp,'"') = 0; if ( stricmp(sharp+1,keyword) == 0 ) { *sharp = 0; strcpy(helpfilename,strchr(line,'"')+1); } else if ( strstr(sharp+1,keyword) && (relcount < RELMAX) ) { strcpy(info[relcount].filename,strchr(line,'"')+1); strcpy(info[relcount].name,sharp+1); relcount++; } } fclose(fd); if ( found ) goto print_related; if ( helpfilename[0] == 0 ) { sprintf(msg,"Cannot find help entry for \"%s\". \n", keyword); outstring(msg); goto print_related; } fd = path_open(helpfilename,NOTDATAFILENAME); if ( fd == NULL ) { perror(helpfilename); return; } ul_level = 0; strcpy(name+6,keyword); strcat(name,"\""); while ( fgets(line,sizeof(line)-1,fd) ) { if ( strstr(line,name) ) printflag = 1; else if ( (printflag==1) && ( ((strstr(line,"2) ) || strstr(line,"") || strstr(line,"EndName") )) { break; } strtok(line,"\r\n"); /* remove trailing newline */ strcat(line," "); if ( printflag ) { if ( !strstr(line," line */ while ( fgets(line,sizeof(line)-1,fd) ) { char *keyspot; if ( strncmp(line,"<---",4) != 0 ) continue; #ifndef MPI_EVOLVER if ( strstr(line,"MPI Evolver") ) continue; #endif keyspot = strstr(line,keyword); if ( keyspot == NULL ) continue; if ( (keyspot[-2] == '-') && (keyspot[keylen+2] == '-') ) { int lines_done = 0; int blanklines = 0; /* found exact match */ found = 1; /* print nice title bar */ outstring("<"); for ( i = 1 ; i < (70 - keylen)/2 ; i++ ) outstring("-"); outstring(" "); outstring(keyword); outstring(" "); for ( i = (70 + keylen)/2 ; i < 70 ; i++ ) outstring("-"); outstring(">\n"); /* read and echo info */ while ( fgets(line,sizeof(line)-1,fd) ) { if ( line[0] != '<' ) { if ( line[1] == 0 ) { blanklines++; if ( blanklines < 2 ) outstring(line); } else { outstring(line); blanklines = 0; } lines_done++; } else { /* skip if just extra title */ if ( lines_done > 0 ) break; } } /* next title line falls through to relevance recording */ } if ( strstr(line,keyword) && (relcount < RELMAX) ) { char *spot = strchr(line,' '); int spaceflag = 0; if ( spot ) for ( spot++, i = 0 ; !((spot[0] == ' ') && (spot[1] == '-')) ; spot++, i++ ) { info[relcount].name[i] = *spot; if ( *spot == ' ' ) spaceflag = 1; } if ( spaceflag ) /* enclose in quotes */ { char c; c = info[relcount].name[0]; info[relcount].name[0] = '"'; for ( n = 1 ; n < i ; n++ ) { char cc = info[relcount].name[n]; info[relcount].name[n] = c; c = cc; } info[relcount].name[i] = c; info[relcount].name[i+1] = '"'; i += 2; } info[relcount].name[i] = 0; relcount++; } } fclose(fd); if ( !found ) { sprintf(msg,"Cannot find help entry for \"%s\". \n", keyword); outstring(msg); } if ( relcount && (strlen(keyword) > 1) ) { outstring("Possibly related entries:\n"); for ( n = 0 ; n < relcount ; n+=2 ) { if ( n < relcount-1 ) sprintf(msg," %-32s %s\n",info[n].name,info[n+1].name); else sprintf(msg," %-32s \n",info[n].name); outstring(msg); } } fclose(fd); return 1; } /*************************************************************************** * * function: keyword_help() * * purpose: Print help for keyword. */ void keyword_help(keyword) char *keyword; { int type; int found = 0; int entry; #ifndef NOPIPE if ( outfd == stdout ) { outfd = popen("more","w"); if ( outfd == NULL ) { perror("more"); outfd = stdout; } } #endif if ( keyword == NULL ) { erroutstring("Usage: help keyword or help \"phrase\" \n"); goto help_exit; } if ( isdigit(keyword[0]) ) { error_help(keyword); goto help_exit; } /* check for user-defined names */ /* search symbol table */ if ( symbol_lookup(keyword) ) { sprintf(msg,"\n%s: user-defined element name.\n\n",keyword); outstring(msg); found = 1; goto help_exit; } /* search parameter names */ entry = lookup_global_hash(keyword,0,0,HASH_LOOK); if ( entry != -1 ) switch ( entry & NAMETYPEMASK ) { case VARIABLENAME: { struct global *gp = globals((entry & INDEXMASK)|EPHGLOBAL); if ( gp->flags & (SUBROUTINE|PROCEDURE_NAME) ) { sprintf(msg,"\n%s: user-defined procedure. Prototype:\n\n ",keyword); outstring(msg); list_procedure_proto(gp); outstring("\n"); } else if ( gp->flags & FUNCTION_NAME ) { sprintf(msg,"\n%s: user-defined function. Prototype:\n\n ",keyword); outstring(msg); list_function_proto(gp); outstring("\n"); } else if ( gp->flags & STRINGVAL ) { sprintf(msg,"\n%s: user-defined string variable.\n\n",keyword); outstring(msg); } else if ( gp->flags & QUANTITY_NAME ) { sprintf(msg,"\n%s: user-defined named quantity.\n\n",keyword); outstring(msg); } else if ( gp->flags & METHOD_NAME ) { sprintf(msg,"\n%s: user-defined named method.\n\n",keyword); outstring(msg); } else if ( gp->flags & CONSTRAINT_NAME ) { sprintf(msg,"\n%s: user-defined named constraint.\n\n",keyword); outstring(msg); } else if ( gp->flags & BOUNDARY_NAME ) { sprintf(msg,"\n%s: user-defined named boundary.\n\n",keyword); outstring(msg); } else if ( gp->flags & DYNAMIC_LOAD_FUNC ) { sprintf(msg,"\n%s: dynamic load library function.\n\n",keyword); outstring(msg); } else if ( gp->flags & ARRAY_PARAM ) { struct array *a = gp->attr.arrayptr; if ( a ) { sprintf(msg,"\n%s: user-defined array of type %s", keyword,datatype_name[a->datatype]); outstring(msg); if ( a->dim > 1 || a->sizes[0] > 1 ) { int i; for ( i = 0 ; i < a->dim ; i++ ) { sprintf(msg,"[%d]",a->sizes[i]); outstring(msg); } } outstring(".\n\n"); } else { sprintf(msg,"\n%s: user-defined array; declaration not yet executed.\n\n", keyword); outstring(msg); } } else { if ( gp->flags & OPTIMIZING_PARAMETER ) sprintf(msg,"\n%s: user-defined optimizing parameter\n\n", keyword); else if ( gp->type ) sprintf(msg,"\n%s: user-defined parameter of type %s\n\n", keyword, datatype_name[gp->type]); else sprintf(msg,"\n%s: user-defined parameter (numeric variable)\n\n", keyword); outstring(msg); } found = 1; goto help_exit; } break; case PERM_NAME: { struct global *gg = globals((entry & INDEXMASK)|PERMGLOBAL); if ( gg->flags & (SUBROUTINE|PROCEDURE_NAME) ) { sprintf(msg,"\n%s: permanent user-defined procedure. Prototype:\n\n ", keyword); outstring(msg); list_procedure_proto(gg); outstring("\n"); } else if ( gg->flags & FUNCTION_NAME ) { sprintf(msg,"\n%s: permanent user-defined function. Prototype:\n\n ", keyword); outstring(msg); list_function_proto(gg); outstring("\n"); } else if ( gg->flags & STRINGVAL ) { sprintf(msg,"\n%s: permanent user-defined string variable.\n\n",keyword); outstring(msg); } else if ( gg->flags & INTERNAL_NAME ) { if ( gg->flags & ARRAY_PARAM ) sprintf(msg,"\n%s: permanent internal %s array.\n\n",keyword, datatype_name[gg->attr.arrayptr->datatype]); else sprintf(msg,"\n%s: permanent internal variable.\n\n",keyword); outstring(msg); found = 0; /* so prints out regular documentation */ goto help_exit; } else { sprintf(msg,"\n%s: permanent user-defined parameter (numeric variable)\n\n", keyword); outstring(msg); } found = 1; goto help_exit; } break; case QUANTITYNAME: { struct gen_quant *g = GEN_QUANT(entry & INDEXMASK); sprintf(msg,"\n%s: user-defined named quantity.\n\n",g->name); outstring(msg); found = 1; goto help_exit;} break; case METHODNAME: { struct method_instance *mi = METH_INSTANCE(entry & INDEXMASK); sprintf(msg,"\n%s: user-defined named method, belongs to quantity %s.\n\n", keyword, GEN_QUANT(mi->quant)->name); outstring(msg); found = 1; goto help_exit;} break; } /* search extra attributes */ for ( type = 0 ; type <= BODY ; type++ ) { struct extra *ex; int i,k; for ( i = 0, ex = EXTRAS(type) ; i < web.skel[type].extra_count ; i++ , ex++ ) if ( stricmp(keyword, ex->name) == 0 ) { sprintf(msg,"\n%s: %s attribute, type %s, ", keyword,typenames[type],datatype_name[ex->type]); if ( ex->array_spec.dim == 0 ) strcat(msg,"scalar"); else strcat(msg," dimension "); for ( k = 0 ; k < ex->array_spec.dim ; k++ ) sprintf(msg+strlen(msg),"[%d]",ex->array_spec.sizes[k]); strcat(msg,"\n\n"); outstring(msg); found = 1; goto help_exit; } } help_exit: /* find_related: */ /* Go to help file, even if found, for related entries */ // if ( !found ) if ( !new_text_help(keyword,found) && !found ) old_html_help(keyword,found); #ifndef NOPIPE pclose(outfd); outfd = stdout; #else ; #endif } /* end keyword_help() */ /*************************************************************************** * * function: error_help() * * purpose: Print help for given error number from error.hlp. * * Nonfunctional yet. */ void error_help(keyword) char *keyword; /* with error number as string */ { outstring("Help by error number not available.\n"); #ifdef HAVEHLP FILE *fd; char line[200]; int errnum = atoi(keyword); int flag = 0; /* if found */ fd = path_open("error.hlp",NOTDATAFILENAME); if ( fd == NULL ) { erroutstring("Cannot open error.hlp. Is error.hlp on EVOLVERPATH?\n"); return; } while ( fgets(line,sizeof(line)-1,fd) ) if ( (strncmp(line,"Error ",6) == 0) && (atoi(line+6) == errnum) ) { flag = 1; break; } if ( flag ) { outstring(line); while ( fgets(line,sizeof(line)-1,fd) ) if ( strncmp(line,"Error ",6) == 0 ) break; else outstring(line); } else kb_error(1862,"Illegal error number.\n",WARNING); fclose(fd); #endif } evolver-2.30c.dfsg/src/rl_mid.h0000644000175300017530000001346411410765113016644 0ustar hazelscthazelsct"abs", "acos", "acosh", "actual_volume", "alice", "ambient_pressure", "and", "approx_curv", "approx_curvature", "approximate_curvature", "area", "area_fixed", "area_method_name", "area_normalization", "areaweed", "asin", "asinh", "assume_oriented", "atan", "atan2", "atanh", "attribute", //"attributes", "augmented_hessian", "autochop", "autodisplay", "autopop", "autorecalc", "avg", "axial_point", "backbody", "backcolor", "backcull", "bare", "bezier_basis", "blas_flag", "blue", "bodies", "body", "bottominfo", //"boundaries", "boundary", "boundary_curvature", "break", "break_after_warning", "bunch_kauffman", "bunch_kaufman", "burchard", "bye", "ceil", "chdir", "check", "check_increase", "circular_arc_draw", "clear", "clipped", "clipped_cells", "close_show", "color", "colorfile", "colormap", "conducting_knot_energy", "conf_edge", "conformal_metric", "conj_grad", "connected", "connected_cells", "conserved", "constraint", "constraint_tolerance", //"constraints", "content", "continue", "convert_to_quantities", "convex", "cos", "cosh", "count", "counts", "crossingflag", "cyan", "datafilename", "date_and_time", "debug", "define", "delete", "density", "deturck", "diffusion", "dihedral", "dirichlet", "dirichlet_mode", "dirichlet_seek", "dissolve", "div_normal_curvature", "do", "dump", //"e", "edge", "edge_divide", "edges", "edgeswap", "edgeweed", "effective_area", "efixed", "eigenprobe", "element_modulus", "ellipticE", "ellipticK", "else", "energy", "eprint", "equiangulate", "errprintf", "estimate", "everything_quantities", "evolver_version", "exec", "exit", "exp", "exprint", "extrapolate", "face", //"faces", //"facet", "facet_colors", "facet_edge", //"facet_edges", "facetedge", //"facetedges", //"facets", "fix", "fixed", "fixed_area", "floor", "for", "force_pos_def", "foreach", "form_integrand", "formula", "frontbody", "frontcolor", "function", //"g", "gap_constant", "gauss_curvature", "geompipe", "geomview", "global", "global_method", "go", "green", "gravity", "gravity_constant", "gridflag", "gv_binary", "h_inverse_metric", "help", "hessian", "hessian_diff", "hessian_double_normal", "hessian_menu", "hessian_normal", "hessian_normal_one", "hessian_normal_perp", "hessian_quiet", "hessian_seek", "hessian_special_normal", "hessian_special_normal_vector", "histogram", "history", "hit_constraint", "hit_partner", "homothety", "id", "idiv", "if", "ignore_constraints", "ignore_fixed", "imod", "incompleteEllipticE", "incompleteEllipticF", "info_only", "insulating_knot_energy", "integer", "integral_order", "integral_order_1d", "integral_order_2d", //"integration_order", //"integration_order_1d", //"integration_order_2d", "interp_bdry_param", "interp_normals", "is_defined", "itdebug", "jiggle", "k_vector_order", "keep_macros", "keep_originals", "keylogfile", "klein_metric", "kmetis", "kraynikpopedge", "kraynikpopvertex", "kusner", "labelflag", "lagrange", "lagrange_multiplier", "lagrange_order", "lanczos", "length", "length_method_name", "lightblue", "lightcyan", "lightgray", "lightgreen", "lightmagenta", "lightred", "linear", "linear_metric", "list", "load", "load_library", "local", "log", "logfile", "loghistogram", "longj", "magenta", "max", "maximum", "mean_curvature", "mean_curvature_integral", "memdebug", "merit_factor", "method", "method_instance", "metis", "metis_factor", "metric", "metric_conversion", "metric_convert", "midv", "min", "minimum", "mobility", "mobility_tensor", "mod", "modulus", "move", "new_body", "new_edge", "new_facet", "new_vertex", "no_display", "no_refine", "nodisplay", "noncontent", "nonnegative", "nonpositive", "nonwall", "normal_curvature", "normal_motion", "not", "notch", "off", "oid", "old_area", "ometis", "on", "on_boundary", "on_constraint", "ooglfile", "opacity", //"optimise", //"optimising_parameter", "optimize", "optimizing_parameter", "or", "orientation", "original", "parameter", "parameter_1", "parameter_file", "parameters", "partner_hitting", "pause", "pdelta", "periods", "permload", "phase", "phasefile", //"pi", "pinning", "pop", "pop_edge_to_tri", "pop_quad_to_quad", "pop_tri_to_edge", "post_project", "postscript", "pow", "pressure", "print", "printf", "procedure", "procedures", "pscale", "pscolorflag", "quadratic", "quantities_only", "quantity", //"quiet", //"quietgo", //"quietload", "quit", "raw_cells", "raw_vertex_average", "rawest_vertex_average", "rawestv", "rawv", "read", "real", "rebody", "recalc", "refine", "renumber_all", "reorder_storage", "return", "rgb_colors", "ribiere", "ritz", "runge_kutta", "saddle", "scalar_integrand", "scale", "scale_limit", "self", "self_similar", "set", "shading", "shell", "show", "show_all_quantities", "show_expr", "show_inner", "show_off", "show_outer", "show_trans", "show_vol", "showq", "simplex_representation", "sin", "sinh", "sizeof", "soapfilm", "sobolev", "sobolev_mode", "sobolev_seek", "space_dimension", "sparse_constraints", "spring_constant", "sprintf", "sqcurve", "sqgauss", //"sqr", "sqrt", "square_curvature", "square_gaussian_curvature", "squared_curvature", "squared_gaussian_curvature", "squared_gradient", "stability_test", "string", "sum", "surface_dimension", "surface_energy", "swap_colors", "symmetric_content", "symmetry_group", "system", "tag", "tan", "tanh", "target", "temperature", "tension", "tetra_point", "then", "thicken", "tolerance", "topinfo", "torus", "torus_filled", "torus_periods", "total", "total_time", "transform_depth", "transform_expr", "transforms", "triple_point", "ulong", "unfix", "unset", "utest", "valence", "value", "vector_integrand", "verbose", "vertex", "vertex_average", "vertexnormal", "vertices", "view_4d", "view_matrix", "view_transform_generators", "view_transforms", "visibility_test", "volconst", "volfixed", "volgrads_every", "volume", "volume_method_name", "warning_messages", "where", "while", "wrap", "wrap_compose", "wrap_inverse", "wrap_vertex", "wulff", "yellow", "ysmp", "zener_drag", "zoom", "zoom_radius", "zoom_vertex", evolver-2.30c.dfsg/src/readline.c0000644000175300017530000001213511410765113017146 0ustar hazelscthazelsct/************************************************************* * This file is an extension for the Surface Evolver * * Programmer: Laszlo Csirmaz, csirmaz@renyi.hu * ************************************************************/ /************************************************************************ * * File: readline.c * * Purpose: automatic extension by using the "readline" library * * */ #ifdef USE_READLINE #include #include static int whspace(char c) { switch(c){ case ' ': case '\t': case '\r': case '\n': return 1; default: break; } return 0; } static char *stripwhite(char *str) {char *s,*t; for(s=str;whspace(*s);s++); if(*s==0) return s; t=s+strlen(s)-1; while(t>s && whspace(*t))t--; *++t='\0'; return s; } /* list of extended words in alphabetical order; no single letter word is extended */ static char *head[]={ #include "rl_head.h" NULL }; static char *middle[]={ #include "rl_mid.h" NULL }; static char **expand_from; static int do_file_expand=0; static int save_history=1; static char *lastline = NULL; static int lastlinepos = 0; static int readline_initialized=0; static char * match_generator(const char *txt, int state) {static int list_index,len; char *name; if(state==0){ /* first call */ list_index=0; len=strlen(txt); } while( (name=expand_from[list_index])!=NULL ){ list_index++; if(strncmp(name,txt,len)==0) return strdup(name); } return (char*)NULL; } static char **my_comp( const char *text, int start, int end) { // the string to be completed starts at "start" if(do_file_expand == 0){ /* no filename extension */ expand_from = start ? middle : head; return rl_completion_matches(text,match_generator); } rl_attempted_completion_over = 0; return (char **)NULL; /* filename extension */ } void save_readline_history(void) { char *HOME, hist[PATHSIZE]; HOME=getenv("HOME"); if(HOME){ hist[PATHSIZE-1]=0; strncpy(hist,HOME,PATHSIZE-1); strncat(hist,"/.evolver",PATHSIZE-1-strlen(HOME)); write_history(hist); } } static int use_readline(char *prompt, char *inmsg, int max ) {int len; char *s; if(readline_initialized==0){ char *HOME, hist[PATHSIZE]; readline_initialized=1; rl_readline_name="evolver"; rl_attempted_completion_function=my_comp; using_history(); /* initialize */ HOME=getenv("HOME"); if(HOME){ hist[PATHSIZE-1]=0; strncpy(hist,HOME,PATHSIZE-1); strncat(hist,"/.evolver",PATHSIZE-1-strlen(HOME)); read_history_range(hist,0,100); } } if(prompt == CONTPROMPT ){ /* next part of lastline */ s=lastline+lastlinepos; len=strlen(s); strncpy(inmsg,s,max); if(len >= max-1) { inmsg[max-1]=MOREIN; inmsg[max]=0; len=max-1; } lastlinepos += len; return 1; } if(prompt == MOREPROMPT ){ /* continuation line */ /* do not add to the history yet */ char *contline,*new; contline = readline( topflag ? "" : "more> " ); if(!contline){ contline = strdup(""); } s=stripwhite(contline); new=malloc(lastlinepos+strlen(s)+2); if(!new){ /* pretend got nothing */ free(contline); s=lastline+lastlinepos; } else { strncpy(new,lastline,lastlinepos); strcpy(new+lastlinepos," "); lastlinepos++; strcpy(new+lastlinepos,s); free(lastline); free(contline); lastline=new; s=lastline+lastlinepos; } } else { /* read next line */ if(lastline){ s=stripwhite(lastline); if(save_history && *s && s[1] && s[2]){ HIST_ENTRY **hl; int n,i,found; /* if it is not on the history list, put it on; otherwise move the line to the last one */ hl=history_list(); /* get the history list */ found=0; if(hl){ /* find the current line */ i=0; for(n=0; *hl && (*hl)->line ; hl++,n++); while(found==0 && n>0 && i<10){ i++; n--; hl--; if(strcmp((*hl)->line,s)==0){ HIST_ENTRY *old; found=1; old=remove_history(n); if(old && old->line) free(old->line); } } } add_history(s); } free(lastline); } do_file_expand=0; save_history = 1; if( strstr(prompt,"file") ) do_file_expand=1; if( strcmp(prompt,"Graphics command: ")==0) save_history = 0; lastline = readline(prompt); if(!lastline) { /* EOF */ return EOF; } s=stripwhite(lastline); lastlinepos=s-lastline; } len=strlen(s); strncpy(inmsg,s,max); if(len >= max-1) { inmsg[max-1]=MOREIN; inmsg[max]=0; len=max-1; } lastlinepos += len; return 1; } #endif evolver-2.30c.dfsg/src/gnugraph.c0000644000175300017530000001522511410765113017201 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /* DJGPP GNU C graphics module for 32 bit DOS extender */ /* All coordinates in absolute pixels in current viewport */ /* Uses GRX graphics */ #include "include.h" #include #include #include #include static int maxx,maxy; /* max viewport coordinates */ static double xscale,yscale; /* for scaling to screen size */ static int numcolors; /* available */ static int color_perm[16] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; #define MAXGRAY 15 #define CINDEX(color,gray) \ ((numcolors == 32768) ? ( (int)(2*gray*rgb_colors[color][0])*0x400 \ + (int)(2*gray*rgb_colors[color][1])*0x20 + (int)(2*gray*rgb_colors[color][2]) ):\ (shading_flag?( color_flag ? ((gray)*(MAXGRAY+1)+color_perm[color]): \ ((color)?(gray):1) ) \ : color_perm[color])) /* lets index 0 be screen background */ void init_gnugraph() { int k; int c; /* color, 0-15 */ int g; /* gray level, 0-15 */ GrSetMode(GR_default_graphics); numcolors = GrNumColors(); if ( (numcolors < 256) && color_flag ) shading_flag = 0; /* not enough for shading */ /* first index background color */ for ( c = 0 ; c < 16 ; c++ ) color_perm[c] = c; color_perm[background_color] = 0; color_perm[0] = background_color; if ( shading_flag && color_flag ) { for ( c = 0 ; c < 16 ; c++ ) for ( g = 0 ; g <= MAXGRAY ; g++ ) GrSetColor(CINDEX(c,g), (int)(0xff*(g+12.)/(MAXGRAY+12.)*rgb_colors[c][0]), (int)(0xff*(g+12.)/(MAXGRAY+12.)*rgb_colors[c][1]), (int)(0xff*(g+12.)/(MAXGRAY+12.)*rgb_colors[c][2])); } else if ( shading_flag ) /* shading */ { for ( g = 0 ; g <= MAXGRAY ; g++ ) GrSetColor(CINDEX(WHITE,g), (int)(0xff*(g+12.)/(MAXGRAY+12.)), (int)(0xff*(g+12.)/(MAXGRAY+12.)), (int)(0xff*(g+12.)/(MAXGRAY+12.))); } else if ( color_flag ) { for ( c = 0 ; c < 16 ; c++ ) GrSetColor(CINDEX(c,MAXGRAY), (int)(0xff*rgb_colors[c][0]), (int)(0xff*rgb_colors[c][1]), (int)(0xff*rgb_colors[c][2])); } if ( numcolors == 32768 ) GrClearScreen(CINDEX(WHITE,MAXGRAY)); else { GrFreeColor(0); GrSetColor(0,(int)(0xff*rgb_colors[background_color][0]), (int)(0xff*rgb_colors[background_color][1]), (int)(0xff*rgb_colors[background_color][2])); } maxx = GrMaxX(); maxy = GrMaxY(); if ( maxx/3 < maxy/3 ) { xscale = maxx/3; yscale = xscale; /* assume square pixel screen */ } else { yscale = maxy/3; xscale = yscale; /* assume square pixel screen */ } } void gnugraph_edge(t) struct tsort *t; { int x[MAXCOORD],y[MAXCOORD]; int i; if ( t->color == CLEAR ) return; for ( i = 0 ; i < 2 ; i++ ) { x[i] = (int)(t->x[i][0]*xscale) + maxx/2; y[i] = maxy/2 - (int)(t->x[i][1]*yscale); } GrLine(x[0],y[0],x[1],y[1],CINDEX(t->color,MAXGRAY)); } #define swap(a,b) { tmp = a; a = b; b = tmp; } /* line by line, for graphics without filled regions */ void draw_triangle(x1,y1,x2,y2,x3,y3,color) int x1,y1,x2,y2,x3,y3,color; { int tmp; int y; double xx1,xx2,dx1,dx2; /* sort in y from bottom */ if ( y2 < y1 ) { swap(x1,x2); swap(y1,y2); } if ( y3 < y2 ) { swap(x2,x3); swap(y2,y3); } if ( y2 < y1 ) { swap(x1,x2); swap(y1,y2); } /* bottom part */ if ( y2 > y1 ) { dx1 = (x2 - x1)/(double)(y2 - y1); dx2 = (x3 - x1)/(double)(y3 - y1); for ( y = y1, xx1=x1+.5, xx2=x1+.5 ; y < y2 ; y++,xx1 += dx1,xx2 += dx2 ) GrLine((int)xx1,y,(int)xx2,y,color); } /* top part */ if ( y3 > y2 ) { dx1 = (x3 - x2)/(double)(y3 - y2); dx2 = (x3 - x1)/(double)(y3 - y1); for ( y = y3, xx1=x3+.5, xx2=x3+.5 ; y >= y2 ; y--,xx1 -= dx1,xx2 -= dx2 ) GrLine((int)xx1,y,(int)xx2,y,color); } } void gnugraph_facet(t) struct tsort *t; { int n = FACET_VERTS; /* vertices in polygon */ int x[6][2]; /* vertex coords */ int i,j,k,color,icolor; color = t->color; for ( i = 0 ; i < web.sdim ; i++ ) { x[i][0] = (int)(t->x[i][0]*xscale) + maxx/2; x[i][1] = maxy/2 - (int)(t->x[i][1]*yscale); } if (color != CLEAR) { if ( shading_flag && color_flag ) icolor = CINDEX(color,(int)(MAXGRAY*gray_level(t->normal))); else if ( color_flag ) icolor = CINDEX(color,MAXGRAY); else if ( shading_flag ) icolor = CINDEX(WHITE,(int)(MAXGRAY*gray_level(t->normal))); else icolor = CINDEX(WHITE,MAXGRAY); GrFilledConvexPolygon(3,x,icolor); /* draw_triangle(x[0][0],x[0][1],x[1][0],x[1][1],x[2][0],x[2][1],icolor); */ } if ( edgeshow_flag ) /* all grid edges */ for ( k = 0 ; k < 3 ; k++ ) { if ( t->etype[k]&EBITS == SPLITTING_EDGE ) continue; GrLine(x[k][0],x[k][1],x[(k+1)%3][0],x[(k+1)%3][1], CINDEX(t->ecolor[k],MAXGRAY)); } else for ( k = 0 ; k < 3 ; k++ ) /* show designated edges */ { if ( t->etype[k]&EBITS == INVISIBLE_EDGE ) continue; if ( t->etype[k]&EBITS == SPLITTING_EDGE ) continue; if ( t->ecolor[k] == CLEAR ) continue; GrLine(x[k][0],x[k][1],x[(k+1)%3][0],x[(k+1)%3][1], CINDEX(t->ecolor[k],MAXGRAY)); } } void display() { int x,y; MouseEvent e; int c,r,g,b; ENTER_GRAPH_MUTEX init_graphics = init_gnugraph; finish_graphics = null_function; graph_edge = painter_edge; display_edge = gnugraph_edge; graph_start = painter_start; graph_facet = painter_facet; display_facet = gnugraph_facet; graph_end = painter_end; graphgen(); MouseSetColors(CINDEX(WHITE,MAXGRAY), CINDEX(BLACK,MAXGRAY)); x = maxx/2; y = maxy/2; MouseWarp(x, y); while (1) { MouseGetEvent(M_LEFT_DOWN|M_LEFT_UP|M_KEYPRESS, &e); if (e.flags & M_LEFT_DOWN) { x = e.x, y = e.y; } if (e.flags & M_LEFT_UP) { fix_ctm(view,(double)(e.x - x),(double)(y - e.y)); graphgen(); } if (e.flags & M_KEYPRESS) break; } #ifdef XXX { int cc; GR_DRIVER_MODE_ENTRY *ttable = NULL,*gtable = NULL,*gt; cc = GrNumColors(); GrGetDriverModes(&ttable,>able); GrSetMode(GR_default_text); printf("colors: %d maxx: %d maxy: %d\n",cc,maxx,maxy); if ( gtable ) for ( gt = gtable ; gt->width > 0 ; gt++ ) printf("w: %d h: %d colors: %d BIOS: %d\n",gt->width,gt->height, gt->number_of_colors,gt->BIOS_mode); else printf("gtable is NULL.\n"); } #else GrSetMode(GR_default_text); #endif LEAVE_GRAPH_MUTEX } evolver-2.30c.dfsg/src/quotient.c0000644000175300017530000006025611410765113017242 0ustar hazelscthazelsct /************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************* * * file: quotient.c * * Purpose: This file implements symmetry groups. * At the moment, it has one group for rotations in xy-plane. * See the files torus.c, khyp.c, etc for other examples. * Four functions should be implemented for each group. * These routines get added to the table in registry.c * Group elements (wraps) are encoded any way you like * into an integer "wrap". * One function applies the group element to a point, * another composes two elements, another takes inverses, * and the last pulls back a one-form under the group action. * * NOTE: The compose function needs special care in the * order of composition for nonabelian groups. The * compose function is called to find the net wrap along * consecutive edges, with the first edge wrap being the * first argument. If the first edge goes from U to V * with wrap A, and the second goes from V to W with wrap B, * then the wrapped edges are (U,AV) and (V,BW), so * the wrapped triplet is (U,AV,ABW). So compose(A,B) * should return AB. * *************************************************************/ /************************************************************* * Symmetry group of rotations in the xy-plane. * Uses parameter "rotation_order" for the order of the group. * wrap is just an integer mod this order * By John Sullivan. * *************************************************************/ #include "include.h" /* by default the order will be four */ int rotorder = 4, genpower = 1; static REAL rotmat[2][2] = {{0.,-1.},{1.,0.}}; /* name and pointer to the global user variable */ #define ROTORDER_NAME "rotation_order" #define GENPOWER_NAME "generator_power" int rotorder_var = -1; /* reset at start of surface */ int genpower_var = -1; /* reset at start of surface */ /* handle arithmetic mod rotorder nicely */ #define stdwrap(w) \ ( (( ((w)<0? -(rotorder-1)*(w) : (w)) \ + (rotorder/2)) % rotorder) - (rotorder/2) ) /* prototypes */ void rot_new_order ARGS((void)); void new_rot_order ARGS((void)); void rot_wrap ARGS((REAL*,REAL*,long)); WRAPTYPE rot_compose ARGS((WRAPTYPE,WRAPTYPE)); WRAPTYPE rot_inverse ARGS((WRAPTYPE)); void rot_form_pullback ARGS(( REAL *,REAL *,REAL *,WRAPTYPE)); void frot_form_pullback ARGS(( REAL *,REAL *,REAL *,WRAPTYPE)); void pgcube_form_pullback ARGS(( REAL *,REAL *,REAL *,WRAPTYPE)); WRAPTYPE pgcube_compose ARGS((WRAPTYPE,WRAPTYPE)); /* note reverse order */ WRAPTYPE pg_tr_compose ARGS((WRAPTYPE,WRAPTYPE)); WRAPTYPE cubel_compose ARGS((WRAPTYPE,WRAPTYPE)); void cubel_form_pullback ARGS(( REAL *,REAL *,REAL *,WRAPTYPE)); void xyz_wrap ARGS((REAL*,REAL*,long)); WRAPTYPE xyz_compose ARGS((WRAPTYPE,WRAPTYPE)); WRAPTYPE xyz_inverse ARGS(( WRAPTYPE )); void xyz_form_pullback ARGS(( REAL *, REAL *, REAL *, WRAPTYPE )); void central_wrap ARGS(( REAL *, REAL *, WRAPTYPE )); WRAPTYPE central_compose ARGS(( WRAPTYPE, WRAPTYPE)); WRAPTYPE central_inverse ARGS(( WRAPTYPE )); void central_form_pullback ARGS(( REAL *, REAL *, REAL *, WRAPTYPE )); void screw_wrap ARGS(( REAL *, REAL *, WRAPTYPE )); WRAPTYPE screw_compose ARGS((WRAPTYPE,WRAPTYPE)); WRAPTYPE screw_inverse ARGS(( WRAPTYPE )); void screw_form_pullback ARGS(( REAL *, REAL *, REAL *, WRAPTYPE )); /* this function simply checks to see if the user has changed the order, and if so, updates the matrix */ void new_rot_order() { if ((rotorder_var = lookup_global(ROTORDER_NAME)) < 0) { rotorder_var = add_global(ROTORDER_NAME); globals(rotorder_var)->value.real = 4.; globals(rotorder_var)->flags = ORDINARY_PARAM | RECALC_PARAMETER | ALWAYS_RECALC; } rotorder = (int)globals(rotorder_var)->value.real; if ((genpower_var = lookup_global(GENPOWER_NAME)) < 0) { genpower_var = add_global(GENPOWER_NAME); globals(genpower_var)->value.real = 1.; globals(genpower_var)->flags = ORDINARY_PARAM | RECALC_PARAMETER | ALWAYS_RECALC; } genpower = (int)globals(genpower_var)->value.real; rotmat[0][0] = rotmat[1][1] = cos(2*M_PI*genpower/rotorder); rotmat[1][0] = -(rotmat[0][1] = sin(2*M_PI*genpower/rotorder)); } /* called by recalc() even if not using symmetry, hence don't set user var */ void reset_rot_order() { if (lookup_global(ROTORDER_NAME) >= 0) new_rot_order(); } /* this is called so often if shouldn't do any work unless never used before */ void rot_new_order() { if (rotorder_var<0) new_rot_order(); } /******************************************************************* * * function: rot_wrap * * purpose: Provide adjusted coordinates for vertices that get * wrapped under the group. Serves as example for user-written * symmetry function. * */ void rot_wrap(x,y,wrap) REAL *x; /* original coordinates */ REAL *y; /* wrapped coordinates */ WRAPTYPE wrap; /* encoded symmetry group element */ { int i,j,k; REAL t[MAXCOORD]; /* copy original coordinates */ memcpy((char*)y,(char*)x,SDIM*sizeof(REAL)); if (!wrap) return; /* 0 should always be used to encode the identity */ rot_new_order(); /* check that we're using the right order */ if ( 0 == (wrap = stdwrap(wrap))) return; /* adjust to have small abs value */ /* adjust for wrapping */ for ( i = 0 ; i < (wrap>0?wrap:-wrap); i++ ) { memcpy((char*)t,(char*)y,SDIM*sizeof(REAL)); /* copy in all coords, then adjust x,y */ for (j=0; j<2; j++) { t[j]=0.; for (k=0;k<2;k++) if (wrap>0) t[j] += y[k]*rotmat[j][k]; else t[j] += y[k]*rotmat[k][j]; } memcpy((char*)y,(char*)t,SDIM*sizeof(REAL)); } } /******************************************************************** * * function: rot_compose() * * purpose: do composition of two group elements * */ WRAPTYPE rot_compose(wrap1,wrap2) WRAPTYPE wrap1,wrap2; /* the elements to compose */ { rot_new_order(); return stdwrap(wrap1 + wrap2); } /******************************************************************** * * function: rot_inverse() * * purpose: return inverse of group element. * */ WRAPTYPE rot_inverse(wrap) WRAPTYPE wrap; /* the element to invert */ { rot_new_order(); return stdwrap(-wrap); } /******************************************************************* * * function: rot_form_pullback * * purpose: Pull back differential forms at vertices that get * wrapped under the group. Serves as example for user-written * symmetry function. * */ void rot_form_pullback(x,xform,yform,wrap) REAL *x; /* original coordinates */ REAL *xform; /* result pullback */ REAL *yform; /* original form input */ WRAPTYPE wrap; /* encoded symmetry group element */ { rot_new_order(); rot_wrap(yform,xform,-wrap); } /************************************************************************* * frot is a similar group but with a flip * (z -> -z) with every odd rotation * This uses the same rotorder variable (which better be even) * By John Sullivan, October, 1995 **************************************************************************/ /* prototype */ void frot_wrap ARGS((REAL *,REAL *,WRAPTYPE)); /******************************************************************* * function: frot_wrap */ void frot_wrap(x,y,wrap) REAL *x; /* original coordinates */ REAL *y; /* wrapped coordinates */ WRAPTYPE wrap; /* encoded symmetry group element */ { int i,j,k; REAL t[MAXCOORD]; /* copy original coordinates */ memcpy((char*)y,(char*)x,SDIM*sizeof(REAL)); if (!wrap) return; /* 0 should always be used to encode the identity */ rot_new_order(); /* check that we're using the right order */ if (rotorder&1) kb_error(1618,"Can't use flip_rotations with odd order\n",COMMAND_ERROR); if ( 0 == (wrap = stdwrap(wrap))) return; /* adjust to have small abs value */ /* adjust for wrapping */ for ( i = 0 ; i < (wrap>0?wrap:-wrap); i++ ) { memcpy((char*)t,(char*)y,SDIM*sizeof(REAL)); /* copy in all coords, then adjust x,y */ for (j=0; j<2; j++) { t[j]=0.; for (k=0;k<2;k++) if (wrap>0) t[j] += y[k]*rotmat[j][k]; else t[j] += y[k]*rotmat[k][j]; } memcpy((char*)y,(char*)t,SDIM*sizeof(REAL)); if (wrap&1) y[2] = -y[2]; /* the flip */ } } /******************************************************************* * * function: frot_form_pullback * * purpose: Pull back differential forms at vertices that get * wrapped under the group. Serves as example for user-written * symmetry function. * */ void frot_form_pullback(x,xform,yform,wrap) REAL *x; /* original coordinates */ REAL *xform; /* result pullback */ REAL *yform; /* original form input */ WRAPTYPE wrap; /* encoded symmetry group element */ { frot_wrap(yform,xform,-wrap); } /************************************************************************* * pgcube is the full point group of a cube * That is, all permutations and sign changes of the coordinates. * By John Sullivan, December, 1995 * * Wrap encoding: wrap&{1,2,4} give sign changes for x,y,z; * (wrap&24)/8 is power of (xyz) to cycle * (wrap&32)/32 tells whether to then swap x,y **************************************************************************/ #define pgcube_wr(signs,perm) (((perm)<<3)+(signs)) #define pgcube_perm(wrap) (((wrap)>>3)&7) #define pgcube_sgns(wrap) ((wrap)&7) #define pgcube_invperm(perm) ( (((perm)&4) || (!(perm)))? (perm) : 3-(perm) ) /* prototypes */ void pgcube_wrap ARGS((REAL *,REAL *,WRAPTYPE)); int pgcube_compperm ARGS((int,int)); /* compose two elts of S3 */ WRAPTYPE pgcube_inverse ARGS((WRAPTYPE)); int pgcube_permutesigns ARGS((int, int)); /******************************************************************* * function: pgcube_wrap */ void pgcube_wrap(x,y,wrap) REAL *x; /* original coordinates */ REAL *y; /* wrapped coordinates */ WRAPTYPE wrap; /* encoded symmetry group element */ { int i,j; REAL yy; /* copy original coordinates */ memcpy((char*)y,(char*)x,SDIM*sizeof(REAL)); if (!wrap) return; /* 0 should always be used to encode the identity */ /* adjust for wrapping */ for ( i = 0 ; i < 3; i++, wrap >>= 1 ) y[i] = x[i] * ((wrap&1)? -1:1); for ( j = 0 ; j < (wrap&3); j++ ) { yy = y[2]; y[2] = y[1]; y[1] = y[0]; y[0] = yy; } wrap >>= 2; if (wrap&1) { yy = y[1]; y[1] = y[0]; y[0] = yy; } } /************************************** * * function: pgcube_permutesigns * * purpose: use an S3 element to permute the bits of 3-bit number * */ static REAL xxx[3] = {1.,1.,1.}; int pgcube_permutesigns(signs, perm) int perm, signs; { int retval; REAL y[3]; pgcube_wrap(xxx,y,pgcube_wr(signs,perm)); retval = (y[0]<0.0) + ((y[1]<0.0) << 1) + ((y[2]<0.0) << 2); return retval; } /******************************************************************** * * function: pgcube_compose() * * purpose: do composition of two group elements * */ int pgcube_compperm(perm1,perm2) /* compose two elts of S3 */ int perm1,perm2; { int flip1,flip2,rot; flip1=perm1&4; flip2=perm2&4; rot = (perm2&3) + (flip2?-1:1)*(perm1&3); rot = (rot<0)? rot+3 : ( (rot>2)? rot-3:rot ); return (flip1^flip2) + rot; } WRAPTYPE pgcube_compose(wrap1,wrap2) WRAPTYPE wrap1,wrap2; /* the elements to compose */ { int sgns1,sgns2,perm1,perm2,comp,inv,xxx,yyy; sgns1 = pgcube_sgns(wrap1); perm1 = pgcube_perm(wrap1); sgns2 = pgcube_sgns(wrap2); perm2 = pgcube_perm(wrap2); comp = pgcube_compperm(perm1,perm2); inv = pgcube_invperm(perm1); xxx = pgcube_permutesigns(sgns2,inv); yyy = pgcube_wr(sgns1 ^ xxx,comp); return yyy; } /******************************************************************** * * function: pgcube_inverse() * * purpose: return inverse of group element. * */ WRAPTYPE pgcube_inverse(wrap) WRAPTYPE wrap; /* the element to invert */ { int sgns, perm; WRAPTYPE result; sgns = pgcube_sgns(wrap); perm = pgcube_perm(wrap); result = pgcube_wr(pgcube_permutesigns(sgns,perm), pgcube_invperm(perm)); return result; } /******************************************************************* * * function: pgcube_form_pullback * * purpose: Pull back differential forms at vertices that get * wrapped under the group. Serves as example for user-written * symmetry function. * */ void pgcube_form_pullback(x,xform,yform,wrap) REAL *x; /* original coordinates */ REAL *xform; /* result pullback */ REAL *yform; /* original form input */ WRAPTYPE wrap; /* encoded symmetry group element */ { pgcube_wrap(yform,xform,pgcube_inverse(wrap)); } /************************************************************************* * cubel is the full symmetry group of the unit cubic lattice * That is, all permutations and sign changes of the coordinates, * together with adding or subtracting integers. * By John Sullivan, December, 1995 * * Wrap encoding: wrap&{1,2,4} give sign changes for x,y,z; * (wrap&24)/8 is power of (xyz) to cycle * (wrap&32)/32 tells whether to then swap x,y * That is, wrap&63 is the same as for pgcube. * Then tr=wrap/64 is a torus wrap as in torus.c: three six-bit fields; * using TWRAPBITS (=6) and WRAPMASK (=037) from extern.h * Translation is to be applied _after_ other operations. **************************************************************************/ #define cubel_pg(w) ((w)&077) #define cubel_tr(w) ((w)>>6) #define cubel_wr(tr,pg) (( ((tr)&ALLWRAPMASK) <<6)+(pg)) #define WRAPNUM(x) ( (x)>(1<<(TWRAPBITS-2)) ? (x)-(1<<(TWRAPBITS-1)) : (x)) /* prototypes */ void cubel_wrap ARGS((REAL *,REAL *,WRAPTYPE)); WRAPTYPE cubel_inverse ARGS((WRAPTYPE)); /******************************************************************* * function: cubel_wrap */ void cubel_wrap(x,y,wrap) REAL *x; /* original coordinates */ REAL *y; /* wrapped coordinates */ WRAPTYPE wrap; /* encoded symmetry group element */ { int i,j; pgcube_wrap(x,y,cubel_pg(wrap)); wrap = cubel_tr(wrap); for (i=0; i<3; i++, wrap >>= TWRAPBITS) { int w = WRAPNUM(wrap & WRAPMASK); if ( web.torus_period ) for ( j = 0 ; j < SDIM ; j++ ) y[j] += w*web.torus_period[i][j]; else y[i] += w; } } /******************************************************************** * * function: cubel_compose() * * purpose: do composition of two group elements * */ /* pg_tr_compose: apply a cube pointgroup element to the translation part of another wrap */ WRAPTYPE pg_tr_compose(pg,tr) WRAPTYPE pg,tr; { WRAPTYPE wrap=0; int i; REAL x[3],y[3]; for (i=0; i<3; i++, tr >>= TWRAPBITS) { x[i] = WRAPNUM(tr & WRAPMASK); } pgcube_wrap(x,y,pg); for (i=2; i>=0; i--) { wrap = (wrap< -X * Encoding: 0 is identity, 1 is inversion */ void central_wrap(x,y,wrap) REAL *x; /* original coordinates */ REAL *y; /* wrapped coordinates */ WRAPTYPE wrap; /* encoded symmetry group element */ { int n; for ( n = 0 ; n < SDIM ; n++ ) y[n] = wrap ? -x[n] : x[n]; } WRAPTYPE central_compose(wrap1,wrap2) WRAPTYPE wrap1,wrap2; { return wrap1 ^ wrap2; } WRAPTYPE central_inverse(wrap) WRAPTYPE wrap; { return wrap; } void central_form_pullback(x,xform,yform,wrap) REAL *x; REAL *xform; REAL *yform; WRAPTYPE wrap; { int n; for ( n = 0 ; n < SDIM ; n++ ) xform[n] = wrap ? -yform[n] : yform[n]; } /**************************************************************** * * Screw symmetry, about z axis. Free group with one generator. * Wrap number is order of generator. */ #define LIFT 4.0 #define TWIST 0.0 static int lift_var = -1; static int twist_var = -1; #define LIFTNAME "screw_height" #define TWISTNAME "screw_angle" static long screw_stamp = -1; /* when global variables checked */ void screw_wrap(x,y,wrap) REAL *x; /* original coordinates */ REAL *y; /* wrapped coordinates */ WRAPTYPE wrap; /* encoded symmetry group element */ { REAL angle,lift; if ( screw_stamp < reset_timestamp ) { if ((lift_var = lookup_global(LIFTNAME)) < 0) { lift_var = add_global(LIFTNAME); globals(lift_var)->value.real = (REAL)LIFT; globals(lift_var)->flags = ORDINARY_PARAM | RECALC_PARAMETER | ALWAYS_RECALC; } if ((twist_var = lookup_global(TWISTNAME)) < 0) { twist_var = add_global(TWISTNAME); globals(twist_var)->value.real = (REAL)TWIST; globals(twist_var)->flags = ORDINARY_PARAM | RECALC_PARAMETER | ALWAYS_RECALC; } screw_stamp = reset_timestamp; } lift = globals(lift_var)->value.real; angle = globals(twist_var)->value.real * M_PI/180; y[0] = x[0]*cos(wrap*angle) - x[1]*sin(wrap*angle); y[1] = x[0]*sin(wrap*angle) + x[1]*cos(wrap*angle); y[2] = x[2] + wrap*lift; } WRAPTYPE screw_compose(wrap1,wrap2) WRAPTYPE wrap1,wrap2; { return wrap1 + wrap2; } WRAPTYPE screw_inverse(wrap) WRAPTYPE wrap; { return -wrap; } void screw_form_pullback(x,xform,yform,wrap) REAL *x; REAL *xform; REAL *yform; WRAPTYPE wrap; { REAL angle; angle = globals(twist_var)->value.real * M_PI/180; xform[0] = yform[0]*cos(wrap*angle) + yform[1]*sin(wrap*angle); xform[1] = -yform[0]*sin(wrap*angle) + yform[1]*cos(wrap*angle); xform[2] = yform[2]; } /************************************************************************** Symmetry group: quarter_turn 3D torus with quarter turn in identification of top and bottom. x and y periods taken to be 1. z period is the user-defined variable quarter_turn_period. Generators x,y,z. x and y as in regular torus mode. z is vertical translation with quarter turn: (x,y,z)->(-y,x,z). Relations: x z = z y^-1, y z = z x Numerical representation: as in torus, for powers of x,y,z with generators applied in that order. ***************************************************************************/ #define QUARTERTURNNAME "quarter_turn_period" int quarter_turn_var; void quarter_turn_wrap(x,y,wrap) REAL *x; /* original coordinates */ REAL *y; /* wrapped coordinates */ WRAPTYPE wrap; /* encoded symmetry group element */ { REAL zper; int xwrap,ywrap,zwrap; if ( quarter_turn_var < 0 ) { quarter_turn_var = lookup_global(QUARTERTURNNAME); if ( quarter_turn_var < 0) kb_error(4008, "quarter_turn_period variable not defined for quarter_turn symmetry\n", RECOVERABLE); } xwrap = wrap & WRAPMASK; if ( xwrap & 0x10 ) xwrap -= 0x20; ywrap = (wrap >> TWRAPBITS) & WRAPMASK; if ( ywrap & 0x10 ) ywrap -= 0x20; zwrap = (wrap >> 2*TWRAPBITS) & WRAPMASK; if ( zwrap & 0x10 ) zwrap -= 0x20; y[0] = x[0] + xwrap; y[1] = x[1] + ywrap; if ( zwrap ) { zper = globals(quarter_turn_var)->value.real; y[2] = x[2] + zwrap * zper; zwrap = (zwrap + 20) % 4; if ( zwrap == 1 ) { y[0] = -(x[1] + ywrap); y[1] = x[0] + xwrap; } else if ( zwrap == 2 ) { y[0] = -(x[0] + xwrap); y[1] = -(x[1] + ywrap); } else if ( zwrap == 3 ) { y[0] = x[1] + ywrap; y[1] = -(x[0] + xwrap); } } else y[2] = x[2]; } WRAPTYPE quarter_turn_compose(wrap1,wrap2) WRAPTYPE wrap1,wrap2; { int xwrap1,ywrap1,zwrap1; int xwrap2,ywrap2,zwrap2; if ( quarter_turn_var < 0 ) { quarter_turn_var = lookup_global(QUARTERTURNNAME); if ( quarter_turn_var < 0) kb_error(3877, "quarter_turn_period variable not defined for quarter_turn symmetry\n", RECOVERABLE); } xwrap1 = wrap1 & WRAPMASK; if ( xwrap1 & 0x10 ) xwrap1 -= 0x20; ywrap1 = (wrap1 >> TWRAPBITS) & WRAPMASK; if ( ywrap1 & 0x10 ) ywrap1 -= 0x20; zwrap1 = (wrap1 >> 2*TWRAPBITS) & WRAPMASK; if ( zwrap1 & 0x10 ) zwrap1 -= 0x20; xwrap2 = wrap2 & WRAPMASK; if ( xwrap2 & 0x10 ) xwrap2 -= 0x20; ywrap2 = (wrap2 >> TWRAPBITS) & WRAPMASK; if ( ywrap2 & 0x10 ) ywrap2 -= 0x20; zwrap2 = (wrap2 >> 2*TWRAPBITS) & WRAPMASK; if ( zwrap2 & 0x10 ) zwrap2 -= 0x20; zwrap2 = (zwrap2 + 20) % 4; if ( zwrap2 == 1 ) { int temp = xwrap1; xwrap1 = ywrap1; ywrap1 = -temp; } else if ( zwrap2 == 2 ) { xwrap1 = -xwrap1; ywrap1 = -ywrap1; } else if ( zwrap2 == 3 ) { int temp = xwrap1; xwrap1 = -ywrap1; ywrap1 = temp; } return (((zwrap1 + zwrap2) & WRAPMASK) << (2*TWRAPBITS)) + (((ywrap1 + ywrap2) & WRAPMASK) << TWRAPBITS) + ((xwrap1 + xwrap2) & WRAPMASK); } WRAPTYPE quarter_turn_inverse(wrap) WRAPTYPE wrap; { int temp; int xwrap,ywrap,zwrap; xwrap = wrap & WRAPMASK; if ( xwrap & 0x10 ) xwrap -= 0x20; ywrap = (wrap >> TWRAPBITS) & WRAPMASK; if ( ywrap & 0x10 ) ywrap -= 0x20; zwrap = (wrap >> 2*TWRAPBITS) & WRAPMASK; if ( zwrap & 0x10 ) zwrap -= 0x20; xwrap = -xwrap; ywrap = -ywrap; zwrap = -zwrap; switch ( (zwrap + 20) % 4 ) { case 1: temp = xwrap; xwrap = ywrap; ywrap = -temp; break; case 2: xwrap = -xwrap; ywrap = -ywrap; break; case 3: temp = xwrap; xwrap = -ywrap; ywrap = temp; break; } return ((zwrap & WRAPMASK) << (2*TWRAPBITS)) + ((ywrap & WRAPMASK) << TWRAPBITS) + (xwrap & WRAPMASK); } void quarter_turn_form_pullback(x,xform,yform,wrap) REAL *x; REAL *xform; REAL *yform; WRAPTYPE wrap; { int zwrap; zwrap = (wrap >> 2*TWRAPBITS) & WRAPMASK; yform[2] = xform[2]; switch ( (zwrap + 20) % 4 ) { case 0: yform[0] = xform[0]; yform[1] = xform[1]; break; case 1: yform[0] = xform[1]; yform[1] = -xform[0]; break; case 2: yform[0] = -xform[0]; yform[1] = -xform[1]; break; case 3: yform[0] = -xform[1]; yform[1] = xform[0]; break; break; } } evolver-2.30c.dfsg/src/khyp.c0000644000175300017530000002124611410765113016341 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************* * * file: khyp.c * * Purpose: Illustration of how to do quotient spaces. * This example does a Klein hyperbolic model in R^2. * The user needs to define the integer representation * used for group elements and three functions to * do group operations. WRAPTYPE is the type of * group element representation, currently long int. * You must write your own versions of group_wrap(), * group_compose(), and group_inverse(). * * You don't have the privilege * of defining your own datafile syntax, but you can access * values of parameters defined in the datafile as * *var, where var = get_global(char *name). */ #include "include.h" #define PDIM 4 /* Minkowski space dimension */ /* Minkowski dim is tacked on before other dimensions so this file can be used in different dimensions */ /* Description of group element encoding: The Klein disk is always mapped back to a Minkowski hyperbola for transformations. There are 8 translation elements that translate the fundamental region to one of its neighbors. Translating around a vertex gives a circular string of the 8 elements. The group elements encoded are substrings of the 8, with null string being the identity. Encoding is 4 bits for start element, 4 bits for stop (actually the one after stop so 0 0 is identity). */ #define GENUS2BITS 4 #define GENUS2MASK 7 #define CHOP(g) ((g)&GENUS2MASK) #define END(g) CHOP((g)>>GENUS2BITS) #define DFF(g,h,d) (CHOP((h)-(g))==(d)) #define ENC(g1,gk) (CHOP(g1) | (CHOP(gk)<>GENUS2BITS); if (gk=g1; g--,j=1-j) { matmult(eg[CHOP(g)],z[j],z[1-j]); } for ( i = 0 ; i < SDIM ; i++ ) y[i] = z[j][i+1]/z[j][0]; } /******************************************************************** * * function: khyp_compose() * * purpose: do composition of two group elements khyp_wrap(khyp_wrap(pt,w),v) = khyp_wrap(pt,khyp_compose(v,w)) If the current wrap is v, and we get to an edge with wrap w, and its head is at pt, then either of these should give the unwrapped coordinates of its head (relative to khyp_wrap(tail,v) being its tail). * */ /* generators 1=A,2=b,3=C,4=d,5=a,6=B,7=c,0=8=D */ /* inverses differ by 4 */ #define TRY(g1,gk,h1,hk) if (DFF(gk,h1,0)) return WRAP(g1,hk) WRAPTYPE khyp_compose(gw,hw) WRAPTYPE gw,hw; /* the elements to compose */ { int g1,gk, h1,hk; if (gw==0) return hw; if (hw==0) return gw; g1 = CHOP(gw); gk = END(gw); h1 = CHOP(hw); hk = END(hw); TRY(g1,gk,h1,hk); /* h nicely follows after g */ if (DFF(h1,hk,1)) TRY(g1,gk,h1-3,h1-4); /* equiv string of 7 */ if (DFF(g1,gk,1)) TRY(g1-3,g1-4,h1,hk); /* equiv string of 7 */ if (DFF(h1,hk,1) && DFF(g1,gk,1)) TRY(g1-3,g1-4,h1-3,h1-4); sprintf(msg,"Trying to compose %d-%d and %d-%d\n",g1,gk,h1,hk); strcat(msg,"khyp: Wrap outside known range\n"); kb_error(1305,msg,WARNING); return 0; } /******************************************************************** * * function: khyp_inverse() * * purpose: return inverse of group element. * */ WRAPTYPE khyp_inverse(wrap) WRAPTYPE wrap; /* the element to invert */ { return WRAP(END(wrap),CHOP(wrap)); } /******************************************************************* * * function: khyp_form_pullback * * purpose: Pull back differential forms at vertices that get * wrapped. * */ void khyp_form_pullback(x,xform,yform,wrap) REAL *x; /* original coordinates */ REAL *xform; /* result pullback */ REAL *yform; /* original form input */ WRAPTYPE wrap; /* encoded symmetry group element */ { int i,j; REAL trans[PDIM][PDIM]; REAL jac[PDIM][PDIM]; /* Jacobian matrix */ REAL y[PDIM]; REAL w[PDIM]; /* Minkowski coord of x */ int g1,gk,g; /* element numbers */ if ( wrap == 0 ) /* just copy */ { memcpy((char *)xform,(char*)yform,SDIM*sizeof(REAL)); return; } g1 = CHOP(wrap); gk = CHOP(wrap>>GENUS2BITS); if (gkid) & NEGBOUNDARY) ? -1.0 : 1.0; if ( web.modeltype == LAGRANGE ) return facet_2form_integral_lagrange(f_info); for ( m = 0 ; m < gauss2D_num ; m++ ) { f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( i = 0, k = 0 ; i < SDIM ; i++ ) for ( j = i+1 ; j < SDIM ; j++,k++ ) { REAL form = gauss2Dwt[m]* eval(METH_INSTANCE(f_info->method)->expr[k],f_info->gauss_pt[m],f_info->id,NULL); value += (f_info->sides[0][0][i]*f_info->sides[0][1][j] - f_info->sides[0][0][j]*f_info->sides[0][1][i])*form; } } return sign*value/2; /* 2 is triangle factor for normal */ } /********************************************************************* * * function: facet_2form_integral_grad() * * purpose: method gradient * */ REAL facet_2form_integral_grad(f_info) struct qinfo *f_info; { int m,n,i,j,k; REAL value = 0.0; REAL form[MAXCOORD][MAXCOORD]; /* as antisymmetric matrix */ REAL derivs[MAXCOORD][MAXCOORD][MAXCOORD]; /* coord is last index */ REAL sum; REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; if ( web.modeltype == LAGRANGE ) return facet_2form_integral_lagrange_grad(f_info); for ( i = 0 ; i < SDIM ; i++ ) form[i][i] = 0.0; for ( m = 0 ; m < gauss2D_num ; m++ ) { REAL weight = sign*gauss2Dwt[m]; f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( i = 0, k = 0 ; i < SDIM ; i++ ) { for ( n = 0 ; n < SDIM ;n++ ) derivs[i][i][n] = 0.0; for ( j = i+1 ; j < SDIM ; j++,k++ ) { eval_all(METH_INSTANCE(f_info->method)->expr[k],f_info->gauss_pt[m],SDIM, form[i]+j, derivs[i][j],f_info->id); form[j][i] = - form[i][j]; for ( n = 0 ; n < SDIM ;n++ ) derivs[j][i][n] = -derivs[i][j][n]; } } for ( i = 0, sum = 0.0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) sum += f_info->sides[0][0][i]*form[i][j]*f_info->sides[0][1][j]; value += weight*sum; for ( k = 0 ; k < SDIM ; k++ ) { for ( i = 0, sum = 0.0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) sum += f_info->sides[0][0][i]*derivs[i][j][k]*f_info->sides[0][1][j]; f_info->grad[0][k] += weight*(gauss2Dpt[m][0]*sum - SDIM_dot(form[k],f_info->sides[0][1]) + SDIM_dot(form[k],f_info->sides[0][0]))/2; f_info->grad[1][k] += weight*(gauss2Dpt[m][1]*sum + SDIM_dot(form[k],f_info->sides[0][1]))/2; f_info->grad[2][k] += weight*(gauss2Dpt[m][2]*sum - SDIM_dot(form[k],f_info->sides[0][0]))/2; } } return value/2; } /********************************************************************* * * function: facet_2form_integral_hess() * * purpose: method gradient and hessian * */ /* derivatives of facet sides with respect to vertices of facet */ int sign1[FACET_VERTS] = { -1, 1, 0 }; int sign2[FACET_VERTS] = { -1, 0, 1 }; REAL facet_2form_integral_hess(f_info) struct qinfo *f_info; { int m,n,i,j,k,p,q; REAL value = 0.0; REAL form[MAXCOORD][MAXCOORD]; /* as antisymmetric matrix */ REAL derivs[MAXCOORD][MAXCOORD][MAXCOORD]; /* coord is last index */ MAT4D(second,MAXCOORD,MAXCOORD,MAXCOORD,MAXCOORD); REAL sum; REAL *s1 = f_info->sides[0][0]; REAL *s2 = f_info->sides[0][1]; REAL s1Fu,s2Fu,Fvs1,Fvs2,s1Fuvs2; REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; if ( web.modeltype == LAGRANGE ) return facet_2form_integral_lagrange_hess(f_info); for ( i = 0, k = 0 ; i < SDIM ; i++ ) { form[i][i] = 0.0; for ( n = 0 ; n < SDIM ;n++ ) { derivs[i][i][n] = 0.0; for ( j = 0 ; j < SDIM ;j++ ) second[i][i][n][j] = 0.0; } } for ( m = 0 ; m < gauss2D_num ; m++ ) { REAL weight = sign*gauss2Dwt[m]; f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( i = 0, k = 0 ; i < SDIM ; i++ ) { for ( j = i+1 ; j < SDIM ; j++,k++ ) { eval_second(METH_INSTANCE(f_info->method)->expr[k],f_info->gauss_pt[m],SDIM, form[i]+j, derivs[i][j],second[i][j],f_info->id); form[j][i] = - form[i][j]; for ( n = 0 ; n < SDIM ;n++ ) derivs[j][i][n] = -derivs[i][j][n]; for ( n = 0 ; n < SDIM ;n++ ) for ( p = 0 ; p < SDIM ;p++ ) second[j][i][n][p] = -second[i][j][n][p]; } } for ( i = 0, sum = 0.0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) sum += s1[i]*form[i][j]*s2[j]; value += weight*sum; /* gradient */ for ( k = 0 ; k < SDIM ; k++ ) { for ( i = 0, sum = 0.0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) sum += s1[i]*derivs[i][j][k]*s2[j]; f_info->grad[0][k] += weight*(gauss2Dpt[m][0]*sum - SDIM_dot(form[k],s2) + SDIM_dot(form[k],s1))/2; f_info->grad[1][k] += weight*(gauss2Dpt[m][1]*sum + SDIM_dot(form[k],s2))/2; f_info->grad[2][k] += weight*(gauss2Dpt[m][2]*sum - SDIM_dot(form[k],s1))/2; } /* hessian */ for ( p = 0 ; p < SDIM ; p++ ) for ( q = 0 ; q < SDIM ; q++ ) { for ( i = 0, s1Fu = 0.0 ; i < SDIM ; i++ ) s1Fu += s1[i]*derivs[i][q][p]; for ( i = 0, s2Fu = 0.0 ; i < SDIM ; i++ ) s2Fu += s2[i]*derivs[i][q][p]; for ( i = 0, Fvs1 = 0.0 ; i < SDIM ; i++ ) Fvs1 += derivs[p][i][q]*s1[i]; for ( i = 0, Fvs2 = 0.0 ; i < SDIM ; i++ ) Fvs2 += derivs[p][i][q]*s2[i]; for ( i = 0, s1Fuvs2 = 0.0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) s1Fuvs2 += s1[i]*second[i][j][p][q]*s2[j]; for ( i = 0 ; i < FACET_VERTS ; i++ ) for ( j = 0 ; j < FACET_VERTS ; j++ ) f_info->hess[i][j][p][q] += weight*( sign1[i]*form[p][q]*sign2[j] - sign2[i]*form[p][q]*sign1[j] + gauss2Dpt[m][i]*(s1Fu*sign2[j] - s2Fu*sign1[j]) + gauss2Dpt[m][j]*(sign1[i]*Fvs2 - sign2[i]*Fvs1) + gauss2Dpt[m][i]*gauss2Dpt[m][j]*s1Fuvs2 )/2; } } return value/2; } /****************************************************************************** facet_2form_integral Lagrangian model ******************************************************************************/ /********************************************************************* * * function: facet_2form_integral_lagrange() * * purpose: method value * */ REAL facet_2form_integral_lagrange(f_info) struct qinfo *f_info; { int m,i,j,k; REAL value=0.0,form; REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; int dim = web.dimension; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; for ( i = 0, k = 0 ; i < SDIM ; i++ ) for ( j = i+1 ; j < SDIM ; j++,k++ ) { for ( m = 0 ; m < gl->gnumpts ; m++ ) { f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ form = gl->gausswt[m]* eval(METH_INSTANCE(f_info->method)->expr[k],f_info->gauss_pt[m],f_info->id,NULL); value += (f_info->sides[m][0][i]*f_info->sides[m][1][j] - f_info->sides[m][0][j]*f_info->sides[m][1][i])*form; } } return sign*value/2; /* 2 is triangle factor */ } /********************************************************************* * * function: facet_2form_integral_lagrange_grad() * * purpose: method gradient * */ REAL facet_2form_integral_lagrange_grad(f_info) struct qinfo *f_info; { int m,n,i,j,k; REAL value = 0.0; REAL form[MAXCOORD][MAXCOORD]; /* as antisymmetric matrix */ REAL derivs[MAXCOORD][MAXCOORD][MAXCOORD]; /* coord is last index */ REAL sum; int dim = web.dimension; REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; int ctrl = web.skel[FACET].ctrlpts; for ( i = 0 ; i < SDIM ; i++ ) { form[i][i] = 0.0; for ( n = 0 ; n < SDIM ;n++ ) derivs[i][i][n] = 0.0; } for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL weight = sign*gl->gausswt[m]/2; f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( i = 0, k = 0 ; i < SDIM ; i++ ) { for ( j = i+1 ; j < SDIM ; j++,k++ ) { eval_all(METH_INSTANCE(f_info->method)->expr[k],f_info->gauss_pt[m],SDIM, form[i]+j, derivs[i][j],f_info->id); form[j][i] = - form[i][j]; for ( n = 0 ; n < SDIM ;n++ ) derivs[j][i][n] = -derivs[i][j][n]; } } for ( i = 0, sum = 0.0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { sum += f_info->sides[m][0][i]*form[i][j]*f_info->sides[m][1][j]; for ( n = 0 ; n < ctrl ; n++ ) { f_info->grad[n][i] += weight*gl->gpolypart[m][0][n]*form[i][j] *f_info->sides[m][1][j]; for ( k= 0 ; k < SDIM ; k++ ) f_info->grad[n][k] += weight*f_info->sides[m][0][i] *gl->gpoly[m][n]*derivs[i][j][k]*f_info->sides[m][1][j]; f_info->grad[n][j] += weight*f_info->sides[m][0][i]*form[i][j] *gl->gpolypart[m][1][n]; } } value += weight*sum; } return value; } /********************************************************************* * * function: facet_2form_integral_lagrange_hess() * * purpose: method gradient and hessian * */ REAL facet_2form_integral_lagrange_hess(f_info) struct qinfo *f_info; { int m,n,i,j,k,p; REAL value = 0.0; REAL form[MAXCOORD][MAXCOORD]; /* as antisymmetric matrix */ REAL derivs[MAXCOORD][MAXCOORD][MAXCOORD]; /* coord is last index */ MAT4D(second,MAXCOORD,MAXCOORD,MAXCOORD,MAXCOORD); REAL sum; REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; int dim = web.dimension; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; int ctrl = web.skel[FACET].ctrlpts; for ( i = 0; i < SDIM ; i++ ) { form[i][i] = 0.0; for ( n = 0 ; n < SDIM ;n++ ) { derivs[i][i][n] = 0.0; for ( j = 0 ; j < SDIM ;j++ ) second[i][i][n][j] = 0.0; } } for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL weight = sign*gl->gausswt[m]/2; f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( i = 0, k = 0 ; i < SDIM ; i++ ) { for ( j = i+1 ; j < SDIM ; j++,k++ ) { eval_second(METH_INSTANCE(f_info->method)->expr[k],f_info->gauss_pt[m], SDIM, form[i]+j, derivs[i][j],second[i][j],f_info->id); form[j][i] = - form[i][j]; for ( n = 0 ; n < SDIM ;n++ ) derivs[j][i][n] = -derivs[i][j][n]; for ( n = 0 ; n < SDIM ;n++ ) for ( p = 0 ; p < SDIM ;p++ ) second[j][i][n][p] = -second[i][j][n][p]; } } for ( i = 0, sum = 0.0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { sum += f_info->sides[m][0][i]*form[i][j]*f_info->sides[m][1][j]; for ( n = 0 ; n < ctrl ; n++ ) { int nn,kk; /* gradient terms */ f_info->grad[n][i] += weight*gl->gpolypart[m][0][n]*form[i][j] *f_info->sides[m][1][j]; for ( k= 0 ; k < SDIM ; k++ ) f_info->grad[n][k] += weight*f_info->sides[m][0][i] *gl->gpoly[m][n]*derivs[i][j][k]*f_info->sides[m][1][j]; f_info->grad[n][j] += weight*f_info->sides[m][0][i]*form[i][j] *gl->gpolypart[m][1][n]; /* hessian terms */ for ( nn = 0; nn < ctrl ; nn++ ) { for ( kk = 0 ; kk < SDIM ; kk++ ) f_info->hess[n][nn][i][kk] += weight*gl->gpolypart[m][0][n] *gl->gpoly[m][nn]*derivs[i][j][kk]*f_info->sides[m][1][j]; f_info->hess[n][nn][i][j] += weight*gl->gpolypart[m][0][n] *form[i][j]*gl->gpolypart[m][1][nn]; for ( k= 0 ; k < SDIM ; k++ ) { f_info->hess[n][nn][k][i] += weight*gl->gpolypart[m][0][nn] *gl->gpoly[m][n]*derivs[i][j][k]*f_info->sides[m][1][j]; for ( kk = 0 ; kk < SDIM ; kk++ ) f_info->hess[n][nn][k][kk] += weight*f_info->sides[m][0][i] *gl->gpoly[m][n]*second[i][j][k][kk]*gl->gpoly[m][nn] *f_info->sides[m][1][j]; f_info->hess[n][nn][k][j] += weight*f_info->sides[m][0][i] *gl->gpoly[m][n]*derivs[i][j][k]*gl->gpolypart[m][1][nn]; } f_info->hess[n][nn][j][i] += weight*gl->gpolypart[m][0][nn] *form[i][j]*gl->gpolypart[m][1][n]; for ( kk = 0 ; kk < SDIM ; kk++ ) f_info->hess[n][nn][j][kk] += weight*f_info->sides[m][0][i] *derivs[i][j][kk]*gl->gpoly[m][nn]*gl->gpolypart[m][1][n]; } } } value += weight*sum; } return value; } /********************************************************************* facet_2form_sq_integral method Integral of facetwise square of 2-form over facet. Works in n dim. 2-form components in method instance expr list in lexicographic order, i.e. 01,02,03,12,13,23. For Andy Hanson's symplectic area minimization. *********************************************************************/ /********************************************************************* * * function: facet_2form_sq_integral_init() * * purpose: Check illegalities * */ void facet_2form_sq_integral_init(mode,mi) int mode; struct method_instance *mi; { if ( web.dimension != 2 ) kb_error(2412,"facet_2form_sq_integral method only for 2D facets.\n",RECOVERABLE); if ( web.modeltype != LINEAR ) kb_error(2413,"facet_2form_sq_integral method only for LINEAR model.\n",RECOVERABLE); } /********************************************************************* * * function: facet_2form_sq_integral() * * purpose: method value * */ REAL facet_2form_sq_integral(f_info) struct qinfo *f_info; { int m,i,j,k; REAL value=0.0; for ( m = 0 ; m < gauss2D_num ; m++ ) { f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( i = 0, k = 0 ; i < SDIM ; i++ ) for ( j = i+1 ; j < SDIM ; j++,k++ ) { REAL form = gauss2Dwt[m]* eval(METH_INSTANCE(f_info->method)->expr[k],f_info->gauss_pt[m], f_info->id,NULL); value += (f_info->sides[0][0][i]*f_info->sides[0][1][j] - f_info->sides[0][0][j]*f_info->sides[0][1][i])*form; } } return value*value/4; /* 2 is triangle factor for normal */ } /********************************************************************* * * function: facet_2form_sq_integral_grad() * * purpose: method gradient * */ REAL facet_2form_sq_integral_grad(f_info) struct qinfo *f_info; { int m,n,i,j,k; REAL value = 0.0; REAL form[MAXCOORD][MAXCOORD]; /* as antisymmetric matrix */ REAL derivs[MAXCOORD][MAXCOORD][MAXCOORD]; /* coord is last index */ REAL sum; for ( i = 0 ; i < SDIM ; i++ ) form[i][i] = 0.0; for ( m = 0 ; m < gauss2D_num ; m++ ) { REAL weight = gauss2Dwt[m]; f_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( i = 0, k = 0 ; i < SDIM ; i++ ) { for ( n = 0 ; n < SDIM ;n++ ) derivs[i][i][n] = 0.0; for ( j = i+1 ; j < SDIM ; j++,k++ ) { eval_all(METH_INSTANCE(f_info->method)->expr[k],f_info->gauss_pt[m],SDIM, form[i]+j, derivs[i][j],f_info->id); form[j][i] = - form[i][j]; for ( n = 0 ; n < SDIM ;n++ ) derivs[j][i][n] = -derivs[i][j][n]; } } for ( i = 0, sum = 0.0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) sum += f_info->sides[0][0][i]*form[i][j]*f_info->sides[0][1][j]; value += weight*sum; for ( k = 0 ; k < SDIM ; k++ ) { for ( i = 0, sum = 0.0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) sum += f_info->sides[0][0][i]*derivs[i][j][k]*f_info->sides[0][1][j]; f_info->grad[0][k] += weight*(gauss2Dpt[m][0]*sum - SDIM_dot(form[k],f_info->sides[0][1]) + SDIM_dot(form[k],f_info->sides[0][0]))/2; f_info->grad[1][k] += weight*(gauss2Dpt[m][1]*sum + SDIM_dot(form[k],f_info->sides[0][1]))/2; f_info->grad[2][k] += weight*(gauss2Dpt[m][2]*sum - SDIM_dot(form[k],f_info->sides[0][0]))/2; } } /* adjust grad for being the square */ for ( i = 0 ; i < FACET_VERTS ; i++ ) for ( k = 0 ; k < SDIM ; k++ ) f_info->grad[i][k] *= value; return value*value/4; } /****************************************************************************** Soapfilm Gravity quantity stuff ******************************************************************************/ /*************************************************************** * * function: gravity_init() * * purpose: initialization for gravity method */ void gravity_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { /* method modulus is gravitation constant */ if ( gravity_quantity_num >= 0 ) GEN_QUANT(gravity_quantity_num)->modulus = web.gravflag ? web.grav_const : 0.0; } /********************************************************************************** * * function: full_gravity_init() * * purpose: initialization for gravity method using built-in G and body densities */ void full_gravity_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { /* method modulus is gravitation constant */ mi->flags &= FAKE_IMPLICIT; GEN_QUANT(gravity_quantity_num)->modulus = web.gravflag ? web.grav_const : 0.0; } /************************************************************** * * function: gravity_all() * * purpose: calculates value, gradient, and hessian of one * facet due to gravitational potential. * * input: info about vertex is in qinfo structure. * */ REAL gravity_all_q ARGS((struct qinfo *,int)); REAL gravity_all_lagrange ARGS((struct qinfo *,int)); REAL gravity_all(f_info,mode) struct qinfo *f_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ { int i,j; REAL jac; /* jacobian */ REAL sum = 0.0; /* quadratic z sum of facet */ REAL djdx[MAXCOORD],djdy[MAXCOORD],dsdz[MAXCOORD]; REAL c = 1/24.; /* coefficient */ body_id b_id; REAL gdensity; if ( web.modeltype == QUADRATIC ) return gravity_all_q(f_info,mode); if ( web.modeltype == LAGRANGE ) return gravity_all_lagrange(f_info,mode); b_id = get_facet_body(f_info->id); if ( METH_INSTANCE(f_info->method)->flags & (IMPLICIT_INSTANCE|FAKE_IMPLICIT) ) { /* include density difference */ gdensity = 0.0; if ( valid_id(b_id) ) gdensity += get_body_density(b_id); b_id = get_facet_body(facet_inverse(f_info->id)); if ( valid_id(b_id) ) gdensity -= get_body_density(b_id); if ( gdensity == 0.0 ) return 0.0; c *= gdensity; } jac = f_info->sides[0][0][0]*f_info->sides[0][1][1] - f_info->sides[0][1][0]*f_info->sides[0][0][1]; for ( i = 0 ; i < f_info->vcount ; i++ ) for ( j = 0 ; j <= i ; j++ ) sum += f_info->x[i][2]*f_info->x[j][2]; if ( mode == METHOD_VALUE ) return jac*sum*c; djdx[0] = (-f_info->sides[0][1][1] + f_info->sides[0][0][1]); djdx[1] = f_info->sides[0][1][1]; djdx[2] = -f_info->sides[0][0][1]; djdy[0] = (-f_info->sides[0][0][0] + f_info->sides[0][1][0]); djdy[1] = -f_info->sides[0][1][0]; djdy[2] = f_info->sides[0][0][0]; dsdz[0] = 2*f_info->x[0][2]+f_info->x[1][2]+f_info->x[2][2]; dsdz[1] = f_info->x[0][2]+2*f_info->x[1][2]+f_info->x[2][2]; dsdz[2] = f_info->x[0][2]+f_info->x[1][2]+2*f_info->x[2][2]; for ( i = 0 ; i < 3; i++ ) { f_info->grad[i][0] = djdx[i]*sum*c; f_info->grad[i][1] = djdy[i]*sum*c; f_info->grad[i][2] = dsdz[i]*jac*c; } if ( mode == METHOD_GRADIENT ) return jac*sum*c; /* second partials, self */ for ( i = 0 ; i < 3; i++ ) { f_info->hess[i][i][0][2] = f_info->hess[i][i][2][0] = djdx[i]*dsdz[i]*c; f_info->hess[i][i][1][2] = f_info->hess[i][i][2][1] = djdy[i]*dsdz[i]*c; f_info->hess[i][i][2][2] = 2*jac*c; f_info->hess[i][i][0][0] = f_info->hess[i][i][0][1] = f_info->hess[i][i][1][0] = f_info->hess[i][i][1][1] = 0.0; } /* second partials, mixed */ for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < 3 ; j++ ) { if ( j == i ) continue; f_info->hess[i][j][0][0] = f_info->hess[i][j][1][1] = 0.0; f_info->hess[i][j][0][1] = (j==((i+1)%3)) ? sum*c : -sum*c; f_info->hess[i][j][1][0] = (j==((i+1)%3)) ? -sum*c : sum*c; f_info->hess[i][j][0][2] = djdx[i]*dsdz[j]*c; f_info->hess[i][j][2][0] = djdx[j]*dsdz[i]*c; f_info->hess[i][j][1][2] = djdy[i]*dsdz[j]*c; f_info->hess[i][j][2][1] = djdy[j]*dsdz[i]*c; f_info->hess[i][j][2][2] = jac*c; } return jac*sum*c; } /************************************************************** * * function: gravity_all_q() * * purpose: calculates value, gradient, and hessian of one * facet due to gravitational potential. Quadratic model. * * input: info about vertex is in qinfo structure. * */ REAL gravity_all_q(f_info,mode) struct qinfo *f_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ { int k,kk,m; REAL jac; /* jacobian */ REAL **x,**tang; REAL **g=NULL,****h=NULL; REAL value = 0.0; body_id b_id; REAL gdensity = 1.0; x = f_info->x; if ( mode == METHOD_GRADIENT ) { g = f_info->grad; } else if ( mode == METHOD_HESSIAN ) { g = f_info->grad; h = f_info->hess; } if ( METH_INSTANCE(f_info->method)->flags & (IMPLICIT_INSTANCE|FAKE_IMPLICIT) ) { /* include density difference */ b_id = get_facet_body(f_info->id); gdensity = 0.0; if ( valid_id(b_id) ) gdensity += get_body_density(b_id); b_id = get_facet_body(facet_inverse(f_info->id)); if ( valid_id(b_id) ) gdensity -= get_body_density(b_id); if ( gdensity == 0.0 ) return 0.0; } for ( m = 0 ; m < gauss2D_num ; m++ ) { REAL weight = gdensity*gauss2Dwt[m]/2; REAL z; for ( k = 0, z = 0.0 ; k < ctrl_num ; k++ ) z += gpoly[m][k]*x[k][2]; tang = f_info->sides[m]; jac = tang[0][0]*tang[1][1] - tang[0][1]*tang[1][0]; value += weight*z*z*jac/2; if ( mode == METHOD_VALUE ) continue; for ( k = 0 ; k < ctrl_num ; k++ ) { g[k][0] += weight*z*z/2*(gpolypartial[m][0][k]*tang[1][1] - tang[0][1]*gpolypartial[m][1][k]); g[k][1] += weight*z*z/2*(tang[0][0]*gpolypartial[m][1][k] - gpolypartial[m][0][k]*tang[1][0]); g[k][2] += weight*z*gpoly[m][k]*jac; } if ( mode == METHOD_GRADIENT ) continue; /* hessian */ for ( k = 0 ; k < ctrl_num ; k++ ) for ( kk = 0 ; kk < ctrl_num ; kk++ ) { h[k][kk][0][1] += weight*z*z/2*(gpolypartial[m][0][k]*gpolypartial[m][1][kk] - gpolypartial[m][0][kk]*gpolypartial[m][1][k]); h[k][kk][0][2] += weight*z*gpoly[m][kk]*(gpolypartial[m][0][k]*tang[1][1] - tang[0][1]*gpolypartial[m][1][k]); h[k][kk][1][0] += weight*z*z/2*(gpolypartial[m][0][kk]*gpolypartial[m][1][k] - gpolypartial[m][0][k]*gpolypartial[m][1][kk]); h[k][kk][1][2] += weight*z*gpoly[m][kk]*(tang[0][0]*gpolypartial[m][1][k] - gpolypartial[m][0][k]*tang[1][0]); h[k][kk][2][0] += weight*z*gpoly[m][k]*(gpolypartial[m][0][kk]*tang[1][1] - tang[0][1]*gpolypartial[m][1][kk]); h[k][kk][2][1] += weight*z*gpoly[m][k]*(tang[0][0]*gpolypartial[m][1][kk] - gpolypartial[m][0][kk]*tang[1][0]); h[k][kk][2][2] += weight*gpoly[m][kk]*gpoly[m][k]*jac; } } return value; } /************************************************************** * * function: gravity_all_lagrange() * * purpose: calculates value, gradient, and hessian of one * facet due to gravitational potential. Lagrange model. * * input: info about vertex is in qinfo structure. * */ REAL gravity_all_lagrange(f_info,mode) struct qinfo *f_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ { REAL value = 0.0; body_id b_id; REAL gdensity = 1.0; int i,m,j,k,jj,ii,kk; REAL z; REAL val; MAT2D(mat,MAXCOORD,MAXCOORD); REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; int dim = web.dimension; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; MAT4D(dethess,MAXCOORD,MAXCOORD,MAXCOORD,MAXCOORD); if ( METH_INSTANCE(f_info->method)->flags & (IMPLICIT_INSTANCE|FAKE_IMPLICIT) ) { /* include density difference */ b_id = get_facet_body(f_info->id); gdensity = 0.0; if ( valid_id(b_id) ) gdensity += get_body_density(b_id); b_id = get_facet_body(facet_inverse(f_info->id)); if ( valid_id(b_id) ) gdensity -= get_body_density(b_id); if ( gdensity == 0.0 ) return 0.0; } for ( j = 0 ; j < SDIM ; j++ ) mat[web.dimension][j] = 0.0; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL det; REAL weight = sign*gdensity*gl->gausswt[m]/factorial[dim]; for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) mat[i][j] = f_info->sides[m][i][j]; /* mat destroyed by det */ z = f_info->gauss_pt[m][SDIM-1]; val = z*z/2; mat[web.dimension][SDIM-1] = val; if ( mode == METHOD_HESSIAN ) det_hess(mat,dethess,SDIM); det = det_adjoint(mat,SDIM); value += weight*det; if ( mode == METHOD_VALUE ) continue; /* gradient */ for ( k = 0 ; k < gl->lagpts; k++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( i = 0 ; i < dim ; i++ ) { f_info->grad[k][j] += weight*gl->gpolypart[m][i][k]*mat[j][i]; } for ( k = 0 ; k < gl->lagpts ; k++ ) f_info->grad[k][SDIM-1] += weight*gl->gpoly[m][k]*z*mat[SDIM-1][dim]; if ( mode == METHOD_GRADIENT ) continue; /* hessian */ for ( k = 0 ; k < gl->lagpts ; k++ ) for ( kk = 0 ; kk < gl->lagpts ; kk++ ) { REAL h; for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) { h = 0.0; for ( i = 0 ; i < dim ; i++ ) for ( ii = 0 ; ii < dim ; ii++ ) { h += dethess[i][j][ii][jj] *gl->gpolypart[m][i][k]*gl->gpolypart[m][ii][kk]; } f_info->hess[k][kk][j][jj] += weight*h; } for ( j = 0 ; j < SDIM ; j++ ) { h = 0.0; for ( i = 0 ; i < dim ; i++ ) h += dethess[i][j][dim][SDIM-1]*gl->gpolypart[m][i][k] *gl->gpoly[m][kk]*z; f_info->hess[k][kk][j][SDIM-1] += weight*h; } for ( jj = 0 ; jj < SDIM ; jj++ ) { h = 0.0; for ( ii = 0 ; ii < dim ; ii++ ) h += dethess[dim][SDIM-1][ii][jj]*gl->gpolypart[m][ii][kk] *gl->gpoly[m][k]*z; f_info->hess[k][kk][SDIM-1][jj] += weight*h; } h = gl->gpoly[m][k]*gl->gpoly[m][kk]*mat[SDIM-1][web.dimension]; f_info->hess[k][kk][SDIM-1][SDIM-1] += weight*h; } } return value; } /************************************************************** * * function: gravity_energy() * * purpose: calculates energy of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL gravity_energy(f_info) struct qinfo *f_info; { return gravity_all(f_info,METHOD_VALUE); } /************************************************************** * * function: gravity_grads() * * purpose: calculates gradient of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL gravity_grads(f_info) struct qinfo *f_info; { return gravity_all(f_info,METHOD_GRADIENT); } /************************************************************** * * function: gravity_hessian() * * purpose: calculates hessian of one facet due to potential * * input: info about vertex is in qinfo structure. * */ REAL gravity_hessian(f_info) struct qinfo *f_info; { return gravity_all(f_info,METHOD_HESSIAN); } /****************************************************************** * * function: pos_area_hess() * * purpose: facet area hessian adjustable to force it to be * positive definite. * */ REAL gga_coeff; /* term coefficient for part of hessian */ REAL fgagfa_coeff; /* term coefficient for part of hessian */ REAL gfa_2_coeff; /* term coefficient for part of hessian */ REAL gfagfa_coeff; /* term coefficient for neg part of hessian */ REAL gfaafg_coeff; /* term coefficient for correction */ void pos_area_hess_init(mode,mi) int mode; struct method_instance *mi; { int k; k = lookup_global("gga_coeff"); if ( k >= 0 ) gga_coeff = globals(k)->value.real; else gga_coeff = 1.0; k = lookup_global("fgagfa_coeff"); if ( k >= 0 ) fgagfa_coeff = globals(k)->value.real; else fgagfa_coeff = -1.0; k = lookup_global("gfa_2_coeff"); if ( k >= 0 ) gfa_2_coeff = globals(k)->value.real; else gfa_2_coeff = 1.0; k = lookup_global("gfagfa_coeff"); if ( k >= 0 ) gfagfa_coeff = globals(k)->value.real; else gfagfa_coeff = -1.0; k = lookup_global("gfaafg_coeff"); if ( k >= 0 ) gfaafg_coeff = globals(k)->value.real; else gfaafg_coeff = 0.0; } REAL pos_area_hess(f_info) struct qinfo *f_info; { MAT2D(Ainv,MAXCOORD,MAXCOORD); MAT2D(FtAinv,MAXCOORD,MAXCOORD); MAT2D(FtAF,MAXCOORD,MAXCOORD); MAT2D(FtAAF,MAXCOORD,MAXCOORD); int i,j,m,n; int dim = web.dimension; REAL area; mat_mul_tr(f_info->sides[0],f_info->sides[0],Ainv,dim,SDIM,dim); area = det_adjoint(Ainv,dim); if ( area == 0.0 ) return area; for ( i = 0 ; i < dim ; i++ ) /* get true inverse */ for ( j = 0 ; j < dim ; j++ ) Ainv[i][j] /= area; area = sqrt(area)/web.simplex_factorial; /* simplex area */ tr_mat_mul(f_info->sides[0],Ainv,FtAinv,dim,SDIM,dim); mat_mult(FtAinv,f_info->sides[0],FtAF,SDIM,dim,SDIM); mat_mul_tr(FtAinv,FtAinv,FtAAF,SDIM,dim,SDIM); /* gradient */ for ( i = 0 ; i < dim ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { f_info->grad[i+1][j] = FtAinv[j][i]*area; f_info->grad[0][j] -= FtAinv[j][i]*area; } /* approximate hessian */ for ( m = 0 ; m < dim ; m++ ) for ( n = 0 ; n < dim ; n++ ) for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { REAL val; val = (i==j) ? gga_coeff*Ainv[m][n] : 0.0; val += gfagfa_coeff*FtAinv[i][n]*FtAinv[j][m]; val += fgagfa_coeff*FtAF[i][j]*Ainv[m][n]; val += (m==n) ? gfaafg_coeff*FtAAF[i][j] : 0.0; val += gfa_2_coeff*FtAinv[i][m]*FtAinv[j][n]; val *= area; f_info->hess[m+1][n+1][i][j] += val; f_info->hess[0][n+1][i][j] -= val; f_info->hess[m+1][0][i][j] -= val; f_info->hess[0][0][i][j] += val; } return area; } /******************************************************************** Sobolev area hessian, after Renka and Neuberger *********************************************************************/ void sobolev_area_init(mode,mi) int mode; struct method_instance *mi; { if ( web.modeltype != LINEAR ) kb_error(2146,"Sobolev_area only for LINEAR model.\n",RECOVERABLE); } REAL sobolev_area_hess(f_info) struct qinfo *f_info; { MAT2D(Ainv,MAXCOORD,MAXCOORD); MAT2D(FtAinv,MAXCOORD,MAXCOORD); MAT2D(FtAF,MAXCOORD,MAXCOORD); int i,j,m,n; int dim = web.dimension; REAL area; mat_mul_tr(f_info->sides[0],f_info->sides[0],Ainv,dim,SDIM,dim); area = det_adjoint(Ainv,dim); if ( area == 0.0 ) return area; for ( i = 0 ; i < dim ; i++ ) /* get true inverse */ for ( j = 0 ; j < dim ; j++ ) Ainv[i][j] /= area; area = sqrt(area)/web.simplex_factorial; /* simplex area */ /* area *= get_facet_density(f_info->id); */ tr_mat_mul(f_info->sides[0],Ainv,FtAinv,dim,SDIM,dim); mat_mult(FtAinv,f_info->sides[0],FtAF,SDIM,dim,SDIM); /* gradient */ for ( i = 0 ; i < dim ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { f_info->grad[i+1][j] = FtAinv[j][i]*area; f_info->grad[0][j] -= FtAinv[j][i]*area; } /* approximate hessian */ for ( m = 0 ; m < dim ; m++ ) for ( n = 0 ; n < dim ; n++ ) for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { REAL val; val = (i==j) ? Ainv[m][n] : 0.0; val -= FtAF[i][j]*Ainv[m][n]; val += FtAinv[i][m]*FtAinv[j][n]; val *= area; f_info->hess[m+1][n+1][i][j] += val; f_info->hess[0][n+1][i][j] -= val; f_info->hess[m+1][0][i][j] -= val; f_info->hess[0][0][i][j] += val; } return area; } /******************************************************************** Dirichelt area hessian, after Polthier and Pinkall *********************************************************************/ void dirichlet_area_init(mode,mi) int mode; struct method_instance *mi; { if ( web.modeltype != LINEAR ) kb_error(2147,"dirichlet_area only for LINEAR model.\n",RECOVERABLE); } REAL dirichlet_area_hess(f_info) struct qinfo *f_info; { MAT2D(Ainv,MAXCOORD,MAXCOORD); MAT2D(FtAinv,MAXCOORD,MAXCOORD); MAT2D(FtAF,MAXCOORD,MAXCOORD); int i,j,m,n; int dim = web.dimension; REAL area; mat_mul_tr(f_info->sides[0],f_info->sides[0],Ainv,dim,SDIM,dim); area = det_adjoint(Ainv,dim); if ( area == 0.0 ) return area; for ( i = 0 ; i < dim ; i++ ) /* get true inverse */ for ( j = 0 ; j < dim ; j++ ) Ainv[i][j] /= area; area = sqrt(area)/web.simplex_factorial; /* simplex area */ /* area *= get_facet_density(f_info->id); */ tr_mat_mul(f_info->sides[0],Ainv,FtAinv,dim,SDIM,dim); mat_mult(FtAinv,f_info->sides[0],FtAF,SDIM,dim,SDIM); /* gradient */ for ( i = 0 ; i < dim ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { f_info->grad[i+1][j] = FtAinv[j][i]*area; f_info->grad[0][j] -= FtAinv[j][i]*area; } /* approximate hessian */ for ( m = 0 ; m < dim ; m++ ) for ( n = 0 ; n < dim ; n++ ) for ( i = 0 ; i < SDIM ; i++ ) { REAL val; val = Ainv[m][n]; val *= area; f_info->hess[m+1][n+1][i][i] += val; f_info->hess[0][n+1][i][i] -= val; f_info->hess[m+1][0][i][i] -= val; f_info->hess[0][0][i][i] += val; } return area; } /********************************************************************* stress_integral method *********************************************************************/ /********************************************************************* * * function: stress_integral_init() * * purpose: Check illegalities * */ void stress_integral_init(mode,mi) int mode; struct method_instance *mi; { if ( web.dimension != 2 ) kb_error(1781,"stress_integral method only for 2D facets.\n",RECOVERABLE); if ( SDIM != 3 ) kb_error(1782,"stress_integral method only for 3D space.\n",RECOVERABLE); if ( web.modeltype != LINEAR ) kb_error(1783,"stress_integral method only for LINEAR model.\n",RECOVERABLE); } /********************************************************************* * * function: stress_integral() * * purpose: method value * */ REAL stress_integral(f_info) struct qinfo *f_info; { REAL value = 0.0; REAL area; REAL ss,st,tt; int stress_comp; REAL un[MAXCOORD]; REAL density = get_facet_density(f_info->id); ss = SDIM_dot(f_info->sides[0][0],f_info->sides[0][0]); st = SDIM_dot(f_info->sides[0][0],f_info->sides[0][1]); tt = SDIM_dot(f_info->sides[0][1],f_info->sides[0][1]); area = sqrt(ss*tt-st*st)/2; un[0] = f_info->normal[0]/(2*area); un[1] = f_info->normal[1]/(2*area); un[2] = f_info->normal[2]/(2*area); stress_comp = (int)eval(METH_INSTANCE(f_info->method)->expr[0],NULL,f_info->id,NULL); switch( stress_comp ) { case 11: value = (REAL)1 - un[0]*un[0]; break; case 12: value = - un[0]*un[1]; break; case 13: value = - un[0]*un[2]; break; case 22: value = (REAL)1 - un[1]*un[1]; break; case 23: value = - un[1]*un[2]; break; case 33: value = (REAL)1 - un[2]*un[2]; break; case 10: value = - un[1]*un[1] + un[2]*un[2]; break; case 20: value = - un[2]*un[2] + un[0]*un[0]; break; case 30: value = - un[0]*un[0] + un[1]*un[1]; break; case 40: value = (REAL)3 - un[0]*un[0] - un[1]*un[1] - un[2]*un[2]; break; case 120: value = - un[0]*un[0] + un[1]*un[1]; break; case 130: value = - un[0]*un[0] + un[2]*un[2]; break; case 230: value = - un[1]*un[1] + un[2]*un[2]; break; case 210: value = - un[1]*un[1] + un[0]*un[0]; break; case 310: value = - un[2]*un[2] + un[0]*un[0]; break; case 320: value = - un[2]*un[2] + un[1]*un[1]; break; default: ; } value *= area; value *= density; return value; } /********************************************************************* * * function: stress_integral_grad() * * purpose: method gradient * */ REAL stress_integral_grad(f_info) struct qinfo *f_info; { return 0.0; } /********************************************************************** Linear volume quantity **********************************************************************/ /********************************************************************* * * function: q_facet_volume_init() * * purpose: Check illegalities * */ void q_facet_volume_init(mode,mi) int mode; struct method_instance *mi; { if ( (web.representation == SIMPLEX) && !(web.modeltype == LAGRANGE) ) { kb_error(1454,"Use Lagrange model for simplex volumes.\n",RECOVERABLE); } else if ( (web.modeltype != LINEAR) && (web.gauss2D_order < 3) ) { web.gauss2D_order = 3; gauss_setup(); } } /********************************************************************** * * function: q_facet_volume() * * purpose: value of volume integral on facet */ REAL q_facet_volume(f_info) struct qinfo *f_info; { REAL **x,**s; REAL vol; if ( web.torus_flag ) return q_facet_torus_volume(f_info); if ( web.modeltype == QUADRATIC ) return q_facet_volume_q(f_info); if ( web.modeltype == LAGRANGE ) return lagrange_facet_volume(f_info); if ( web.representation == SIMPLEX ) return lagrange_facet_volume(f_info); x = f_info->x; s = f_info->sides[0]; vol = (x[0][2]+x[1][2]+x[2][2])*(s[0][0]*s[1][1] - s[0][1]*s[1][0])/6; return vol; } /********************************************************************** * * function: q_facet_volume_grad() * * purpose: gradient and value of volume integral on quadratic facet */ REAL q_facet_volume_grad(f_info) struct qinfo *f_info; { REAL **x; REAL zsum,ssum; if ( web.torus_flag ) return q_facet_torus_volume_grad(f_info); if ( web.modeltype == QUADRATIC ) return q_facet_volume_q_grad(f_info); if ( web.modeltype == LAGRANGE ) return lagrange_facet_volume_grad(f_info); if ( web.representation == SIMPLEX ) return lagrange_facet_volume_grad(f_info); x = f_info->x; zsum = (x[0][2]+x[1][2]+x[2][2])/6; ssum = f_info->sides[0][0][0]*f_info->sides[0][1][1] - f_info->sides[0][0][1]*f_info->sides[0][1][0]; f_info->grad[0][0] = zsum*(x[1][1]-x[2][1]); f_info->grad[0][1] = zsum*(x[2][0]-x[1][0]); f_info->grad[0][2] = ssum/6; f_info->grad[1][0] = zsum*(x[2][1]-x[0][1]); f_info->grad[1][1] = zsum*(x[0][0]-x[2][0]); f_info->grad[1][2] = ssum/6; f_info->grad[2][0] = zsum*(x[0][1]-x[1][1]); f_info->grad[2][1] = zsum*(x[1][0]-x[0][0]); f_info->grad[2][2] = ssum/6; return zsum*ssum; } /********************************************************************** * * function: q_facet_volume_hess() * * purpose: hessian, grad, and value of volume integral on quadratic facet */ REAL q_facet_volume_hess(f_info) struct qinfo *f_info; { REAL **x,****h; REAL zsum,ssum; int i; if ( web.torus_flag ) return q_facet_torus_volume_hess(f_info); if ( web.modeltype == QUADRATIC ) return q_facet_volume_q_hess(f_info); if ( web.modeltype == LAGRANGE ) return lagrange_facet_volume_hess(f_info); if ( web.representation == SIMPLEX ) return lagrange_facet_volume_hess(f_info); x = f_info->x; zsum = (x[0][2]+x[1][2]+x[2][2])/6; ssum = f_info->sides[0][0][0]*f_info->sides[0][1][1] - f_info->sides[0][0][1]*f_info->sides[0][1][0]; f_info->grad[0][0] = zsum*(x[1][1]-x[2][1]); f_info->grad[0][1] = zsum*(x[2][0]-x[1][0]); f_info->grad[0][2] = ssum/6; f_info->grad[1][0] = zsum*(x[2][1]-x[0][1]); f_info->grad[1][1] = zsum*(x[0][0]-x[2][0]); f_info->grad[1][2] = ssum/6; f_info->grad[2][0] = zsum*(x[0][1]-x[1][1]); f_info->grad[2][1] = zsum*(x[1][0]-x[0][0]); f_info->grad[2][2] = ssum/6; h = f_info->hess; h[1][2][0][1] = h[1][0][1][0] = h[0][2][1][0] = zsum; h[2][1][1][0] = h[0][1][0][1] = h[2][0][0][1] = zsum; h[1][0][0][1] = h[0][2][0][1] = h[1][2][1][0] = -zsum; h[0][1][1][0] = h[2][0][1][0] = h[2][1][0][1] = -zsum; for ( i = 0 ; i < 3 ; i++ ) { h[i][0][2][0] = h[0][i][0][2] = (x[1][1]-x[2][1])/6; h[i][0][2][1] = h[0][i][1][2] = (x[2][0]-x[1][0])/6; h[i][1][2][0] = h[1][i][0][2] = (x[2][1]-x[0][1])/6; h[i][1][2][1] = h[1][i][1][2] = (x[0][0]-x[2][0])/6; h[i][2][2][0] = h[2][i][0][2] = (x[0][1]-x[1][1])/6; h[i][2][2][1] = h[2][i][1][2] = (x[1][0]-x[0][0])/6; } return zsum*ssum; } /********************************************************************* Facet area upper bound quantity *********************************************************************/ void q_facet_tension_u_init(mode,mi) int mode; struct method_instance *mi; { if ( web.gauss2D_order < 5 ) { web.gauss2D_order = 5; gauss_setup(); } } REAL q_facet_tension_u_value(f_info) struct qinfo *f_info; { if ( web.modeltype == QUADRATIC ) return q_facet_tension_uq(f_info); if ( web.modeltype == LAGRANGE ) kb_error(1785,"Non-Lagrange quantity: facet_tension_u\n",RECOVERABLE); return q_facet_tension_value(f_info); } REAL q_facet_tension_u_gradient(f_info) struct qinfo *f_info; { if ( web.modeltype == QUADRATIC ) return q_facet_tension_uq_grad(f_info); if ( web.modeltype == LAGRANGE ) kb_error(1786,"Non-Lagrange quantity: facet_tension_u\n",RECOVERABLE); return q_facet_tension_gradient(f_info); } REAL q_facet_tension_u_hessian(f_info) struct qinfo *f_info; { if ( web.modeltype == QUADRATIC ) return q_facet_tension_uq_hess(f_info); if ( web.modeltype == LAGRANGE ) kb_error(1787,"Non-Lagrange quantity: facet_tension_u\n",RECOVERABLE); return q_facet_tension_hessian(f_info); } /********************************************************************* facet_general method, linear and quadratic Integrand is general function of position and normal. 3D only. *********************************************************************/ /* antisymmetric tensor for cross product */ static REAL e[3][3][3] = { {{0.,0.,0.},{0.,0.,1.},{0.,-1.,0.}}, {{0.,0.,-1.},{0.,0.,0.},{1.,0.,0.}}, {{0.,1.,0.},{-1.,0.,0.},{0.,0.,0.}} }; /********************************************************************* * * function: facet_general_init() * * purpose: check illegalities * */ void facet_general_init(mode,mi) int mode; struct method_instance *mi; { if ( web.dimension != 2 ) kb_error(1788,"facet_general_integral method only for surface dimension 2.\n",RECOVERABLE); /* if ( SDIM != 3 ) kb_error(1789,"facet_general_integral method only for space dimension 3.\n",RECOVERABLE); */ } /********************************************************************* * * function: facet_general_value() * * purpose: method value * */ REAL facet_general_value(f_info) struct qinfo *f_info; { int m,k; REAL value = 0.0; REAL z[2*MAXCOORD+1]; /* pointers to coord and tangent */ REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; REAL *normal = z+SDIM; if ( web.modeltype == LAGRANGE ) return facet_general_value_lagr(f_info); facet_general_flag = 1; for ( m = 0 ; m < gauss2D_num ; m++ ) { for ( k = 0 ; k < SDIM ; k++ ) z[k] = f_info->gauss_pt[m][k]; cross_prod(f_info->sides[m][0],f_info->sides[m][1],normal); if ( sign == -1.0 ) for ( k = 0 ; k < SDIM ; k++ ) z[k+SDIM] *= sign ; z[2*SDIM] = m; /* kludge for attr interpolation. */ value += gauss2Dwt[m]*eval(METH_INSTANCE(f_info->method)->expr[0],z,f_info->id,NULL); } facet_general_flag = 0; return value/web.simplex_factorial; /* triangle factor */ } /********************************************************************* * * function: facet_general_grad() * * purpose: method gradient * */ REAL facet_general_grad(f_info) struct qinfo *f_info; { int m,j,k; REAL value = 0.0; REAL val; REAL derivs[2*MAXCOORD]; REAL z[2*MAXCOORD+1]; /* pointers to coord and tangent */ REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; REAL *normal = z+SDIM; if ( web.modeltype == LAGRANGE ) return facet_general_grad_lagr(f_info); facet_general_flag = 1; for ( m = 0 ; m < gauss2D_num ; m++ ) { REAL coeff = gauss2Dwt[m]/web.simplex_factorial; REAL **s = f_info->sides[m]; z[2*SDIM] = m; /* kludge for attr interpolation. */ for ( k = 0 ; k < SDIM ; k++ ) z[k] = f_info->gauss_pt[m][k]; cross_prod(f_info->sides[m][0],f_info->sides[m][1],normal); if ( sign == -1.0 ) for ( k = 0 ; k < SDIM ; k++ ) z[k+SDIM] *= sign ; eval_all(METH_INSTANCE(f_info->method)->expr[0],z,2*SDIM,&val,derivs,f_info->id); value += coeff*val; for ( j = 0 ; j < 3 ; j++ ) { int jj = (j+1)%3; int jjj = (j+2)%3; for ( k = 0 ; k < ctrl_num ; k++ ) { REAL **gpd = gpolypartial[m]; f_info->grad[k][j] += coeff*(gpoly[m][k]*derivs[j] - sign*derivs[jj+SDIM]*(s[1][jjj]*gpd[0][k]-s[0][jjj]*gpd[1][k]) - sign*derivs[jjj+SDIM]*(s[0][jj]*gpd[1][k]-s[1][jj]*gpd[0][k])); } } } facet_general_flag = 0; return value; } /********************************************************************* * * function: facet_general_hess() * * purpose: method gradient and hessian * */ REAL facet_general_hess(f_info) struct qinfo *f_info; { int m,j,k,kk,J; REAL value = 0.0; REAL val; REAL derivs[2*MAXCOORD]; REAL z[2*MAXCOORD+1]; /* pointers to coord and tangent */ MAT2D(second,2*MAXCOORD,2*MAXCOORD); /* second derivatives */ REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; REAL *normal = z+SDIM; if ( web.modeltype == LAGRANGE ) return facet_general_hess_lagr(f_info); facet_general_flag = 1; for ( m = 0 ; m < gauss2D_num ; m++ ) { REAL coeff = gauss2Dwt[m]/web.simplex_factorial; REAL **gpd = gpolypartial[m]; REAL **s = f_info->sides[m]; z[2*SDIM] = m; /* kludge for attr interpolation. */ for ( k = 0 ; k < SDIM ; k++ ) z[k] = f_info->gauss_pt[m][k]; cross_prod(f_info->sides[m][0],f_info->sides[m][1],normal); if ( sign == -1.0 ) for ( k = 0 ; k < SDIM ; k++ ) z[k+SDIM] *= sign ; eval_second(METH_INSTANCE(f_info->method)->expr[0],z,2*SDIM,&val,derivs,second,f_info->id); value += coeff*val; /* gradients */ for ( j = 0 ; j < 3 ; j++ ) { int jj = (j+1)%3; int jjj = (j+2)%3; for ( k = 0 ; k < ctrl_num ; k++ ) { f_info->grad[k][j] += coeff*(gpoly[m][k]*derivs[j] - sign*derivs[jj+SDIM]*(s[1][jjj]*gpd[0][k]-s[0][jjj]*gpd[1][k]) - sign*derivs[jjj+SDIM]*(s[0][jj]*gpd[1][k]-s[1][jj]*gpd[0][k])); } } /* hessian */ for ( k = 0 ; k < ctrl_num ; k++ ) for ( kk = 0 ; kk < ctrl_num ; kk++ ) for ( j = 0 ; j < 3 ; j++ ) for ( J = 0 ; J < 3 ; J++ ) { int jj = (j==2) ? 0 : j+1; int jjj = (j==0) ? 2 : j-1 ; int JJ = (J==2) ? 0 : J+1; int JJJ = (J==0) ? 2 : J-1 ; REAL sum; /* have to break into parts, else too big expression for compiler */ sum = gpoly[m][k]*gpoly[m][kk]*second[j][J]; sum += sign*SDIM_dot(e[j][J],derivs+SDIM) *(gpd[1][kk]*gpd[0][k]-gpd[0][kk]*gpd[1][k]); sum += sign*gpoly[m][kk]*(s[1][jj]*gpd[0][k]-s[0][jj]*gpd[1][k]) *second[J][jjj+SDIM] - sign*gpoly[m][kk]*(s[1][jjj]*gpd[0][k]-s[0][jjj]*gpd[1][k]) *second[J][jj+SDIM] + sign*gpoly[m][k]*(s[1][JJ]*gpd[0][kk]-s[0][JJ]*gpd[1][kk]) *second[j][JJJ+SDIM] - sign*gpoly[m][k]*(s[1][JJJ]*gpd[0][kk]-s[0][JJJ]*gpd[1][kk]) *second[j][JJ+SDIM]; sum += (s[1][jj]*gpd[0][k] - s[0][jj]*gpd[1][k]) *(s[1][JJ]*gpd[0][kk] - s[0][JJ]*gpd[1][kk]) *second[jjj+SDIM][JJJ+SDIM] - (s[1][jjj]*gpd[0][k] - s[0][jjj]*gpd[1][k]) *(s[1][JJ]*gpd[0][kk] - s[0][JJ]*gpd[1][kk]) *second[jj+SDIM][JJJ+SDIM] - (s[1][jj]*gpd[0][k] - s[0][jj]*gpd[1][k]) *(s[1][JJJ]*gpd[0][kk] - s[0][JJJ]*gpd[1][kk]) *second[jjj+SDIM][JJ+SDIM] + (s[1][jjj]*gpd[0][k] - s[0][jjj]*gpd[1][k]) *(s[1][JJJ]*gpd[0][kk] - s[0][JJJ]*gpd[1][kk]) *second[jj+SDIM][JJ+SDIM]; f_info->hess[k][kk][j][J] += coeff*sum; } } facet_general_flag = 0; return value; } /******************************************************************** Lagrange version of facet_general_integral ********************************************************************/ /********************************************************************* * * function: facet_general_value_lagr() * * purpose: method value * */ REAL facet_general_value_lagr(f_info) struct qinfo *f_info; { int m,k; REAL value = 0.0; REAL z[2*MAXCOORD+1]; /* pointers to coord and tangent */ REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; int dim = web.dimension; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; REAL *normal = z+SDIM; facet_general_flag = 1; for ( m = 0 ; m < gl->gnumpts ; m++ ) { for ( k = 0 ; k < SDIM ; k++ ) z[k] = f_info->gauss_pt[m][k]; cross_prod(f_info->sides[m][0],f_info->sides[m][1],normal); if ( sign == -1.0 ) for ( k = 0 ; k < SDIM ; k++ ) z[k+SDIM] *= sign ; z[2*SDIM] = m; /* kludge for attr interpolation. */ value += gl->gausswt[m]*eval(METH_INSTANCE(f_info->method)->expr[0],z,f_info->id,NULL); } facet_general_flag = 0; return value/web.simplex_factorial; } /********************************************************************* * * function: facet_general_grad_lagr() * * purpose: method gradient * */ REAL facet_general_grad_lagr(f_info) struct qinfo *f_info; { int m,j,k; REAL value = 0.0; REAL val; REAL derivs[2*MAXCOORD]; REAL z[2*MAXCOORD+1]; /* pointers to coord and tangent */ REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; int dim = web.dimension; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; REAL *normal = z+SDIM; facet_general_flag = 1; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL coeff = gl->gausswt[m]/web.simplex_factorial; REAL **s = f_info->sides[m]; z[2*SDIM] = m; /* kludge for attr interpolation. */ for ( k = 0 ; k < SDIM ; k++ ) z[k] = f_info->gauss_pt[m][k]; cross_prod(f_info->sides[m][0],f_info->sides[m][1],normal); if ( sign == -1.0 ) for ( k = 0 ; k < SDIM ; k++ ) z[k+SDIM] *= sign ; eval_all(METH_INSTANCE(f_info->method)->expr[0],z,2*SDIM,&val,derivs,f_info->id); value += coeff*val; for ( j = 0 ; j < SDIM ; j++ ) { int jj = (j+1)%3; int jjj = (j+2)%3; for ( k = 0 ; k < gl->lagpts ; k++ ) { REAL **gpd = gl->gpolypart[m]; f_info->grad[k][j] += coeff*(gl->gpoly[m][k]*derivs[j] - sign*derivs[jj+SDIM]*(s[1][jjj]*gpd[0][k]-s[0][jjj]*gpd[1][k]) - sign*derivs[jjj+SDIM]*(s[0][jj]*gpd[1][k]-s[1][jj]*gpd[0][k])); } } } facet_general_flag = 0; return value; } /********************************************************************* * * function: facet_general_hess_lagr() * * purpose: method gradient and hessian * */ REAL facet_general_hess_lagr(f_info) struct qinfo *f_info; { int m,j,k,kk,J; REAL value = 0.0; REAL val; REAL derivs[2*MAXCOORD]; REAL z[2*MAXCOORD+1]; /* pointers to coord and tangent */ MAT2D(second,2*MAXCOORD,2*MAXCOORD); /* second derivatives */ REAL sign = (get_fattr(f_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; int dim = web.dimension; struct gauss_lag *gl = &gauss_lagrange[dim][web.gauss2D_order]; REAL *normal = z+SDIM; facet_general_flag = 1; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL coeff = gl->gausswt[m]/web.simplex_factorial; REAL **gpd = gl->gpolypart[m]; REAL **s = f_info->sides[m]; z[2*SDIM] = m; /* kludge for attr interpolation. */ for ( k = 0 ; k < SDIM ; k++ ) z[k] = f_info->gauss_pt[m][k]; cross_prod(f_info->sides[m][0],f_info->sides[m][1],normal); if ( sign == -1.0 ) for ( k = 0 ; k < SDIM ; k++ ) z[k+SDIM] *= sign ; eval_second(METH_INSTANCE(f_info->method)->expr[0],z,2*SDIM,&val,derivs, second,f_info->id); value += coeff*val; /* gradients */ for ( j = 0 ; j < SDIM ; j++ ) { int jj = (j+1)%3; int jjj = (j+2)%3; for ( k = 0 ; k < gl->lagpts ; k++ ) { f_info->grad[k][j] += coeff*(gl->gpoly[m][k]*derivs[j] - sign*derivs[jj+SDIM]*(s[1][jjj]*gpd[0][k]-s[0][jjj]*gpd[1][k]) - sign*derivs[jjj+SDIM]*(s[0][jj]*gpd[1][k]-s[1][jj]*gpd[0][k])); } } /* hessian */ for ( k = 0 ; k < gl->lagpts ; k++ ) for ( kk = 0 ; kk < gl->lagpts ; kk++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( J = 0 ; J < SDIM ; J++ ) { int jj = (j==2) ? 0 : j+1; int jjj = (j==0) ? 2 : j-1 ; int JJ = (J==2) ? 0 : J+1; int JJJ = (J==0) ? 2 : J-1 ; REAL sum; /* have to break into parts, else too big expression for compiler */ sum = gl->gpoly[m][k]*gl->gpoly[m][kk]*second[j][J]; sum += sign*SDIM_dot(e[j][J],derivs+SDIM) *(gpd[1][kk]*gpd[0][k]-gpd[0][kk]*gpd[1][k]); sum += sign*gl->gpoly[m][kk]*(s[1][jj]*gpd[0][k]-s[0][jj]*gpd[1][k]) *second[J][jjj+SDIM] - sign*gl->gpoly[m][kk]*(s[1][jjj]*gpd[0][k]-s[0][jjj]*gpd[1][k]) *second[J][jj+SDIM] + sign*gl->gpoly[m][k]*(s[1][JJ]*gpd[0][kk]-s[0][JJ]*gpd[1][kk]) *second[j][JJJ+SDIM] - sign*gl->gpoly[m][k]*(s[1][JJJ]*gpd[0][kk]-s[0][JJJ]*gpd[1][kk]) *second[j][JJ+SDIM]; sum += (s[1][jj]*gpd[0][k] - s[0][jj]*gpd[1][k]) *(s[1][JJ]*gpd[0][kk] - s[0][JJ]*gpd[1][kk]) *second[jjj+SDIM][JJJ+SDIM] - (s[1][jjj]*gpd[0][k] - s[0][jjj]*gpd[1][k]) *(s[1][JJ]*gpd[0][kk] - s[0][JJ]*gpd[1][kk]) *second[jj+SDIM][JJJ+SDIM] - (s[1][jj]*gpd[0][k] - s[0][jj]*gpd[1][k]) *(s[1][JJJ]*gpd[0][kk] - s[0][JJJ]*gpd[1][kk]) *second[jjj+SDIM][JJ+SDIM] + (s[1][jjj]*gpd[0][k] - s[0][jjj]*gpd[1][k]) *(s[1][JJJ]*gpd[0][kk] - s[0][JJJ]*gpd[1][kk]) *second[jj+SDIM][JJ+SDIM]; f_info->hess[k][kk][j][J] += coeff*sum; } } facet_general_flag = 0; return value; } /********************************************************************* Facet area^2 quantity due to Renka and Neuberger *********************************************************************/ /********************************************************************* * * function: area_square_value() * * purpose: General quantity value of facet tension. */ REAL area_square_value(f_info) struct qinfo *f_info; { REAL energy; mat_tsquare(f_info->sides[0],f_info->ss,web.dimension,SDIM); energy = det_adjoint(f_info->ss,web.dimension); return energy/web.simplex_factorial/web.simplex_factorial; } /********************************************************************* * * function: area_square_gradient() * * purpose: General quantity value and gradient */ REAL area_square_gradient(f_info) struct qinfo *f_info; { REAL energy; int i,j; mat_tsquare(f_info->sides[0],f_info->ss,web.dimension,SDIM); energy = det_adjoint(f_info->ss,web.dimension); for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < web.dimension ; j++ ) f_info->ss[i][j] *= 2/web.simplex_factorial/web.simplex_factorial; mat_mult(f_info->ss,f_info->sides[0],f_info->grad+1,web.dimension,web.dimension, SDIM); /* head forces */ memset((char*)f_info->grad[0],0,SDIM*sizeof(REAL)); for ( i = 0 ; i < web.dimension ; i++ ) /* tail forces */ vector_sub(f_info->grad[0],f_info->grad[i+1],SDIM); return energy/web.simplex_factorial/web.simplex_factorial; } evolver-2.30c.dfsg/src/model.c0000644000175300017530000024103211410765113016463 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************* * * file: model.c * * Purpose: Switch between LINEAR and QUADRATIC models, * and other such transformations. * */ #include "include.h" /************************************************************************* * * function: adjust_integration_orders () * * purpose: set minimal orders of numerical integration to fit element order. * */ void adjust_integration_orders ARGS((int)); void adjust_integration_orders(l_order) int l_order; /* new element order */ { int ord1,ord2; /* 2D orders actually implemented are 1,2,5,6,8,11,13,14,... */ if ( l_order == 1 ) { ord1 = 3; ord2 = 3; } else if ( l_order == 2 ) { ord1 = 5; ord2 = 3; } else { ord1 = 2*l_order; ord2 = 2*l_order; } if ( (web.gauss1D_order < ord1) || (set_by_user_gauss_1D < ord1) ) web.gauss1D_order = ord1; if ( (web.gauss2D_order < ord2) || (set_by_user_gauss_2D < ord2) ) web.gauss2D_order = ord2; } /************************************************************************* * * Function: lagrange_to_linear() * * purpose: Convert from Lagrange model to Linear model. */ void lagrange_to_linear() { #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) mpi_model(1,LINEAR); #endif lagrange_to_lagrange(1); web.headvnum = 1; web.modeltype = LINEAR; gauss_setup(); /* set up gaussian integration arrays */ recalc(); } /************************************************************************* * * Function: lagrange_to_quad() * * purpose: Convert from Lagrange model to quadratic model. */ void lagrange_to_quad() { edge_id e_id; vertex_id *v,vv; #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) mpi_model(2,QUADRATIC); #endif lagrange_to_lagrange(2); ENTER_GRAPH_MUTEX; MFOR_ALL_EDGES(e_id) { v = get_edge_vertices(e_id); vv = v[1]; v[1] = v[2]; v[2] = vv; unset_attr(vv,Q_MIDEDGE); set_attr(vv,Q_MIDPOINT); } web.headvnum = 1; web.modeltype = QUADRATIC; gauss_setup(); /* set up gaussian integration arrays */ LEAVE_GRAPH_MUTEX; recalc(); } /************************************************************************* * * Function: linear_to_lagrange() * * purpose: Convert from linear model to lagrange model. */ void linear_to_lagrange(l_order) int l_order; { int dim; facet_id f_id; facetedge_id fe; vertex_id *v; if ( web.skel[VERTEX].count > 0 ) if ( !everything_quantities_flag ) { if ( auto_convert_flag ) convert_to_quantities(); else kb_error(1797,"Must do convert_to_quantities for Lagrange model.\n", RECOVERABLE); } #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) mpi_model(l_order,LAGRANGE); #endif if ( web.representation == SOAPFILM ) MFOR_ALL_FACETS(f_id) { fe = get_facet_fe(f_id); v = get_facet_vertices(f_id); v[0] = get_fe_tailv(fe); fe = get_next_edge(fe); v[1] = get_fe_tailv(fe); fe = get_next_edge(fe); v[2] = get_fe_tailv(fe); } web.modeltype = LAGRANGE; web.lagrange_order = 1; dim = (web.representation==STRING)? 1 : web.dimension-1; adjust_integration_orders(l_order); gauss_lagrange_setup(web.dimension,web.lagrange_order,web.gauss2D_order); gauss_lagrange_setup(dim,web.lagrange_order,web.gauss1D_order); lagrange_to_lagrange(l_order); if ( everything_quantities_flag ) recalc(); } /************************************************************************* * * Function: quad_to_lagrange() * * purpose: Convert from quadratic model to Lagrange model. */ void quad_to_lagrange(l_order) int l_order; { int dim; facet_id f_id; facetedge_id fe; edge_id e_id; vertex_id *v,vv; if ( !everything_quantities_flag ) { if ( auto_convert_flag ) convert_to_quantities(); else kb_error(1798,"Must do convert_to_quantities for Lagrange model.\n", RECOVERABLE); } #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) mpi_model(l_order,LAGRANGE); #endif ENTER_GRAPH_MUTEX; MFOR_ALL_FACETS(f_id) { fe = get_facet_fe(f_id); v = get_facet_vertices(f_id); v[0] = get_fe_tailv(fe); v[1] = get_fe_midv(fe); fe = get_next_edge(fe); v[2] = get_fe_tailv(fe); v[4] = get_fe_midv(fe); fe = get_next_edge(fe); v[5] = get_fe_tailv(fe); v[3] = get_fe_midv(fe); } MFOR_ALL_EDGES(e_id) { v = get_edge_vertices(e_id); vv = v[1]; v[1] = v[2]; v[2] = vv; unset_attr(v[1],Q_MIDPOINT); set_attr(v[1],Q_MIDEDGE); } web.headvnum = 2; web.modeltype = LAGRANGE; web.lagrange_order = 2; dim = (web.representation==STRING)? 1 : web.dimension-1; adjust_integration_orders(l_order); gauss_lagrange_setup(web.dimension,web.lagrange_order,web.gauss2D_order); gauss_lagrange_setup(dim,web.lagrange_order,web.gauss1D_order); LEAVE_GRAPH_MUTEX; lagrange_to_lagrange(l_order); if ( everything_quantities_flag ) recalc(); } /************************************************************************* * * Function: lagrange_to_lagrange() * * purpose: Convert order of Lagrange model. */ void lagrange_to_lagrange(l_order) int l_order; { int new_ectrl,new_fctrl; /* new number of control points */ int old_ectrl = web.skel[EDGE].ctrlpts; int old_fctrl = web.skel[FACET].ctrlpts; int i,k,m,spot; int old,new; int inx[MAXCOORD+1]; int oldinx[MAXCOORD+1]; REAL **trans; /* transformation matrix old to new points */ REAL **newx; /* new coords */ REAL **oldx; /* old coords */ int zeros; facet_id f_id; edge_id e_id; int dim; if ( web.lagrange_order == l_order ) return; /* already OK */ if ( l_order < 1 ) kb_error(1799,"Lagrange order must be at least 1.\n",RECOVERABLE); if ( l_order > MAXLAGRANGE ) { sprintf(errmsg,"Lagrange order must be at most %d.\n",MAXLAGRANGE); kb_error(3505,errmsg, RECOVERABLE); } #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) mpi_model(l_order,LAGRANGE); #endif ENTER_GRAPH_MUTEX; bezier_refine_1d_init(l_order); bezier_refine_2d_init(l_order); if ( web.representation == SIMPLEX ) { simplex_lagrange_to_lagrange(l_order); return; } if ( web.representation == STRING ) { new_ectrl = binom_coeff(l_order+web.dimension,web.dimension); new_fctrl = binom_coeff(l_order+web.dimension+1,web.dimension+1); } else { new_ectrl = binom_coeff(l_order+web.dimension-1,web.dimension-1); new_fctrl = binom_coeff(l_order+web.dimension,web.dimension); } if ( l_order > web.lagrange_order ) { expand_attribute(EDGE,E_VERTICES_ATTR,&new_ectrl); expand_attribute(FACET,F_VERTICES_ATTR,&new_fctrl); } /* have to wait for later for shrink case */ /* recalc all facet interior points */ trans = dmatrix(0,new_fctrl,0,old_fctrl); newx = dmatrix(0,new_fctrl,0,SDIM); oldx = dmatrix(0,old_fctrl,0,SDIM); /* calculate transition matrix */ dim = web.dimension; adjust_integration_orders(l_order); gauss_lagrange_setup(dim,l_order,web.gauss2D_order); if ( bezier_flag ) bezier_trans_2d(web.lagrange_order,l_order,trans); else { for ( m = 0 ; m <= dim ; m++ ) inx[m] = 0; inx[0] = l_order; new = 0; do { old = 0; for ( m = 0 ; m <= dim ; m++ ) oldinx[m] = 0; oldinx[0] = web.lagrange_order; do { REAL prod; for ( k = 0, prod = 1.0 ; k <= dim ; k++ ) for ( m = 0 ; m < oldinx[k] ; m++ ) prod *= ((REAL)(inx[k])/l_order*web.lagrange_order-m)/(oldinx[k]-m); trans[new][old] = prod; old++; } while ( increment_lagrange_index(dim,oldinx) ); new++; } while ( increment_lagrange_index(dim,inx) ); } /* calc indices of extreme vertices; handy elsewhere */ for ( m = 0 ; m <= dim ; m++ ) inx[m] = 0; for ( k = 0 ; k <= dim ; k++ ) { inx[k] = l_order; new = lagrange_index(dim,l_order,inx); inx[k] = 0; web.skel[FACET].extreme[k] = new; } if ( web.representation != STRING ) MFOR_ALL_FACETS(f_id) { vertex_id *v; dim = web.dimension; get_facet_verts(f_id,oldx,NULL); mat_mult(trans,oldx,newx,new_fctrl,web.skel[FACET].ctrlpts,SDIM); v = get_facet_vertices(f_id); /* free old interior */ for ( m = 0 ; m <= dim ; m++ ) inx[m] = 0; inx[0] = web.lagrange_order; for ( k = 0 ; k < old_fctrl ; k++ ) { for ( m = 0 ; m <= dim ; m++ ) if ( inx[m] == 0 ) break; if ( m > dim ) free_element(v[k]); /* free only interior */ increment_lagrange_index(dim,inx); } /* move extreme vertices */ for ( m = 0 ; m <= dim ; m++ ) inx[m] = 0; if ( l_order < web.lagrange_order ) for ( k = 0 ; k <= dim ; k++ ) { inx[k] = web.lagrange_order; old = lagrange_index(dim,web.lagrange_order,inx); inx[k] = l_order; new = lagrange_index(dim,l_order,inx); inx[k] = 0; v[new] = v[old]; } else for ( k = dim ; k >= 0 ; k-- ) { inx[k] = web.lagrange_order; old = lagrange_index(dim,web.lagrange_order,inx); inx[k] = l_order; new = lagrange_index(dim,l_order,inx); inx[k] = 0; v[new] = v[old]; } /* create new interior */ for ( m = 0 ; m <= dim ; m++ ) inx[m] = 0; inx[0] = l_order; for ( k = 0 ; k < new_fctrl ; k++ ) { for ( m = 0 ; m <= dim ; m++ ) if ( inx[m] == 0 ) break; if ( m > dim ) /* create interior vertex */ { v[k] = new_vertex(newx[k],f_id); if ( get_fattr(f_id) & FIXED ) set_attr(v[k],FIXED); set_attr(v[k],Q_MIDFACET); set_vertex_facet(v[k],f_id); /* since doesn't have edge */ /* find centerpoint parameters for facet on boundary */ if ( get_fattr(f_id) & BOUNDARY ) /* not working for torus */ { REAL defaultp[MAXCOORD]; REAL *paramb,*parammid,*xb; vertex_id base_v; REAL s[MAXCOORD]; struct boundary *bdry; facetedge_id fe; set_attr(v[k],BOUNDARY); bdry = get_facet_boundary(f_id); set_boundary_num(v[k],bdry->num); /* v[k] parameters extrapolate from a vertex */ /* try to find a vertex on same boundary */ base_v = NULLVERTEX; fe = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++, fe = get_next_edge(fe) ) { if ( bdry == get_boundary(get_fe_tailv(fe)) ) base_v = get_fe_tailv(fe); } if ( valid_id(base_v) ) { paramb = get_param(base_v); xb = get_coord(base_v); for ( i = 0 ; i < SDIM ; i++ ) s[i] = xb[i]; /* displacement vector */ } else { paramb = defaultp; defaultp[0] = defaultp[1] = defaultp[2] = 0.1; for ( i = 0 ; i < SDIM ; i++ ) s[i] = eval(bdry->coordf[i],defaultp,NULLID,NULL); sprintf(msg, "Could not find vertex on same boundary as facet %s.\n", ELNAME(f_id)); } parammid = get_param(v[k]); b_extrapolate(bdry,s,newx[k],newx[k],paramb,parammid,v[k]); } else if ( get_fattr(f_id) & CONSTRAINT ) { ATTR attr = get_fattr(f_id) & (BDRY_ENERGY|BDRY_CONTENT|CONSTRAINT); conmap_t * conmap = get_f_constraint_map(f_id); set_attr(v[k],attr); set_v_conmap(v[k],conmap); project_v_constr(v[k],ACTUAL_MOVE,RESET_ONESIDEDNESS); } } increment_lagrange_index(dim,inx); } } /* do edges */ /* calculate transition matrix */ dim = (web.representation==STRING)? 1 : web.dimension-1; gauss_lagrange_setup(dim,l_order,web.gauss1D_order); if ( bezier_flag ) { bezier_trans_1d(web.lagrange_order,l_order,trans); } else { /* regular lagrange stuff */ for ( m = 0 ; m <= dim ; m++ ) inx[m] = 0; inx[0] = l_order; new = 0; do { old = 0; for ( m = 0 ; m <= dim ; m++ ) oldinx[m] = 0; oldinx[0] = web.lagrange_order; do { REAL prod; for ( k = 0, prod = 1.0 ; k <= dim ; k++ ) for ( m = 0 ; m < oldinx[k] ; m++ ) prod *= ((REAL)(inx[k])/l_order*web.lagrange_order - m)/(oldinx[k]-m); trans[new][old] = prod; old++; } while ( increment_lagrange_index(dim,oldinx) ); new++; } while ( increment_lagrange_index(dim,inx) ); } /* calc indices of extreme vertices; handy elsewhere */ for ( m = 0 ; m <= dim ; m++ ) inx[m] = 0; for ( k = 0 ; k <= dim ; k++ ) { inx[k] = l_order; new = lagrange_index(dim,l_order,inx); inx[k] = 0; web.skel[EDGE].extreme[k] = new; } MFOR_ALL_EDGES(e_id) { vertex_id *v; get_edge_verts(e_id,oldx,NULL); mat_mult(trans,oldx,newx,new_ectrl,old_ectrl,SDIM); v = get_edge_vertices(e_id); /* free old interior */ for ( m = 0 ; m <= dim ; m++ ) inx[m] = 0; inx[0] = web.lagrange_order; for ( k = 0 ; k < old_ectrl ; k++ ) { for ( m = 0 ; m <= dim ; m++ ) if ( inx[m] == 0 ) break; if ( m > dim ) free_element(v[k]); /* free only interior */ increment_lagrange_index(dim,inx); } /* move extreme vertices */ for ( m = 0 ; m <= dim ; m++ ) inx[m] = 0; if ( l_order < web.lagrange_order ) for ( k = 0 ; k <= dim ; k++ ) { inx[k] = web.lagrange_order; old = lagrange_index(dim,web.lagrange_order,inx); inx[k] = l_order; new = lagrange_index(dim,l_order,inx); inx[k] = 0; v[new] = v[old]; } else for ( k = dim ; k >= 0 ; k-- ) { inx[k] = web.lagrange_order; old = lagrange_index(dim,web.lagrange_order,inx); inx[k] = l_order; new = lagrange_index(dim,l_order,inx); inx[k] = 0; v[new] = v[old]; } /* create new interior */ for ( m = 0 ; m <= dim ; m++ ) inx[m] = 0; inx[0] = l_order; for ( k = 0 ; k < new_ectrl ; k++ ) { for ( m = 0,zeros=0 ; m <= dim ; m++ ) if ( inx[m] == 0 ) zeros++; if ( zeros != dim ) { v[k] = new_vertex(newx[k],e_id); if ( get_eattr(e_id) & FIXED ) set_attr(v[k],FIXED); set_attr(v[k],Q_MIDEDGE); set_vertex_edge(v[k],e_id); /* for boundary edges, cannot just interpolate parameters due to wrap-around of angular parameters. So tangent extrapolate from one endpoint. */ if ( get_eattr(e_id) & BOUNDARY ) { struct boundary *bdry; REAL *paramb,*parammid,*mu,*mv; vertex_id base_v = NULLID; vertex_id headv = v[l_order]; vertex_id tailv = v[0]; if ( web.representation == SIMPLEX ) kb_error(1802,"Can't do Lagrange boundaries in SIMPLEX model.\n",RECOVERABLE); bdry = get_edge_boundary(e_id); if ( get_boundary(headv) == bdry ) base_v = headv; else if ( get_boundary(tailv) == bdry ) base_v = tailv; else { sprintf(errmsg, "Vertices %s and %s of edge %s are on different boundaries, %s, %s, %s.\n", ELNAME(headv),ELNAME1(tailv),ELNAME2(e_id), get_boundary(headv) ? get_boundary(headv)->name : "none", get_boundary(tailv) ? get_boundary(tailv)->name : "none", bdry ? bdry->name : "none"); kb_error(1803,errmsg,RECOVERABLE); } set_attr(v[k],BOUNDARY); set_boundary_num(v[k],bdry->num); /* projecting on tangent */ mv = get_coord(v[k]); mu = get_coord(base_v); paramb = get_param(base_v); parammid = get_param(v[k]); b_extrapolate(bdry,mu,mv,mv,paramb,parammid,v[k]); } else if ( get_eattr(e_id) & CONSTRAINT ) { ATTR attr = get_eattr(e_id) & (BDRY_ENERGY|BDRY_CONTENT|CONSTRAINT ); conmap_t * conmap = get_e_constraint_map(e_id); set_attr(v[k],attr); set_v_conmap(v[k],conmap); project_v_constr(v[k],ACTUAL_MOVE,RESET_ONESIDEDNESS); } } increment_lagrange_index(dim,inx); } } /* now install edge vertices in facets */ /* being careful with edge orientation */ if ( web.representation != STRING ) MFOR_ALL_FACETS(f_id) { vertex_id *v,*ev; facetedge_id fe; dim = web.dimension; v = get_facet_vertices(f_id); /* just 2D facets for now */ fe=get_facet_fe(f_id); e_id = get_fe_edge(fe); ev = get_edge_vertices(e_id); if ( inverted(f_id) == inverted(e_id) ) for ( k = 1 ; k < l_order ; k++ ) v[k] = ev[k]; else for ( k = 1 ; k < l_order ; k++ ) v[k] = ev[l_order-k]; fe=get_next_edge(fe); e_id = get_fe_edge(fe); ev = get_edge_vertices(e_id); for ( k = 1 ; k < l_order ; k++ ) { inx[1] = l_order-k; inx[2] = k; inx[0] = 0; spot = lagrange_index(dim,l_order,inx); if ( inverted(f_id) == inverted(e_id) ) v[spot] = ev[k]; else v[spot] = ev[l_order-k]; } fe=get_next_edge(fe); e_id = get_fe_edge(fe); ev = get_edge_vertices(e_id); for ( k = 1 ; k < l_order ; k++ ) { inx[1] = 0; inx[0] = k; inx[2] = l_order - k; spot = lagrange_index(dim,l_order,inx); if ( inverted(f_id) == inverted(e_id) ) v[spot] = ev[k]; else v[spot] = ev[l_order-k]; } } free_matrix(trans); free_matrix(newx); free_matrix(oldx); if ( l_order < web.lagrange_order ) { expand_attribute(EDGE,E_VERTICES_ATTR,&new_ectrl); expand_attribute(FACET,F_VERTICES_ATTR,&new_fctrl); } /* can now shrink */ web.lagrange_order = l_order; web.skel[FACET].ctrlpts = new_fctrl; web.skel[EDGE].ctrlpts = edge_ctrl = new_ectrl; change_flag = 1; web_timestamp = top_timestamp = ++global_timestamp; web.headvnum = l_order; LEAVE_GRAPH_MUTEX; } /******************************************************************* * * Function: change_model() * * Purpose: Ask user what model he wants. */ void change_model() { char ans[100]; if ( web.modeltype == LINEAR ) { outstring("Current model type is LINEAR.\n"); if ( web.symmetric_content ) { outstring("SYMMETRIC CONTENT supported only in linear model.\n"); return; } if ( SDIM > 3 ) { outstring("Space dimension > 3 supported only in linear model.\n"); return; } prompt("Pick new model type, 1 LINEAR, 2 QUADRATIC, >2 LAGRANGE: ", ans,sizeof(ans)); if ( logfd ) fprintf(logfd,"%s\n",ans); switch ( ans[0] ) { case '1': break; case '2': linear_to_quad(); break; default : linear_to_lagrange(ans[0]-'0'); break; } } else if ( web.modeltype == QUADRATIC ) { outstring("Current model type is QUADRATIC.\n"); prompt("Pick new model type, 1 LINEAR, 2 QUADRATIC, >2 LAGRANGE: ", ans,sizeof(ans)); if ( logfd ) fprintf(logfd,"%s\n",ans); switch ( ans[0] ) { case '1': quad_to_linear(); break; case '2': break; default : quad_to_lagrange(ans[0]-'0'); break; } } else if ( web.modeltype == LAGRANGE ) { outstring("Current model type is LAGRANGE.\n"); prompt("Pick new model type, 1 LINEAR, 2 QUADRATIC, >2 LAGRANGE: ",ans,sizeof(ans)); if ( logfd ) fprintf(logfd,"%s\n",ans); switch ( ans[0] ) { case '1': lagrange_to_linear(); break; case '2': lagrange_to_quad(); break; default : lagrange_to_lagrange(ans[0]-'0'); break; } } } /*************************************************************** * * Function: linear_to_quad() * * Purpose: Changes linear patch model to quadratic patch * model by inserting midpoints in all edges * and resetting function pointers. */ void linear_to_quad() { int three = 3,facet_ctrl = FACET_CTRL; edge_id e_id; if ( web.modeltype == QUADRATIC ) return; if ( square_curvature_flag ) kb_error(1827,"Cannot do square curvature in quadratic model.\n", RECOVERABLE); if ( web.symmetry_flag && (web.skel[BODY].count > web.full_flag) && !everything_quantities_flag ) { body_id b_id; FOR_ALL_BODIES(b_id) if ( get_battr(b_id) & FIXEDVOL ) { if ( auto_convert_flag ) convert_to_quantities(); else { kb_error(1804,"Quotient spaces volume needs convert_to_quantities.\n", RECOVERABLE); break; } } } if ( web.symmetric_content && !everything_quantities_flag ) { if ( auto_convert_flag ) convert_to_quantities(); else kb_error(1805,"Do convert_to_quantities for quadratic SYMMETRIC CONTENT.\n", RECOVERABLE); } if ( web.metric_flag && !everything_quantities_flag ) { if ( auto_convert_flag ) convert_to_quantities(); else kb_error(2849,"Do convert_to_quantities for quadratic metric model.\n", RECOVERABLE); } if ( (SDIM > 3) && !(everything_quantities_flag) ) { if ( auto_convert_flag ) convert_to_quantities(); else kb_error(1807,"Do convert_to_quantities to do high dimension space quadratic.\n", RECOVERABLE); } #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) mpi_model(2,QUADRATIC); #endif ENTER_GRAPH_MUTEX; expand_attribute(EDGE,E_VERTICES_ATTR,&three); expand_attribute(FACET,F_VERTICES_ATTR,&facet_ctrl); MFOR_ALL_EDGES(e_id) { REAL *t,x[MAXCOORD]; int i; vertex_id headv,tailv; vertex_id new_v; REAL side[MAXCOORD]; headv = get_edge_headv(e_id); tailv = get_edge_tailv(e_id); t = get_coord(tailv); get_edge_side(e_id,side); for ( i = 0 ; i < SDIM ; i++ ) x[i] = t[i] + side[i]/2; new_v = new_vertex(x,e_id); set_edge_midv(e_id,new_v); if ( get_eattr(e_id) & FIXED ) set_attr(new_v,FIXED); /* for boundary edges, cannot just interpolate parameters due to wrap-around of angular parameters. So tangent extrapolate from one endpoint. */ if ( get_eattr(e_id) & BOUNDARY ) { struct boundary *bdry; REAL *paramb,*parammid,*mu,*mv; vertex_id base_v = NULLID; bdry = get_edge_boundary(e_id); if ( get_boundary(headv) == bdry ) base_v = headv; else if ( get_boundary(tailv) == bdry ) base_v = tailv; else { sprintf(errmsg, "Vertices %s and %s of edge %s are on different boundaries than the edge.\n", ELNAME(headv),ELNAME1(tailv),ELNAME2(e_id)+1); kb_error(1808,errmsg,RECOVERABLE); } set_attr(new_v,BOUNDARY); set_boundary_num(new_v,bdry->num); /* projecting on tangent */ mv = get_coord(new_v); mu = get_coord(base_v); paramb = get_param(base_v); parammid = get_param(new_v); b_extrapolate(bdry,mu,mv,mv,paramb,parammid,new_v); } else if ( get_eattr(e_id) & CONSTRAINT ) { ATTR attr = get_eattr(e_id) & (BDRY_ENERGY|BDRY_CONTENT|CONSTRAINT ); conmap_t * conmap = get_e_constraint_map(e_id); set_attr(new_v,attr); set_v_conmap(new_v,conmap); project_v_constr(new_v,ACTUAL_MOVE,RESET_ONESIDEDNESS); } } web.modeltype = QUADRATIC; web.lagrange_order = 2; web.skel[EDGE].ctrlpts = edge_ctrl = 3; web.skel[FACET].ctrlpts = 6; /* redirect functions */ calc_facet_volume = facet_volume_q; film_grad = film_grad_q; calc_edge_area = edge_area_q; string_grad = string_grad_q; calc_facet_energy = facet_energy_q; calc_facet_forces = facet_force_q; if ( web.metric_flag ) { calc_edge_energy = edge_energy_q_metric; calc_edge_forces = edge_force_q_metric; } else { calc_edge_energy = edge_energy_q; calc_edge_forces = edge_force_q; } adjust_integration_orders(2); gauss_setup(); /* set up gaussian integration arrays */ change_flag = 1; web_timestamp = top_timestamp = ++global_timestamp; LEAVE_GRAPH_MUTEX; } /*************************************************************** * * Function: quad_to_linear() * * Purpose: Changes quadratic patch model to linear patch * model by deleting midpoints from all edges * and resetting function pointers. */ void quad_to_linear() { edge_id e_id; if ( web.modeltype == LINEAR ) return; #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) mpi_model(1,LINEAR); #endif ENTER_GRAPH_MUTEX; MFOR_ALL_EDGES(e_id) { free_element(get_edge_midv(e_id)); } web.modeltype = LINEAR; web.lagrange_order = 1; calc_facet_energy = facet_energy_l; calc_facet_forces = facet_force_l; calc_facet_volume = facet_volume_l; film_grad = film_grad_l; if ( web.metric_flag ) { calc_edge_energy = edge_energy_l_metric; calc_edge_forces = edge_force_l_metric; } else { calc_edge_energy = edge_energy_l; calc_edge_forces = edge_force_l; } calc_edge_area = edge_area_l; string_grad = string_grad_l; web.skel[EDGE].ctrlpts = edge_ctrl = web.skel[EDGE].dimension+1; web.skel[FACET].ctrlpts = web.skel[FACET].dimension+1; adjust_integration_orders(1); gauss_setup(); /* set up gaussian integration arrays */ change_flag = 1; web_timestamp = top_timestamp = ++global_timestamp; LEAVE_GRAPH_MUTEX; } /********************************************************************** * * function: gauss_setup() * * purpose: Initialize arrays used in gaussian integration. * * Does arbitrary order in 1D. * Currently does only 7-pt integration for 2D * and n+1 pt for over 2D. * ***********************************************************************/ #include "f2c.h" int gaussq_ ARGS((integer*, integer*, doublereal*, doublereal*, integer*, doublereal*, doublereal*, doublereal*, doublereal*)); int class_ ARGS((integer*, integer*, doublereal*, doublereal*, doublereal*, doublereal*, doublereal*)); int gausq2_ ARGS((integer*, doublereal*, doublereal*, doublereal*, integer*)); REAL pow_dd ARGS((REAL*,REAL*)); REAL dgamma_ ARGS((REAL*)); REAL d1mach_ ARGS((void)); REAL d_sign ARGS((REAL*,REAL*)); /* from f2c's libf77 */ void gauss_setup() { int i,j,k,m; if ( web.representation == STRING ) gauss_lagrange_setup(1,web.lagrange_order,web.gauss1D_order); else { gauss_lagrange_setup(web.dimension-1,web.lagrange_order,web.gauss1D_order); gauss_lagrange_setup(web.dimension,web.lagrange_order,web.gauss2D_order); } /* set number of control points */ ctrl_num = web.dimension + 1; if ( web.modeltype == QUADRATIC ) ctrl_num += web.dimension*(web.dimension+ 1)/2; /* set number of integration points and weights */ gauss1D_num = (abs(web.gauss1D_order)+2)/2; if ( web.dimension == 2 ) { if ( web.gauss2D_order >= 14 ) { /* use conical product formula */ int npts = 1+web.gauss2D_order/2; /* per dimension */ if ( conical_x ) free_matrix(conical_x); if ( conical_w ) myfree((char*)conical_w); conical_x = dmatrix(0,npts*npts-1,0,2); conical_w = (REAL*)mycalloc(npts*npts,sizeof(REAL)); simplex_quadrature(2,web.gauss2D_order,npts*npts,conical_x,conical_w); gauss2D_num = npts*npts; gauss2Dpt = (barytype*)(conical_x[0]); gauss2Dwt = conical_w; } else if ( web.gauss2D_order >= 12 ) { gauss2D_num = 37; gauss2Dpt = gauss2Dpt13; gauss2Dwt = gauss2Dwt13; } else if ( web.gauss2D_order >= 9 ) { gauss2D_num = 28; gauss2Dpt = gauss2Dpt11; gauss2Dwt = gauss2Dwt11; } else if ( web.gauss2D_order >= 7 ) { gauss2D_num = 16; gauss2Dpt = gauss2Dpt8; gauss2Dwt = gauss2Dwt8; } else if ( web.gauss2D_order >= 6 ) { gauss2D_num = 12; gauss2Dpt = gauss2Dpt6; gauss2Dwt = gauss2Dwt6; } else if ( web.gauss2D_order >= 3 ) { gauss2D_num = 7; gauss2Dpt = gauss2Dpt5; gauss2Dwt = gauss2Dwt5; } else if ( web.gauss2D_order >= 2 ) { gauss2D_num = 3; gauss2Dpt = gauss2Dpt2; gauss2Dwt = gauss2Dwt2; } else { gauss2D_num = 1; gauss2Dpt = gauss2Dpt1; gauss2Dwt = gauss2Dwt1; } } else gauss2D_num = web.dimension + 1; /* always have 1D integration for edges on constraints */ web.gauss1D_order = abs(web.gauss1D_order); gauss1Dpt = (REAL *)mycalloc(gauss1D_num,sizeof(REAL)); gauss1Dwt = (REAL *)mycalloc(gauss1D_num,sizeof(REAL)); grule(gauss1D_num,gauss1Dpt,gauss1Dwt); if ( gauss1poly ) { free_matrix(gauss1poly); gauss1poly = NULL; free_matrix(gauss1polyd); gauss1polyd = NULL; } if ( web.modeltype == LINEAR ) { edge_ctrl = 2; gauss1poly = dmatrix(0,edge_ctrl-1,0,gauss1D_num-1); gauss1polyd = dmatrix(0,edge_ctrl-1,0,gauss1D_num-1); for ( m = 0 ; m < gauss1D_num ; m++ ) { gauss1poly[0][m] = (1-gauss1Dpt[m]); gauss1poly[1][m] = gauss1Dpt[m]; gauss1polyd[0][m] = -1.0; gauss1polyd[1][m] = 1.0; } } else if ( web.modeltype == QUADRATIC ) { edge_ctrl = 3; gauss1poly = dmatrix(0,edge_ctrl-1,0,gauss1D_num-1); gauss1polyd = dmatrix(0,edge_ctrl-1,0,gauss1D_num-1); for ( m = 0 ; m < gauss1D_num ; m++ ) { gauss1poly[0][m] = (1-2*gauss1Dpt[m])*(1-gauss1Dpt[m]); gauss1poly[1][m] = 4*gauss1Dpt[m]*(1-gauss1Dpt[m]); gauss1poly[2][m] = gauss1Dpt[m]*(2*gauss1Dpt[m]-1); gauss1polyd[0][m] = 4*gauss1Dpt[m]-3; gauss1polyd[1][m] = 4 - 8*gauss1Dpt[m]; gauss1polyd[2][m] = 4*gauss1Dpt[m]-1; } } /* could have more modeltypes here */ /* would have to up EDGE_CTRL to max of possible edge_ctrl */ if ( web.dimension == 1 ) { gauss2Dwt = gauss1Dwt; gauss2Dpt = gauss2Dpt; gauss2D_num = gauss1D_num; } else if ( web.dimension > 2 ) { gauss2Dwt = (REAL *)mycalloc(gauss2D_num,sizeof(REAL)); for ( i = 0 ; i < gauss2D_num ; i++ ) gauss2Dwt[i] = 1.0/gauss2D_num; /* trivial points */ } /* set up interpolation polynomial values */ if ( gpoly ) free_matrix(gpoly); gpoly = dmatrix(0,gauss2D_num-1,0,ctrl_num-1); if ( gpolypartial ) free_matrix3(gpolypartial); gpolypartial = dmatrix3(gauss2D_num,web.dimension,ctrl_num); if ( web.dimension == 1 ) for ( j = 0 ; j < gauss2D_num ; j++ ) for ( i = 0 ; i < ctrl_num ; i++ ) { REAL p=1.0,sum=0.0; int scale = ctrl_num - 1; for ( m = 0 ; m < ctrl_num ; m++ ) { if ( m == i ) continue; p *= (gauss1Dpt[j]*scale - m)/(i - m); if ( p == 0.0 ) break; sum += scale/(gauss1Dpt[j]*scale - m); } gpoly[j][i] = p; gpolypartial[j][0][i] = sum*p; } else if ( web.dimension == 2 ) { if ( web.modeltype == LINEAR ) { for ( k = 0 ; k < gauss2D_num ; k++ ) { gpoly[k][0] = gauss2Dpt[k][0]; gpoly[k][1] = gauss2Dpt[k][1]; gpoly[k][2] = gauss2Dpt[k][2]; gpolypartial[k][0][0] = -1.0; gpolypartial[k][1][0] = -1.0; gpolypartial[k][0][1] = 1.0; gpolypartial[k][1][2] = 1.0; /* others 0 */ } } else /* QUADRATIC */ { for ( k = 0 ; k < gauss2D_num ; k++ ) for ( j = 0 ; j < ctrl_num ; j++ ) { gpoly[k][j] = intpoly6(j,2*gauss2Dpt[k][1],2*gauss2Dpt[k][2]); for ( i = 0 ; i < 2 ; i++ ) gpolypartial[k][i][j] = 2*intpoly6part(j,i, 2*gauss2Dpt[k][1], 2*gauss2Dpt[k][2]); /* since intpoly was on side 2 triangle */ /* and gauss2Dpt barycentric */ } } } else /* higher dimension */ { /* crude: gauss pts same as control points */ for ( k = 0 ; k < gauss2D_num ; k++ ) { gpoly[k][k] = 1.0; /* rest 0 */ for ( j = 0 ; j < web.dimension ; j++ ) { gpolypartial[k][j][0] = -1.0; gpolypartial[k][j][j+1] = 1.0; } } } } /************************************************************************* * * function: simplex_quadrature() * * purpose: Set up quadrature coefficients on simplex. Uses conical product * formula from Stroud that represents simplex as crushed cube, with * product of 1D Gauss-Jacobi formulas. * * return: Number of quadrature points, ((order+1)/2)^dim. * */ int simplex_quadrature(dim,order,maxpt,x,w) int dim; /* dimension of simplex */ int order; /* order of polynomial to do correctly */ int maxpt; /* points available in x,w */ REAL **x; /* for barycentric coordinates of quadrature points */ REAL *w; /* weights at quadrature points */ { int N = 1+order/2; /* points needed in 1D integration */ int bsize; /* number of total points */ int iter[100]; /* iteration variables */ int n; int count; int depth; REAL **gj_x; /* gauss-jacobi nodes [alpha][j] */ REAL **gj_w; /* gauss-jacobi weights [alpha][j] */ REAL coeff; /* simplex factor */ if ( N < 2 ) N = 2; /* the lowest we have */ for ( n = 0, bsize = 1 ; n < dim ; n++ ) bsize *= N; if ( bsize > maxpt ) { puts("too few points.\n"); exit(1); } gj_x = dmatrix(0,dim-1,0,N); gj_w = dmatrix(0,dim-1,0,N); /* get gauss-jacobi info */ for ( n = 0 ; n < dim ; n++ ) gauss_jacobi(N,(REAL)n,gj_x[n],gj_w[n]); /* overall simplex factor */ coeff = (REAL)(1 << ((dim*(dim+1))/2)); for ( n = 1 ; n <= dim ; n++ ) coeff /= n; /* iteration over all N^dim points */ /* a little tricky since nesting depth is not fixed */ for ( n = 0 ; n < dim ; n++ ) iter[n] = 0; count = 0; /* points so far */ do { REAL prod; /* at bottom, do some work */ /* weight */ for ( w[count] = 1., n = 0 ; n < dim ; n++ ) w[count] *= gj_w[dim-1-n][iter[n]]; w[count] /= coeff; /* coordinates */ for ( prod = 1., n = 0, x[count][dim] = 1.0 ; n < dim ; n++ ) { x[count][n] = (1-gj_x[dim-1-n][iter[n]])/2*prod; x[count][dim] -= x[count][n]; /* barycentric */ prod *= 1 - (1-gj_x[dim-1-n][iter[n]])/2; } count++; depth = dim-1; while ( ++iter[depth] >= N ) { iter[depth] = 0; /* prepare for next descent */ depth--; /* back up a level */ if ( depth < 0 ) break; } } while ( depth >= 0 ); free_matrix(gj_x); free_matrix(gj_w); return count; } /**************************************************************************** * * function: gauss_jacobi() * * purpose: calculate nodes and weights for gauss-jacobi integration * on (-1,1) with weight (1+x)^beta */ void gauss_jacobi(n,beta,t,w) int n; /* the number of points used for the quadrature rule */ REAL beta; /* parameter */ REAL *t; /* will contain the desired nodes. */ REAL *w; /* will contain the desired weights w(j). */ { int kind = 5; /* jacobi, w(x) = (1-x)**alpha * (1+x)**beta on (-1,1) */ REAL alpha = 0.0; int kpts = 0; /* endpoint nodes */ REAL endpts[2]; /* fixed endpint nodes */ REAL *b; /* real scratch array of length n */ b = (REAL*)temp_calloc(n,sizeof(REAL)); gaussq_(&kind, &n, &alpha, &beta, &kpts, endpts, b, t, w); temp_free((char*)b); } /* some stuff for gaussq() */ REAL dgamma_(x) REAL *x; { REAL b; int n,k; n = (int)*x; if ( *x != (REAL)n ) kb_error(1810,"Noninteger dgamma_ argument.\n",RECOVERABLE); if ( n < 1 ) kb_error(1811,"Nonpositive dgamma_ argument.\n",RECOVERABLE); for ( b = 1.0, k = 2 ; k < n ; k++ ) b *= k; return b; } REAL d1mach_() { /* return DBL_EPSILON; */ REAL eps; REAL one = 1.0; for ( eps = 1.0 ; one + eps != one ; eps /= 2.0 ) ; return 2.0*eps; } REAL pow_dd(x,y) REAL *x,*y; { REAL a = pow(*x,*y); return a; } REAL d_sign(a,b) /* from f2c's libf77 */ REAL *a,*b; { REAL x; x = (*a >= 0. ? *a : - *a); return( *b >= 0. ? x : -x); } /***********************************************************************/ /* gaussq.f -- translated by f2c (version 19950314). You must link the resulting object file with the libraries: -lf2c -lm (in that order) */ /* Table of constant values */ static doublereal c_b19 = 2.; doublereal solve_ ARGS((REAL *, int *, REAL *, REAL *)); /* ====================================================================== */ /* NIST Guide to Available Math Software. */ /* Fullsource for module GAUSSQ from package GO. */ /* Retrieved from NETLIB on Sat Mar 18 10:07:24 1995. */ /* ====================================================================== */ /* Subroutine */ int gaussq_(kind, n, alpha, beta, kpts, endpts, b, t, w) integer *kind, *n; doublereal *alpha, *beta; integer *kpts; doublereal *endpts, *b, *t, *w; { /* System generated locals */ integer i__1; doublereal d__1; /* Local variables */ integer ierr, i; doublereal t1; doublereal muzero, gam; /* this set of routines computes the nodes t(j) and weights */ /* w(j) for gaussian-type quadrature rules with pre-assigned */ /* nodes. these are used when one wishes to approximate */ /* integral (from a to b) f(x) w(x) dx */ /* n */ /* by sum w f(t ) */ /* j=1 j j */ /* (note w(x) and w(j) have no connection with each other.) */ /* here w(x) is one of six possible non-negative weight */ /* functions (listed below), and f(x) is the */ /* function to be integrated. gaussian quadrature is particularly */ /* useful on infinite intervals (with appropriate weight */ /* functions), since then other techniques often fail. */ /* associated with each weight function w(x) is a set of */ /* orthogonal polynomials. the nodes t(j) are just the zeroes */ /* of the proper n-th degree polynomial. */ /* input parameters (all real numbers are in REAL precision) */ /* kind an integer between 1 and 6 giving the type of */ /* quadrature rule: */ /* kind = 1: legendre quadrature, w(x) = 1 on (-1, 1) */ /* kind = 2: chebyshev quadrature of the first kind */ /* w(x) = 1/sqrt(1 - x*x) on (-1, +1) */ /* kind = 3: chebyshev quadrature of the second kind */ /* w(x) = sqrt(1 - x*x) on (-1, 1) */ /* kind = 4: hermite quadrature, w(x) = exp(-x*x) on */ /* (-infinity, +infinity) */ /* kind = 5: jacobi quadrature, w(x) = (1-x)**alpha * (1+x)** */ /* beta on (-1, 1), alpha, beta .gt. -1. */ /* note: kind=2 and 3 are a special case of this. */ /* kind = 6: generalized laguerre quadrature, w(x) = exp(-x)* */ /* x**alpha on (0, +infinity), alpha .gt. -1 */ /* n the number of points used for the quadrature rule */ /* alpha real parameter used only for gauss-jacobi and gauss- */ /* laguerre quadrature (otherwise use 0.d0). */ /* beta real parameter used only for gauss-jacobi quadrature-- */ /* (otherwise use 0.d0) */ /* kpts (integer) normally 0, unless the left or right end- */ /* point (or both) of the interval is required to be a */ /* node (this is called gauss-radau or gauss-lobatto */ /* quadrature). then kpts is the number of fixed */ /* endpoints (1 or 2). */ /* endpts real array of length 2. contains the values of */ /* any fixed endpoints, if kpts = 1 or 2. */ /* b real scratch array of length n */ /* output parameters (both REAL precision arrays of length n) */ /* t will contain the desired nodes. */ /* w will contain the desired weights w(j). */ /* underflow may sometimes occur, but is harmless. */ /* references */ /* 1. golub, g. h., and welsch, j. h., "calculation of gaussian */ /* quadrature rules," mathematics of computation 23 (april, */ /* 1969), pp. 221-230. */ /* 2. golub, g. h., "some modified matrix eigenvalue problems," */ /* siam review 15 (april, 1973), pp. 318-334 (section 7). */ /* 3. stroud and secrest, gaussian quadrature formulas, prentice- */ /* hall, englewood cliffs, n.j., 1966. */ /* original version 20 jan 1975 from stanford */ /* modified 21 dec 1983 by eric grosse */ /* imtql2 => gausq2 */ /* hex constant => d1mach (from core library) */ /* compute pi using datan */ /* removed accuracy claims, description of method */ /* added single precision version */ /* Parameter adjustments */ --w; --t; --b; --endpts; /* Function Body */ class_(kind, n, alpha, beta, &b[1], &t[1], &muzero); /* the matrix of coefficients is assumed to be symmetric. */ /* the array t contains the diagonal elements, the array */ /* b the off-diagonal elements. */ /* make appropriate changes in the lower right 2 by 2 */ /* submatrix. */ if (*kpts == 0) { goto L100; } if (*kpts == 2) { goto L50; } /* if kpts=1, only t(n) must be changed */ /* Computing 2nd power */ d__1 = b[*n - 1]; t[*n] = solve_(&endpts[1], n, &t[1], &b[1]) * (d__1 * d__1) + endpts[1]; goto L100; /* if kpts=2, t(n) and b(n-1) must be recomputed */ L50: gam = solve_(&endpts[1], n, &t[1], &b[1]); t1 = (endpts[1] - endpts[2]) / (solve_(&endpts[2], n, &t[1], &b[1]) - gam) ; b[*n - 1] = sqrt(t1); t[*n] = endpts[1] + gam * t1; /* note that the indices of the elements of b run from 1 to n-1 */ /* and thus the value of b(n) is arbitrary. */ /* now compute the eigenvalues of the symmetric tridiagonal */ /* matrix, which has been modified as necessary. */ /* the method used is a ql-type method with origin shifting */ L100: w[1] = 1.; i__1 = *n; for (i = 2; i <= i__1; ++i) { /* L105: */ w[i] = 0.; } gausq2_(n, &t[1], &b[1], &w[1], &ierr); i__1 = *n; for (i = 1; i <= i__1; ++i) { /* L110: */ w[i] = muzero * w[i] * w[i]; } return 0; } /* gaussq_ */ doublereal solve_(shift, n, a, b) doublereal *shift; integer *n; doublereal *a, *b; { /* System generated locals */ integer i__1; doublereal ret_val, d__1; /* Local variables */ integer i; doublereal alpha; integer nm1; /* this procedure performs elimination to solve for the */ /* n-th component of the solution delta to the equation */ /* (jn - shift*identity) * delta = en, */ /* where en is the vector of all zeroes except for 1 in */ /* the n-th position. */ /* the matrix jn is symmetric tridiagonal, with diagonal */ /* elements a(i), off-diagonal elements b(i). this equation */ /* must be solved to obtain the appropriate changes in the lower */ /* 2 by 2 submatrix of coefficients for orthogonal polynomials. */ /* Parameter adjustments */ --b; --a; /* Function Body */ alpha = a[1] - *shift; nm1 = *n - 1; i__1 = nm1; for (i = 2; i <= i__1; ++i) { /* L10: */ /* Computing 2nd power */ d__1 = b[i - 1]; alpha = a[i] - *shift - d__1 * d__1 / alpha; } ret_val = 1. / alpha; return ret_val; } /* solve_ */ /* Subroutine */ int class_(kind, n, alpha, beta, b, a, muzero) integer *kind, *n; doublereal *alpha, *beta, *b, *a, *muzero; { /* System generated locals */ integer i__1; doublereal d__1, d__2, d__3; /* Local variables */ integer i; doublereal ab; doublereal pi; integer nm1; doublereal a2b2, abi; /* this procedure supplies the coefficients a(j), b(j) of the */ /* recurrence relation */ /* b p (x) = (x - a ) p (x) - b p (x) */ /* j j j j-1 j-1 j-2 */ /* for the various classical (normalized) orthogonal polynomials, */ /* and the zero-th moment */ /* muzero = integral w(x) dx */ /* of the given polynomial's weight function w(x). since the */ /* polynomials are orthonormalized, the tridiagonal matrix is */ /* guaranteed to be symmetric. */ /* the input parameter alpha is used only for laguerre and */ /* jacobi polynomials, and the parameter beta is used only for */ /* jacobi polynomials. the laguerre and jacobi polynomials */ /* require the gamma function. */ /* Parameter adjustments */ --a; --b; /* Function Body */ pi = atan(1.) * 4.; nm1 = *n - 1; switch ((int)*kind) { case 1: goto L10; case 2: goto L20; case 3: goto L30; case 4: goto L40; case 5: goto L50; case 6: goto L60; } /* kind = 1: legendre polynomials p(x) */ /* on (-1, +1), w(x) = 1. */ L10: *muzero = 2.; i__1 = nm1; for (i = 1; i <= i__1; ++i) { a[i] = 0.; abi = (doublereal) i; /* L11: */ b[i] = abi / sqrt(abi * 4 * abi - 1.); } a[*n] = 0.; return 0; /* kind = 2: chebyshev polynomials of the first kind t(x) */ /* on (-1, +1), w(x) = 1 / sqrt(1 - x*x) */ L20: *muzero = pi; i__1 = nm1; for (i = 1; i <= i__1; ++i) { a[i] = 0.; /* L21: */ b[i] = .5; } b[1] = sqrt(.5); a[*n] = 0.; return 0; /* kind = 3: chebyshev polynomials of the second kind u(x) */ /* on (-1, +1), w(x) = sqrt(1 - x*x) */ L30: *muzero = pi / 2.; i__1 = nm1; for (i = 1; i <= i__1; ++i) { a[i] = 0.; /* L31: */ b[i] = .5; } a[*n] = 0.; return 0; /* kind = 4: hermite polynomials h(x) on (-infinity, */ /* +infinity), w(x) = exp(-x**2) */ L40: *muzero = sqrt(pi); i__1 = nm1; for (i = 1; i <= i__1; ++i) { a[i] = 0.; /* L41: */ b[i] = sqrt(i / 2.); } a[*n] = 0.; return 0; /* kind = 5: jacobi polynomials p(alpha, beta)(x) on */ /* (-1, +1), w(x) = (1-x)**alpha + (1+x)**beta, alpha and */ /* beta greater than -1 */ L50: ab = *alpha + *beta; abi = ab + 2.; d__1 = ab + 1.; d__2 = *alpha + 1.; d__3 = *beta + 1.; *muzero = pow_dd(&c_b19, &d__1) * dgamma_(&d__2) * dgamma_(&d__3) / dgamma_(&abi); a[1] = (*beta - *alpha) / abi; b[1] = sqrt((*alpha + 1.) * 4. * (*beta + 1.) / ((abi + 1.) * abi * abi)); a2b2 = *beta * *beta - *alpha * *alpha; i__1 = nm1; for (i = 2; i <= i__1; ++i) { abi = i * 2. + ab; a[i] = a2b2 / ((abi - 2.) * abi); /* L51: */ b[i] = sqrt(i * 4. * (i + *alpha) * (i + *beta) * (i + ab) / ((abi * abi - 1) * abi * abi)); /* ok so far */ } abi = *n * 2. + ab; a[*n] = a2b2 / ((abi - 2.) * abi); return 0; /* kind = 6: laguerre polynomials l(alpha)(x) on */ /* (0, +infinity), w(x) = exp(-x) * x**alpha, alpha greater */ /* than -1. */ L60: d__1 = *alpha + 1.; *muzero = dgamma_(&d__1); i__1 = nm1; for (i = 1; i <= i__1; ++i) { a[i] = i * 2. - 1. + *alpha; /* L61: */ b[i] = sqrt(i * (i + *alpha)); } a[*n] = *n * 2. - 1 + *alpha; return 0; } /* class_ */ /* Subroutine */ int gausq2_(n, d, e, z, ierr) integer *n; doublereal *d, *e, *z; integer *ierr; { /* System generated locals */ integer i__1, i__2; doublereal d__1, d__2, d__3; /* Local variables */ doublereal b, c, f, g; integer i, j, k, l, m; doublereal p, r, s; integer ii; doublereal machep; integer mml; /* this subroutine is a translation of an algol procedure, */ /* num. math. 12, 377-383(1968) by martin and wilkinson, */ /* as modified in num. math. 15, 450(1970) by dubrulle. */ /* handbook for auto. comp., vol.ii-linear algebra, 241-248(1971). */ /* this is a modified version of the 'eispack' routine imtql2. */ /* this subroutine finds the eigenvalues and first components of the */ /* eigenvectors of a symmetric tridiagonal matrix by the implicit ql */ /* method. */ /* on input: */ /* n is the order of the matrix; */ /* d contains the diagonal elements of the input matrix; */ /* e contains the subdiagonal elements of the input matrix */ /* in its first n-1 positions. e(n) is arbitrary; */ /* z contains the first row of the identity matrix. */ /* on output: */ /* d contains the eigenvalues in ascending order. if an */ /* error exit is made, the eigenvalues are correct but */ /* unordered for indices 1, 2, ..., ierr-1; */ /* e has been destroyed; */ /* z contains the first components of the orthonormal eigenvectors */ /* of the symmetric tridiagonal matrix. if an error exit is */ /* made, z contains the eigenvectors associated with the stored */ /* eigenvalues; */ /* ierr is set to */ /* zero for normal return, */ /* j if the j-th eigenvalue has not been */ /* determined after 30 iterations. */ /* ------------------------------------------------------------------ */ /* Parameter adjustments */ --z; --e; --d; /* Function Body */ machep = d1mach_(); *ierr = 0; if (*n == 1) { goto L1001; } e[*n] = 0.; i__1 = *n; for (l = 1; l <= i__1; ++l) { j = 0; /* :::::::::: look for small sub-diagonal element :::::::::: */ L105: i__2 = *n; for (m = l; m <= i__2; ++m) { if (m == *n) { goto L120; } if ((d__1 = e[m], fabs(d__1)) <= machep * ((d__2 = d[m], fabs(d__2)) + (d__3 = d[m + 1], fabs(d__3)))) { goto L120; } /* L110: */ } L120: p = d[l]; if (m == l) { goto L240; } if (j == 30) { goto L1000; } ++j; /* :::::::::: form shift :::::::::: */ g = (d[l + 1] - p) / (e[l] * 2.); r = sqrt(g * g + 1.); g = d[m] - p + e[l] / (g + d_sign(&r, &g)); s = 1.; c = 1.; p = 0.; mml = m - l; /* :::::::::: for i=m-1 step -1 until l do -- :::::::::: */ i__2 = mml; for (ii = 1; ii <= i__2; ++ii) { i = m - ii; f = s * e[i]; b = c * e[i]; if (fabs(f) < fabs(g)) { goto L150; } c = g / f; r = sqrt(c * c + 1.); e[i + 1] = f * r; s = 1. / r; c *= s; goto L160; L150: s = f / g; r = sqrt(s * s + 1.); e[i + 1] = g * r; c = 1. / r; s *= c; L160: g = d[i + 1] - p; r = (d[i] - g) * s + c * 2. * b; p = s * r; d[i + 1] = g + p; g = c * r - b; /* :::::::::: form first component of vector :::::::::: */ f = z[i + 1]; z[i + 1] = s * z[i] + c * f; /* L200: */ z[i] = c * z[i] - s * f; } d[l] -= p; e[l] = g; e[m] = 0.; goto L105; L240: ; } /* :::::::::: order eigenvalues and eigenvectors :::::::::: */ i__1 = *n; for (ii = 2; ii <= i__1; ++ii) { i = ii - 1; k = i; p = d[i]; i__2 = *n; for (j = ii; j <= i__2; ++j) { if (d[j] >= p) { goto L260; } k = j; p = d[j]; L260: ; } if (k == i) { goto L300; } d[k] = d[i]; d[i] = p; p = z[i]; z[i] = z[k]; z[k] = p; L300: ; } goto L1001; /* :::::::::: set error -- no convergence to an */ /* eigenvalue after 30 iterations :::::::::: */ L1000: *ierr = l; L1001: return 0; /* :::::::::: last card of gausq2 :::::::::: */ } /* gausq2_ */ /***************************************************************************** ****************************************************************************** Gauss-Lagrange elements of arbitrary order and dimension. Lagrange element of order n is simplex with control points in barycentric lattice with n points along each 1-edge. ******************************************************************************/ /****************************************************************************** * * function: lagrange_index() * * purpose: convert lagrange barycentric index vector to linear index. * inx[0] least significant, inx[dim] most significant * */ int lagrange_index(dim,order,inx) int dim; int order; int *inx; { int spot; int k; int left; spot = binom_coeff(order+dim,dim)-1; left = order; for ( k = dim ; k > 0 ; k-- ) { left -= inx[k]; spot -= binom_coeff(left-1+k,k); } return spot; } /****************************************************************************** * * function: increment_lagrange_index() * * purpose: increment lagrange barycentric index vector. * inx[0] least significant, inx[dim] most significant. * So corner vertices appear in proper order. * Before first call, initialize inx[0] = lagrange_order * Return: 1 valid next index * 0 done */ int increment_lagrange_index(dim,inx) int dim; int *inx; { int j; for ( j = 0 ; j < dim ; j++ ) if ( inx[j] > 0 ) { inx[j+1]++; inx[0] = inx[j]-1; if ( j > 0 ) inx[j] = 0; return 1; } return 0; /* done */ } /****************************************************************************** * * function: gauss_lagrange_setup() * * purpose: initialize lagrange element interpolation matrices * for finding gauss points from lagrange points. * * Uses new global gauss arrays. */ void gauss_lagrange_setup(dim,lagrange_order,gauss_order) int dim; /* dimension of lagrange element */ int lagrange_order; /* order of lagrange element */ int gauss_order; /* order of polynomial to integrate exactly */ { struct gauss_lag *gl; int i,j,k,n; REAL *temp; int linx[MAXCOORD+1]; if ( dim > MAXCOORD ) kb_error(1812,"Surface dimension too high in gauss_lagrange_setup().\n", RECOVERABLE); if ( dim < 1 ) kb_error(1813,"Dimension too low in gauss_lagrange_setup().\n", RECOVERABLE); if ( gauss_order > maxgaussorder[dim] ) { gauss_lagrange[dim] = (struct gauss_lag *)kb_realloc( (char*)(gauss_lagrange[dim]), (gauss_order+1)*sizeof(struct gauss_lag)); maxgaussorder[dim] = gauss_order; } gl = &gauss_lagrange[dim][gauss_order]; if ( gl->gausspt ) goto do_lagrange; /* first, do gauss points and weights */ switch ( dim ) { case 1: gl->gnumpts = (abs(gauss_order)+2)/2; gl->gausspt = dmatrix(0,gl->gnumpts-1,0,1); gl->gausswt = (REAL*)mycalloc(gl->gnumpts,sizeof(REAL)); temp = (REAL*)temp_calloc(gl->gnumpts,sizeof(REAL)); grule(gl->gnumpts,temp,gl->gausswt); for ( i = 0 ; i < gl->gnumpts ; i++ ) { gl->gausspt[i][0] = temp[i]; gl->gausspt[i][1] = 1.0 - temp[i]; } temp_free((char*)temp); break; case 2: if ( gauss_order >= 14 ) { /* use conical product formula */ int npts = 1+gauss_order/2; /* per dimension */ gl->gnumpts = npts*npts; gl->gausspt = dmatrix(0,gl->gnumpts-1,0,2); gl->gausswt = (REAL*)mycalloc(npts*npts,sizeof(REAL)); simplex_quadrature(2,gauss_order,npts*npts,gl->gausspt,gl->gausswt); } else if ( gauss_order >= 12 ) { gl->gnumpts = 37; gl->gausspt = dmatrix(0,gl->gnumpts-1,0,2); for ( i = 0 ; i < gl->gnumpts ; i++ ) for ( j = 0 ; j <= dim ; j++ ) gl->gausspt[i][j] = gauss2Dpt13[i][j]; gl->gausswt = gauss2Dwt13; } else if ( gauss_order >= 9 ) { gl->gnumpts = 28; gl->gausspt = dmatrix(0,gl->gnumpts-1,0,2); for ( i = 0 ; i < gl->gnumpts ; i++ ) for ( j = 0 ; j <= dim ; j++ ) gl->gausspt[i][j] = gauss2Dpt11[i][j]; gl->gausswt = gauss2Dwt11; } else if ( gauss_order >= 7 ) { gl->gnumpts = 16; gl->gausspt = dmatrix(0,gl->gnumpts-1,0,2); for ( i = 0 ; i < gl->gnumpts ; i++ ) for ( j = 0 ; j <= dim ; j++ ) gl->gausspt[i][j] = gauss2Dpt8[i][j]; gl->gausswt = gauss2Dwt8; } else if ( gauss_order >= 6 ) { gl->gnumpts = 12; gl->gausspt = dmatrix(0,gl->gnumpts-1,0,2); for ( i = 0 ; i < gl->gnumpts ; i++ ) for ( j = 0 ; j <= dim ; j++ ) gl->gausspt[i][j] = gauss2Dpt6[i][j]; gl->gausswt = gauss2Dwt6; } else if ( gauss_order >= 3 ) { gl->gnumpts = 7; gl->gausspt = dmatrix(0,gl->gnumpts-1,0,2); for ( i = 0 ; i < gl->gnumpts ; i++ ) for ( j = 0 ; j <= dim ; j++ ) gl->gausspt[i][j] = gauss2Dpt5[i][j]; gl->gausswt = gauss2Dwt5; } else if ( gauss_order >= 2 ) { gl->gnumpts = 3; gl->gausspt = dmatrix(0,gl->gnumpts-1,0,2); for ( i = 0 ; i < gl->gnumpts ; i++ ) for ( j = 0 ; j <= dim ; j++ ) gl->gausspt[i][j] = gauss2Dpt2[i][j]; gl->gausswt = gauss2Dwt2; } else { gl->gnumpts = 1; gl->gausspt = dmatrix(0,gl->gnumpts-1,0,2); for ( i = 0 ; i < gl->gnumpts ; i++ ) for ( j = 0 ; j <= dim ; j++ ) gl->gausspt[i][j] = gauss2Dpt1[i][j]; gl->gausswt = gauss2Dwt1; } break; default: /* over 2 */ { int npts = 1+gauss_order/2; /* per dimension */ for ( i = 0, gl->gnumpts = 1 ; i < dim ; i++ ) gl->gnumpts *= npts; gl->gausspt = dmatrix(0,gl->gnumpts-1,0,dim); gl->gausswt = (REAL*)mycalloc(gl->gnumpts,sizeof(REAL)); simplex_quadrature(dim,gauss_order,gl->gnumpts,gl->gausspt,gl->gausswt); } break; } do_lagrange: /* now set up lagrange to gauss interpolation matrices */ /* Lagrange points are in linear array in barycentric order */ if ( gl->gpoly ) free_matrix(gl->gpoly); if ( gl->gpolypart ) free_matrix3(gl->gpolypart); if ( gl->lpolypart ) free_matrix3(gl->lpolypart); gl->lagrange_order = lagrange_order; gl->lagpts = binom_coeff(dim+lagrange_order,lagrange_order); gl->gpoly = dmatrix(0,gl->gnumpts-1,0,gl->lagpts-1); gl->gpolypart = dmatrix3(gl->gnumpts,dim,gl->lagpts); gl->lpolypart = dmatrix3(gl->lagpts,dim,gl->lagpts); for ( n = 0 ; n < gl->gnumpts ; n++ ) { REAL *x; int inx[MAXCOORD+1]; x = gl->gausspt[n]; /* full barycentric coords, adding to 1 */ for ( i = 0 ; i <= dim ; i++ ) inx[i] = 0; inx[0] = lagrange_order; do { REAL prod, prodpart[MAXCOORD]; int ix,mm; for ( k = 0 ; k <= dim ; k++ ) prodpart[k] = 0.0; for ( i = 0, prod = 1.0, mm = 1 ; i <= dim ; i++ ) for ( j = 0 ; j < inx[i] ; j++, mm++ ) { if ( bezier_flag ) { for ( k = 0 ; k <= dim ; k++ ) { prodpart[k] *= mm*x[i]/(j+1); if ( i == k ) prodpart[k] += prod*mm/(j+1.); } prod *= mm*x[i]/(j+1.); } else /* regular Lagrange polynomials */ { for ( k = 0 ; k <= dim ; k++ ) { prodpart[k] *= (lagrange_order*x[i]-j)/(inx[i]-j); if ( i == k ) prodpart[k] += prod*lagrange_order/(inx[i]-j); } prod *= (lagrange_order*x[i]-j)/(inx[i]-j); } } ix = lagrange_index(dim,lagrange_order,inx); gl->gpoly[n][ix] = prod; for ( k = 0 ; k < dim ; k++ ) gl->gpolypart[n][k][ix] = -(prodpart[k] - prodpart[dim]); /* neg since natural coord runs opposite barycentric coord */ } while ( increment_lagrange_index(dim,inx) ); } /* partials to calculate tangents at lagrange points */ for ( i = 0 ; i <= dim ; i++ ) linx[i] = 0; linx[0] = lagrange_order; for ( n = 0 ; n < gl->lagpts ; n++ ) { int inx[MAXCOORD+1]; for ( i = 0 ; i <= dim ; i++ ) inx[i] = 0; inx[0] = lagrange_order; do { REAL prod, prodpart[MAXCOORD]; int ix; for ( k = 0 ; k <= dim ; k++ ) prodpart[k] = 0.0; for ( i = 0, prod = 1.0 ; i <= dim ; i++ ) { for ( j = 0 ; j < inx[i] ; j++ ) { for ( k = 0 ; k <= dim ; k++ ) { prodpart[k] *= ((REAL)linx[i]-j)/(inx[i]-j); if ( i == k ) prodpart[k] += prod/(inx[i]-j); } prod *= ((REAL)linx[i]-j)/(inx[i]-j); } } ix = lagrange_index(dim,lagrange_order,inx); for ( k = 0 ; k < dim ; k++ ) gl->lpolypart[n][k][ix] = -(prodpart[k] - prodpart[dim]); /* neg since natural coord runs opposite barycentric coord */ } while ( increment_lagrange_index(dim,inx) ); increment_lagrange_index(dim,linx); } } /************************************************************************* Simplex model lagrange model changing. Can't assume edges join facets. Have to construct catalog of points, indexed by barycentric coords. *************************************************************************/ struct baryhash { int nn; /* number of vertices */ vertex_id vv[MAXCOORD+1]; /* vertices */ int xx[MAXCOORD+1]; /* barycentric coordinates */ vertex_id v_id; /* the vertex */ } *baryhash_table; int baryhash_size; /* allocated size */ int baryhash_count; /* active entries */ void baryhash_init ARGS((int)); int baryhash_func ARGS((int,vertex_id*,int*)); vertex_id baryhash_find ARGS((int,vertex_id*,int*,vertex_id)); void baryhash_end ARGS((void)); void baryhash_init(fctrl) int fctrl; /* points per facet */ { baryhash_size = (3*fctrl/2)*web.skel[FACET].count; baryhash_table = (struct baryhash*)temp_calloc(baryhash_size, sizeof(struct baryhash)); baryhash_count = 0; } void baryhash_end() { temp_free((char*)baryhash_table); baryhash_size = baryhash_count = 0; } int baryhash_func(n,v,x) int n; vertex_id *v; int *x; { unsigned int hash; int k; for ( k = 0, hash = 0 ; k < n ; k ++ ) { hash += (unsigned int)(v[k]*1843723 + x[k]*17977); } return hash % baryhash_size; } /* find and/or insert */ vertex_id baryhash_find(n,v,x,newv) int n; /* number of vertices */ vertex_id *v; /* vertices */ int *x; /* barycentric coords, relative to l_order */ vertex_id newv; /* insert if nonnul and not found */ { int nn; /* number of nonzero coords */ vertex_id vv[MAXCOORD+1]; /* needed vertices, in order */ int xx[MAXCOORD+1]; /* needed coordinates */ int spot; int i,j,k; struct baryhash *b; /* clean up key with insertion sort */ for ( i = 0, j = 0 ; i < n ; i++ ) { if ( x[i] == 0 ) continue; for ( k = j; k > 0 ; k-- ) if ( vv[k-1] > v[i] ) { vv[k] = vv[k-1]; xx[k] = xx[k-1]; } else break; vv[k] = v[i]; xx[k] = x[i]; j++; } nn = j; spot = baryhash_func(nn,vv,xx); for (;; spot++) { if ( spot >= baryhash_size ) spot = 0; b = baryhash_table + spot; if ( b->nn == 0 ) /* vacant */ break; if ( b->nn != nn ) continue; for ( i = 0 ; i < nn ; i++ ) if ( (b->vv[i] != vv[i]) || (b->xx[i] != xx[i]) ) break; if ( i == nn ) /* match */ { if ( (newv != NULLID) && (newv != b->v_id) ) { printf("nn = %d newv = %08lX b->v_id = %08lX\n",nn, (unsigned long)newv,(unsigned long)b->v_id); for ( j = 0 ; j < nn ; j++ ) printf("%08lX %d ",vv[j],xx[j]); printf("\n"); kb_error(1815,"Internal error: Duplicate vertex in baryhash!\n",RECOVERABLE); } return b->v_id; } } /* empty_spot: */ if ( newv != NULLID ) { /* insert */ b->nn = nn; for ( k = 0 ; k < nn ; k++ ) { b->vv[k] = vv[k]; b->xx[k] = xx[k]; } b->v_id = newv; baryhash_count++; if ( (REAL)baryhash_count > .9*baryhash_size ) kb_error(1816,"Internal error: Baryhash near full!\n",WARNING); } return NULLID; } /************************************************************************ * * Function: simplex_lagrange_to_lagrange() * * purpose: Convert order of Lagrange model. */ void simplex_lagrange_to_lagrange(l_order) int l_order; { int new_ectrl,new_fctrl; /* new number of control points */ int old_ectrl = web.skel[EDGE].ctrlpts; int old_fctrl = web.skel[FACET].ctrlpts; int i,k,m; int old,new; int inx[MAXCOORD+1]; int oldinx[MAXCOORD+1]; REAL **trans; /* transformation matrix old to new points */ REAL **newx; /* new coords */ REAL **oldx; /* old coords */ facet_id f_id; edge_id e_id; vertex_id v_id; int dim; vertex_id cornerv[MAXCOORD+1]; /* extreme vertices of facet */ if ( web.lagrange_order == l_order ) return; /* already OK */ if ( l_order < 1 ) kb_error(1817,"Lagrange order must be at least 1.\n",RECOVERABLE); ENTER_GRAPH_MUTEX; MFOR_ALL_VERTICES(v_id) unset_attr(v_id,NEWVERTEX); /* so can tell which new */ new_ectrl = binom_coeff(l_order+web.dimension-1,web.dimension-1); new_fctrl = binom_coeff(l_order+web.dimension,web.dimension); baryhash_init(new_fctrl); if ( l_order > web.lagrange_order ) { expand_attribute(EDGE,E_VERTICES_ATTR,&new_ectrl); expand_attribute(FACET,F_VERTICES_ATTR,&new_fctrl); } /* have to wait for later for shrink case */ /* recalc all facet points */ trans = dmatrix(0,new_fctrl,0,old_fctrl); newx = dmatrix(0,new_fctrl,0,SDIM); oldx = dmatrix(0,old_fctrl,0,SDIM); /* calculate transition matrix */ dim = web.dimension; adjust_integration_orders(l_order); gauss_lagrange_setup(dim,l_order,web.gauss2D_order); for ( m = 0 ; m <= dim ; m++ ) inx[m] = 0; inx[0] = l_order; new = 0; do { old = 0; for ( m = 0 ; m <= dim ; m++ ) oldinx[m] = 0; oldinx[0] = web.lagrange_order; do { REAL prod; for ( k = 0, prod = 1.0 ; k <= dim ; k++ ) for ( m = 0 ; m < oldinx[k] ; m++ ) prod *= ((REAL)(inx[k])/l_order*web.lagrange_order - m)/(oldinx[k]-m); trans[new][old] = prod; old++; } while ( increment_lagrange_index(dim,oldinx) ); new++; } while ( increment_lagrange_index(dim,inx) ); /* calc indices of extreme vertices; handy elsewhere */ for ( m = 0 ; m <= dim ; m++ ) inx[m] = 0; for ( k = 0 ; k <= dim ; k++ ) { inx[k] = l_order; new = lagrange_index(dim,l_order,inx); inx[k] = 0; web.skel[FACET].extreme[k] = new; } MFOR_ALL_FACETS(f_id) { vertex_id *v; dim = web.dimension; get_facet_verts(f_id,oldx,NULL); mat_mult(trans,oldx,newx,new_fctrl,web.skel[FACET].ctrlpts,SDIM); v = get_facet_vertices(f_id); /* take care of extreme vertices */ for ( m = 0 ; m <= dim ; m++ ) inx[m] = 0; for ( k = 0 ; k <= dim ; k++ ) { inx[k] = web.lagrange_order; old = lagrange_index(dim,web.lagrange_order,inx); inx[k] = 0; cornerv[k] = v[old]; baryhash_find(1,&cornerv[k],&l_order,cornerv[k]); } /* create new interior */ for ( m = 0 ; m <= dim ; m++ ) inx[m] = 0; inx[0] = l_order; for ( k = 0 ; k < new_fctrl ; k++ ) { int fixed; vertex_id newv; conmap_t conmap[30]; newv = baryhash_find(dim+1,cornerv,inx,NULLID); if ( newv == NULLID ) { newv = new_vertex(newx[k],f_id); set_attr(newv,Q_MIDFACET); set_vertex_facet(newv,f_id); /* since doesn't have edge */ /* set common attributes */ for ( i = 0, fixed = 0; i <= dim ; i++ ) { if ( inx[i] == 0 ) continue; /* doesn't contribute */ if ( get_vattr(cornerv[i]) & FIXED ) fixed = 0; /* find centerpoint parameters for facet on boundary */ if ( get_vattr(cornerv[i]) & BOUNDARY ) /* not working for torus */ kb_error(1819,"Can't do simplex Lagrange with boundaries yet.\n", RECOVERABLE); if ( get_vattr(cornerv[i]) & CONSTRAINT ) { get_v_common_conmap(newv,cornerv[i],conmap); set_v_conmap(newv,conmap); } } if ( fixed ) set_attr(newv,FIXED); if ( get_vattr(newv) & CONSTRAINT ) project_v_constr(newv,ACTUAL_MOVE,RESET_ONESIDEDNESS); baryhash_find(dim+1,cornerv,inx,newv); } v[k] = newv; increment_lagrange_index(dim,inx); } } /* do edges */ /* calculate transition matrix */ dim = web.dimension-1; gauss_lagrange_setup(dim,l_order,web.gauss1D_order); for ( m = 0 ; m <= dim ; m++ ) inx[m] = 0; inx[0] = l_order; new = 0; do { old = 0; for ( m = 0 ; m <= dim ; m++ ) oldinx[m] = 0; oldinx[0] = web.lagrange_order; do { REAL prod; for ( k = 0, prod = 1.0 ; k <= dim ; k++ ) for ( m = 0 ; m < oldinx[k] ; m++ ) prod *= ((REAL)(inx[k])/l_order*web.lagrange_order - m)/(oldinx[k]-m); trans[new][old] = prod; old++; } while ( increment_lagrange_index(dim,oldinx) ); new++; } while ( increment_lagrange_index(dim,inx) ); /* calc indices of extreme vertices; handy elsewhere */ for ( m = 0 ; m <= dim ; m++ ) inx[m] = 0; for ( k = 0 ; k <= dim ; k++ ) { inx[k] = l_order; new = lagrange_index(dim,l_order,inx); inx[k] = 0; web.skel[EDGE].extreme[k] = new; } MFOR_ALL_EDGES(e_id) { vertex_id *v; get_edge_verts(e_id,oldx,NULL); mat_mult(trans,oldx,newx,new_ectrl,old_ectrl,SDIM); v = get_edge_vertices(e_id); /* take care of extreme vertices */ for ( m = 0 ; m <= dim ; m++ ) inx[m] = 0; for ( k = 0 ; k <= dim ; k++ ) { inx[k] = web.lagrange_order; old = lagrange_index(dim,web.lagrange_order,inx); cornerv[k] = v[old]; baryhash_find(1,&cornerv[k],&l_order,cornerv[k]); } /* create new interior */ for ( m = 0 ; m <= dim ; m++ ) inx[m] = 0; inx[0] = l_order; for ( k = 0 ; k < new_ectrl ; k++ ) { int fixed; vertex_id newv; newv = baryhash_find(dim+1,cornerv,inx,NULLID); if ( newv == NULLID ) { conmap_t conmap[30]; newv = new_vertex(newx[k],e_id); set_attr(newv,Q_MIDEDGE); set_vertex_edge(newv,e_id); /* set common attributes */ for ( i = 0, fixed = 0; i <= dim ; i++ ) { if ( inx[i] == 0 ) continue; /* doesn't contribute */ if ( get_vattr(cornerv[i]) & FIXED ) fixed = 0; /* find centerpoint parameters for facet on boundary */ if ( get_vattr(cornerv[i]) & BOUNDARY ) /* not working for torus */ kb_error(1820,"Can't do simplex Lagrange with boundaries yet.\n", RECOVERABLE); if ( get_vattr(cornerv[i]) & CONSTRAINT ) { get_v_common_conmap(newv,cornerv[i],conmap); set_v_conmap(newv,conmap); } } if ( fixed ) set_attr(newv,FIXED); if ( get_vattr(newv) & CONSTRAINT ) project_v_constr(newv,ACTUAL_MOVE,RESET_ONESIDEDNESS); baryhash_find(dim+1,cornerv,inx,newv); } v[k] = newv; increment_lagrange_index(dim,inx); } } free_matrix(trans); free_matrix(newx); free_matrix(oldx); baryhash_end(); if ( l_order < web.lagrange_order ) { expand_attribute(EDGE,E_VERTICES_ATTR,&new_ectrl); expand_attribute(FACET,F_VERTICES_ATTR,&new_fctrl); } /* can now shrink */ /* delete all old non-extreme vertices */ MFOR_ALL_VERTICES(v_id) { if ( !(get_vattr(v_id) & NEWVERTEX) && (get_vattr(v_id) & (Q_MIDFACET|Q_MIDEDGE)) ) free_element(v_id); } web.lagrange_order = l_order; web.skel[FACET].ctrlpts = new_fctrl; web.skel[EDGE].ctrlpts = edge_ctrl = new_ectrl; change_flag = 1; web_timestamp = top_timestamp = ++global_timestamp; web.headvnum = l_order; LEAVE_GRAPH_MUTEX; } /*********************************************************************** * * function: lagrange_to_bezier() * * purpose: convert lagrange control points to bezier control points. */ void lagrange_to_bezier() { int i,j; REAL *oldx[(MAXLAGRANGE+1)*(MAXLAGRANGE+2)/2]; REAL **newx; edge_id e_id; facet_id f_id; vertex_id *v; if ( web.modeltype != LAGRANGE ) return; newx = dmatrix(0,web.skel[FACET].ctrlpts,0,SDIM-1); /* do facet interiors first, so can use unchanged edge vertices */ if ( web.representation == SOAPFILM ) MFOR_ALL_FACETS(f_id) { v = get_facet_vertices(f_id); for ( i = 0 ; i <= web.skel[FACET].ctrlpts ; i++ ) oldx[i] = get_coord(v[i]); mat_mult(bezier2invert[web.lagrange_order],oldx,newx, web.skel[FACET].ctrlpts,web.skel[FACET].ctrlpts,SDIM); for ( i = 1 ; i < web.skel[FACET].ctrlpts ; i++ ) { if ( get_vattr(v[i]) & Q_MIDFACET ) for ( j = 0 ; j < SDIM ; j++ ) oldx[i][j] = newx[i][j]; } } MFOR_ALL_EDGES(e_id) { v = get_edge_vertices(e_id); for ( i = 0 ; i <= web.lagrange_order ; i++ ) oldx[i] = get_coord(v[i]); mat_mult(bezier1invert[web.lagrange_order],oldx,newx, web.lagrange_order+1,web.lagrange_order+1,SDIM); for ( i = 1 ; i < web.lagrange_order ; i++ ) /* no changing ends */ for ( j = 0 ; j < SDIM ; j++ ) oldx[i][j] = newx[i][j]; } free_matrix(newx); } /*********************************************************************** * * function: bezier_to_lagrange() * * purpose: convert bezier control points to lagrange control points. */ void bezier_to_lagrange() { int i,j; REAL *oldx[(MAXLAGRANGE+1)*(MAXLAGRANGE+2)/2]; REAL **newx; edge_id e_id; facet_id f_id; vertex_id *v; if ( web.modeltype != LAGRANGE ) return; newx = dmatrix(0,web.skel[FACET].ctrlpts,0,SDIM-1); /* do facet interior vertices first, so can use unchanged edge verts */ if ( web.representation == SOAPFILM ) MFOR_ALL_FACETS(f_id) { v = get_facet_vertices(f_id); for ( i = 0 ; i <= web.skel[FACET].ctrlpts ; i++ ) oldx[i] = get_coord(v[i]); mat_mult(bezier2revert[web.lagrange_order],oldx,newx, web.skel[FACET].ctrlpts,web.skel[FACET].ctrlpts,SDIM); for ( i = 1 ; i < web.skel[FACET].ctrlpts ; i++ ) { if ( get_vattr(v[i]) & Q_MIDFACET ) for ( j = 0 ; j < SDIM ; j++ ) oldx[i][j] = newx[i][j]; } } MFOR_ALL_EDGES(e_id) { v = get_edge_vertices(e_id); for ( i = 0 ; i <= web.lagrange_order ; i++ ) oldx[i] = get_coord(v[i]); mat_mult(bezier1revert[web.lagrange_order],oldx,newx, web.lagrange_order+1,web.lagrange_order+1,SDIM); for ( i = 1 ; i < web.lagrange_order ; i++ ) /* no changing ends */ for ( j = 0 ; j < SDIM ; j++ ) oldx[i][j] = newx[i][j]; } free_matrix(newx); } /************************************************************************ * * Function: simplex_to_fe() * * Purpose: Convert from simplex model to facetedge model. * Only does 1D and 2D surfaces. */ struct vpair { vertex_id v[2]; edge_id e; } *vpair_list; int vpair_count; int vpaircomp(a,b) struct vpair *a,*b; { if ( a->v[0] < b->v[0] ) return -1; if ( a->v[0] > b->v[0] ) return 1; if ( a->v[1] < b->v[1] ) return -1; if ( a->v[1] > b->v[1] ) return 1; return 0; } void add_to_vpair_list(v1,v2) vertex_id v1,v2; { if ( v1 < v2 ) { vpair_list[vpair_count].v[0] = v1; vpair_list[vpair_count].v[1] = v2; } else { vpair_list[vpair_count].v[0] = v2; vpair_list[vpair_count].v[1] = v1; } vpair_count++; } facetedge_id vpair_fe(v1,v2,f_id) vertex_id v1,v2; facet_id f_id; { struct vpair key, *spot; facetedge_id fe,oldfe; if ( v1 < v2 ) { key.v[0] = v1; key.v[1] = v2; } else { key.v[0] = v2; key.v[1] = v1; } spot = bsearch(&key,vpair_list,vpair_count,sizeof(struct vpair), FCAST vpaircomp); if ( spot == NULL ) { kb_error(6432,"simplex_to_fe: could not find edge in vpair_list.\n", RECOVERABLE); } fe = new_facetedge((v1 < v2) ? f_id : inverse_id(f_id),spot->e); oldfe = get_edge_fe(spot->e); if ( valid_id(oldfe) ) { facetedge_id tempfe = get_next_facet(oldfe); set_next_facet(oldfe,fe); set_prev_facet(fe,oldfe); set_next_facet(fe,tempfe); set_prev_facet(tempfe,fe); } else { set_next_facet(fe,fe); set_prev_facet(fe,fe); set_edge_fe(spot->e,fe); } return (v1 < v2) ? fe : inverse_id(fe); } void simplex_to_fe() { int i,j; facet_id f_id; edge_id e_id; /* Check surface properties */ if ( web.representation != SIMPLEX ) { outstring("Surface is not in simplex representation.\n"); return; } if ( web.dimension > 2 ) { sprintf(errmsg, "simplex_to_fe: Surface is %d dimensional; must be 1 or 2.\n", web.dimension); kb_error(5366,errmsg,RECOVERABLE); } /* Create new edges that may not be represented */ /* First, make list of all vertex pairs needed */ vpair_list = (struct vpair *)mycalloc(3*web.skel[FACET].count, sizeof(struct vpair)); vpair_count = 0; FOR_ALL_FACETS(f_id) { vertex_id *v = get_facet_vertices(f_id); add_to_vpair_list(v[0],v[1]); add_to_vpair_list(v[1],v[2]); add_to_vpair_list(v[2],v[0]); } /* Sort list and get unique edges */ qsort(vpair_list,vpair_count,sizeof(struct vpair),FCAST vpaircomp); for ( i = 1, j = 0 ; i < vpair_count ; i++ ) if ( vpaircomp(vpair_list+j,vpair_list+i) < 0 ) vpair_list[++j] = vpair_list[i]; vpair_count = j+1; /* Record existing edges */ FOR_ALL_EDGES(e_id) { vertex_id headv = get_edge_headv(e_id); vertex_id tailv = get_edge_tailv(e_id); struct vpair keypair, *spot; if ( headv < tailv ) { keypair.v[0] = headv; keypair.v[1] = tailv; } else { keypair.v[0] = tailv; keypair.v[1] = headv; } spot = bsearch(&keypair,vpair_list,vpair_count,sizeof(struct vpair), FCAST vpaircomp ); if ( spot ) spot->e = (headv < tailv) ? inverse_id(e_id) : e_id; } /* Create needed new edges */ for ( i = 0 ; i < vpair_count ; i++ ) if ( !valid_id(vpair_list[i].e) ) vpair_list[i].e = new_edge(vpair_list[i].v[0],vpair_list[i].v[1],NULLID); /* Create facet-edges */ FOR_ALL_FACETS(f_id) { vertex_id *v = get_facet_vertices(f_id); facetedge_id fe1 = vpair_fe(v[0],v[1],f_id); facetedge_id fe2 = vpair_fe(v[1],v[2],f_id); facetedge_id fe3 = vpair_fe(v[2],v[0],f_id); set_next_edge(fe1,fe2); set_next_edge(fe2,fe3); set_next_edge(fe3,fe1); set_prev_edge(fe1,fe3); set_prev_edge(fe2,fe1); set_prev_edge(fe3,fe2); set_facet_fe(f_id,fe1); } /* flip representation */ web.representation = (web.dimension==1) ? STRING : SOAPFILM; /* set links around edges */ if ( web.representation == SOAPFILM ) FOR_ALL_EDGES(e_id) fe_reorder(e_id); } /* end simplex_to_fe */ evolver-2.30c.dfsg/src/simequi2.c0000644000175300017530000003127411410765113017126 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************* * * file: simequi2.c * * Contents: Equiangulation for simplex model. * Local triangulation flipping. */ #include "include.h" /********************************************************************* * * function: simplex_equiangulate() * * purpose: equiangulation for simplex representation. * * return: number of equiangulations done. */ struct fstruct { vertex_id v[MAXCOORD]; /* face verts */ vertex_id opp[2]; /* opposite verts */ facet_id f_id[2]; /* facets involved */ } *facelist; int void_test2 ARGS((struct fstruct *)); int scomp ARGS((vertex_id *,vertex_id *)); int scomp(a,b) vertex_id *a,*b; { int i; for ( i = 0 ; i < web.dimension ; i++,a++,b++ ) if ( *a < *b ) return -1; else if ( *a > *b ) return 1; return 0; /* equal */ } int simplex_equiangulate() { int i,j,k; facet_id f_id,ff_id; struct fstruct *fl,*f1; /* pointer into face list */ struct fstruct tetra_tmp; int fcount; int same; int flipcount = 0; vertex_id *v; /* create face list with pairs of opposite vertices */ fcount = web.skel[FACET].count*(web.dimension+1); facelist = (struct fstruct *)temp_calloc(fcount,sizeof(struct fstruct)); fl = facelist; FOR_ALL_FACETS(f_id) { int opp; /* which vertex opposite */ vertex_id *fv = get_facet_vertices(f_id); unset_attr(f_id,NEWFACET); /* clear marks */ for ( opp = 0 ; opp <= web.dimension ; opp++ ) { int flips = opp; /* track orientation */ /* copy face in sorted order vertices */ for ( i = 0,j = 0 ; i <= web.dimension ; i++ ) { if ( i == opp ) continue; k = j; while ( (k > 0) && (fv[i] < fl->v[k-1]) ) { fl->v[k] = fl->v[k-1]; k--; flips++; } fl->v[k] = fv[i]; j++; } /* add opposite vertex in appropriate slot */ fl->opp[flips & 1] = fv[opp]; fl->f_id[flips & 1] = f_id; fl++; } } /* sort face list */ qsort((char*)facelist,fcount,sizeof(struct fstruct),FCAST scomp); /* join pairs of faces */ /* saving those faces with exactly two neighbors */ same = 1; /* number of facets face j has so far */ for ( i = 1, j = 0 ; i < fcount ; i++ ) { if ( scomp((vertex_id *)(facelist+j),(vertex_id *)(facelist+i)) == 0 ) { /* same face */ for ( k = 0 ; k < 2 ; k++ ) if ( valid_id(facelist[i].opp[k]) ) { facelist[j].opp[k] = facelist[i].opp[k]; facelist[j].f_id[k] = facelist[i].f_id[k]; } same++; } else /* new face */ { if ( same==2 ) j++; facelist[j] = facelist[i]; same = 1; } } fcount = j; /* number of unique faces */ /* look for all void-violating opposite vertices */ for ( fl = facelist, i = 0 ; i < fcount ; i++,fl++ ) { int retval; facet_id newf[MAXCOORD]; /* see if facets involved are legit */ if ( !valid_element(fl->f_id[0]) ) continue; if ( !valid_element(fl->f_id[1]) ) continue; if ( get_fattr(fl->f_id[0]) & (NEWFACET|FIXED) ) continue; if ( get_fattr(fl->f_id[1]) & (NEWFACET|FIXED) ) continue; if ( !valid_id(fl->f_id[0]) || !valid_id(fl->f_id[1]) ) { sprintf(msg,"Face doesn't have valid opposite facets.\n"); outstring(msg); continue; } retval = void_test2(fl); switch ( retval ) { case 0: break; case 1: /* inside, want split along central axis */ /* check equality of facet constraints and stuff */ /* test for equal density */ if ( (get_fattr(fl->f_id[0])&DENSITY) || (get_fattr(fl->f_id[1])&DENSITY) ) if ( fabs(get_facet_density(fl->f_id[0]) - get_facet_density(fl->f_id[1])) > 1e-10 ) break; /* test for equal constraints */ if ( !equal_constr(fl->f_id[0],fl->f_id[1]) ) break; /* test for equal boundary */ if ( get_facet_boundary(fl->f_id[0]) != get_facet_boundary(fl->f_id[1]) ) break; newf[0] = fl->f_id[0]; newf[1] = fl->f_id[1]; for ( j = 2; j < web.dimension ; j++ ) newf[j] = dup_facet(newf[0]); for ( j = 0 ; j < web.dimension ; j++ ) { v = get_facet_vertices(newf[j]); for ( k = 0 ; k < web.dimension-1 ; k++ ) v[k] = fl->v[(k+j) % web.dimension]; v[k++] = fl->opp[0]; v[k] = fl->opp[1]; set_attr(newf[j],NEWFACET); /* so others won't try to flip */ } flipcount++; break; default: /* outside side j, retval = 2 + j */ if ( web.dimension != 3 ) { kb_error(1875,"Simplex consolidation equiangulation only for surface dimension 3.\n", WARNING); break; } j = retval-2; /* look for third tetra on edge */ for ( i = k = 0 ; i < web.dimension ; i++ ) if ( i != j ) tetra_tmp.v[k++] = fl->v[i]; for ( i = web.dimension-1 ; i > 0 ; i-- ) if ( fl->opp[0] > tetra_tmp.v[i-1] ) break; else tetra_tmp.v[i] = tetra_tmp.v[i-1]; tetra_tmp.v[i] = fl->opp[0]; f1 = (struct fstruct *)bsearch((char*)&tetra_tmp,(char*)facelist,fcount, sizeof(struct fstruct), FCAST scomp); if ( f1 == NULL ) break; ff_id = f1->f_id[0]; if ( equal_id(ff_id,fl->f_id[0]) ) ff_id = f1->f_id[1]; if ( equal_id(ff_id,fl->f_id[1]) ) ff_id = f1->f_id[0]; v = get_facet_vertices(ff_id); for ( i = 0 ; i <= web.dimension ; i++ ) if ( equal_id(v[i],fl->opp[1]) ) break; if ( i > web.dimension ) break; /* third tetra doesn't fill wedge */ /* now have third tetra */ if ( !valid_element(ff_id) ) continue; /* test for contamination */ if ( get_fattr(ff_id) & (NEWFACET|FIXED) ) continue; /* check equality of facet constraints and stuff */ /* test for equal density */ if ( (get_fattr(fl->f_id[0])&DENSITY) || (get_fattr(fl->f_id[1])&DENSITY) || (get_fattr(ff_id)&DENSITY) ) if ( (fabs(get_facet_density(fl->f_id[0]) - get_facet_density(fl->f_id[1])) > 1e-10) || (fabs(get_facet_density(fl->f_id[0]) - get_facet_density(ff_id)) > 1e-10) ) break; /* test for equal constraints */ if ( !equal_constr(fl->f_id[0],fl->f_id[1]) ) break; if ( !equal_constr(fl->f_id[0],ff_id) ) break; /* test for equal boundary */ if ( get_facet_boundary(fl->f_id[0]) != get_facet_boundary(fl->f_id[1]) ) break; if ( get_facet_boundary(fl->f_id[0]) != get_facet_boundary(ff_id) ) break; free_element(ff_id); v = get_facet_vertices(fl->f_id[0]); v[0] = fl->v[(j+web.dimension-1) % web.dimension]; v[1] = fl->v[j]; v[2] = fl->opp[0]; v[3] = fl->opp[1]; v = get_facet_vertices(fl->f_id[1]); v[0] = fl->v[(j+1) % web.dimension]; v[1] = fl->v[j]; v[2] = fl->opp[1]; v[3] = fl->opp[0]; set_attr(fl->f_id[0],NEWFACET); /* mark as contaminated */ set_attr(fl->f_id[1],NEWFACET); flipcount++; break; } } if ( flipcount > 0 ) top_timestamp = ++global_timestamp; return flipcount; } /******************************************************************* * * function: void_test2() * * purpose: see whether opposite vertices violate void * * return: 0 if not, * 1 if yes and connector inside common facet, * 2+j if yes and connector outside on side j */ int void_test2(fp) struct fstruct *fp; { REAL *x[MAXCOORD+1]; REAL ss[MAXCOORD]; /* squares of sides */ int k,j; REAL rr; /* square radius of void */ REAL center[MAXCOORD]; REAL lam[MAXCOORD]; MAT2D(mat,MAXCOORD,MAXCOORD); MAT2D(side,MAXCOORD,MAXCOORD); int retval; /* result to return */ REAL *y; REAL z[MAXCOORD]; /* first, calculate center of void */ x[0] = get_coord(fp->opp[0]); for ( k = 0 ; k < web.dimension ; k++ ) { x[k+1] = get_coord(fp->v[k]); for ( j = 0 ; j < SDIM ; j++ ) side[k][j] = x[k+1][j] - x[0][j]; } for ( k = 0 ; k < web.dimension ; k++ ) { for ( j = 0 ; j <= k ; j++ ) mat[j][k] = mat[k][j] = SDIM_dot(side[j],side[k]); ss[k] = mat[k][k]/2; } mat_inv(mat,web.dimension); matvec_mul(mat,ss,lam,web.dimension,web.dimension); vec_mat_mul(lam,side,center,web.dimension,SDIM); rr = dot(center,center,web.dimension); for ( k = 0 ; k < SDIM ; k++ ) center[k] += x[0][k]; /* now see if other vertex is in the void */ y = get_coord(fp->opp[1]); for ( j = 0 ; j < SDIM ; j++ ) z[j] = y[j] - center[j]; if ( SDIM_dot(z,z) < rr - 1e-10 ) retval = 1; else retval = 0; if ( retval ) { /* see if connecting segment passes through facet */ REAL q[MAXCOORD]; /* relative coord of opposite point */ REAL s[MAXCOORD]; for ( j = 0 ; j < SDIM ; j++ ) z[j] = y[j] - x[0][j]; matvec_mul(side,z,s,web.dimension,SDIM); matvec_mul(mat,s,q,web.dimension,web.dimension); for ( j = 0 ; j < web.dimension ; j++ ) if ( q[j] < 0.0 ) retval = 2+j; } return retval; } /***************************************************************************** * * function: simplex_delaunay_test() */ void simplex_delaunay_test() { facet_id f_id; FOR_ALL_FACETS(f_id) { vertex_id v_id, *v = get_facet_vertices(f_id); v_id = void_test(v,web.dimension); if ( valid_id(v_id) ) { sprintf(msg,"Vertex %s inside void of facet %s\n",ELNAME(v_id), ELNAME2(f_id)); erroutstring(msg); } } } vertex_id void_test(v,dim) vertex_id *v; int dim; { REAL *x[MAXCOORD+1]; REAL ss[MAXCOORD]; /* squares of sides */ int k,j; REAL rr; /* square radius of void */ REAL center[MAXCOORD]; REAL lam[MAXCOORD]; vertex_id v_id,bad_v = NULLID; REAL **mat = dmatrix(0,web.dimension-1,0,web.dimension-1); REAL **side = dmatrix(0,SDIM-1,0,SDIM-1); /* first, calculate center of void */ x[0] = get_coord(v[0]); for ( k = 1 ; k <= dim ; k++ ) { x[k] = get_coord(v[k]); for ( j = 0 ; j < SDIM ; j++ ) side[k-1][j] = x[k][j] - x[0][j]; } for ( k = 0 ; k < dim ; k++ ) { for ( j = 0 ; j <= k ; j++ ) mat[j][k] = mat[k][j] = SDIM_dot(side[j],side[k]); ss[k] = mat[k][k]; } mat_inv(mat,dim); matvec_mul(mat,ss,lam,dim,dim); rr = dot(lam,ss,dim)/4; vec_mat_mul(lam,side,center,dim,SDIM); for ( k = 0 ; k < SDIM ; k++ ) center[k] = x[0][k] + center[k]/2; /* now see if any other vertices are in the void */ FOR_ALL_VERTICES(v_id) { REAL *y; REAL z[MAXCOORD]; for ( k = 0 ; k <= dim ; k++ ) if ( equal_id(v_id,v[k]) ) break; if ( k <= dim ) continue; /* skip vertices in facet */ y = get_coord(v_id); for ( j = 0 ; j < SDIM ; j++ ) z[j] = y[j] - center[j]; if ( SDIM_dot(z,z) >= rr - 1e-10 ) continue; #if 1 sprintf(msg,"Void violation by %g\n",(DOUBLE)(-SDIM_dot(z,z) + rr)); erroutstring(msg); #endif bad_v = v_id; break; } free_matrix(side); free_matrix(mat); return bad_v; } evolver-2.30c.dfsg/src/ytab.c0000644000175300017530000311727711410765113016342 0ustar hazelscthazelsct/* A Bison parser, made by GNU Bison 2.1. */ /* Skeleton parser for Yacc-like parsing with Bison, Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* As a special exception, when this file is copied by Bison into a Bison output file, you may use that output file without restriction. This special exception was added by the Free Software Foundation in version 1.24 of Bison. */ /* Written by Richard Stallman by simplifying the original so called ``semantic'' parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "2.1" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 1 /* Using locations. */ #define YYLSP_NEEDED 0 /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE /* Put the tokens into the symbol table, so that GDB and other debuggers know about them. */ enum yytokentype { EXPRESSION_START_ = 258, COMMAND_START_ = 259, HISTORY_ = 260, GEOMVIEW_ = 261, VIEW_MATRIX_ = 262, LEAD_INTEGER_ = 263, INTEGER_ = 264, REAL_ = 265, SIGNED_NUMBER_ = 266, NEWIDENT_ = 267, REDEFINE_ = 268, MATHFUNC_ = 269, MATHFUNC2_ = 270, POW_ = 271, USERFUNC_ = 272, MIDV_ = 273, DATAFILENAME_ = 274, LOGFILE_ = 275, PI_ = 276, E_ = 277, G_ = 278, PARAM_ = 279, SYMBOL_ = 280, TOTAL_ = 281, EXTRA_ATTRIBUTE_ = 282, FIXEDVOL_ = 283, IDENT_ = 284, UMINUS_ = 285, SHELL_ = 286, COLOR_ = 287, HESSIAN_ = 288, VOLCONST_ = 289, TORUS_PERIODS_ = 290, VERTICES_ = 291, EDGES_ = 292, FACETS_ = 293, BODIES_ = 294, HESSIAN_MENU_ = 295, POSTSCRIPT_ = 296, LENGTH_ = 297, AREA_ = 298, VOLUME_ = 299, ID_ = 300, OID_ = 301, TAG_ = 302, ORIGINAL_ = 303, FACETEDGES_ = 304, WRAP_ = 305, QUOTATION_ = 306, UNSET_ = 307, TOPINFO_ = 308, OPACITY_ = 309, VALENCE_ = 310, HESSIAN_SADDLE_ = 311, SET_ = 312, FIXED_ = 313, DENSITY_ = 314, PRESSURE_ = 315, CONSTRAINT_ = 316, COORD_ = 317, DISSOLVE_ = 318, WHERE_ = 319, LIST_ = 320, SHOW_ = 321, DELETE_ = 322, REFINE_ = 323, RECALC_ = 324, SHOWQ_ = 325, EDGESWAP_ = 326, FIX_ = 327, UNFIX_ = 328, TOGGLENAME_ = 329, TOGGLEVALUE_ = 330, STAR_ = 331, QUANTITY_NAME_ = 332, PAUSE_ = 333, GO_ = 334, SHOW_VOL_ = 335, CHECK_ = 336, READ_ = 337, ZOOM_ = 338, ON_ = 339, OFF_ = 340, GEOMPIPE_ = 341, SELF_ = 342, SINGLE_LETTER_ = 343, LONG_JIGGLE_ = 344, RAW_VERAVG_ = 345, COUNTS_ = 346, CHDIR_ = 347, ALICE_ = 348, STABILITY_TEST_ = 349, DEFINE_ = 350, UPLUS_ = 351, DATATYPE_ = 352, FLUSH_COUNTS_ = 353, AUTOCHOP_ = 354, UTEST_ = 355, ATTRIBUTE_ = 356, RITZ_ = 357, MOVE_ = 358, VERTEXNORMAL_ = 359, POP_ = 360, SYSTEM_ = 361, TETRA_POINT_ = 362, TRIPLE_POINT_ = 363, LANCZOS_ = 364, EIGENPROBE_ = 365, EXEC_ = 366, AREAWEED_ = 367, EDGEWEED_ = 368, GRAVITY_ = 369, EDGEDIVIDE_ = 370, LINEAR_ = 371, QUADRATIC_ = 372, DIFFUSION_ = 373, EXTRAPOLATE_ = 374, TRANSFORM_DEPTH_ = 375, PRINTF_ = 376, ERRPRINTF_ = 377, PRINT_ = 378, MAX_ = 379, MIN_ = 380, COUNT_ = 381, SUM_ = 382, AVG_ = 383, BREAK_ = 384, CONTINUE_ = 385, SIZEOF_ = 386, TRANSFORM_EXPR_ = 387, BARE_ = 388, BOTTOMINFO_ = 389, METIS_ = 390, KMETIS_ = 391, KEYLOGFILE_ = 392, SCALE_ = 393, BURCHARD_ = 394, REBODY_ = 395, BOUNDARY_ = 396, ORIENTATION_ = 397, OMETIS_ = 398, SQ_MEAN_CURV_ = 399, FRONTCOLOR_ = 400, SINGLE_REDEFD_ = 401, METHOD_NAME_ = 402, TASK_EXEC_ = 403, RAWEST_VERAVG_ = 404, SINGLE_LETTER_ARG_ = 405, BACKCOLOR_ = 406, LAGRANGE_ = 407, RETURN_ = 408, TRANSFORM_EXPR_VERB_ = 409, OOGLFILE_ = 410, PARALLEL_EXEC_ = 411, BINARY_OFF_FILE_ = 412, SPRINTF_ = 413, CONVERT_TO_QUANTS_ = 414, METIS_FACTOR_ = 415, FUNCTION_ = 416, EXPRINT_ = 417, DIHEDRAL_ = 418, WRAP_VERTEX_ = 419, ARRAYIDENT_ = 420, DATE_AND_TIME_ = 421, LOCAL_ = 422, SHOW_EXPR_ = 423, SHOW_TRANS_ = 424, AXIAL_POINT_ = 425, ENERGY_ = 426, CONSERVED_ = 427, INFO_ONLY_ = 428, ASSIGN_ = 429, PROCEDURE_ = 430, FOREACH_ = 431, STRINGGLOBAL_ = 432, EQUIANGULATE_ = 433, HISTOGRAM_ = 434, LOGHISTOGRAM_ = 435, AREA_FIXED_ = 436, QUIT_ = 437, WARNING_MESSAGES_ = 438, IF_ = 439, WHILE_ = 440, DO_ = 441, NO_REFINE_ = 442, STRING_ = 443, NONCONTENT_ = 444, FOR_ = 445, HIT_PARTNER_ = 446, FRONTBODY_ = 447, BACKBODY_ = 448, COLORFILE_ = 449, PERM_STRINGGLOBAL_ = 450, FUNCTION_IDENT_ = 451, THICKEN_ = 452, COLORMAP_ = 453, REDIRECT_ = 454, NEWVERTEX_ = 455, NEWEDGE_ = 456, NEWFACET_ = 457, MODULUS_ = 458, TARGET_ = 459, VALUE_ = 460, INVERSE_PERIODS_ = 461, NEWBODY_ = 462, DELTA_ = 463, GAP_CONSTANT_ = 464, DUMP_ = 465, NOTCH_ = 466, QUANTITY_ = 467, LOAD_ = 468, PERM_PROCEDURE_ = 469, PROCEDURE_WORD_ = 470, DYNAMIC_LOAD_FUNC_ = 471, PERM_IDENT_ = 472, PERMLOAD_ = 473, HELP_ = 474, VERTEX_AVERAGE_ = 475, METHOD_INSTANCE_ = 476, RAW_VERTEX_AVERAGE_ = 477, OPTIMIZE_ = 478, REDIRECTOVER_ = 479, TOLERANCE_ = 480, RAWEST_VERTEX_AVERAGE_ = 481, JIGGLE_ = 482, VIEW_TRANSFORMS_ = 483, CLOSE_SHOW_ = 484, IS_DEFINED_ = 485, NODISPLAY_ = 486, PERM_ASSIGN_ = 487, PHASE_ = 488, VIEW_TRANSFORM_SWAP_COLORS_ = 489, BACKQUOTE_COMMA_ = 490, INTERNAL_VARIABLE_ = 491, DIRICHLET_ = 492, SOBOLEV_ = 493, VIEW_TRANSFORM_PARITY_ = 494, SOBOLEV_SEEK_ = 495, DIRICHLET_SEEK_ = 496, HESSIAN_SEEK_ = 497, REORDER_STORAGE_ = 498, RENUMBER_ALL_ = 499, CONSTRAINT_NAME_ = 500, BOUNDARY_NAME_ = 501, PROCEDURE_IDENT_ = 502, POP_TRI_TO_EDGE_ = 503, POP_EDGE_TO_TRI_ = 504, POP_QUAD_TO_QUAD_ = 505, SHOWVERB_ = 506, PROCEDURES_ = 507, MPI_TASK_ATTR_ = 508, T1_EDGESWAP_ = 509, MERGE_EDGE_ = 510, MERGE_FACET_ = 511, MERGE_VERTEX_ = 512, RESET_COUNTS_ = 513, VALID_ELEMENT_ = 514, MID_EDGE_ = 515, MID_FACET_ = 516, GO_COUNT_ = 517, ELEMENT_IDENT_ = 518, BODY_METIS_ = 519, REVERSE_ORIENTATION_ = 520, MATRIX_MULTIPLY_ = 521, MATRIX_INVERSE_ = 522, BINARY_PRINTF_ = 523, DUMP_MEMLIST_ = 524, FREE_DISCARDS_ = 525, REPARTITION_ = 526, METIS_READJUST_ = 527, MEAN_CURVATURE_ = 528, GLOBAL_ = 529, LEAD_INTEGER_AT_ = 530, INTEGER_AT_ = 531, MATRIX_DETERMINANT_ = 532, SUBCOMMAND_ = 533, ABORT_ = 534, BREAKPOINT_ = 535, WHEREAMI_ = 536, ADDLOAD_ = 537, SIMPLEX_TO_FE_ = 538, DISPLAY_TEXT_ = 539, DELETE_TEXT_ = 540, SUPPRESS_WARNING_ = 541, UNSUPPRESS_WARNING_ = 542, RESET_PROFILING_ = 543, VALID_CONSTRAINT_ = 544, VALID_BOUNDARY_ = 545, ARRAY_ATTRIBUTE_ = 546, PROFILING_ = 547, ASSIGNOP_ = 548, PIPE_ = 549, THEN_ = 550, ELSE_ = 551, OR_ = 552, AND_ = 553, NOT_ = 554, NE_ = 555, GE_ = 556, LE_ = 557, EQ_ = 558, ON_CONSTRAINT_ = 559, HIT_CONSTRAINT_ = 560, ON_BOUNDARY_ = 561, ON_QUANTITY_ = 562, ON_METHOD_INSTANCE_ = 563, DOT_ = 564, IDIV_ = 565, IMOD_ = 566, EPRINT_ = 567 }; #endif /* Tokens. */ #define EXPRESSION_START_ 258 #define COMMAND_START_ 259 #define HISTORY_ 260 #define GEOMVIEW_ 261 #define VIEW_MATRIX_ 262 #define LEAD_INTEGER_ 263 #define INTEGER_ 264 #define REAL_ 265 #define SIGNED_NUMBER_ 266 #define NEWIDENT_ 267 #define REDEFINE_ 268 #define MATHFUNC_ 269 #define MATHFUNC2_ 270 #define POW_ 271 #define USERFUNC_ 272 #define MIDV_ 273 #define DATAFILENAME_ 274 #define LOGFILE_ 275 #define PI_ 276 #define E_ 277 #define G_ 278 #define PARAM_ 279 #define SYMBOL_ 280 #define TOTAL_ 281 #define EXTRA_ATTRIBUTE_ 282 #define FIXEDVOL_ 283 #define IDENT_ 284 #define UMINUS_ 285 #define SHELL_ 286 #define COLOR_ 287 #define HESSIAN_ 288 #define VOLCONST_ 289 #define TORUS_PERIODS_ 290 #define VERTICES_ 291 #define EDGES_ 292 #define FACETS_ 293 #define BODIES_ 294 #define HESSIAN_MENU_ 295 #define POSTSCRIPT_ 296 #define LENGTH_ 297 #define AREA_ 298 #define VOLUME_ 299 #define ID_ 300 #define OID_ 301 #define TAG_ 302 #define ORIGINAL_ 303 #define FACETEDGES_ 304 #define WRAP_ 305 #define QUOTATION_ 306 #define UNSET_ 307 #define TOPINFO_ 308 #define OPACITY_ 309 #define VALENCE_ 310 #define HESSIAN_SADDLE_ 311 #define SET_ 312 #define FIXED_ 313 #define DENSITY_ 314 #define PRESSURE_ 315 #define CONSTRAINT_ 316 #define COORD_ 317 #define DISSOLVE_ 318 #define WHERE_ 319 #define LIST_ 320 #define SHOW_ 321 #define DELETE_ 322 #define REFINE_ 323 #define RECALC_ 324 #define SHOWQ_ 325 #define EDGESWAP_ 326 #define FIX_ 327 #define UNFIX_ 328 #define TOGGLENAME_ 329 #define TOGGLEVALUE_ 330 #define STAR_ 331 #define QUANTITY_NAME_ 332 #define PAUSE_ 333 #define GO_ 334 #define SHOW_VOL_ 335 #define CHECK_ 336 #define READ_ 337 #define ZOOM_ 338 #define ON_ 339 #define OFF_ 340 #define GEOMPIPE_ 341 #define SELF_ 342 #define SINGLE_LETTER_ 343 #define LONG_JIGGLE_ 344 #define RAW_VERAVG_ 345 #define COUNTS_ 346 #define CHDIR_ 347 #define ALICE_ 348 #define STABILITY_TEST_ 349 #define DEFINE_ 350 #define UPLUS_ 351 #define DATATYPE_ 352 #define FLUSH_COUNTS_ 353 #define AUTOCHOP_ 354 #define UTEST_ 355 #define ATTRIBUTE_ 356 #define RITZ_ 357 #define MOVE_ 358 #define VERTEXNORMAL_ 359 #define POP_ 360 #define SYSTEM_ 361 #define TETRA_POINT_ 362 #define TRIPLE_POINT_ 363 #define LANCZOS_ 364 #define EIGENPROBE_ 365 #define EXEC_ 366 #define AREAWEED_ 367 #define EDGEWEED_ 368 #define GRAVITY_ 369 #define EDGEDIVIDE_ 370 #define LINEAR_ 371 #define QUADRATIC_ 372 #define DIFFUSION_ 373 #define EXTRAPOLATE_ 374 #define TRANSFORM_DEPTH_ 375 #define PRINTF_ 376 #define ERRPRINTF_ 377 #define PRINT_ 378 #define MAX_ 379 #define MIN_ 380 #define COUNT_ 381 #define SUM_ 382 #define AVG_ 383 #define BREAK_ 384 #define CONTINUE_ 385 #define SIZEOF_ 386 #define TRANSFORM_EXPR_ 387 #define BARE_ 388 #define BOTTOMINFO_ 389 #define METIS_ 390 #define KMETIS_ 391 #define KEYLOGFILE_ 392 #define SCALE_ 393 #define BURCHARD_ 394 #define REBODY_ 395 #define BOUNDARY_ 396 #define ORIENTATION_ 397 #define OMETIS_ 398 #define SQ_MEAN_CURV_ 399 #define FRONTCOLOR_ 400 #define SINGLE_REDEFD_ 401 #define METHOD_NAME_ 402 #define TASK_EXEC_ 403 #define RAWEST_VERAVG_ 404 #define SINGLE_LETTER_ARG_ 405 #define BACKCOLOR_ 406 #define LAGRANGE_ 407 #define RETURN_ 408 #define TRANSFORM_EXPR_VERB_ 409 #define OOGLFILE_ 410 #define PARALLEL_EXEC_ 411 #define BINARY_OFF_FILE_ 412 #define SPRINTF_ 413 #define CONVERT_TO_QUANTS_ 414 #define METIS_FACTOR_ 415 #define FUNCTION_ 416 #define EXPRINT_ 417 #define DIHEDRAL_ 418 #define WRAP_VERTEX_ 419 #define ARRAYIDENT_ 420 #define DATE_AND_TIME_ 421 #define LOCAL_ 422 #define SHOW_EXPR_ 423 #define SHOW_TRANS_ 424 #define AXIAL_POINT_ 425 #define ENERGY_ 426 #define CONSERVED_ 427 #define INFO_ONLY_ 428 #define ASSIGN_ 429 #define PROCEDURE_ 430 #define FOREACH_ 431 #define STRINGGLOBAL_ 432 #define EQUIANGULATE_ 433 #define HISTOGRAM_ 434 #define LOGHISTOGRAM_ 435 #define AREA_FIXED_ 436 #define QUIT_ 437 #define WARNING_MESSAGES_ 438 #define IF_ 439 #define WHILE_ 440 #define DO_ 441 #define NO_REFINE_ 442 #define STRING_ 443 #define NONCONTENT_ 444 #define FOR_ 445 #define HIT_PARTNER_ 446 #define FRONTBODY_ 447 #define BACKBODY_ 448 #define COLORFILE_ 449 #define PERM_STRINGGLOBAL_ 450 #define FUNCTION_IDENT_ 451 #define THICKEN_ 452 #define COLORMAP_ 453 #define REDIRECT_ 454 #define NEWVERTEX_ 455 #define NEWEDGE_ 456 #define NEWFACET_ 457 #define MODULUS_ 458 #define TARGET_ 459 #define VALUE_ 460 #define INVERSE_PERIODS_ 461 #define NEWBODY_ 462 #define DELTA_ 463 #define GAP_CONSTANT_ 464 #define DUMP_ 465 #define NOTCH_ 466 #define QUANTITY_ 467 #define LOAD_ 468 #define PERM_PROCEDURE_ 469 #define PROCEDURE_WORD_ 470 #define DYNAMIC_LOAD_FUNC_ 471 #define PERM_IDENT_ 472 #define PERMLOAD_ 473 #define HELP_ 474 #define VERTEX_AVERAGE_ 475 #define METHOD_INSTANCE_ 476 #define RAW_VERTEX_AVERAGE_ 477 #define OPTIMIZE_ 478 #define REDIRECTOVER_ 479 #define TOLERANCE_ 480 #define RAWEST_VERTEX_AVERAGE_ 481 #define JIGGLE_ 482 #define VIEW_TRANSFORMS_ 483 #define CLOSE_SHOW_ 484 #define IS_DEFINED_ 485 #define NODISPLAY_ 486 #define PERM_ASSIGN_ 487 #define PHASE_ 488 #define VIEW_TRANSFORM_SWAP_COLORS_ 489 #define BACKQUOTE_COMMA_ 490 #define INTERNAL_VARIABLE_ 491 #define DIRICHLET_ 492 #define SOBOLEV_ 493 #define VIEW_TRANSFORM_PARITY_ 494 #define SOBOLEV_SEEK_ 495 #define DIRICHLET_SEEK_ 496 #define HESSIAN_SEEK_ 497 #define REORDER_STORAGE_ 498 #define RENUMBER_ALL_ 499 #define CONSTRAINT_NAME_ 500 #define BOUNDARY_NAME_ 501 #define PROCEDURE_IDENT_ 502 #define POP_TRI_TO_EDGE_ 503 #define POP_EDGE_TO_TRI_ 504 #define POP_QUAD_TO_QUAD_ 505 #define SHOWVERB_ 506 #define PROCEDURES_ 507 #define MPI_TASK_ATTR_ 508 #define T1_EDGESWAP_ 509 #define MERGE_EDGE_ 510 #define MERGE_FACET_ 511 #define MERGE_VERTEX_ 512 #define RESET_COUNTS_ 513 #define VALID_ELEMENT_ 514 #define MID_EDGE_ 515 #define MID_FACET_ 516 #define GO_COUNT_ 517 #define ELEMENT_IDENT_ 518 #define BODY_METIS_ 519 #define REVERSE_ORIENTATION_ 520 #define MATRIX_MULTIPLY_ 521 #define MATRIX_INVERSE_ 522 #define BINARY_PRINTF_ 523 #define DUMP_MEMLIST_ 524 #define FREE_DISCARDS_ 525 #define REPARTITION_ 526 #define METIS_READJUST_ 527 #define MEAN_CURVATURE_ 528 #define GLOBAL_ 529 #define LEAD_INTEGER_AT_ 530 #define INTEGER_AT_ 531 #define MATRIX_DETERMINANT_ 532 #define SUBCOMMAND_ 533 #define ABORT_ 534 #define BREAKPOINT_ 535 #define WHEREAMI_ 536 #define ADDLOAD_ 537 #define SIMPLEX_TO_FE_ 538 #define DISPLAY_TEXT_ 539 #define DELETE_TEXT_ 540 #define SUPPRESS_WARNING_ 541 #define UNSUPPRESS_WARNING_ 542 #define RESET_PROFILING_ 543 #define VALID_CONSTRAINT_ 544 #define VALID_BOUNDARY_ 545 #define ARRAY_ATTRIBUTE_ 546 #define PROFILING_ 547 #define ASSIGNOP_ 548 #define PIPE_ 549 #define THEN_ 550 #define ELSE_ 551 #define OR_ 552 #define AND_ 553 #define NOT_ 554 #define NE_ 555 #define GE_ 556 #define LE_ 557 #define EQ_ 558 #define ON_CONSTRAINT_ 559 #define HIT_CONSTRAINT_ 560 #define ON_BOUNDARY_ 561 #define ON_QUANTITY_ 562 #define ON_METHOD_INSTANCE_ 563 #define DOT_ 564 #define IDIV_ 565 #define IMOD_ 566 #define EPRINT_ 567 /* Copy the first part of user declarations. */ #include "include.h" #include "lex.h" #define YYSTYPE yystype #define gettxt(a,b) (b) #define yylex kb_yylex int assignbacktrack ARGS((void)); #ifndef __GNUC__ #ifdef YYBISON /* for Bison */ #ifndef __yy_memcpy static void __yy_memcpy ARGS((char *, char *, int )); #endif #endif #endif /* for bison version 2.1 output */ #define __STDC__ 1 /* for non-ANSI compilers */ #define const #ifndef NO_YACC_DEBUG #define YYDEBUG 1 #endif int help_flag; /* avoid error message while doing help */ /* Backtrack to previous := in inputbuffer */ int assignbacktrack () { int spot; for ( spot = inputbufferspot - 1; spot > 0 ; spot-- ) if ( inputbuffer[spot-1] == ':' && inputbuffer[spot] == '=' ) return spot+1; return 0; } /* Enabling traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 0 #endif /* Enabling the token table. */ #ifndef YYTOKEN_TABLE # define YYTOKEN_TABLE 0 #endif #if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) typedef int YYSTYPE; # define yystype YYSTYPE /* obsolescent; will be withdrawn */ # define YYSTYPE_IS_DECLARED 1 # define YYSTYPE_IS_TRIVIAL 1 #endif /* Copy the second part of user declarations. */ /* Line 219 of yacc.c. */ #if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) # define YYSIZE_T __SIZE_TYPE__ #endif #if ! defined (YYSIZE_T) && defined (size_t) # define YYSIZE_T size_t #endif #if ! defined (YYSIZE_T) && (defined (__STDC__) || defined (__cplusplus)) # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t #endif #if ! defined (YYSIZE_T) # define YYSIZE_T unsigned int #endif #ifndef YY_ # if YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(msgid) dgettext ("bison-runtime", msgid) # endif # endif # ifndef YY_ # define YY_(msgid) msgid # endif #endif #if ! defined (yyoverflow) || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # else # define YYSTACK_ALLOC alloca # if defined (__STDC__) || defined (__cplusplus) # include /* INFRINGES ON USER NAME SPACE */ # define YYINCLUDED_STDLIB_H # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's `empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2005 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM ((YYSIZE_T) -1) # endif # ifdef __cplusplus extern "C" { # endif # ifndef YYMALLOC # define YYMALLOC malloc # if (! defined (malloc) && ! defined (YYINCLUDED_STDLIB_H) \ && (defined (__STDC__) || defined (__cplusplus))) void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if (! defined (free) && ! defined (YYINCLUDED_STDLIB_H) \ && (defined (__STDC__) || defined (__cplusplus))) void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifdef __cplusplus } # endif # endif #endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */ #if (! defined (yyoverflow) \ && (! defined (__cplusplus) \ || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { short int yyss; YYSTYPE yyvs; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (short int) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY # if defined (__GNUC__) && 1 < __GNUC__ # define YYCOPY(To, From, Count) \ __builtin_memcpy (To, From, (Count) * sizeof (*(From))) # else # define YYCOPY(To, From, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (To)[yyi] = (From)[yyi]; \ } \ while (0) # endif # endif /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack, Stack, yysize); \ Stack = &yyptr->Stack; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (0) #endif #if defined (__STDC__) || defined (__cplusplus) typedef signed char yysigned_char; #else typedef short int yysigned_char; #endif /* YYFINAL -- State number of the termination state. */ #define YYFINAL 6 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 37216 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 335 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 108 /* YYNRULES -- Number of rules. */ #define YYNRULES 917 /* YYNRULES -- Number of states. */ #define YYNSTATES 1329 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 567 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ static const unsigned short int yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 328, 2, 2, 300, 301, 326, 324, 293, 325, 294, 327, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 309, 295, 314, 307, 313, 308, 334, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 296, 2, 297, 332, 2, 302, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 298, 2, 299, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 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, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 303, 304, 305, 306, 310, 311, 312, 315, 316, 317, 318, 319, 320, 321, 322, 323, 329, 330, 331, 333 }; #if YYDEBUG /* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in YYRHS. */ static const unsigned short int yyprhs[] = { 0, 0, 3, 5, 6, 10, 12, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50, 53, 56, 58, 60, 62, 65, 68, 70, 73, 76, 79, 81, 83, 84, 89, 92, 96, 98, 100, 102, 105, 108, 110, 113, 115, 117, 119, 122, 125, 126, 132, 135, 137, 141, 144, 146, 149, 152, 154, 157, 160, 163, 165, 168, 171, 174, 176, 179, 182, 185, 187, 190, 193, 196, 199, 202, 205, 208, 210, 213, 215, 218, 221, 223, 226, 229, 231, 234, 237, 239, 241, 244, 247, 248, 253, 256, 259, 260, 264, 268, 271, 274, 278, 282, 285, 289, 292, 295, 298, 302, 305, 308, 311, 313, 315, 318, 321, 323, 326, 329, 332, 335, 337, 340, 343, 346, 350, 353, 356, 360, 363, 366, 369, 372, 375, 378, 381, 386, 389, 392, 395, 398, 401, 404, 407, 410, 413, 416, 419, 422, 424, 427, 430, 433, 436, 439, 442, 445, 452, 455, 458, 465, 468, 475, 478, 481, 484, 487, 489, 492, 495, 497, 500, 503, 505, 507, 509, 511, 513, 515, 517, 519, 521, 523, 525, 527, 529, 533, 536, 539, 542, 545, 547, 549, 552, 554, 556, 558, 560, 562, 564, 566, 568, 570, 572, 574, 576, 578, 580, 582, 584, 586, 588, 590, 592, 594, 596, 598, 600, 603, 606, 609, 612, 616, 619, 623, 626, 630, 633, 637, 640, 642, 645, 647, 650, 652, 655, 657, 659, 661, 664, 668, 669, 674, 678, 679, 684, 688, 692, 696, 700, 703, 708, 713, 718, 723, 727, 731, 735, 739, 743, 747, 751, 755, 759, 762, 766, 769, 772, 775, 779, 782, 788, 794, 800, 806, 812, 816, 820, 825, 830, 835, 840, 845, 849, 853, 857, 861, 865, 868, 871, 874, 877, 880, 883, 886, 889, 892, 895, 898, 900, 903, 907, 910, 913, 916, 919, 923, 926, 930, 934, 937, 940, 943, 946, 949, 952, 956, 960, 964, 968, 970, 974, 978, 982, 985, 988, 991, 993, 995, 997, 999, 1001, 1003, 1005, 1007, 1009, 1011, 1013, 1015, 1018, 1022, 1027, 1030, 1033, 1037, 1042, 1045, 1048, 1050, 1052, 1053, 1055, 1057, 1058, 1059, 1067, 1069, 1071, 1072, 1073, 1080, 1084, 1087, 1090, 1092, 1096, 1100, 1103, 1106, 1109, 1113, 1118, 1122, 1127, 1132, 1137, 1142, 1144, 1146, 1148, 1150, 1153, 1156, 1159, 1162, 1166, 1170, 1174, 1180, 1186, 1192, 1196, 1199, 1201, 1203, 1209, 1215, 1218, 1219, 1220, 1228, 1230, 1233, 1236, 1239, 1242, 1245, 1248, 1251, 1252, 1257, 1260, 1263, 1266, 1270, 1273, 1276, 1279, 1282, 1285, 1286, 1290, 1291, 1297, 1301, 1302, 1308, 1312, 1313, 1318, 1319, 1324, 1328, 1332, 1335, 1338, 1340, 1344, 1346, 1348, 1350, 1352, 1354, 1356, 1358, 1360, 1362, 1364, 1366, 1367, 1372, 1373, 1378, 1379, 1384, 1387, 1390, 1394, 1398, 1400, 1404, 1408, 1410, 1413, 1416, 1419, 1422, 1425, 1428, 1430, 1434, 1438, 1440, 1444, 1448, 1450, 1454, 1455, 1460, 1462, 1465, 1468, 1471, 1474, 1477, 1480, 1483, 1486, 1489, 1493, 1496, 1499, 1502, 1505, 1508, 1511, 1513, 1514, 1520, 1523, 1527, 1532, 1535, 1539, 1544, 1547, 1554, 1557, 1562, 1565, 1574, 1577, 1579, 1584, 1587, 1592, 1595, 1602, 1605, 1610, 1613, 1615, 1618, 1620, 1624, 1626, 1629, 1631, 1634, 1642, 1645, 1650, 1653, 1658, 1661, 1668, 1671, 1678, 1681, 1688, 1691, 1700, 1703, 1711, 1717, 1721, 1726, 1733, 1736, 1742, 1746, 1748, 1751, 1753, 1756, 1758, 1760, 1762, 1765, 1768, 1771, 1773, 1776, 1778, 1781, 1783, 1786, 1788, 1791, 1793, 1796, 1798, 1801, 1803, 1806, 1808, 1811, 1813, 1816, 1818, 1821, 1823, 1825, 1828, 1831, 1834, 1837, 1840, 1843, 1845, 1847, 1849, 1851, 1853, 1855, 1858, 1863, 1867, 1872, 1874, 1876, 1878, 1881, 1883, 1889, 1894, 1900, 1902, 1905, 1907, 1910, 1913, 1916, 1919, 1922, 1926, 1930, 1932, 1934, 1936, 1939, 1941, 1943, 1945, 1947, 1949, 1951, 1953, 1956, 1959, 1961, 1965, 1969, 1971, 1973, 1975, 1977, 1979, 1981, 1983, 1985, 1987, 1989, 1991, 1993, 1995, 1997, 1999, 2001, 2003, 2005, 2007, 2009, 2011, 2013, 2015, 2017, 2019, 2021, 2023, 2025, 2027, 2029, 2031, 2033, 2035, 2037, 2039, 2041, 2043, 2045, 2047, 2049, 2051, 2053, 2055, 2057, 2059, 2061, 2064, 2067, 2070, 2073, 2076, 2079, 2082, 2085, 2088, 2091, 2094, 2097, 2100, 2103, 2105, 2108, 2111, 2116, 2119, 2123, 2128, 2133, 2138, 2141, 2143, 2145, 2147, 2150, 2153, 2157, 2160, 2162, 2164, 2166, 2168, 2172, 2176, 2178, 2181, 2185, 2189, 2193, 2197, 2201, 2205, 2209, 2213, 2217, 2221, 2225, 2229, 2232, 2236, 2240, 2243, 2245, 2247, 2249, 2251, 2253, 2255, 2257, 2259, 2261, 2266, 2269, 2276, 2278, 2282, 2286, 2290, 2294, 2298, 2302, 2306, 2310, 2314, 2318, 2322, 2326, 2330, 2334, 2338, 2342, 2346, 2351, 2356, 2360, 2364, 2368, 2372, 2376, 2380, 2384, 2388, 2392, 2396, 2400, 2404, 2408, 2412, 2416, 2420, 2424, 2427, 2430, 2433, 2436, 2439, 2440, 2441, 2449, 2453, 2455, 2457, 2459, 2461, 2463, 2465, 2467, 2470, 2473, 2475, 2480, 2483, 2484, 2488, 2490, 2493, 2494, 2498, 2501, 2502, 2510, 2511, 2519, 2520, 2524, 2526, 2528, 2530, 2532, 2534, 2536, 2538, 2540, 2542, 2544, 2548, 2554, 2556, 2558, 2560, 2562, 2564, 2566, 2568, 2570, 2572, 2574, 2576, 2578, 2580, 2582, 2584, 2586, 2588, 2590, 2592, 2594, 2596, 2599, 2601, 2603, 2605, 2607, 2608, 2615, 2616, 2624, 2629, 2635, 2641, 2646, 2651, 2658, 2666, 2673, 2680, 2688, 2693, 2698, 2705, 2712, 2717, 2722, 2729, 2736, 2740, 2744, 2750, 2756, 2760, 2764, 2770, 2776, 2779, 2781, 2783, 2785, 2787, 2789, 2791, 2793, 2795, 2797, 2799, 2801, 2803, 2805, 2807, 2809, 2811, 2813, 2817, 2823, 2825, 2829, 2831, 2836, 2843, 2849, 2851, 2855, 2857, 2862, 2868, 2875, 2878, 2880, 2883 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const short int yyrhs[] = { 336, 0, -1, 4, -1, -1, 4, 337, 338, -1, 341, -1, 340, -1, 339, 304, 416, -1, 339, 304, 1, -1, 339, 199, 416, -1, 339, 199, 1, -1, 339, 224, 416, -1, 339, 224, 1, -1, 280, 175, 365, -1, 280, 196, 365, -1, 280, 247, 365, -1, 280, 1, -1, 52, 280, -1, 281, -1, 1, -1, 175, -1, 175, 365, -1, 175, 1, -1, 214, -1, 214, 365, -1, 214, 1, -1, 339, 1, -1, 347, -1, 348, -1, -1, 298, 343, 341, 299, -1, 298, 299, -1, 298, 1, 299, -1, 345, -1, 339, -1, 342, -1, 342, 365, -1, 342, 1, -1, 295, -1, 339, 295, -1, 345, -1, 339, -1, 346, -1, 348, 339, -1, 348, 346, -1, -1, 184, 365, 350, 305, 339, -1, 184, 1, -1, 349, -1, 349, 306, 339, -1, 306, 1, -1, 308, -1, 6, 416, -1, 6, 414, -1, 6, -1, 6, 1, -1, 86, 416, -1, 86, 414, -1, 86, -1, 86, 1, -1, 20, 416, -1, 20, 414, -1, 20, -1, 20, 1, -1, 137, 416, -1, 137, 414, -1, 137, -1, 137, 1, -1, 41, 416, -1, 41, 1, -1, 157, 416, -1, 157, 1, -1, 155, 416, -1, 155, 1, -1, 5, -1, 5, 1, -1, 153, -1, 153, 365, -1, 153, 1, -1, 129, -1, 129, 9, -1, 129, 1, -1, 130, -1, 130, 9, -1, 130, 1, -1, 79, -1, 262, -1, 79, 365, -1, 79, 1, -1, -1, 185, 365, 352, 186, -1, 351, 339, -1, 185, 1, -1, -1, 186, 354, 344, -1, 353, 185, 365, -1, 353, 1, -1, 186, 1, -1, 190, 300, 345, -1, 355, 365, 295, -1, 355, 295, -1, 356, 339, 301, -1, 356, 301, -1, 357, 339, -1, 190, 1, -1, 190, 300, 1, -1, 355, 1, -1, 356, 1, -1, 357, 1, -1, 88, -1, 146, -1, 146, 365, -1, 146, 1, -1, 150, -1, 88, 365, -1, 88, 1, -1, 150, 365, -1, 150, 1, -1, 82, -1, 82, 416, -1, 82, 1, -1, 120, 365, -1, 120, 174, 365, -1, 120, 1, -1, 154, 416, -1, 154, 174, 416, -1, 154, 1, -1, 106, 416, -1, 106, 1, -1, 111, 416, -1, 111, 1, -1, 156, 416, -1, 156, 1, -1, 148, 365, 293, 416, -1, 148, 1, -1, 92, 416, -1, 92, 1, -1, 135, 365, -1, 135, 1, -1, 136, 365, -1, 136, 1, -1, 272, 365, -1, 272, 1, -1, 264, 365, -1, 264, 1, -1, 143, 365, -1, 143, -1, 143, 1, -1, 113, 365, -1, 113, 1, -1, 112, 365, -1, 112, 1, -1, 115, 365, -1, 115, 1, -1, 109, 300, 365, 293, 365, 301, -1, 109, 365, -1, 109, 1, -1, 102, 300, 365, 293, 365, 301, -1, 102, 1, -1, 110, 300, 365, 293, 365, 301, -1, 110, 365, -1, 110, 1, -1, 103, 365, -1, 103, 1, -1, 56, -1, 56, 365, -1, 56, 1, -1, 242, -1, 242, 365, -1, 242, 1, -1, 91, -1, 182, -1, 278, -1, 279, -1, 283, -1, 243, -1, 244, -1, 269, -1, 270, -1, 271, -1, 119, -1, 140, -1, 83, -1, 83, 365, 365, -1, 83, 1, -1, 139, 9, -1, 152, 365, -1, 152, 1, -1, 80, -1, 78, -1, 123, 292, -1, 288, -1, 98, -1, 258, -1, 81, -1, 70, -1, 89, -1, 90, -1, 149, -1, 93, -1, 116, -1, 117, -1, 94, -1, 100, -1, 31, -1, 159, -1, 160, -1, 237, -1, 241, -1, 238, -1, 240, -1, 33, -1, 40, -1, 219, -1, 69, -1, 65, 53, -1, 65, 134, -1, 65, 101, -1, 65, 252, -1, 65, 141, 365, -1, 65, 246, -1, 65, 61, 365, -1, 65, 245, -1, 65, 212, 77, -1, 65, 77, -1, 65, 221, 147, -1, 65, 147, -1, 229, -1, 74, 414, -1, 74, -1, 223, 414, -1, 223, -1, 99, 414, -1, 99, -1, 227, -1, 3, -1, 358, 365, -1, 358, 365, 293, -1, -1, 236, 436, 359, 365, -1, 236, 436, 1, -1, -1, 57, 236, 360, 365, -1, 57, 236, 1, -1, 57, 114, 365, -1, 57, 114, 1, -1, 114, 436, 365, -1, 114, 1, -1, 57, 61, 365, 274, -1, 52, 61, 365, 274, -1, 57, 61, 245, 274, -1, 52, 61, 245, 274, -1, 57, 245, 274, -1, 52, 245, 274, -1, 57, 138, 365, -1, 57, 138, 1, -1, 138, 436, 365, -1, 138, 436, 1, -1, 57, 118, 365, -1, 57, 118, 1, -1, 209, 436, 365, -1, 209, 1, -1, 181, 174, 365, -1, 211, 365, -1, 211, 1, -1, 99, 365, -1, 99, 174, 365, -1, 99, 1, -1, 77, 294, 204, 436, 365, -1, 77, 294, 203, 436, 365, -1, 77, 294, 225, 436, 365, -1, 147, 294, 203, 436, 365, -1, 77, 294, 34, 436, 365, -1, 77, 294, 1, -1, 147, 294, 1, -1, 57, 77, 204, 365, -1, 57, 77, 203, 365, -1, 57, 77, 225, 365, -1, 57, 147, 203, 365, -1, 57, 77, 34, 365, -1, 57, 77, 58, -1, 57, 77, 173, -1, 57, 77, 171, -1, 57, 77, 172, -1, 57, 77, 1, -1, 286, 365, -1, 286, 1, -1, 287, 365, -1, 287, 1, -1, 213, 416, -1, 213, 1, -1, 282, 416, -1, 282, 1, -1, 218, 416, -1, 218, 1, -1, 210, 416, -1, 210, -1, 210, 1, -1, 194, 174, 416, -1, 223, 365, -1, 223, 1, -1, 29, 174, -1, 217, 232, -1, 177, 174, 416, -1, 361, 416, -1, 177, 174, 365, -1, 195, 232, 416, -1, 177, 232, -1, 195, 174, -1, 361, 365, -1, 362, 365, -1, 263, 174, -1, 363, 365, -1, 29, 294, 208, -1, 29, 294, 138, -1, 364, 436, 365, -1, 29, 294, 58, -1, 364, -1, 29, 294, 1, -1, 29, 303, 365, -1, 29, 174, 1, -1, 29, 318, -1, 12, 318, -1, 29, 1, -1, 97, -1, 188, -1, 12, -1, 29, -1, 165, -1, 175, -1, 196, -1, 177, -1, 77, -1, 147, -1, 245, -1, 246, -1, 300, 301, -1, 300, 366, 367, -1, 369, 293, 366, 367, -1, 300, 1, -1, 300, 366, -1, 369, 293, 1, -1, 369, 293, 366, 1, -1, 369, 1, -1, 369, 301, -1, 342, -1, 295, -1, -1, 12, -1, 196, -1, -1, -1, 161, 366, 371, 372, 368, 373, 370, -1, 12, -1, 247, -1, -1, -1, 215, 374, 375, 368, 376, 370, -1, 161, 366, 1, -1, 161, 1, -1, 215, 1, -1, 161, -1, 95, 29, 366, -1, 95, 12, 366, -1, 95, 29, -1, 95, 12, -1, 95, 177, -1, 296, 365, 297, -1, 377, 296, 365, 297, -1, 296, 365, 297, -1, 378, 296, 365, 297, -1, 95, 165, 366, 378, -1, 95, 12, 366, 378, -1, 95, 29, 366, 378, -1, 379, -1, 165, -1, 104, -1, 291, -1, 412, 291, -1, 412, 12, -1, 412, 1, -1, 380, 377, -1, 381, 436, 365, -1, 380, 436, 380, -1, 380, 436, 365, -1, 380, 436, 365, 326, 380, -1, 380, 436, 380, 324, 380, -1, 380, 436, 380, 325, 380, -1, 380, 329, 380, -1, 380, 377, -1, 27, -1, 291, -1, 95, 409, 101, 382, 366, -1, 95, 409, 101, 12, 366, -1, 383, 378, -1, -1, -1, 383, 161, 384, 298, 385, 341, 299, -1, 383, -1, 95, 212, -1, 95, 221, -1, 95, 61, -1, 95, 141, -1, 95, 1, -1, 12, 174, -1, 12, 232, -1, -1, 57, 12, 388, 174, -1, 57, 12, -1, 12, 1, -1, 12, 1, -1, 12, 174, 1, -1, 386, 416, -1, 386, 365, -1, 386, 1, -1, 387, 416, -1, 387, 365, -1, -1, 386, 389, 340, -1, -1, 386, 298, 390, 341, 299, -1, 386, 298, 299, -1, -1, 387, 298, 391, 341, 299, -1, 387, 298, 299, -1, -1, 175, 174, 392, 339, -1, -1, 214, 232, 393, 339, -1, 175, 174, 1, -1, 175, 232, 1, -1, 361, 298, -1, 167, 394, -1, 395, -1, 394, 293, 395, -1, 12, -1, 29, -1, 165, -1, 175, -1, 196, -1, 177, -1, 77, -1, 147, -1, 245, -1, 246, -1, 1, -1, -1, 88, 13, 396, 339, -1, -1, 150, 13, 397, 339, -1, -1, 146, 13, 398, 339, -1, 88, 13, -1, 146, 13, -1, 365, 293, 399, -1, 365, 293, 1, -1, 365, -1, 416, 293, 399, -1, 416, 293, 1, -1, 416, -1, 121, 416, -1, 121, 1, -1, 268, 416, -1, 268, 1, -1, 122, 416, -1, 122, 1, -1, 400, -1, 400, 293, 399, -1, 400, 293, 1, -1, 401, -1, 401, 293, 399, -1, 401, 293, 1, -1, 402, -1, 402, 293, 399, -1, -1, 402, 293, 1, 403, -1, 123, -1, 404, 215, -1, 404, 416, -1, 404, 175, -1, 404, 196, -1, 404, 247, -1, 404, 214, -1, 162, 175, -1, 404, 365, -1, 404, 380, -1, 404, 412, 419, -1, 404, 88, -1, 404, 150, -1, 404, 146, -1, 404, 1, -1, 169, 416, -1, 169, 1, -1, 302, -1, -1, 405, 341, 235, 406, 365, -1, 405, 1, -1, 196, 300, 301, -1, 196, 300, 399, 301, -1, 196, 1, -1, 247, 300, 301, -1, 247, 300, 399, 301, -1, 247, 1, -1, 164, 300, 365, 293, 365, 301, -1, 164, 1, -1, 239, 296, 365, 297, -1, 239, 1, -1, 284, 300, 365, 293, 365, 293, 416, 301, -1, 284, 1, -1, 284, -1, 285, 300, 365, 301, -1, 285, 1, -1, 200, 300, 399, 301, -1, 200, 1, -1, 201, 300, 365, 293, 365, 301, -1, 201, 1, -1, 202, 300, 399, 301, -1, 202, 1, -1, 207, -1, 207, 1, -1, 365, -1, 365, 334, 365, -1, 276, -1, 30, 276, -1, 275, -1, 30, 275, -1, 259, 300, 409, 296, 407, 297, 301, -1, 259, 1, -1, 289, 300, 365, 301, -1, 289, 1, -1, 290, 300, 365, 301, -1, 290, 1, -1, 257, 300, 365, 293, 365, 301, -1, 257, 1, -1, 255, 300, 365, 293, 365, 301, -1, 255, 1, -1, 256, 300, 365, 293, 365, 301, -1, 256, 1, -1, 266, 300, 380, 293, 380, 293, 380, 301, -1, 266, 1, -1, 266, 300, 380, 293, 380, 293, 1, -1, 266, 300, 380, 293, 1, -1, 266, 300, 1, -1, 277, 300, 380, 301, -1, 267, 300, 380, 293, 380, 301, -1, 267, 1, -1, 267, 300, 380, 293, 1, -1, 267, 300, 1, -1, 65, -1, 65, 1, -1, 67, -1, 67, 1, -1, 220, -1, 222, -1, 226, -1, 220, 1, -1, 222, 1, -1, 226, 1, -1, 63, -1, 63, 1, -1, 265, -1, 265, 1, -1, 68, -1, 68, 1, -1, 71, -1, 71, 1, -1, 254, -1, 254, 1, -1, 178, -1, 178, 1, -1, 105, -1, 105, 1, -1, 248, -1, 248, 1, -1, 249, -1, 249, 1, -1, 250, -1, 250, 1, -1, 72, -1, 73, -1, 72, 29, -1, 73, 29, -1, 72, 77, -1, 73, 77, -1, 72, 1, -1, 73, 1, -1, 36, -1, 37, -1, 38, -1, 39, -1, 49, -1, 409, -1, 412, 409, -1, 409, 296, 407, 297, -1, 409, 296, 1, -1, 409, 296, 365, 1, -1, 25, -1, 87, -1, 263, -1, 411, 294, -1, 411, -1, 412, 409, 296, 365, 297, -1, 412, 409, 296, 1, -1, 412, 409, 296, 365, 1, -1, 412, -1, 411, 12, -1, 410, -1, 410, 12, -1, 411, 175, -1, 410, 175, -1, 411, 29, -1, 410, 29, -1, 413, 64, 365, -1, 413, 64, 1, -1, 84, -1, 85, -1, 51, -1, 415, 51, -1, 415, -1, 177, -1, 195, -1, 19, -1, 183, -1, 166, -1, 132, -1, 158, 416, -1, 158, 1, -1, 417, -1, 417, 293, 399, -1, 417, 293, 1, -1, 62, -1, 24, -1, 42, -1, 273, -1, 66, -1, 142, -1, 76, -1, 144, -1, 163, -1, 55, -1, 43, -1, 44, -1, 34, -1, 204, -1, 253, -1, 59, -1, 60, -1, 45, -1, 46, -1, 47, -1, 32, -1, 145, -1, 151, -1, 193, -1, 192, -1, 48, -1, 58, -1, 187, -1, 191, -1, 189, -1, 231, -1, 28, -1, 170, -1, 108, -1, 107, -1, 18, -1, 50, -1, 260, -1, 261, -1, 133, -1, 233, -1, 77, -1, 147, -1, 27, -1, 418, -1, 419, -1, 419, 377, -1, 319, 245, -1, 319, 365, -1, 319, 1, -1, 321, 246, -1, 321, 365, -1, 321, 1, -1, 320, 245, -1, 320, 365, -1, 320, 1, -1, 322, 77, -1, 322, 1, -1, 323, 147, -1, 323, 1, -1, 420, -1, 412, 420, -1, 412, 12, -1, 230, 300, 416, 301, -1, 230, 1, -1, 230, 300, 416, -1, 131, 300, 291, 301, -1, 131, 300, 165, 301, -1, 131, 300, 416, 301, -1, 131, 1, -1, 75, -1, 99, -1, 152, -1, 333, 365, -1, 333, 1, -1, 300, 365, 301, -1, 300, 1, -1, 236, -1, 138, -1, 29, -1, 217, -1, 29, 294, 27, -1, 29, 294, 1, -1, 216, -1, 26, 77, -1, 77, 294, 60, -1, 77, 294, 203, -1, 77, 294, 225, -1, 147, 294, 203, -1, 77, 294, 204, -1, 77, 294, 205, -1, 147, 294, 205, -1, 77, 294, 34, -1, 77, 294, 58, -1, 77, 294, 171, -1, 77, 294, 173, -1, 77, 294, 172, -1, 77, 1, -1, 77, 294, 1, -1, 147, 294, 1, -1, 147, 1, -1, 9, -1, 8, -1, 10, -1, 11, -1, 421, -1, 21, -1, 22, -1, 23, -1, 17, -1, 14, 300, 365, 301, -1, 14, 1, -1, 15, 300, 365, 293, 365, 301, -1, 15, -1, 365, 324, 365, -1, 365, 325, 365, -1, 365, 307, 365, -1, 365, 327, 365, -1, 365, 326, 365, -1, 365, 328, 365, -1, 365, 331, 365, -1, 365, 330, 365, -1, 365, 332, 365, -1, 365, 314, 365, -1, 365, 313, 365, -1, 365, 315, 365, -1, 365, 318, 365, -1, 365, 317, 365, -1, 365, 316, 365, -1, 365, 311, 365, -1, 365, 310, 365, -1, 365, 324, 380, 1, -1, 365, 325, 380, 1, -1, 365, 324, 1, -1, 365, 325, 1, -1, 365, 307, 1, -1, 365, 327, 1, -1, 365, 326, 1, -1, 365, 328, 1, -1, 365, 331, 1, -1, 365, 330, 1, -1, 365, 332, 1, -1, 365, 314, 1, -1, 365, 313, 1, -1, 365, 315, 1, -1, 365, 318, 1, -1, 365, 317, 1, -1, 365, 316, 1, -1, 365, 311, 1, -1, 365, 310, 1, -1, 30, 365, -1, 30, 1, -1, 96, 365, -1, 312, 365, -1, 312, 1, -1, -1, -1, 365, 308, 422, 365, 309, 423, 365, -1, 365, 308, 1, -1, 124, -1, 125, -1, 127, -1, 128, -1, 126, -1, 179, -1, 180, -1, 179, 1, -1, 180, 1, -1, 176, -1, 426, 413, 186, 339, -1, 176, 1, -1, -1, 251, 427, 413, -1, 251, -1, 251, 1, -1, -1, 168, 428, 413, -1, 168, 1, -1, -1, 425, 429, 300, 413, 293, 365, 301, -1, -1, 424, 430, 300, 413, 293, 365, 301, -1, -1, 408, 431, 413, -1, 57, -1, 187, -1, 191, -1, 58, -1, 133, -1, 189, -1, 231, -1, 170, -1, 107, -1, 108, -1, 432, 413, 433, -1, 432, 413, 433, 64, 365, -1, 192, -1, 193, -1, 32, -1, 145, -1, 151, -1, 59, -1, 48, -1, 34, -1, 44, -1, 204, -1, 60, -1, 54, -1, 61, -1, 141, -1, 47, -1, 62, -1, 24, -1, 233, -1, 27, -1, 142, -1, 50, -1, 294, 434, -1, 434, -1, 174, -1, 303, -1, 318, -1, -1, 411, 294, 434, 437, 436, 365, -1, -1, 411, 294, 434, 438, 377, 436, 365, -1, 432, 413, 435, 365, -1, 432, 413, 435, 377, 365, -1, 432, 413, 291, 377, 365, -1, 432, 413, 291, 380, -1, 432, 413, 291, 365, -1, 432, 413, 291, 365, 64, 365, -1, 432, 413, 291, 377, 365, 64, 365, -1, 432, 413, 291, 380, 64, 365, -1, 432, 413, 435, 365, 64, 365, -1, 432, 413, 435, 377, 365, 64, 365, -1, 432, 413, 212, 77, -1, 439, 413, 212, 77, -1, 432, 413, 212, 77, 64, 365, -1, 439, 413, 212, 77, 64, 365, -1, 432, 413, 221, 147, -1, 439, 413, 221, 147, -1, 432, 413, 221, 147, 64, 365, -1, 439, 413, 221, 147, 64, 365, -1, 432, 413, 77, -1, 439, 413, 77, -1, 432, 413, 77, 64, 365, -1, 439, 413, 77, 64, 365, -1, 432, 413, 147, -1, 439, 413, 147, -1, 432, 413, 147, 64, 365, -1, 439, 413, 147, 64, 365, -1, 432, 1, -1, 52, -1, 58, -1, 191, -1, 133, -1, 187, -1, 189, -1, 231, -1, 59, -1, 44, -1, 204, -1, 60, -1, 108, -1, 107, -1, 170, -1, 192, -1, 193, -1, 39, -1, 439, 413, 440, -1, 439, 413, 440, 64, 365, -1, 245, -1, 439, 413, 441, -1, 245, -1, 439, 413, 61, 365, -1, 439, 413, 61, 365, 64, 365, -1, 439, 413, 441, 64, 365, -1, 246, -1, 439, 413, 442, -1, 246, -1, 439, 413, 141, 365, -1, 439, 413, 442, 64, 365, -1, 439, 413, 141, 365, 64, 365, -1, 52, 1, -1, 3, -1, 219, 185, -1, 219, 1, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ static const unsigned short int yyrline[] = { 0, 158, 158, 160, 159, 162, 164, 167, 172, 175, 180, 185, 191, 196, 199, 202, 205, 209, 211, 214, 216, 218, 222, 227, 229, 232, 237, 239, 241, 243, 243, 246, 248, 250, 252, 254, 256, 261, 268, 269, 271, 273, 275, 277, 280, 283, 283, 286, 289, 291, 293, 295, 300, 301, 302, 303, 306, 307, 308, 309, 312, 313, 314, 315, 318, 319, 320, 321, 324, 325, 328, 329, 332, 333, 336, 337, 340, 341, 342, 345, 346, 347, 350, 351, 352, 355, 359, 367, 373, 375, 375, 378, 380, 384, 384, 388, 390, 393, 396, 399, 401, 408, 409, 413, 415, 418, 421, 424, 427, 432, 434, 436, 440, 443, 445, 449, 452, 471, 476, 478, 480, 484, 487, 490, 493, 495, 497, 500, 502, 505, 507, 510, 512, 515, 518, 521, 523, 526, 528, 530, 532, 534, 536, 538, 540, 542, 544, 546, 549, 551, 554, 556, 559, 561, 564, 566, 568, 572, 574, 577, 580, 582, 586, 587, 590, 591, 592, 596, 597, 598, 602, 604, 606, 608, 610, 612, 614, 616, 618, 620, 622, 624, 627, 628, 629, 632, 635, 636, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 672, 677, 679, 684, 686, 688, 690, 692, 693, 694, 695, 696, 697, 698, 699, 702, 704, 706, 709, 709, 712, 715, 715, 718, 721, 722, 725, 728, 732, 734, 736, 738, 740, 742, 745, 747, 750, 752, 755, 757, 761, 764, 767, 770, 771, 774, 775, 776, 780, 782, 784, 786, 788, 790, 793, 797, 799, 801, 804, 806, 809, 811, 813, 815, 818, 831, 833, 835, 837, 841, 842, 845, 846, 849, 850, 853, 854, 855, 858, 861, 862, 865, 867, 869, 871, 873, 875, 877, 880, 883, 886, 889, 891, 894, 895, 896, 917, 918, 936, 939, 960, 963, 968, 973, 977, 979, 981, 982, 990, 998, 1006, 1014, 1022, 1030, 1038, 1046, 1056, 1059, 1062, 1065, 1072, 1075, 1079, 1082, 1085, 1089, 1090, 1091, 1096, 1097, 1100, 1104, 1099, 1125, 1126, 1129, 1133, 1128, 1152, 1154, 1156, 1158, 1162, 1163, 1168, 1169, 1173, 1176, 1177, 1179, 1180, 1183, 1190, 1197, 1204, 1227, 1231, 1240, 1262, 1290, 1294, 1300, 1304, 1312, 1319, 1324, 1339, 1361, 1383, 1389, 1396, 1397, 1399, 1416, 1430, 1435, 1440, 1434, 1456, 1459, 1460, 1462, 1463, 1466, 1469, 1477, 1484, 1484, 1490, 1498, 1501, 1505, 1508, 1509, 1510, 1514, 1517, 1519, 1519, 1530, 1530, 1541, 1547, 1547, 1558, 1564, 1564, 1577, 1576, 1591, 1594, 1598, 1607, 1608, 1609, 1612, 1615, 1624, 1633, 1642, 1651, 1660, 1669, 1678, 1687, 1697, 1703, 1702, 1710, 1709, 1718, 1717, 1724, 1726, 1730, 1731, 1733, 1734, 1735, 1737, 1741, 1742, 1744, 1745, 1747, 1749, 1751, 1752, 1753, 1756, 1757, 1758, 1761, 1762, 1763, 1763, 1767, 1768, 1769, 1770, 1771, 1772, 1773, 1775, 1781, 1786, 1791, 1854, 1855, 1856, 1857, 1862, 1863, 1866, 1873, 1872, 1877, 1885, 1890, 1895, 1899, 1904, 1909, 1913, 1916, 1922, 1925, 1930, 1935, 1939, 1943, 1947, 1953, 1956, 1960, 1963, 1966, 1969, 1972, 1973, 1976, 1977, 1978, 1979, 1980, 1981, 1983, 1985, 1989, 1991, 1996, 1998, 2003, 2006, 2010, 2013, 2017, 2020, 2024, 2027, 2032, 2037, 2042, 2047, 2051, 2054, 2058, 2062, 2068, 2070, 2084, 2086, 2090, 2091, 2092, 2094, 2097, 2100, 2104, 2105, 2109, 2110, 2114, 2115, 2120, 2121, 2126, 2127, 2131, 2133, 2137, 2138, 2142, 2143, 2148, 2149, 2153, 2154, 2158, 2159, 2161, 2162, 2163, 2166, 2169, 2173, 2178, 2179, 2180, 2181, 2182, 2183, 2189, 2204, 2208, 2210, 2214, 2216, 2225, 2229, 2230, 2231, 2234, 2236, 2243, 2255, 2272, 2287, 2304, 2306, 2308, 2310, 2312, 2316, 2320, 2321, 2323, 2324, 2332, 2333, 2334, 2335, 2336, 2337, 2338, 2339, 2341, 2343, 2344, 2345, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, 2357, 2358, 2359, 2360, 2361, 2362, 2363, 2364, 2365, 2366, 2367, 2368, 2369, 2370, 2371, 2372, 2373, 2374, 2375, 2376, 2377, 2378, 2379, 2380, 2381, 2382, 2383, 2384, 2385, 2386, 2387, 2388, 2389, 2390, 2391, 2392, 2400, 2404, 2416, 2444, 2448, 2452, 2456, 2460, 2464, 2468, 2472, 2476, 2480, 2484, 2488, 2492, 2496, 2511, 2517, 2523, 2524, 2526, 2531, 2537, 2539, 2541, 2549, 2550, 2551, 2552, 2553, 2556, 2557, 2559, 2560, 2561, 2562, 2564, 2565, 2570, 2571, 2572, 2573, 2574, 2575, 2576, 2577, 2578, 2579, 2580, 2581, 2582, 2583, 2585, 2594, 2600, 2604, 2612, 2613, 2617, 2618, 2619, 2620, 2621, 2622, 2623, 2624, 2625, 2629, 2630, 2636, 2637, 2638, 2639, 2640, 2641, 2642, 2643, 2644, 2645, 2646, 2647, 2648, 2649, 2650, 2651, 2652, 2653, 2655, 2657, 2659, 2661, 2663, 2665, 2667, 2669, 2671, 2673, 2675, 2677, 2679, 2681, 2683, 2685, 2687, 2689, 2693, 2694, 2696, 2697, 2698, 2702, 2703, 2702, 2705, 2711, 2712, 2713, 2714, 2715, 2716, 2717, 2718, 2720, 2723, 2725, 2732, 2736, 2736, 2741, 2742, 2745, 2745, 2750, 2753, 2753, 2762, 2762, 2772, 2771, 2782, 2785, 2786, 2787, 2788, 2789, 2790, 2791, 2792, 2793, 2795, 2802, 2812, 2813, 2814, 2815, 2816, 2817, 2818, 2819, 2820, 2821, 2822, 2823, 2824, 2825, 2826, 2827, 2828, 2829, 2830, 2834, 2835, 2836, 2837, 2838, 2839, 2840, 2843, 2843, 2860, 2860, 2878, 2890, 2904, 2926, 2945, 2965, 2997, 3030, 3060, 3080, 3101, 3111, 3121, 3137, 3154, 3164, 3174, 3190, 3207, 3217, 3227, 3243, 3260, 3270, 3280, 3296, 3313, 3324, 3325, 3326, 3327, 3328, 3329, 3330, 3331, 3332, 3333, 3334, 3335, 3336, 3337, 3338, 3339, 3340, 3341, 3348, 3357, 3359, 3369, 3372, 3380, 3395, 3410, 3411, 3421, 3423, 3431, 3446, 3462, 3467, 3469, 3472 }; #endif #if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "EXPRESSION_START_", "COMMAND_START_", "HISTORY_", "GEOMVIEW_", "VIEW_MATRIX_", "LEAD_INTEGER_", "INTEGER_", "REAL_", "SIGNED_NUMBER_", "NEWIDENT_", "REDEFINE_", "MATHFUNC_", "MATHFUNC2_", "POW_", "USERFUNC_", "MIDV_", "DATAFILENAME_", "LOGFILE_", "PI_", "E_", "G_", "PARAM_", "SYMBOL_", "TOTAL_", "EXTRA_ATTRIBUTE_", "FIXEDVOL_", "IDENT_", "UMINUS_", "SHELL_", "COLOR_", "HESSIAN_", "VOLCONST_", "TORUS_PERIODS_", "VERTICES_", "EDGES_", "FACETS_", "BODIES_", "HESSIAN_MENU_", "POSTSCRIPT_", "LENGTH_", "AREA_", "VOLUME_", "ID_", "OID_", "TAG_", "ORIGINAL_", "FACETEDGES_", "WRAP_", "QUOTATION_", "UNSET_", "TOPINFO_", "OPACITY_", "VALENCE_", "HESSIAN_SADDLE_", "SET_", "FIXED_", "DENSITY_", "PRESSURE_", "CONSTRAINT_", "COORD_", "DISSOLVE_", "WHERE_", "LIST_", "SHOW_", "DELETE_", "REFINE_", "RECALC_", "SHOWQ_", "EDGESWAP_", "FIX_", "UNFIX_", "TOGGLENAME_", "TOGGLEVALUE_", "STAR_", "QUANTITY_NAME_", "PAUSE_", "GO_", "SHOW_VOL_", "CHECK_", "READ_", "ZOOM_", "ON_", "OFF_", "GEOMPIPE_", "SELF_", "SINGLE_LETTER_", "LONG_JIGGLE_", "RAW_VERAVG_", "COUNTS_", "CHDIR_", "ALICE_", "STABILITY_TEST_", "DEFINE_", "UPLUS_", "DATATYPE_", "FLUSH_COUNTS_", "AUTOCHOP_", "UTEST_", "ATTRIBUTE_", "RITZ_", "MOVE_", "VERTEXNORMAL_", "POP_", "SYSTEM_", "TETRA_POINT_", "TRIPLE_POINT_", "LANCZOS_", "EIGENPROBE_", "EXEC_", "AREAWEED_", "EDGEWEED_", "GRAVITY_", "EDGEDIVIDE_", "LINEAR_", "QUADRATIC_", "DIFFUSION_", "EXTRAPOLATE_", "TRANSFORM_DEPTH_", "PRINTF_", "ERRPRINTF_", "PRINT_", "MAX_", "MIN_", "COUNT_", "SUM_", "AVG_", "BREAK_", "CONTINUE_", "SIZEOF_", "TRANSFORM_EXPR_", "BARE_", "BOTTOMINFO_", "METIS_", "KMETIS_", "KEYLOGFILE_", "SCALE_", "BURCHARD_", "REBODY_", "BOUNDARY_", "ORIENTATION_", "OMETIS_", "SQ_MEAN_CURV_", "FRONTCOLOR_", "SINGLE_REDEFD_", "METHOD_NAME_", "TASK_EXEC_", "RAWEST_VERAVG_", "SINGLE_LETTER_ARG_", "BACKCOLOR_", "LAGRANGE_", "RETURN_", "TRANSFORM_EXPR_VERB_", "OOGLFILE_", "PARALLEL_EXEC_", "BINARY_OFF_FILE_", "SPRINTF_", "CONVERT_TO_QUANTS_", "METIS_FACTOR_", "FUNCTION_", "EXPRINT_", "DIHEDRAL_", "WRAP_VERTEX_", "ARRAYIDENT_", "DATE_AND_TIME_", "LOCAL_", "SHOW_EXPR_", "SHOW_TRANS_", "AXIAL_POINT_", "ENERGY_", "CONSERVED_", "INFO_ONLY_", "ASSIGN_", "PROCEDURE_", "FOREACH_", "STRINGGLOBAL_", "EQUIANGULATE_", "HISTOGRAM_", "LOGHISTOGRAM_", "AREA_FIXED_", "QUIT_", "WARNING_MESSAGES_", "IF_", "WHILE_", "DO_", "NO_REFINE_", "STRING_", "NONCONTENT_", "FOR_", "HIT_PARTNER_", "FRONTBODY_", "BACKBODY_", "COLORFILE_", "PERM_STRINGGLOBAL_", "FUNCTION_IDENT_", "THICKEN_", "COLORMAP_", "REDIRECT_", "NEWVERTEX_", "NEWEDGE_", "NEWFACET_", "MODULUS_", "TARGET_", "VALUE_", "INVERSE_PERIODS_", "NEWBODY_", "DELTA_", "GAP_CONSTANT_", "DUMP_", "NOTCH_", "QUANTITY_", "LOAD_", "PERM_PROCEDURE_", "PROCEDURE_WORD_", "DYNAMIC_LOAD_FUNC_", "PERM_IDENT_", "PERMLOAD_", "HELP_", "VERTEX_AVERAGE_", "METHOD_INSTANCE_", "RAW_VERTEX_AVERAGE_", "OPTIMIZE_", "REDIRECTOVER_", "TOLERANCE_", "RAWEST_VERTEX_AVERAGE_", "JIGGLE_", "VIEW_TRANSFORMS_", "CLOSE_SHOW_", "IS_DEFINED_", "NODISPLAY_", "PERM_ASSIGN_", "PHASE_", "VIEW_TRANSFORM_SWAP_COLORS_", "BACKQUOTE_COMMA_", "INTERNAL_VARIABLE_", "DIRICHLET_", "SOBOLEV_", "VIEW_TRANSFORM_PARITY_", "SOBOLEV_SEEK_", "DIRICHLET_SEEK_", "HESSIAN_SEEK_", "REORDER_STORAGE_", "RENUMBER_ALL_", "CONSTRAINT_NAME_", "BOUNDARY_NAME_", "PROCEDURE_IDENT_", "POP_TRI_TO_EDGE_", "POP_EDGE_TO_TRI_", "POP_QUAD_TO_QUAD_", "SHOWVERB_", "PROCEDURES_", "MPI_TASK_ATTR_", "T1_EDGESWAP_", "MERGE_EDGE_", "MERGE_FACET_", "MERGE_VERTEX_", "RESET_COUNTS_", "VALID_ELEMENT_", "MID_EDGE_", "MID_FACET_", "GO_COUNT_", "ELEMENT_IDENT_", "BODY_METIS_", "REVERSE_ORIENTATION_", "MATRIX_MULTIPLY_", "MATRIX_INVERSE_", "BINARY_PRINTF_", "DUMP_MEMLIST_", "FREE_DISCARDS_", "REPARTITION_", "METIS_READJUST_", "MEAN_CURVATURE_", "GLOBAL_", "LEAD_INTEGER_AT_", "INTEGER_AT_", "MATRIX_DETERMINANT_", "SUBCOMMAND_", "ABORT_", "BREAKPOINT_", "WHEREAMI_", "ADDLOAD_", "SIMPLEX_TO_FE_", "DISPLAY_TEXT_", "DELETE_TEXT_", "SUPPRESS_WARNING_", "UNSUPPRESS_WARNING_", "RESET_PROFILING_", "VALID_CONSTRAINT_", "VALID_BOUNDARY_", "ARRAY_ATTRIBUTE_", "PROFILING_", "','", "'.'", "';'", "'['", "']'", "'{'", "'}'", "'('", "')'", "'`'", "ASSIGNOP_", "PIPE_", "THEN_", "ELSE_", "'='", "'?'", "':'", "OR_", "AND_", "NOT_", "'>'", "'<'", "NE_", "GE_", "LE_", "EQ_", "ON_CONSTRAINT_", "HIT_CONSTRAINT_", "ON_BOUNDARY_", "ON_QUANTITY_", "ON_METHOD_INSTANCE_", "'+'", "'-'", "'*'", "'/'", "'%'", "DOT_", "IDIV_", "IMOD_", "'^'", "EPRINT_", "'@'", "$accept", "whole", "@1", "commandline", "command", "vcommand", "commands", "commandblock", "@2", "onecommand", "commandsemic", "commandterm", "commandlist", "commandlistterm", "ifhead", "@3", "whilehead", "@4", "dohead", "@5", "forentry", "forhead", "fortop", "estart", "@6", "@7", "identassign", "permidentassign", "elidassign", "lvalue", "rexpr", "datatype", "argident", "arglist", "argliststart", "protobody", "functionname", "@8", "@9", "procedurename", "@10", "@11", "indexset", "dimensionset", "arraydecl", "arraylvalue", "arraylvalueindexset", "extraat", "defextra", "@12", "@13", "newlvalue", "new_permlvalue", "@14", "@15", "@16", "@17", "@18", "@19", "localidlist", "localid", "@20", "@21", "@22", "exprlist", "printfhead", "binaryprintfhead", "errprintfhead", "@23", "print", "backquote", "@24", "elindex", "verb", "eltype", "multiple", "single", "singlep", "element_gen", "toggle", "quotation_concat", "stringexpr", "sprintfhead", "getattrib", "ggetattrib", "fullattrib", "signed_expr", "@25", "@26", "aggregate", "histotype", "foreachhead", "@27", "@28", "@29", "@30", "@31", "set", "set_attrib", "setattrib", "setattribb", "assignop", "@32", "@33", "unset", "unsetattrib", "conname", "bdryname", 0 }; #endif # ifdef YYPRINT /* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to token YYLEX-NUM. */ static const unsigned short int yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 44, 46, 59, 91, 93, 123, 125, 40, 41, 96, 548, 549, 550, 551, 61, 63, 58, 552, 553, 554, 62, 60, 555, 556, 557, 558, 559, 560, 561, 562, 563, 43, 45, 42, 47, 37, 564, 565, 566, 94, 567, 64 }; # endif /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const unsigned short int yyr1[] = { 0, 335, 336, 337, 336, 338, 339, 339, 339, 339, 339, 339, 339, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 339, 341, 341, 343, 342, 342, 342, 344, 344, 339, 339, 339, 345, 345, 346, 347, 348, 347, 348, 350, 349, 340, 340, 340, 340, 340, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 352, 351, 340, 340, 354, 353, 340, 340, 339, 355, 356, 356, 357, 357, 340, 340, 340, 356, 357, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 339, 339, 339, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 339, 339, 339, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 358, 336, 336, 359, 339, 339, 360, 340, 340, 340, 340, 339, 339, 340, 340, 340, 340, 340, 340, 340, 340, 339, 339, 340, 340, 339, 339, 339, 340, 340, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 340, 340, 340, 340, 340, 340, 340, 340, 340, 339, 340, 339, 361, 362, 339, 339, 339, 339, 339, 339, 339, 339, 363, 339, 364, 364, 339, 365, 365, 339, 339, 339, 339, 339, 339, 366, 366, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 368, 369, 369, 368, 369, 369, 369, 369, 368, 370, 370, 370, 371, 371, 372, 373, 340, 374, 374, 375, 376, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 377, 377, 378, 378, 379, 379, 379, 340, 380, 380, 380, 380, 380, 380, 381, 340, 340, 340, 340, 340, 340, 365, 365, 382, 382, 383, 383, 340, 384, 385, 340, 340, 340, 340, 340, 340, 340, 386, 387, 388, 386, 386, 339, 365, 339, 339, 339, 339, 339, 339, 389, 339, 390, 339, 339, 391, 339, 339, 392, 339, 393, 339, 339, 339, 339, 340, 394, 394, 395, 395, 395, 395, 395, 395, 395, 395, 395, 395, 395, 396, 340, 397, 340, 398, 340, 340, 340, 399, 399, 399, 399, 399, 399, 400, 400, 401, 401, 402, 402, 340, 340, 340, 340, 340, 340, 340, 340, 403, 340, 404, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 405, 406, 365, 365, 365, 365, 365, 340, 340, 340, 340, 340, 365, 365, 365, 365, 339, 339, 339, 365, 365, 365, 365, 365, 365, 365, 365, 407, 407, 407, 407, 407, 407, 365, 365, 365, 365, 365, 365, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 365, 365, 365, 365, 365, 408, 340, 408, 340, 408, 408, 408, 340, 340, 340, 408, 340, 408, 340, 408, 340, 408, 340, 408, 340, 408, 340, 408, 340, 408, 340, 408, 340, 408, 340, 408, 408, 340, 340, 340, 340, 340, 340, 409, 409, 409, 409, 409, 410, 410, 411, 411, 411, 411, 411, 411, 412, 412, 411, 411, 411, 413, 413, 413, 413, 413, 413, 413, 413, 413, 413, 414, 414, 415, 415, 416, 416, 416, 416, 416, 416, 416, 417, 417, 416, 416, 416, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 418, 419, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 421, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 365, 422, 423, 365, 365, 424, 424, 424, 424, 424, 425, 425, 340, 340, 426, 340, 340, 427, 340, 340, 340, 428, 340, 340, 429, 340, 430, 365, 431, 340, 432, 433, 433, 433, 433, 433, 433, 433, 433, 433, 340, 340, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 435, 435, 436, 436, 436, 437, 339, 438, 339, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 439, 440, 440, 440, 440, 440, 440, 440, 440, 440, 440, 440, 440, 440, 440, 440, 440, 340, 340, 441, 340, 365, 340, 340, 340, 442, 340, 365, 340, 340, 340, 340, 336, 340, 340 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ static const unsigned char yyr2[] = { 0, 2, 1, 0, 3, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 1, 1, 1, 2, 2, 1, 2, 2, 2, 1, 1, 0, 4, 2, 3, 1, 1, 1, 2, 2, 1, 2, 1, 1, 1, 2, 2, 0, 5, 2, 1, 3, 2, 1, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 2, 2, 1, 2, 2, 1, 2, 2, 1, 1, 2, 2, 0, 4, 2, 2, 0, 3, 3, 2, 2, 3, 3, 2, 3, 2, 2, 2, 3, 2, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 3, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 6, 2, 2, 6, 2, 6, 2, 2, 2, 2, 1, 2, 2, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 2, 2, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 2, 3, 2, 3, 2, 3, 2, 1, 2, 1, 2, 1, 2, 1, 1, 1, 2, 3, 0, 4, 3, 0, 4, 3, 3, 3, 3, 2, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 2, 2, 2, 3, 2, 5, 5, 5, 5, 5, 3, 3, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 2, 2, 2, 2, 3, 2, 3, 3, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 1, 3, 3, 3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 2, 2, 3, 4, 2, 2, 1, 1, 0, 1, 1, 0, 0, 7, 1, 1, 0, 0, 6, 3, 2, 2, 1, 3, 3, 2, 2, 2, 3, 4, 3, 4, 4, 4, 4, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 5, 5, 5, 3, 2, 1, 1, 5, 5, 2, 0, 0, 7, 1, 2, 2, 2, 2, 2, 2, 2, 0, 4, 2, 2, 2, 3, 2, 2, 2, 2, 2, 0, 3, 0, 5, 3, 0, 5, 3, 0, 4, 0, 4, 3, 3, 2, 2, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 4, 0, 4, 0, 4, 2, 2, 3, 3, 1, 3, 3, 1, 2, 2, 2, 2, 2, 2, 1, 3, 3, 1, 3, 3, 1, 3, 0, 4, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 1, 0, 5, 2, 3, 4, 2, 3, 4, 2, 6, 2, 4, 2, 8, 2, 1, 4, 2, 4, 2, 6, 2, 4, 2, 1, 2, 1, 3, 1, 2, 1, 2, 7, 2, 4, 2, 4, 2, 6, 2, 6, 2, 6, 2, 8, 2, 7, 5, 3, 4, 6, 2, 5, 3, 1, 2, 1, 2, 1, 1, 1, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 2, 4, 3, 4, 1, 1, 1, 2, 1, 5, 4, 5, 1, 2, 1, 2, 2, 2, 2, 2, 3, 3, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 4, 2, 3, 4, 4, 4, 2, 1, 1, 1, 2, 2, 3, 2, 1, 1, 1, 1, 3, 3, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 2, 6, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 0, 0, 7, 3, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 4, 2, 0, 3, 1, 2, 0, 3, 2, 0, 7, 0, 7, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 0, 6, 0, 7, 4, 5, 5, 4, 4, 6, 7, 6, 6, 7, 4, 4, 6, 6, 4, 4, 6, 6, 3, 3, 5, 5, 3, 3, 5, 5, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 1, 3, 1, 4, 6, 5, 1, 3, 1, 4, 5, 6, 2, 1, 2, 2 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state STATE-NUM when YYTABLE doesn't specify something else to do. Zero means the default is an error. */ static const unsigned short int yydefact[] = { 0, 235, 3, 0, 0, 0, 1, 732, 731, 733, 734, 0, 0, 743, 739, 665, 736, 737, 738, 631, 596, 0, 673, 661, 709, 0, 650, 642, 586, 587, 588, 589, 632, 640, 641, 647, 648, 649, 655, 590, 666, 639, 656, 645, 646, 630, 634, 700, 636, 0, 597, 0, 701, 376, 664, 663, 789, 790, 793, 791, 792, 0, 669, 708, 635, 637, 651, 0, 652, 702, 638, 375, 662, 657, 659, 658, 654, 653, 0, 0, 0, 0, 643, 0, 713, 710, 0, 660, 670, 707, 0, 904, 910, 644, 0, 667, 668, 598, 0, 633, 0, 0, 0, 0, 377, 0, 493, 0, 0, 0, 0, 0, 0, 0, 317, 236, 0, 0, 0, 600, 0, 674, 675, 690, 735, 810, 19, 0, 0, 0, 0, 0, 204, 211, 212, 0, 0, 0, 814, 0, 0, 0, 0, 214, 195, 0, 0, 0, 229, 0, 189, 0, 188, 194, 0, 0, 0, 0, 196, 197, 170, 0, 199, 202, 0, 192, 0, 203, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 200, 201, 180, 0, 0, 0, 476, 0, 0, 0, 0, 0, 0, 0, 181, 0, 0, 0, 0, 198, 0, 0, 0, 0, 0, 0, 0, 205, 206, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 171, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 234, 227, 0, 207, 209, 210, 208, 0, 175, 176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 193, 86, 598, 0, 0, 0, 0, 177, 178, 179, 0, 172, 173, 0, 18, 0, 174, 509, 0, 0, 0, 191, 38, 0, 0, 51, 4, 0, 6, 5, 0, 40, 42, 27, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 374, 0, 0, 398, 0, 0, 466, 469, 472, 0, 812, 600, 0, 808, 0, 0, 0, 410, 741, 0, 0, 714, 0, 781, 780, 727, 0, 782, 699, 0, 730, 0, 499, 0, 513, 0, 515, 0, 517, 0, 519, 694, 0, 506, 0, 527, 0, 545, 0, 0, 508, 0, 529, 0, 531, 0, 706, 0, 784, 783, 679, 677, 678, 685, 683, 684, 682, 680, 681, 687, 686, 689, 688, 704, 703, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 389, 496, 0, 0, 599, 380, 692, 671, 672, 378, 0, 691, 676, 0, 75, 55, 621, 616, 614, 615, 624, 0, 623, 619, 622, 620, 53, 618, 52, 627, 409, 0, 405, 322, 63, 61, 60, 323, 0, 0, 0, 321, 69, 68, 914, 0, 0, 17, 166, 165, 408, 0, 0, 0, 0, 0, 0, 0, 0, 559, 549, 215, 0, 224, 217, 216, 0, 226, 0, 0, 222, 220, 218, 551, 563, 565, 584, 580, 582, 585, 581, 583, 228, 0, 88, 87, 120, 119, 184, 0, 59, 57, 56, 115, 446, 114, 136, 135, 403, 365, 364, 401, 402, 0, 366, 399, 400, 0, 267, 0, 265, 232, 158, 0, 163, 162, 571, 128, 127, 156, 0, 155, 161, 0, 160, 130, 129, 151, 150, 149, 148, 247, 849, 850, 851, 0, 153, 152, 123, 0, 121, 461, 460, 465, 464, 190, 81, 80, 84, 83, 138, 137, 140, 139, 67, 65, 64, 0, 185, 147, 145, 112, 450, 111, 0, 134, 0, 117, 448, 116, 187, 186, 78, 77, 126, 0, 124, 73, 72, 132, 131, 71, 70, 359, 324, 325, 0, 483, 504, 0, 445, 435, 436, 441, 442, 437, 438, 440, 439, 443, 444, 432, 433, 807, 0, 492, 491, 22, 0, 0, 21, 800, 0, 307, 569, 796, 797, 0, 47, 45, 92, 89, 97, 0, 104, 0, 0, 308, 0, 261, 0, 297, 295, 264, 263, 290, 289, 25, 427, 24, 360, 353, 354, 355, 302, 294, 293, 917, 916, 555, 556, 300, 299, 230, 557, 0, 169, 168, 502, 0, 573, 575, 577, 804, 0, 567, 535, 0, 537, 0, 533, 0, 311, 144, 143, 561, 539, 0, 463, 462, 142, 141, 16, 0, 0, 0, 292, 291, 511, 0, 286, 285, 288, 287, 0, 31, 0, 50, 26, 0, 0, 39, 0, 37, 36, 0, 44, 0, 0, 96, 0, 106, 100, 0, 107, 102, 0, 19, 0, 431, 309, 304, 310, 312, 0, 381, 0, 0, 395, 0, 394, 414, 419, 413, 0, 412, 422, 416, 415, 0, 0, 0, 490, 487, 489, 488, 479, 0, 482, 477, 481, 484, 485, 0, 478, 0, 599, 379, 0, 591, 606, 600, 604, 0, 882, 0, 0, 0, 0, 712, 711, 316, 314, 313, 728, 722, 723, 715, 724, 726, 725, 716, 719, 720, 717, 0, 0, 0, 729, 718, 721, 497, 456, 0, 459, 0, 0, 0, 695, 0, 0, 547, 0, 0, 0, 0, 0, 705, 765, 746, 788, 0, 779, 760, 778, 759, 773, 754, 772, 753, 774, 755, 777, 758, 776, 757, 775, 756, 763, 744, 0, 764, 745, 0, 767, 748, 766, 747, 768, 749, 770, 751, 769, 750, 771, 752, 0, 388, 0, 494, 594, 0, 524, 522, 0, 0, 0, 0, 626, 625, 617, 0, 411, 320, 318, 319, 904, 0, 253, 0, 904, 0, 284, 0, 280, 282, 283, 281, 0, 0, 0, 245, 244, 259, 258, 255, 254, 0, 243, 0, 252, 221, 219, 223, 225, 273, 0, 0, 0, 0, 183, 0, 363, 362, 0, 0, 266, 0, 0, 0, 246, 122, 257, 256, 0, 274, 0, 0, 0, 125, 358, 348, 349, 350, 0, 0, 806, 429, 0, 430, 305, 303, 262, 0, 0, 0, 94, 33, 105, 0, 98, 298, 306, 260, 0, 0, 240, 0, 500, 0, 802, 0, 0, 0, 542, 0, 13, 14, 15, 0, 32, 0, 10, 9, 12, 11, 8, 7, 0, 95, 99, 101, 315, 384, 383, 382, 0, 0, 0, 421, 0, 814, 233, 0, 93, 0, 231, 418, 424, 0, 468, 467, 471, 470, 474, 473, 675, 813, 842, 844, 828, 833, 834, 840, 832, 846, 837, 831, 836, 838, 841, 839, 845, 829, 830, 826, 827, 835, 843, 852, 0, 607, 611, 609, 605, 610, 608, 592, 0, 0, 817, 874, 822, 823, 818, 878, 821, 815, 819, 816, 0, 0, 820, 0, 0, 824, 848, 0, 899, 891, 884, 890, 893, 0, 875, 895, 894, 886, 0, 879, 896, 887, 888, 885, 897, 898, 892, 0, 0, 889, 902, 908, 900, 903, 909, 740, 0, 697, 696, 698, 0, 498, 0, 512, 0, 516, 693, 505, 0, 0, 543, 0, 528, 530, 0, 761, 762, 367, 0, 0, 525, 523, 595, 0, 593, 602, 0, 0, 629, 628, 251, 249, 407, 250, 248, 279, 276, 275, 277, 278, 242, 0, 0, 0, 0, 0, 372, 373, 371, 0, 390, 391, 0, 0, 0, 0, 0, 0, 133, 0, 0, 0, 434, 0, 0, 90, 0, 0, 356, 0, 239, 501, 0, 0, 0, 0, 510, 30, 0, 0, 0, 396, 369, 0, 0, 0, 475, 0, 0, 0, 613, 612, 0, 0, 0, 866, 870, 860, 0, 859, 847, 0, 856, 0, 905, 0, 911, 0, 867, 871, 0, 0, 0, 0, 455, 454, 458, 457, 0, 520, 0, 546, 0, 0, 786, 368, 495, 521, 603, 601, 0, 272, 269, 268, 270, 393, 392, 0, 0, 0, 271, 351, 0, 0, 339, 336, 340, 347, 343, 0, 344, 0, 0, 0, 541, 0, 385, 386, 387, 0, 370, 420, 423, 0, 0, 0, 876, 880, 0, 0, 0, 858, 0, 825, 0, 857, 0, 877, 0, 881, 0, 0, 901, 907, 912, 742, 514, 0, 544, 0, 0, 0, 157, 154, 159, 347, 503, 326, 327, 332, 333, 328, 329, 331, 330, 334, 335, 337, 346, 345, 357, 341, 0, 534, 536, 532, 0, 0, 853, 0, 0, 868, 872, 861, 0, 863, 864, 0, 906, 913, 869, 873, 526, 0, 787, 811, 352, 342, 338, 540, 0, 397, 855, 809, 862, 865, 507, 538 }; /* YYDEFGOTO[NTERM-NUM]. */ static const short int yydefgoto[] = { -1, 3, 5, 286, 287, 288, 289, 290, 697, 937, 291, 292, 293, 294, 295, 934, 296, 935, 297, 624, 298, 299, 300, 4, 948, 890, 301, 302, 303, 114, 793, 587, 1288, 1149, 1150, 1291, 924, 1141, 1276, 644, 946, 1228, 401, 731, 305, 116, 307, 1133, 308, 977, 1240, 309, 310, 870, 735, 981, 990, 929, 945, 602, 603, 902, 919, 915, 794, 311, 312, 313, 1167, 314, 117, 1100, 856, 315, 118, 761, 119, 120, 764, 512, 428, 795, 430, 121, 122, 123, 124, 812, 1271, 125, 318, 319, 665, 605, 759, 414, 756, 320, 1046, 1020, 1048, 536, 1168, 1169, 321, 1073, 1074, 1075 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ #define YYPACT_NINF -1183 static const int yypact[] = { 92, 71, 107, 111, 31079, 32882, -1183, -1183, -1183, -1183, -1183, 97, 2, -188, -1183, -1183, -1183, -1183, -1183, -1183, -1183, 41, -1183, -1183, -173, 12050, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, 1497, -1183, 31079, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, 30, -1183, -1183, -1183, -1183, -1183, 1851, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, 39, 62, 72, 76, -1183, 2187, -1183, -1183, 78, -1183, -1183, -1183, 26, -1183, -1183, -1183, 96, -1183, -1183, -1183, 98, -1183, -176, 101, 112, 114, -1183, 12367, -1183, 12684, 13001, 13318, 13635, 73, 54, 13952, -1183, 35914, -215, 33173, -171, -158, 32025, -1183, -153, -1183, -1183, -1183, -1183, 1293, 1147, 1, 31823, 9, -1183, -1183, -1183, 1922, 1686, 4741, 827, 606, 35925, 972, 1340, -1183, -1183, 2326, 750, 1577, -9, -156, -1183, 5058, -1183, -1183, 1167, 5375, 35405, 3473, -1183, -1183, -1183, 2258, -1183, -1183, 4497, -1183, 2522, -1183, 115, 14269, 2575, 2642, 14586, 14903, 2871, 15220, 15537, 37, 15854, -1183, -1183, -1183, 11416, 2959, 3276, -148, 597, 616, 16171, 16488, 35436, -91, 137, -1183, 5692, 3790, -149, 16805, -1183, 4107, 17122, 6009, 1883, 3593, 3910, 4046, -1183, -1183, 405, -26, 118, 514, 2595, 4139, 2839, 2711, -102, 2912, 127, 128, -20, -1183, 17439, 17756, 33464, 130, -17, -90, 55, 35478, 18073, 4227, 4424, 38, -73, 4364, 1668, 3229, 3295, 3156, 3339, -1183, -1183, -91, -1183, -1183, -1183, -1183, 6326, -1183, -1183, 133, 3546, 3612, 3656, 35463, 3863, 134, 136, 138, -1183, -1183, -13, 18390, 3929, 139, 4456, -1183, -1183, -1183, 18707, -1183, -1183, 283, -1183, 4544, -1183, -1183, 140, 19024, 19341, -1183, -1183, 32591, 162, -1183, -1183, 66, -1183, -1183, 6643, -1183, -1183, -1183, 32300, -140, 34337, 64, 11733, 33755, 34626, 28226, 31079, 31079, -91, -1183, -81, -91, -110, 7603, 28543, -126, -120, -117, 8246, -1183, -113, 202, -1183, 1356, 3973, 1356, -1183, -1183, 31079, 31079, -1183, 42, -1183, -155, -1183, 954, -155, -1183, 921, -1183, 69, -1183, 28860, -1183, 29494, -1183, 31079, -1183, 29494, -1183, -1183, 646, -1183, 31079, -1183, 242, -1183, 563, 2310, -1183, 31079, -1183, 31079, -1183, 31079, -1183, 36358, -1183, 5129, -1183, -1183, 1255, -1183, -1183, 1255, -1183, -1183, 1255, -1183, -1183, -1183, -1183, -1183, -1183, -1183, 19658, 19975, 20292, 20609, 20926, 21243, 21560, 21877, 22194, 22511, 22828, 23145, 23462, 23779, 24096, 24413, 24730, 25047, 31079, 2310, -109, 53, -56, 10782, -1183, -1183, 518, -1183, -1183, -1183, -107, -1183, -109, -106, -1183, -1183, -1183, -1183, -1183, -1183, -1183, 4680, -1183, -1183, -1183, -1183, -1183, 149, -1183, -95, -1183, 7929, -1183, -1183, -1183, -1183, -1183, -1183, 8563, 61, 31079, -1183, -1183, -1183, -1183, 31396, -67, -1183, -1183, 36858, 34, 31713, 898, 25364, 25681, 25998, 40, 26315, -61, -1183, -1183, -1183, 31079, -1183, -1183, -1183, 31079, -1183, 144, 70, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, 57, -1183, 36858, -1183, -1183, -1183, 27900, -1183, -1183, -1183, -1183, 33, 36858, -1183, -1183, -1183, -40, -40, -1183, -1183, -40, -1183, -1183, -1183, 124, -1183, 31079, 36858, -1183, -1183, 31079, -1183, 36858, -1183, -1183, -1183, -1183, 12367, 36858, -1183, 12367, 36858, -1183, -1183, -1183, 36858, -1183, 36858, -1183, -1183, -1183, -1183, 31079, -1183, 36858, -1183, 31079, 36858, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, 36858, -1183, 36858, -1183, -1183, -1183, 26632, -1183, -1183, 36858, -1183, 435, 36858, 28, -1183, 35940, -1183, -1183, 36858, -1183, 36858, -1183, 36858, -1183, 646, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, 52, -1183, -1183, 31079, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -51, -1183, -1183, 1356, -1183, -1183, -1183, 34915, 246, 36858, -1183, 29494, -1183, -1183, -1183, -1183, 31079, -1183, 36858, -1183, 36858, -1183, 32882, -1183, 34046, 646, -1183, 646, -1183, 31079, -1183, -1183, -1183, 36858, -1183, -1183, -1183, -1183, 36858, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, 36858, -1183, -1183, 26949, -1183, 36858, -1183, 29177, -1183, -1183, -1183, -1183, 1356, -1183, -1183, 31079, -1183, 31079, -1183, 31079, -1183, -1183, 36858, -1183, -1183, 899, -1183, -1183, -1183, 36858, -1183, 31079, 31079, 31079, -1183, -1183, -1183, 31079, -1183, 36858, -1183, 36858, -46, -1183, 32882, -1183, -1183, 4681, 4773, -1183, 4851, -1183, 36858, 567, -1183, 34337, 2004, -1183, 31079, -1183, -1183, 35693, 10, -1183, 59, -1183, 2694, -1183, 36858, -1183, 36858, 36858, 31079, -109, 31079, 31079, -1183, 31079, -42, -1183, -43, 36858, 35203, -1183, -36, 36858, -1183, 8880, 9197, 9514, -1183, -1183, -1183, -1183, -1183, 1113, -1183, -1183, -1183, 36858, -215, 32025, -1183, 1356, 35492, -1183, -11, -171, 49, 233, 242, -16, -1183, 35744, 35908, 36386, 35966, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -14, -8, -6, -1183, -1183, -1183, -1183, 35992, -5, -1, 12, 36018, 24, 58, 36226, 27, -1183, 60, 106, 36044, 36414, 36442, -1183, -1183, 4953, -1183, 31079, -1183, 4812, -1183, 5129, -1183, 5129, -1183, 5129, -1183, 5129, -1183, 5129, -1183, 5129, -1183, 5129, -1183, 181, 21, -1183, 181, 22, -1183, -155, -1183, -155, -1183, -155, -1183, -155, -1183, -155, -1183, -1183, 36252, -1183, 31079, -1183, -1183, 11099, -1183, -1183, 157, 119, 27266, 1356, -1183, -1183, -1183, 9831, -1183, -1183, -1183, 36858, 145, 31782, -1183, 247, 151, 35800, -1183, 31079, -1183, -1183, -1183, -1183, 31079, 31079, 31079, -1183, 36858, -1183, 36858, -1183, 36858, 31079, -1183, 31079, -1183, 36858, 36858, -1183, -1183, -1183, -91, -91, -91, -91, 36858, 34337, 135, 135, 135, 18, 36858, 36070, 35848, 35888, 36858, 36858, -1183, 36858, 34337, -1183, -91, 646, 34337, -1183, -1183, -1183, -1183, -1183, 36096, 514, 267, -1183, 34337, -1183, 36858, -1183, 36858, 148, 258, 31, -1183, -1183, 109, 125, -1183, -1183, -1183, 36858, 34337, 132, -1183, 31079, -1183, 159, 267, 36122, 36148, 36174, -1183, 152, 36858, 36858, 36858, 36470, -1183, 150, -1183, -1183, -1183, -1183, -1183, -1183, 2809, 36858, -1183, -1183, 36858, 36884, -169, 36858, 164, 36278, 31079, -1183, 32882, 680, -9, 6960, -1183, 7277, 29811, -1183, -1183, 32882, -1183, -1183, -1183, -1183, -1183, -1183, 918, 267, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, 170, 1356, -1183, -1183, -1183, -1183, -1183, -1183, -107, 27583, 34337, -1183, 412, -1183, -1183, -1183, 413, -1183, -1183, -1183, -1183, 392, 331, -1183, 30445, 35492, 416, -1183, 30445, -1183, -1183, -1183, -1183, -1183, 31079, 428, -1183, -1183, -1183, 31079, 430, -1183, -1183, -1183, -1183, -1183, -1183, -1183, 418, 349, -1183, -1183, -1183, 433, 434, 440, -1183, 31079, -1183, -1183, -1183, 10148, -1183, 10465, -1183, 31079, -1183, -1183, -1183, 30128, 932, -1183, 31079, -1183, -1183, 36832, -1183, -1183, -1183, 36304, 31079, -1183, -1183, -1183, 31079, -1183, -1183, 245, -23, -1183, -1183, -1183, -1183, -1183, -1183, -1183, 36858, 36858, 36858, 36858, 36858, 36858, 31079, 31079, 31079, 31079, 3011, -42, -42, -42, -40, -1183, -1183, -40, 31079, 31079, 31079, 3126, 31079, -1183, 3328, 132, 31079, -1183, 3443, 34337, -1183, 3645, 35, -1183, 14, 36858, -1183, 31079, 31079, 31079, 1400, -1183, -1183, 23462, 2310, 2310, -1183, -1183, 36330, 200, 206, -1183, -91, -153, -22, -1183, 36858, 3760, 31079, 31079, 450, 452, 31829, 30762, -29, -1183, 31079, 31876, 30762, 31920, 31079, 35502, 31079, 457, 458, 31079, 31079, 31079, 36498, -1183, -1183, -1183, -1183, 36526, 36804, 226, -1183, 223, 36200, -1183, -1183, 36858, 36858, -1183, -1183, 31079, 36858, 36858, 36858, 36858, -1183, -1183, 36554, 36582, 36610, 36858, -1183, 36638, 3962, -1183, -1183, 4198, -213, -1183, 85, -1183, 36666, 36694, 36722, -1183, 232, -215, -1183, -1183, 32882, -1183, -1183, -1183, 31079, 366, 31079, 36858, 36858, 31079, 31079, 31079, 35529, 31079, 36858, 31079, 35592, 31079, 36858, 31079, 36858, 31079, 31079, 36858, 36858, 36858, -1183, -1183, 227, -1183, 646, 31079, 36750, -1183, -1183, -1183, -213, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, 1029, -1183, -1183, -1183, 2640, 230, 36858, 31079, 36778, 36858, 36858, 36858, 31079, 36858, 36858, 31079, 36858, 36858, 36858, 36858, -1183, 234, 4953, -1183, -1183, -1183, -1183, -1183, 236, -1183, 36858, -1183, 36858, 36858, -1183, -1183 }; /* YYPGOTO[NTERM-NUM]. */ static const short int yypgoto[] = { -1183, -1183, -1183, -1183, -282, -202, -101, -1182, -1183, -1183, -534, 240, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, 75, -4, -476, -752, -597, -1183, -731, -1183, -1183, -1183, -1183, -1183, -1183, -118, -816, -1183, 147, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -380, -1183, -1183, -1183, -331, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -541, -1183, -115, -1183, 47, 103, -301, -122, -1183, 1155, -1183, -1183, -205, -119, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -1183, -729, -1183, -184, -1183, -1183, -1183, -1183, -1183, -1183 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If zero, do what YYDEFACT says. If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -916 static const short int yytable[] = { 115, 412, 431, 323, 413, 411, 427, 558, 436, 796, 438, -19, 706, 798, 709, 1229, 403, 717, 719, 766, 767, 329, 1096, 1097, 903, 904, 483, 348, 905, 916, 1130, 333, 699, -452, 492, 1253, 1225, 1047, 532, 641, 337, 1029, 1029, 770, 631, 1131, 1290, 332, 1029, 508, 642, 729, 316, 921, -19, 376, 630, 585, 896, 656, 699, 1022, 865, 339, 922, 710, -41, 699, 556, 771, 789, -915, 613, 341, 374, 419, 420, 343, 1023, 346, 304, 399, 1289, 533, 628, 283, 1292, 1127, 1128, 1129, 938, 897, 941, 533, 1290, 1, 2, 350, 322, 352, 772, 362, 355, 364, 367, 370, 373, -2, 317, 379, -19, 6, 325, 357, 400, 359, 513, 654, 326, 589, 725, 327, 727, 728, 354, 404, 699, 399, 616, 617, 614, 625, 585, 450, 659, 667, 405, 669, 484, 671, 677, 689, 629, 399, 546, 565, 559, 486, 586, 588, 375, 490, 306, 496, 618, 1160, 1161, 627, 1103, 645, 400, 673, 511, 698, 316, 516, 708, 740, 522, 525, 1030, 529, 531, 741, 538, 432, 742, 398, 541, 850, 773, 757, 585, 439, 552, 554, 730, 849, 726, 857, 561, 564, 304, 567, 858, 570, 572, 574, 862, 773, 861, 377, 411, 406, 760, 760, 760, 869, -406, -19, 611, 533, 534, 891, 758, 399, -34, 895, 620, 622, 317, 894, 534, 586, 1024, 906, 635, 535, 640, 533, 700, 917, -452, 433, -19, 653, 801, 535, 28, 29, 30, 31, 926, 888, 658, 1025, 1209, 930, 923, 711, 774, 39, -19, 961, 979, 701, 980, -452, 700, 675, 898, 899, 1026, 989, 306, 700, 682, 399, -452, 774, 1211, 1246, 790, 586, 791, 692, 694, -19, 28, 29, 30, 31, 900, 701, 683, 643, 705, 1078, -19, 1021, 701, 39, 1083, 1079, 714, 1080, 1082, 721, 723, 724, 400, -41, 324, 440, 927, 734, 738, 1230, -19, 1132, 752, -19, 441, 1084, -19, 1231, 1181, 399, 399, 434, 768, 769, 349, 1089, 700, 1086, 702, 442, -452, 950, 334, 1029, -452, -19, -452, 703, 1226, -452, 797, 338, 534, 316, 936, 316, 940, 800, 316, 316, -19, 701, 400, 400, 805, 1090, 806, 535, 807, -19, 534, 1087, 972, 702, 340, 703, 951, -41, 762, 762, 762, 304, 703, 304, 342, 535, 304, 304, 344, 810, 347, 814, 816, 818, 820, 822, 824, 826, 828, 830, 833, 836, 838, 840, 842, 844, 846, 847, 351, 317, 353, 317, 855, 356, 317, 317, -19, -361, 584, 1091, 1027, 992, 994, 996, 358, -19, 360, 514, 1105, 754, 590, 1111, 702, 1113, 763, 763, 763, 1114, 969, -794, -795, 703, 626, 730, 1148, 660, 668, -453, 670, 866, 672, 678, 690, 306, 868, 306, 1146, 1156, 306, 306, 872, 1158, 883, 885, 887, 1145, -520, 998, 317, 317, 684, 892, 1152, 753, 1162, 893, 381, 382, -854, 383, 384, 1176, 385, 386, 387, 388, 389, 390, 1174, 1175, 1177, 685, 1182, 391, 392, 393, 394, 395, 901, 396, 397, 398, 760, 1104, 1186, 410, 1188, 1189, 1190, 1191, 1192, 1242, 803, 804, 585, 317, 1193, 1243, 907, 393, 394, 395, 908, 396, 397, 398, 1249, 591, 1250, 909, -379, -379, 910, 1261, 1262, 1268, 1269, 1297, 592, 405, 1313, 1322, 686, 1110, 911, 988, 707, 1327, 912, 1328, 831, 834, 533, 1319, 1210, 593, 1222, 1317, 1143, 848, 1201, 997, 760, 0, 381, 382, 914, 383, 384, 1108, 385, 386, 387, 388, 389, 390, 802, 0, 0, -43, 699, 391, 392, 393, 394, 395, 0, 396, 397, 398, 0, 0, 0, 0, -379, 0, 0, 0, 925, 0, 20, 0, -361, 594, 0, 586, 0, 0, 962, -79, 547, 28, 29, 30, 31, 0, -361, 0, 548, 460, 0, 931, 0, 0, 39, 0, 933, 0, -82, 549, 0, 0, 1126, 0, 0, 0, 0, 550, 0, 944, 0, -361, 0, -558, 0, 1137, -453, 412, 0, 1140, 0, 411, -361, 760, -558, -558, -558, -558, 0, 1144, 1028, 0, 50, 0, 762, 0, 1216, -558, 0, 1217, 0, -453, 0, 595, 849, 1147, 952, 417, 953, 53, 954, 534, -453, 316, 1227, 316, 0, 0, 0, 0, 0, 596, 957, 958, 959, 0, 535, 0, 960, 0, 0, 597, 0, 598, 0, -558, 0, 0, 0, 418, 0, 304, -361, 304, 0, -379, -361, 0, -361, 970, 763, -361, 599, -361, 762, 1122, 1123, 1124, 1125, -379, 0, 0, 1170, 973, 0, 974, 976, 0, 978, 317, 71, 317, -453, 0, 0, 1138, -453, 0, -453, 0, 0, -453, 0, 452, -379, 760, 316, 0, 0, 0, 1173, 0, 1196, 477, 1198, -379, 1293, 316, 0, 0, 0, 600, 601, 0, 0, 0, 0, 0, 700, 0, 763, 0, 0, 306, 304, 306, 0, -578, 0, 0, 421, 478, 0, 317, -79, 304, 0, 0, -578, -578, -578, -578, 0, 701, 0, 0, 454, 0, -79, 0, 455, -578, 317, -82, -43, 762, 422, 0, 0, 0, 1095, 0, 0, 317, 423, -379, -379, -82, 0, -379, 456, -379, 0, -79, -379, 424, -379, 956, 97, 479, 0, 425, 0, 0, -79, 0, 0, 0, 0, -578, 317, 451, -82, 426, -379, -379, 306, 1099, 0, -379, 329, 0, 0, -82, 0, 1107, 104, 306, 0, 0, 0, 763, 0, 0, 702, 1224, 0, 654, -43, 0, 0, -558, 1116, 703, 0, 0, 975, 1117, 1118, 1119, 0, 413, 1165, 0, 306, 0, 1120, 0, 1121, 0, 452, 1166, 0, 0, -79, 0, 0, 0, -79, 0, -79, 873, 955, -79, 0, -79, 453, 762, 760, 0, 0, 0, 0, -82, 0, 0, 0, -82, 458, -82, -486, -486, -82, 0, -82, 0, 20, 459, 1179, 0, 0, 0, 1184, 0, 874, 1202, 0, 28, 29, 30, 31, 0, 417, 454, 0, 0, 1151, 455, 0, 0, 39, 316, 0, 0, 0, 0, 0, 775, 875, 20, 0, 0, 0, 763, 316, 0, 0, 456, 316, 0, 28, 29, 30, 31, 418, 474, 457, 1164, 316, 304, 0, 0, 611, 39, 640, 653, 1244, 0, 50, 0, 776, 0, 304, 0, 316, 0, 304, 0, 0, -550, 0, 0, 0, 0, 0, 53, 304, 317, 0, 0, -550, -550, -550, -550, 777, -578, 778, 0, 0, 0, 317, 50, 304, -550, 317, 0, 0, 1172, 0, 0, 316, 0, 1318, 0, 317, 0, 0, 0, 53, 316, 0, 0, 1178, 1278, 0, 0, 1183, 0, 0, 0, 317, 306, 1185, 1245, 0, 421, 0, 1187, 304, 0, 1279, -550, 0, 1300, 306, 458, 71, 304, 306, 0, 762, 876, 877, 878, 459, 1194, 0, 0, 306, 316, 0, 422, 0, 1199, 0, 0, 317, 1200, 786, 423, 1204, 0, 0, 0, 306, 317, 0, 0, 1207, 71, 424, 0, 1208, 879, 880, -486, 425, 304, 1280, 0, 0, 0, 0, 0, 0, -480, 337, 0, 426, -486, 1212, 1213, 1214, 1215, 0, 881, 763, 779, 780, 781, 306, 0, 1218, 1219, 1220, 317, 1221, 0, 0, 306, 1223, 1298, 0, 0, -486, 0, 0, 0, 0, -54, 416, 1232, 1233, 1234, 0, -486, 0, 836, 0, 782, 783, 784, 0, 0, 97, 0, 0, 0, 417, -118, 487, 0, 1247, 1248, 0, 0, 0, 1252, 1281, 306, 1254, 785, 1256, 0, 1258, 0, 1260, 0, 417, 1263, 1264, 1265, 104, 1180, 316, 317, 1282, 97, 0, 0, 418, 0, 0, 0, 0, 0, 1283, 0, 1284, 1272, 0, 0, 0, 0, 787, -486, 399, 0, 0, -486, 418, -486, 304, 0, -486, 104, -486, 1285, 0, 0, 0, 0, 0, 419, 420, 0, 0, -550, 0, 1203, 0, 0, 1299, 0, 1301, 0, 0, 1302, 1303, 1304, 317, 1306, 0, 1307, 0, 1309, 0, 1310, 0, 1311, 1312, 317, 0, 0, 0, 317, 317, 0, 0, 1315, 0, 0, 0, 0, 0, 0, 1286, 1287, 0, 0, 0, 421, 0, 0, 0, 429, 0, 437, 0, 316, 0, 0, 444, 0, 306, -74, 415, 0, 1323, 0, -480, 421, 0, 1325, 0, 1236, 1326, 422, 1237, 1238, 1239, 488, 0, 493, -480, 423, 0, 304, 498, 0, 0, 0, 0, 0, 0, 0, 424, 422, 519, 0, 0, 527, 425, 0, -54, 423, 0, 0, 0, -480, 543, 545, 0, 475, 426, 317, 424, 557, -54, 0, -480, 0, 425, 0, -118, 0, 0, 0, 0, 577, 579, 581, 583, 0, 426, 0, 0, -562, -118, 0, 607, 0, 0, -54, 0, 0, 0, 0, -562, -562, -562, -562, 0, 20, -54, 0, 633, 0, 637, 306, 0, -562, 647, -118, 28, 29, 30, 31, 0, 0, 0, 0, 317, 1235, -118, 0, 0, 39, 0, 0, -480, 0, 0, 0, -480, 338, -480, 0, 0, -480, 0, -480, 0, 680, 0, 0, 0, 20, 0, -562, 0, 0, 688, 0, 0, 0, 0, 0, 28, 29, 30, 31, 0, 0, -54, 50, 1321, 0, -54, 0, -54, 39, 0, -54, 0, -54, 0, 0, 722, 0, 0, 0, 0, 0, -118, 0, 736, 739, -118, 0, -118, 755, 0, -118, 0, -118, 0, 0, 0, 0, -74, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 788, 0, 0, -74, 0, 0, 0, 0, -671, 330, 0, 0, 0, 799, 0, 53, -671, -671, -671, -671, -671, 0, -671, -671, 0, -671, -671, 0, -74, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -74, -671, 0, -671, 0, -671, -671, -671, -671, 0, 0, -671, -671, -671, -671, -671, -671, -671, -671, -671, 0, 0, 0, -671, -671, 0, 0, -671, -671, -671, -671, -671, 0, -671, 0, -671, 0, 71, 0, 0, 0, 0, 0, 0, -671, -671, -671, 0, 0, 860, 480, 391, 392, 393, 394, 395, -671, 396, 397, 398, -74, 0, 0, 0, -74, -671, -74, 0, -671, -74, 0, -74, 0, -671, -579, -562, -671, -671, 481, 0, 0, 0, 0, 0, 0, -579, -579, -579, -579, 0, 0, 97, 0, -671, -671, -671, -671, -671, -579, 0, -671, 0, -671, 0, 0, 0, 0, -671, 0, 0, -671, -671, 0, -671, -671, 0, -671, 0, 0, 0, -671, -671, 0, 0, 0, 0, 482, 0, 0, 0, 0, 0, -671, 0, -671, 97, -579, 0, 0, -671, -213, 648, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -671, -671, -671, 0, -671, 445, -671, -671, -671, 104, 0, -671, 0, 0, -671, -671, -671, -671, 0, -671, 0, 0, -671, 0, 0, 0, 0, -671, 0, -883, 0, -671, -671, 0, 0, 0, -671, 0, 0, -671, -883, -883, -883, -883, 0, -671, -671, 0, -671, 920, -671, -671, 0, -883, -671, 0, 0, 0, 0, 0, -671, -671, 0, 0, 0, 446, 0, 0, -671, 0, 0, 0, 0, 0, -671, -671, -671, 0, -671, 0, 0, 0, -671, 0, 0, 0, 932, 0, -671, -671, 0, -883, -671, 0, 0, 0, 0, 0, 0, -671, 942, 0, 943, 0, -671, -671, -671, 0, -671, 331, -671, -671, -671, 0, -671, -671, -671, -671, 0, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, -671, 0, -671, -671, -671, -671, -671, 0, 0, 0, 0, 0, 0, 0, 0, -579, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -672, 335, 649, 0, 964, 966, 0, 968, -672, -672, -672, -672, -672, 0, -672, -672, -213, -672, -672, 0, 0, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, 0, -672, 575, -672, 0, -672, -672, -672, -672, 0, -213, -672, -672, -672, -672, -672, -672, -672, -672, -672, 417, -213, 0, -672, -672, 0, 0, -672, -672, -672, -672, -672, 0, -672, 0, -672, 0, 0, 0, 0, 0, 443, 0, 0, -672, -672, -672, 0, 0, 447, 0, 0, 418, 0, 0, 0, -672, 0, 0, 417, 0, 0, 0, 0, 0, -672, 0, -883, -672, 0, 0, 0, 0, -672, 0, 0, -672, -672, 0, 0, 0, -213, 0, 0, 448, -213, 0, -213, 0, 0, -213, 418, -213, -672, -672, -672, -672, -672, 0, 0, -672, 0, -672, 0, 0, 0, 0, -672, 0, 0, -672, -672, 0, -672, -672, 0, -672, 0, 0, 0, -672, -672, -91, 699, 0, 0, 0, 0, 0, 0, 0, 0, -672, 421, -672, 0, 0, 0, 0, -672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -672, -672, -672, 0, -672, 422, -672, -672, -672, 0, 0, -672, 0, 423, -672, -672, -672, -672, 421, -672, 0, 576, -672, 0, 424, 0, 0, -672, 0, 0, 425, -672, -672, 0, 0, 0, -672, 1139, 0, -672, 0, 0, 426, 0, 422, -672, -672, 0, -672, 0, -672, -672, 423, 0, -672, 0, 0, 0, 0, 0, -672, -672, 0, 424, 0, 0, 0, 0, -672, 425, 0, 0, 0, 0, -672, -672, -672, 0, -672, 0, 0, 426, -672, 0, 0, 0, 0, 0, -672, -672, 0, 0, -672, 0, 0, 0, 0, 0, 0, -672, 0, 0, 0, 0, -672, -672, -672, 0, -672, 336, -672, -672, -672, 0, -672, -672, -672, -672, 0, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, -672, 0, -672, -672, -672, -672, -672, 0, -518, 345, -91, 0, 0, 0, 0, 0, -518, -518, -518, -518, -518, 0, -518, -518, 700, -518, -518, 0, 0, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, 0, -518, 0, -518, 0, -518, -518, -518, -518, 0, 701, -518, -518, -518, -518, -518, -518, -518, -518, -518, 0, -91, 0, -518, -518, 0, 0, -518, -518, -518, -518, -518, 0, -518, 0, -518, 0, 0, 0, 0, 0, 497, 0, 0, -518, -518, -518, 0, 0, 0, 0, 0, 0, 0, 0, 0, -518, 0, 0, 417, 0, 0, 0, 0, 0, -518, 0, 0, -518, 0, 0, 0, 0, -518, 0, 0, -518, -518, 0, 0, 0, -91, 0, 0, 0, -91, 0, -91, 0, 0, 703, 418, -91, -518, -518, -518, -518, -518, 0, 0, -518, 0, -518, 0, 0, 0, 0, -518, 0, 476, -518, -518, 0, -518, -518, 0, -518, 20, 0, 0, -518, -518, 0, 0, 0, 0, 0, 0, 28, 29, 30, 31, -518, -564, -518, 0, 0, 0, 0, -518, 0, 39, 0, 0, -564, -564, -564, -564, 0, 0, 0, 0, 0, 0, -518, -518, -518, -564, -518, 0, -518, -518, -518, 0, 0, -518, 0, 0, -518, -518, -518, -518, 421, -518, 0, 0, -518, 0, 0, 50, 0, -518, 0, 0, 0, -518, -518, 0, 0, 0, -518, 0, 0, -518, 0, -564, 53, 0, 422, -518, -518, 0, -518, 0, -518, -518, 423, 1314, -518, 0, 0, 0, 0, 0, -518, -518, 0, 424, 0, 0, 0, 0, -518, 425, 0, 0, 0, 0, -518, -518, -518, 0, -518, 0, 0, 426, -518, 0, 0, 0, 0, 0, -518, -518, 0, 0, -518, 0, 0, 0, 0, 0, 0, -518, 0, 0, 0, 71, -518, -518, -518, 0, -518, -518, -518, 0, -518, 0, -518, -518, -518, -518, 0, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, -518, 0, -518, -518, -518, -518, -518, -233, 509, 0, 0, 0, 0, 0, 0, 7, 8, 9, 10, 11, 0, 12, 13, 0, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 97, 0, 0, 517, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, -564, 0, 0, 0, 0, 0, 0, 604, 47, 48, 49, -570, 104, 0, 0, 0, 0, 419, 420, 0, 50, 0, -570, -570, -570, -570, 0, 0, 0, 51, 0, -805, 52, 0, 0, -570, 0, 53, 0, 0, 54, 55, -805, -805, -805, -805, 0, 0, 0, 0, 0, 0, 1320, 0, 518, -805, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 417, -570, 0, 64, 20, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 28, 29, 30, 31, 0, 0, -805, 0, 0, 70, 0, 71, 0, 39, 0, 0, 72, 418, -103, 699, 510, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -233, 0, 73, 0, 74, 612, 75, 76, 77, 0, 0, 78, 0, 0, -233, 79, 80, 81, 0, 82, 50, 0, 83, 0, 0, 0, 0, 0, 0, -798, 0, 84, 85, 0, 0, 0, 0, 53, 0, -233, -798, -798, -798, -798, 0, 86, 87, 0, 88, 0, -233, 89, 0, -798, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 421, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, -798, 100, 422, 0, 0, 0, 0, 71, 101, 0, 423, -49, 699, 102, 103, 104, 0, 0, 0, -233, 0, 424, 0, -233, 105, -233, 106, 425, -233, 0, -233, 0, 0, 0, 0, 0, 107, 0, 0, 426, -570, -20, 608, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, -805, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 526, 27, 0, 28, 29, 30, 31, -103, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 417, 0, 0, 700, 41, 0, 0, 42, 43, 44, 0, 45, 0, 97, 0, 46, 0, 0, 0, 0, 0, 0, 0, 615, 47, 48, 49, 0, 701, 0, 0, 0, 418, 0, 0, 0, 50, 0, 0, -103, 0, 104, 0, 0, 0, 51, 0, -568, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, -568, -568, -568, -568, 0, 0, 0, 0, 0, 0, 0, 0, 542, -568, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, -798, 0, 0, 63, 417, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, -103, 68, 69, 0, -103, -49, -103, 0, 0, 703, -568, -103, 0, 70, 421, 71, 0, 0, 0, 700, 72, 418, -447, 699, 609, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -20, 0, 73, 0, 74, 422, 75, 76, 77, 701, 0, 78, 0, 423, -20, 79, 80, 81, 0, 82, -49, 0, 83, 0, 424, 0, 0, 0, 0, 0, 425, 84, 85, 0, 0, 0, 0, 0, 0, -20, 0, 0, 426, 0, 0, 86, 87, 610, 88, 0, -20, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 421, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, -49, 0, 98, 0, -49, 0, -49, 0, 99, -49, 0, -49, 100, 422, 0, 0, 0, 0, 0, 101, 0, 423, -451, 699, 102, 103, 104, 0, 0, 0, -20, 0, 424, 0, -20, 105, -20, 106, 425, -20, 0, -20, 0, 0, 0, 0, 0, 107, 0, 0, 426, 0, -231, 652, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, -568, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, -447, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 700, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 650, 47, 48, 49, 0, 701, 0, 0, 0, 0, 419, 420, 0, 50, 0, 0, -447, 0, 0, 0, 0, 0, 51, 0, -552, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, -552, -552, -552, -552, 0, 0, 0, 0, 0, 0, 0, 0, 544, -552, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 417, 651, 0, 64, 0, 65, 66, 0, 67, 0, 0, -447, 68, 69, 0, -447, -451, -447, 0, 0, 703, -552, -447, 0, 70, -553, 71, 0, 0, 0, 700, 72, 418, -449, 699, 0, -553, -553, -553, -553, 0, 0, 0, 0, 0, 655, -231, 0, 73, -553, 74, 0, 75, 76, 77, 701, 0, 78, 0, 0, -231, 79, 80, 81, 0, 82, -451, 0, 83, -554, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, -554, -554, -554, -554, 0, -231, 0, -553, 0, 0, 0, 86, 87, -554, 88, 0, -231, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 421, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, -451, 0, 98, 0, -451, -554, -451, 0, 99, 703, 0, -451, 100, 422, 0, 0, 0, 0, 0, 101, 0, 423, -426, 699, 102, 103, 104, 0, 0, 0, -231, 0, 424, 0, -231, 105, -231, 106, 425, -231, 0, -231, 0, 0, 0, 0, 0, 107, 0, 0, 426, 0, -109, 494, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 495, 12, 13, 113, 14, 15, -552, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, -449, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 700, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 661, 47, 48, 49, 0, 701, 0, 0, 0, 0, 0, -553, 0, 50, 0, 0, -449, 0, 0, 0, 0, 0, 51, 0, -572, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, -572, -572, -572, -572, 0, 0, 0, 0, 0, 0, 0, 0, 578, -572, 0, 56, 57, 58, 59, 60, -554, 0, 61, 0, 62, 0, 0, 0, 0, 63, 417, 662, 0, 64, 0, 65, 66, 0, 67, 0, 0, -449, 68, 69, 0, -449, -426, -449, 0, 0, 703, -572, -449, 0, 70, -574, 71, 0, 0, 0, 700, 72, 418, -428, 699, 0, -574, -574, -574, -574, 0, 0, 0, 0, 0, 663, -109, 0, 73, -574, 74, 0, 75, 76, 77, 701, 0, 78, 0, 0, -109, 79, 80, 81, 0, 82, -426, 0, 83, -576, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, -576, -576, -576, -576, 0, -109, 0, -574, 0, 0, 0, 86, 87, -576, 88, 0, -109, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 421, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, -426, 0, 98, 0, -426, -576, -426, 0, 99, 703, 0, -426, 100, 422, 0, 0, 0, 0, 0, 101, 0, 423, -799, 699, 102, 103, 104, 0, 0, 0, -109, 0, 424, 0, -109, 105, -109, 106, 425, -109, 0, -109, 0, 0, 0, 0, 0, 107, 0, 0, 426, 0, -110, 562, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 563, 12, 13, 113, 14, 15, -572, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, -428, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 700, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 666, 47, 48, 49, 0, 701, 0, 0, 0, 0, 0, -574, 0, 50, 0, 0, -428, 0, 0, 0, 0, 0, 51, 0, -566, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, -566, -566, -566, -566, 0, 0, 0, 0, 0, 0, 0, 0, 580, -566, 0, 56, 57, 58, 59, 60, -576, 0, 61, 0, 62, 0, 0, 0, 0, 63, 417, 676, 0, 64, 0, 65, 66, 0, 67, 0, 0, -428, 68, 69, 0, -428, -799, -428, 0, 0, 703, -566, -428, 0, 70, -560, 71, 0, 0, 0, 700, 72, 418, -46, 699, 0, -560, -560, -560, -560, 0, 0, 0, 0, 0, 765, -110, 0, 73, -560, 74, 0, 75, 76, 77, 701, 0, 78, 0, 0, -110, 79, 80, 81, 0, 82, -799, 0, 83, 20, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 28, 29, 30, 31, 0, -110, 0, -560, 0, 0, 0, 86, 87, 39, 88, 0, -110, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 421, 93, 0, 0, 0, 582, 0, 94, 95, 96, 0, 97, 0, -799, 0, 98, 0, -799, 50, -799, 0, 99, 703, 417, -799, 100, 422, 0, 0, 0, 0, 0, 101, 0, 423, 0, 0, 102, 103, 104, 0, 0, 0, -110, 0, 424, 0, -110, 105, -110, 106, 425, -110, 0, -110, 418, 0, 0, 0, 0, 107, 0, 0, 426, 0, -113, 568, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 569, 12, 13, 113, 14, 15, -566, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 606, 27, 0, 28, 29, 30, 31, -46, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 417, 0, 0, 700, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 421, 0, 0, 0, 47, 48, 49, 0, 701, 0, 0, 0, 418, 0, -560, 0, 50, 0, 0, -46, 0, 0, 0, 0, 0, 51, 422, 0, 52, 0, 0, 0, 1278, 53, 423, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 424, 0, 0, 0, 1279, 636, 425, 0, 56, 57, 58, 59, 60, 97, 0, 61, 0, 62, 426, 0, 0, 0, 63, 417, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, -46, 68, 69, 0, -46, 0, -46, 0, 0, -46, 0, -46, 0, 70, 421, 71, 0, 0, 1280, 0, 72, 418, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -113, 0, 73, 0, 74, 422, 75, 76, 77, 0, 0, 78, 0, 423, -113, 79, 80, 81, 0, 82, 0, 0, 83, 0, 424, 0, 0, 0, 0, 0, 425, 84, 85, 0, 0, 0, 0, 0, 0, -113, 0, 0, 426, 0, 0, 86, 87, 0, 88, 0, -113, 89, 0, 1281, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 421, 93, 0, 0, 1282, 0, 646, 94, 95, 96, 0, 97, 0, 0, 1283, 98, 1284, 0, 0, 0, 0, 99, 0, 0, 417, 100, 422, 0, 0, 0, 0, 0, 101, 0, 423, 1285, 0, 102, 103, 104, 0, 0, 0, -113, 0, 424, 0, -113, 105, -113, 106, 425, -113, 0, -113, 0, 418, 0, 0, 0, 107, 0, 0, 426, 0, -23, 638, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 1286, 1287, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 679, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 417, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 421, 0, 499, 47, 48, 49, 0, 0, 0, 0, 0, 418, 0, 500, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 422, 52, 0, 0, 501, 0, 53, 0, 423, 54, 55, 28, 29, 30, 31, 0, 0, 0, 0, 424, 0, 0, 0, 687, 39, 425, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 502, 426, 0, 0, 63, 417, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 421, 71, 0, 0, 0, 0, 72, 418, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -23, 0, 73, 0, 74, 422, 75, 76, 77, 0, 0, 78, 0, 423, -23, 79, 80, 81, 0, 82, 0, 0, 83, 0, 424, 0, 0, 0, 0, 503, 425, 84, 85, 0, 0, 0, 0, 0, 0, -23, 0, 0, 426, 0, 0, 86, 87, 639, 88, 0, -23, 89, 0, 504, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 505, 0, 421, 93, 0, 0, 0, 859, 963, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 417, 417, 100, 422, 0, 0, 0, 0, 0, 101, 506, 423, 0, 0, 102, 103, 104, 0, 0, 507, -23, 0, 424, 0, -23, 105, -23, 106, 425, -23, 0, -23, 418, 418, 0, 0, 0, 107, 0, 0, 426, 0, -164, 449, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 965, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 417, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 421, 421, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 418, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 422, 422, 52, 0, 0, 0, 0, 53, 423, 423, 54, 55, 0, 0, 967, 0, 0, 0, 0, 424, 424, 0, 0, 0, 0, 425, 425, 56, 57, 58, 59, 60, 417, 0, 61, 0, 62, 426, 426, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 418, 0, 70, 421, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -164, 0, 73, 0, 74, 422, 75, 76, 77, 0, 0, 78, 0, 423, -164, 79, 80, 81, 0, 82, 0, 0, 83, 0, 424, 0, 0, 0, 0, 0, 425, 84, 85, 0, 0, 0, 0, 0, 0, -164, 0, 0, 426, 0, 0, 86, 87, 0, 88, 0, -164, 89, 0, 0, 90, 0, 0, 421, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 422, 0, 0, 0, 0, 99, 0, 0, 423, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 424, 0, 102, 103, 104, 0, 425, 0, -164, 0, 0, 0, -164, 105, -164, 106, 0, -164, 426, -164, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, -85, 485, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 384, 46, 385, 386, 387, 388, 389, 390, 0, 0, 47, 48, 49, 391, 392, 393, 394, 395, 0, 396, 397, 398, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -85, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, -85, 79, 80, 81, 382, 82, 383, 384, 83, 385, 386, 387, 388, 389, 390, 0, 0, 84, 85, 0, 391, 392, 393, 394, 395, -85, 396, 397, 398, 0, 0, 86, 87, 0, 88, 0, -85, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, -85, 0, 0, 0, -85, 105, -85, 106, 0, -85, 0, -85, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, -182, 489, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 385, 386, 387, 388, 389, 390, 0, 0, 47, 48, 49, 391, 392, 393, 394, 395, 0, 396, 397, 398, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -182, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, -182, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, -182, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, -182, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, -182, 0, 0, 0, -182, 105, -182, 106, 0, -182, 0, -182, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, -146, 560, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -146, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, -146, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, -146, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, -146, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, -146, 0, 0, 0, -146, 105, -146, 106, 0, -146, 0, -146, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, -76, 573, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -76, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, -76, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, -76, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, -76, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, -76, 0, 0, 0, -76, 105, -76, 106, 0, -76, 0, -76, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, -167, 657, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -167, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, -167, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, -167, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, -167, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, -167, 0, 0, 0, -167, 105, -167, 106, 0, -167, 0, -167, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, -35, 704, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -35, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, -35, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, -35, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, -35, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, -35, 0, 0, 0, -35, 105, -35, 106, 0, -35, 0, -35, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, -20, 608, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -20, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, -20, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, -20, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, -20, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, -20, 0, 0, 0, -20, 105, -20, 106, 0, -20, 0, -20, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, -23, 638, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -23, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, -23, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, -23, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, -23, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, -23, 0, 0, 0, -23, 105, -23, 106, 0, -23, 0, -23, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 0, 108, 109, 110, 111, 112, 0, 0, 0, 732, 0, 0, 0, -417, 0, 113, 7, 8, 9, 10, 11, 0, 12, 13, 0, 14, 15, 417, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -417, 26, -417, 27, 0, 28, 29, 30, 31, -417, -417, 32, 33, 34, 35, 36, 37, 38, 39, 40, 418, -417, 0, 0, 41, -417, -417, 42, 43, 44, 0, 45, -417, 0, -417, 46, -417, -417, -417, -417, -417, -417, -417, -417, 47, 48, 49, -417, -417, -417, -417, -417, -417, 0, 0, 0, 50, -417, -417, -417, -417, -417, -417, -417, -417, 51, 0, -417, 52, -417, 0, -417, -417, 53, -417, -417, 54, 55, -417, -417, -417, -417, -417, 0, -417, -417, -417, 0, -417, 0, -417, -417, -417, 56, 57, 58, 59, 60, -417, -417, 61, 421, 62, 0, -417, -417, 0, 63, -417, -417, 0, 64, 0, 65, 66, -417, 67, -417, -417, -417, 68, 69, -417, -417, -417, -417, -417, 422, -417, -417, -417, -417, 70, -417, 71, 423, -417, -417, -417, 72, 0, 0, 0, 0, -417, -417, 424, -417, -417, -417, 0, -417, 425, -417, -417, -417, 73, 0, 74, -417, 75, 76, 77, 0, 426, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, -417, -417, 0, -417, -417, -417, 84, 85, -417, -417, -417, 0, -417, -417, 0, 0, -417, -417, 0, -417, 86, 87, 0, 88, 0, 0, 89, -417, -417, 90, -417, -417, -417, -417, -417, 91, 92, -417, -417, -417, -417, -417, 0, 93, -417, -417, -417, -417, -417, 94, 95, 96, -417, 97, -417, -417, -417, 98, -417, -417, -417, -417, -417, 99, 0, 0, 0, 100, -417, -417, -417, -417, -417, -417, 101, 0, 0, 0, -417, 102, 103, 104, 0, 0, 0, 0, 0, 0, 733, 0, 105, 0, 106, 0, 0, 0, -417, 0, -417, 0, 0, 0, 107, 0, 0, 0, 0, 0, 0, 108, 109, 110, 111, 112, 0, 0, 0, 863, 0, 0, 0, -404, 0, 113, -404, -404, -404, -404, -404, 0, -404, -404, 0, -404, -404, -404, 0, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, 0, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, 0, 0, -404, -404, -404, -404, -404, -404, 0, -404, -404, 0, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, 0, 0, 0, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, 0, -404, -404, -404, 0, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, 0, -404, -404, -404, 0, -404, 0, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, 0, -404, -404, 0, -404, -404, -404, 0, -404, 0, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, 0, 0, 0, 0, -404, -404, -404, -404, -404, -404, 0, -404, -404, -404, -404, -404, -404, 0, -404, -404, -404, -404, -404, 0, -404, -404, 0, 0, 0, -404, -404, -404, 0, -404, 0, 0, -404, 0, 0, -404, -404, 0, -404, -404, -404, -404, -404, -404, -404, -404, 0, -404, -404, 0, 0, -404, -404, 0, -404, -404, -404, 0, -404, 0, 0, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, 0, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, -404, 0, 0, 0, -404, -404, -404, -404, -404, -404, -404, -404, 0, 0, 0, -404, -404, -404, -404, 0, 0, 0, 0, 0, 0, -404, 0, -404, 0, -404, 0, 0, 0, -404, 0, -404, 0, 0, 0, -404, 0, 0, 0, 0, 0, 743, -404, -404, -404, -404, -404, 0, 7, 8, 9, 10, 11, 0, 12, 13, -404, 14, 15, 417, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 418, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 744, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 421, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 745, 67, 0, 0, 746, 68, 69, 0, 0, 0, 0, 0, 422, 0, 0, 0, 0, 70, 0, 71, 423, 0, 0, 0, 72, 0, 0, 0, 0, 747, 0, 424, 0, 0, 0, 0, 0, 425, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 426, 748, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 749, 750, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 751, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 864, 108, 109, 110, 111, 112, 0, -301, -301, -301, -301, -301, 0, -301, -301, 113, -301, -301, -301, 0, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, 0, -301, 0, -301, 0, -301, -301, -301, -301, 0, 0, -301, -301, -301, -301, -301, -301, -301, -301, -301, -301, 0, 0, 0, -301, 0, 0, -301, -301, -301, 0, -301, 0, 0, 0, -301, 0, 0, 0, 0, 0, 0, 0, 0, -301, -301, -301, 0, 0, 0, 0, 0, 0, 0, 0, 0, -301, 0, 0, 0, 0, 0, 0, 0, 0, -301, 0, 0, -301, 0, 0, 0, 0, -301, 0, 0, -301, -301, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -301, -301, -301, -301, -301, 0, 0, -301, -301, -301, 0, 0, 0, 0, -301, 0, 0, 0, -301, 0, -301, -301, 0, -301, 0, 0, 0, -301, -301, 0, 0, 0, 0, 0, -301, 0, 0, 0, 0, -301, 0, -301, -301, 0, 0, 0, -301, 0, 0, 0, 0, 0, 0, -301, 0, 0, 0, 0, 0, -301, 0, 0, 0, -301, 0, -301, 0, -301, -301, -301, 0, -301, -301, 0, 0, 0, -301, -301, -301, 0, -301, 0, 0, -301, 0, 0, 0, 0, 0, 0, 0, 0, -301, -301, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -301, -301, 0, -301, 0, 0, -301, 0, 0, -301, 0, 0, 0, 0, 0, -301, -301, 0, 0, 0, 0, 0, 0, -301, 0, 0, 0, 0, 0, -301, -301, -301, 0, -301, 0, 0, 0, -301, 0, 0, 0, 0, 0, -301, 0, 0, 0, -301, 0, 0, 0, 0, 0, 0, -301, 0, 0, 0, 0, -301, -301, -301, 0, 0, 0, 0, 0, 0, -301, 0, -301, 0, -301, 0, 0, 0, 0, 0, 0, 0, 0, 0, -301, 0, 0, 0, 0, 0, 991, -301, -301, -301, -301, -301, 0, 7, 8, 9, 10, 11, 0, 12, 13, -301, 14, 15, 417, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 418, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 421, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 422, 0, 0, 0, 0, 70, 0, 71, 423, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 424, 0, 0, 0, 0, 0, 425, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 426, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 993, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 417, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 418, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 421, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 422, 0, 0, 0, 0, 70, 0, 71, 423, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 424, 0, 0, 0, 0, 0, 425, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 426, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 995, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 417, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 418, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 421, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 422, 0, 0, 0, 0, 70, 0, 71, 423, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 424, 0, 0, 0, 0, 0, 425, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 426, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 1109, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 417, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 418, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 421, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 422, 0, 0, 0, 0, 70, 0, 71, 423, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 424, 0, 0, 0, 0, 0, 425, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 426, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 1195, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 417, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 418, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 421, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 422, 0, 0, 0, 0, 70, 0, 71, 423, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 424, 0, 0, 0, 0, 0, 425, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 426, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 1197, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 417, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 418, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 421, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 422, 0, 0, 0, 0, 70, 0, 71, 423, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 424, 0, 0, 0, 0, 0, 425, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 426, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 851, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 852, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 853, 854, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 328, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 1101, 1102, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 539, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 540, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 712, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 713, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 328, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 361, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 363, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 365, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 366, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 368, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 369, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 371, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 372, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 378, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 515, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 520, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 521, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 523, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 524, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 528, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 530, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 537, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 551, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 553, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 566, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 571, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 619, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 621, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 634, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 674, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 681, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 691, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 693, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 809, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 811, 108, 109, 110, 111, 112, 0, -785, -785, -785, -785, -785, 0, -785, -785, 113, -785, -785, 0, 0, -785, -785, -785, -785, -785, -785, -785, -785, -785, -785, 0, -785, 0, -785, 0, -785, -785, -785, -785, 0, 0, -785, -785, -785, -785, -785, -785, -785, -785, -785, 0, 0, 0, 0, -785, 0, 0, -785, -785, -785, 0, -785, 0, 0, 0, -785, 0, 0, 0, 0, 0, 0, 0, 0, -785, -785, -785, 0, 0, 0, 0, 0, 0, 0, 0, 0, -785, 0, 0, 0, 0, 0, 0, 0, 0, -785, 0, 0, -785, 0, 0, 0, 0, -785, 0, 0, -785, -785, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -785, -785, -785, -785, -785, 0, 0, -785, 0, -785, 0, 0, 0, 0, -785, 0, 0, 0, -785, 0, -785, -785, 0, -785, 0, 0, 0, -785, -785, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -785, 0, -785, 0, 0, 0, 0, -785, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -785, 0, -785, 0, -785, -785, -785, 0, 0, -785, 0, 0, 0, -785, -785, -785, 0, -785, 0, 0, -785, 0, 0, 0, 0, 0, 0, 0, 0, -785, -785, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -785, -785, 0, -785, 0, 0, -785, 0, 0, -785, 0, 0, 0, 0, 0, -785, -785, 0, 0, 0, 0, 0, 0, -785, 0, 0, 0, 0, 0, -785, -785, -785, 0, -785, 0, 0, 0, -785, 0, 0, 0, 0, 0, -785, 0, 0, 0, -785, 0, 0, 0, 0, 0, 0, -785, 0, 0, 0, 0, -785, -785, -785, 0, 0, 0, 0, 0, 0, 0, 0, -785, 0, -785, 0, 0, 0, 0, 0, 0, 0, 0, 0, -785, 0, 0, 0, 0, 0, 813, -785, -785, -785, -785, -785, 0, 7, 8, 9, 10, 11, 0, 12, 13, -785, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 815, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 817, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 819, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 821, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 823, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 825, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 827, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 829, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 832, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 835, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 837, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 839, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 841, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 843, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 845, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 882, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 884, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 886, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 889, 108, 109, 110, 111, 112, 0, -241, -241, -241, -241, -241, 0, -241, -241, 113, -241, -241, 0, 0, -241, -241, -241, -241, -241, -241, -241, -241, -241, -241, 0, -241, 0, -241, 0, -241, -241, -241, -241, 0, 0, -241, -241, -241, -241, -241, -241, -241, -241, -241, 0, 0, 0, 0, -241, 0, 0, -241, -241, -241, 0, -241, 0, 0, 0, -241, 0, 0, 0, 0, 0, 0, 0, 0, -241, -241, -241, 0, 0, 0, 0, 0, 0, 0, 0, 0, -241, 0, 0, 0, 0, 0, 0, 0, 0, -241, 0, 0, -241, 0, 0, 0, 0, -241, 0, 0, -241, -241, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -241, -241, -241, -241, -241, 0, 0, -241, 0, -241, 0, 0, 0, 0, -241, 0, 0, 0, -241, 0, -241, -241, 0, -241, 0, 0, 0, -241, -241, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -241, 0, -241, 0, 0, 0, 0, -241, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -241, 0, -241, 0, -241, -241, -241, 0, 0, -241, 0, 0, 0, -241, -241, -241, 0, -241, 0, 0, -241, 0, 0, 0, 0, 0, 0, 0, 0, -241, -241, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -241, -241, 0, -241, 0, 0, -241, 0, 0, -241, 0, 0, 0, 0, 0, -241, -241, 0, 0, 0, 0, 0, 0, -241, 0, 0, 0, 0, 0, -241, -241, -241, 0, -241, 0, 0, 0, -241, 0, 0, 0, 0, 0, -241, 0, 0, 0, -241, 0, 0, 0, 0, 0, 0, -241, 0, 0, 0, 0, -241, -241, -241, 0, 0, 0, 0, 0, 0, 0, 0, -241, 0, -241, 0, 0, 0, 0, 0, 0, 0, 0, 0, -241, 0, 0, 0, 0, 0, 913, -241, -241, -241, -241, -241, 0, 7, 8, 9, 10, 11, 0, 12, 13, -241, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 947, 108, 109, 110, 111, 112, 0, -238, -238, -238, -238, -238, 0, -238, -238, 113, -238, -238, 0, 0, -238, -238, -238, -238, -238, -238, -238, -238, -238, -238, 0, -238, 0, -238, 0, -238, -238, -238, -238, 0, 0, -238, -238, -238, -238, -238, -238, -238, -238, -238, 0, 0, 0, 0, -238, 0, 0, -238, -238, -238, 0, -238, 0, 0, 0, -238, 0, 0, 0, 0, 0, 0, 0, 0, -238, -238, -238, 0, 0, 0, 0, 0, 0, 0, 0, 0, -238, 0, 0, 0, 0, 0, 0, 0, 0, -238, 0, 0, -238, 0, 0, 0, 0, -238, 0, 0, -238, -238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -238, -238, -238, -238, -238, 0, 0, -238, 0, -238, 0, 0, 0, 0, -238, 0, 0, 0, -238, 0, -238, -238, 0, -238, 0, 0, 0, -238, -238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -238, 0, -238, 0, 0, 0, 0, -238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -238, 0, -238, 0, -238, -238, -238, 0, 0, -238, 0, 0, 0, -238, -238, -238, 0, -238, 0, 0, -238, 0, 0, 0, 0, 0, 0, 0, 0, -238, -238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -238, -238, 0, -238, 0, 0, -238, 0, 0, -238, 0, 0, 0, 0, 0, -238, -238, 0, 0, 0, 0, 0, 0, -238, 0, 0, 0, 0, 0, -238, -238, -238, 0, -238, 0, 0, 0, -238, 0, 0, 0, 0, 0, -238, 0, 0, 0, -238, 0, 0, 0, 0, 0, 0, -238, 0, 0, 0, 0, -238, -238, -238, 0, 0, 0, 0, 0, 0, 0, 0, -238, 0, -238, 0, 0, 0, 0, 0, 0, 0, 0, 0, -238, 0, 0, 0, 0, 0, 1106, -238, -238, -238, -238, -238, 0, 7, 8, 9, 10, 11, 0, 12, 13, -238, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 1171, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 0, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 381, 382, 0, 383, 384, 107, 385, 386, 387, 388, 389, 390, 108, 109, 110, 111, 112, 391, 392, 393, 394, 395, 0, 396, 397, 398, 113, 7, 8, 9, 10, 11, 0, 12, 13, 0, 14, 15, 417, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 418, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 421, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 422, 0, 0, 0, 0, 70, 0, 71, 423, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 424, 0, 0, 0, 0, 0, 425, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 426, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 720, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 0, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 417, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 418, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 421, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 422, 0, 0, 0, 0, 70, 0, 71, 423, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 424, 0, 0, 0, 0, 0, 425, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 426, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 737, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 0, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 417, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 418, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 421, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 422, 0, 0, 0, 0, 70, 0, 71, 423, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 424, 0, 0, 0, 0, 0, 425, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 426, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 792, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 0, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 417, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 418, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 421, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 422, 0, 0, 0, 0, 70, 0, 71, 423, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 424, 0, 0, 0, 0, 0, 425, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 426, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 949, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 0, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 417, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 418, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 421, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 422, 0, 0, 0, 0, 70, 0, 71, 423, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 424, 0, 0, 0, 0, 0, 425, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 426, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 0, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 419, 420, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 0, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 852, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 853, 854, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 0, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 399, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 0, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 849, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 0, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 0, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 0, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 0, 0, 0, 867, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 0, 0, 0, 0, 0, 99, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 102, 103, 104, 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107, 0, 0, 0, 0, 0, 0, 108, 109, 110, 111, 112, 0, 7, 8, 9, 10, 11, 0, 12, 13, 113, 14, 15, 0, 0, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 26, 0, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 52, 0, 0, 0, 0, 53, 0, 0, 54, 55, 0, -62, 435, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 57, 58, 59, 60, 417, 0, 61, 0, 62, 0, 0, 0, 0, 63, 0, 0, 0, 64, 0, 65, 66, 0, 67, 0, 0, 0, 68, 69, 0, 0, 0, 0, 0, 0, 0, 0, 418, 0, 70, 0, 71, 0, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1251, 0, 0, 0, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 419, 420, 78, 0, 0, 0, 79, 80, 81, 0, 82, 0, 0, 83, 0, 0, 0, 0, 0, 0, 0, 0, 84, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1255, 0, 0, 86, 87, 0, 88, 0, 0, 89, 0, 0, 90, 0, 0, 421, 0, 0, 871, 92, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 94, 95, 96, 0, 97, 0, 0, 0, 98, 422, 0, 0, 1257, 0, 99, 0, 0, 423, 100, 0, 0, 0, 0, 0, 0, 101, 0, 0, 424, 0, 102, 103, 104, 0, 425, 0, -62, 0, 0, 0, 0, 105, 0, 106, 0, 0, 426, 0, 0, 0, -62, 0, 0, 107, 406, 0, 0, 0, 0, 0, 108, 109, 110, 111, 112, 407, 0, 0, 0, 0, 0, 15, 0, 0, 113, -62, 0, 19, 0, 0, 22, 23, 0, 0, 1112, 26, -62, 27, 0, 28, 29, 30, 31, 0, 0, 32, 33, 34, 35, 36, 37, 38, 39, 40, 0, 0, 0, 0, 41, 0, 0, 42, 43, 44, 0, 45, 0, 381, 382, 46, 383, 384, 0, 385, 386, 387, 388, 389, 390, 48, 408, 0, 0, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 0, 0, 0, -62, 0, 0, 0, -62, 0, -62, 0, 0, -62, 0, -62, 0, 0, 54, 55, 0, 0, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 62, 396, 397, 398, 0, 0, 0, 0, 0, 64, 0, 65, 66, 0, 409, 0, 0, 0, 68, 0, 0, 0, 0, 0, 0, 381, 382, 0, 383, 384, 70, 385, 386, 387, 388, 389, 390, 72, 0, 0, 0, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 0, 0, 0, 73, 0, 74, 0, 75, 76, 77, 0, 0, 0, 0, 0, 0, 0, 0, 381, 382, 82, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 0, 0, 0, 87, 0, 88, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 0, 0, 0, 0, 0, 0, 95, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99, 0, -28, 126, 0, 0, 0, 127, 128, 0, 0, 0, 0, 0, 129, 0, 0, 0, 410, 0, 0, 0, 130, 0, 0, 0, 0, 20, 0, 0, 0, 131, 0, 132, 0, 133, 0, 0, 28, 29, 30, 31, 134, 135, 0, 0, 108, 109, 110, 111, 112, 39, 0, 0, 136, 0, 0, 0, 137, 138, 0, 0, 0, 0, 0, 139, 0, 140, 0, 141, 142, 143, 144, 145, 146, 147, 148, 0, 0, 149, 150, 151, 152, 153, 154, 155, 0, 0, 156, 50, 157, 158, 159, 160, 161, 162, 163, 164, 0, 0, 165, 166, 167, 0, 168, 169, 53, 170, 171, 0, 0, 172, 173, 174, 175, 176, 177, 178, 179, 180, 0, 181, 182, 183, 184, 185, 0, 0, 0, 0, 0, 186, 187, 0, 0, 0, 0, 188, 189, 190, 191, 192, 193, 0, 0, 194, 0, 0, 195, 196, 197, 198, 199, 0, 200, 201, 202, 203, 204, 205, 0, 206, 207, 208, 209, 0, 210, 71, 0, 211, 212, 213, 0, 0, 0, 0, 0, 214, 215, 216, 217, 218, 219, 220, 221, 0, 222, 223, 224, 0, 0, 0, 225, 0, 0, 0, 226, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 229, 230, 0, 231, 232, 233, 0, 234, 235, 236, 237, 0, 238, 239, 0, 0, 240, 241, 0, 242, 0, 0, 0, 0, 0, -28, 243, 244, 245, 0, 246, 247, 248, 249, 250, 0, 0, 251, 252, 253, 254, 255, 0, 0, 256, 257, 258, 259, 260, 0, 0, 0, 261, 262, 263, 264, 265, 0, 266, 267, 268, 269, 270, 0, 0, 0, 0, 0, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 0, 0, 104, 695, 0, 0, 282, -29, -29, 283, -28, 0, 0, 0, -29, 0, 0, 284, 0, 285, 0, 0, -29, 0, 0, 0, 0, -29, 0, 0, 0, -29, 0, -29, 0, -29, 0, 0, -29, -29, -29, -29, -29, -29, 0, 0, 0, 0, 0, 0, 0, -29, 0, 0, -29, 0, 0, 0, -29, -29, 0, 0, 0, 0, 0, -29, 0, -29, 0, -29, -29, -29, -29, -29, -29, -29, -29, 0, 0, -29, -29, -29, -29, -29, -29, -29, 0, 0, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, 0, 0, -29, -29, -29, 0, -29, -29, -29, -29, -29, 0, 0, -29, -29, -29, -29, -29, -29, -29, -29, -29, 0, -29, -29, -29, -29, -29, 0, 0, 0, 0, 0, -29, -29, 0, 0, 0, 0, -29, -29, -29, -29, -29, -29, 0, 0, -29, 0, 0, -29, -29, -29, -29, -29, 0, -29, -29, -29, -29, -29, -29, 0, -29, -29, -29, -29, 0, -29, -29, 0, -29, -29, -29, 0, 0, 0, 0, 0, -29, -29, -29, -29, -29, -29, -29, -29, 0, -29, -29, -29, 0, 0, 0, -29, 0, 0, 0, -29, -29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -29, -29, -29, 0, -29, -29, -29, 0, -29, -29, -29, -29, 0, -29, -29, 0, 0, -29, -29, 0, -29, 0, 0, 0, 0, 0, 0, -29, -29, -29, 0, -29, -29, -29, -29, -29, 0, 0, -29, -29, -29, -29, -29, 0, 0, -29, -29, -29, -29, -29, 0, 0, 0, -29, -29, -29, -29, -29, 0, -29, -29, -29, -29, -29, 0, 0, 0, 0, 0, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, -29, 0, 0, -29, 126, 0, 0, -29, 127, 128, -29, 696, 0, 0, 0, 129, 0, 0, -29, 0, -29, 0, 0, 130, 0, 0, 0, 0, 20, 0, 0, 0, 131, 0, 132, 0, 133, 0, 0, 28, 29, 30, 31, 134, 135, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 136, 0, 0, 0, 137, 138, 0, 0, 0, 0, 0, 139, 0, 140, 0, 141, 142, 143, 144, 145, 146, 147, 148, 0, 0, 149, 150, 151, 152, 153, 154, 155, 0, 0, 156, 50, 157, 158, 159, 160, 161, 162, 163, 164, 0, 0, 165, 166, 167, 0, 168, 169, 53, 170, 171, 0, 0, 172, 173, 174, 175, 176, 177, 178, 179, 180, 0, 181, 182, 183, 184, 185, 0, 0, 0, 0, 0, 186, 187, 0, 0, 0, 0, 188, 189, 190, 191, 192, 193, 0, 0, 194, 0, 0, 195, 196, 197, 198, 199, 0, 200, 201, 202, 203, 204, 205, 0, 206, 207, 208, 209, 0, 210, 71, 0, 211, 212, 213, 0, 0, 0, 0, 0, 214, 215, 216, 217, 218, 219, 220, 221, 0, 222, 223, 224, 0, 0, 0, 225, 0, 0, 0, 226, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 229, 230, 0, 231, 232, 233, 0, 234, 235, 236, 237, 0, 238, 239, 0, 0, 240, 241, 0, 242, 0, 0, 0, 0, 0, 0, 243, 244, 245, 0, 246, 247, 248, 249, 250, 0, 0, 251, 252, 253, 254, 255, 0, 0, 256, 257, 258, 259, 260, 0, 0, 0, 261, 262, 263, 264, 265, 0, 266, 267, 268, 269, 270, 0, 0, 0, 0, 0, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 0, 0, 104, 402, 0, 0, 282, 127, 128, 283, 0, 0, 0, 0, 129, 0, 0, 284, 0, 285, 0, 0, 130, 0, 0, 0, 0, 20, 0, 0, 0, 131, 0, 132, 0, 133, 0, 0, 28, 29, 30, 31, 134, 135, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 136, 0, 0, 0, 137, 138, 0, 0, 0, 0, 0, 139, 0, 140, 0, 141, 142, 143, 144, 145, 146, 147, 148, 0, 0, 149, 150, 151, 152, 153, 154, 155, 0, 0, 156, 50, 157, 158, 159, 160, 161, 162, 163, 164, 0, 0, 165, 166, 167, 0, 168, 169, 53, 170, 171, 0, 0, 172, 173, 174, 175, 176, 177, 178, 179, 180, 0, 181, 182, 183, 184, 185, 0, 0, 0, 0, 0, 186, 187, 0, 0, 0, 0, 188, 189, 190, 191, 192, 193, 0, 0, 194, 0, 0, 195, 196, 197, 198, 199, 0, 200, 201, 202, 203, 204, 205, 0, 206, 207, 208, 209, 0, 210, 71, 0, 211, 212, 213, 0, 0, 0, 0, 0, 214, 215, 216, 217, 218, 219, 220, 221, 0, 222, 223, 224, 0, 0, 0, 225, 0, 0, 0, 226, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 229, 230, 0, 231, 232, 233, 0, 234, 235, 236, 237, 0, 238, 239, 0, 0, 240, 241, 0, 242, 0, 0, 0, 0, 0, 0, 243, 244, 245, 0, 246, 247, 248, 249, 250, 0, 0, 251, 252, 253, 254, 255, 0, 0, 256, 257, 258, 259, 260, 0, 0, 0, 261, 262, 263, 264, 265, 0, 266, 267, 268, 269, 270, 0, 0, 0, 0, 0, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 0, 0, 104, 623, 0, 0, 282, -93, -93, 283, 0, 0, 0, 0, -93, 0, 0, 284, 0, 285, 0, 0, -93, 0, 0, 0, 0, -93, 0, 0, 0, -93, 0, -93, 0, -93, 0, 0, -93, -93, -93, -93, -93, -93, 0, 0, 0, 0, 0, 0, 0, -93, 0, 0, -93, 0, 0, 0, -93, -93, 0, 0, 0, 0, 0, -93, 0, -93, 0, -93, -93, -93, -93, -93, -93, -93, -93, 0, 0, -93, -93, -93, -93, -93, -93, -93, 0, 0, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, 0, 0, -93, -93, -93, 0, -93, -93, -93, -93, -93, 0, 0, -93, -93, -93, -93, -93, -93, -93, -93, -93, 0, -93, -93, -93, -93, -93, 0, 0, 0, 0, 0, -93, -93, 0, 0, 0, 0, -93, -93, -93, -93, -93, -93, 0, 0, -93, 0, 0, -93, -93, -93, -93, -93, 0, -93, -93, -93, -93, -93, -93, 0, -93, -93, -93, -93, 0, -93, -93, 0, -93, -93, -93, 0, 0, 0, 0, 0, -93, -93, -93, -93, -93, -93, -93, -93, 0, -93, -93, -93, 0, 0, 0, -93, 0, 0, 0, -93, -93, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -93, -93, -93, 0, -93, -93, -93, 0, -93, -93, -93, -93, 0, -93, -93, 0, 0, -93, -93, 0, -93, 0, 0, 0, 0, 0, 0, -93, -93, -93, 0, -93, -93, -93, -93, -93, 0, 0, -93, -93, -93, -93, -93, 0, 0, -93, -93, -93, -93, -93, 0, 0, 0, -93, -93, -93, -93, -93, 0, -93, -93, -93, -93, -93, 0, 0, 0, 0, 0, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, -93, 0, 0, -93, 715, 0, 0, -93, 127, 128, -93, 0, 0, 0, 0, 129, 0, 0, -93, 0, -93, 0, 0, 130, 0, 0, 0, 0, 20, 0, 0, 0, 131, 0, 132, 0, 133, 0, 0, 28, 29, 30, 31, 134, 135, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 136, 0, 0, 0, 137, 138, 0, 0, 0, 0, 0, 139, 0, 140, 0, 141, 142, 143, 144, 145, 146, 147, 148, 0, 0, 149, 150, 151, 152, 153, 154, 155, 0, 0, 156, 50, 157, 158, 159, 160, 161, 162, 163, 164, 0, 0, 165, 166, 167, 0, 168, 169, 53, 170, 171, 0, 0, 172, 173, 174, 175, 176, 177, 178, 179, 180, 0, 181, 182, 183, 184, 185, 0, 0, 0, 0, 0, 186, 187, 0, 0, 0, 0, 188, 189, 190, 191, 192, 193, 0, 0, 194, 0, 0, 195, 196, 197, 198, 199, 0, 200, 201, 202, 203, 204, 205, 0, 206, 207, 208, 209, 0, 210, 71, 0, 211, 212, 213, 0, 0, 0, 0, 0, 214, 215, 216, 217, 218, 219, 220, 221, 0, 222, 223, 224, 0, 0, 0, 225, 0, 0, 0, 226, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 229, 230, 0, 231, 232, 233, 0, 234, 235, 236, 237, 0, 238, 239, 0, 0, 240, 241, 0, 242, 0, 0, 0, 0, 0, 0, 243, 244, 245, 0, 246, 247, 248, 249, 250, 0, 0, 251, 252, 253, 254, 255, 0, 0, 256, 257, 258, 259, 260, 0, 0, 0, 261, 262, 263, 264, 265, 0, 266, 267, 268, 269, 270, 0, 0, 0, 0, 0, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 0, 0, 104, 939, 0, 0, 0, 127, 128, 283, 0, 0, 716, 0, 129, 0, 0, 284, 0, 285, 0, 0, 130, 0, 0, 0, 0, 20, 0, 0, 0, 131, 0, 132, 0, 133, 0, 0, 28, 29, 30, 31, 134, 135, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 136, 0, 0, 0, 137, 138, 0, 0, 0, 0, 0, 139, 0, 140, 0, 141, 142, 143, 144, 145, 146, 147, 148, 0, 0, 149, 150, 151, 152, 153, 154, 155, 0, 0, 156, 50, 157, 158, 159, 160, 161, 162, 163, 164, 0, 0, 165, 166, 167, 0, 168, 169, 53, 170, 171, 0, 0, 172, 173, 174, 175, 176, 177, 178, 179, 180, 0, 181, 182, 183, 184, 185, 0, 0, 0, 0, 0, 186, 187, 0, 0, 0, 0, 188, 189, 190, 191, 192, 193, 0, 0, 194, 0, 0, 195, 196, 197, 198, 199, 0, 200, 201, 202, 203, 204, 205, 0, 206, 207, 208, 209, 0, 210, 71, 0, 211, 212, 213, 0, 0, 0, 0, 0, 214, 215, 216, 217, 218, 219, 220, 221, 0, 222, 223, 224, 0, 0, 0, 225, 0, 0, 0, 226, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 229, 230, 0, 231, 232, 233, 0, 234, 235, 236, 237, 0, 238, 239, 0, 0, 240, 241, 0, 242, 0, 0, 0, 0, 0, 0, 243, 244, 245, 0, 246, 247, 248, 249, 250, 0, 0, 251, 252, 253, 254, 255, 0, 0, 256, 257, 258, 259, 260, 0, 0, 0, 261, 262, 263, 264, 265, 0, 266, 267, 268, 269, 270, 0, 0, 0, 0, 0, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 0, 0, 104, 126, 0, 0, 282, 127, 128, 283, 0, 0, 0, 0, 129, 0, 0, 284, 0, 285, 0, 0, 130, 0, 0, 0, 0, 20, 0, 0, 0, 131, 0, 132, 0, 133, 0, 0, 28, 29, 30, 31, 134, 135, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 136, 0, 0, 0, 137, 138, 0, 0, 0, 0, 0, 139, 0, 140, 0, 141, 142, 143, 144, 145, 146, 147, 148, 0, 0, 149, 150, 151, 152, 153, 154, 155, 0, 0, 156, 50, 157, 158, 159, 160, 161, 162, 163, 164, 0, 0, 165, 166, 167, 0, 168, 169, 53, 170, 171, 0, 0, 172, 173, 174, 175, 176, 177, 178, 179, 180, 0, 181, 182, 183, 184, 185, 0, 0, 0, 0, 0, 186, 187, 0, 0, 0, 0, 188, 189, 190, 191, 192, 193, 0, 0, 194, 0, 0, 195, 196, 197, 198, 199, 0, 200, 201, 202, 203, 204, 205, 0, 206, 207, 208, 209, 0, 210, 71, 0, 211, 212, 213, 0, 0, 0, 0, 0, 214, 215, 216, 217, 218, 219, 220, 221, 0, 222, 223, 224, 0, 0, 0, 225, 0, 0, 0, 226, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 229, 230, 0, 231, 232, 233, 0, 234, 235, 236, 237, 0, 238, 239, 0, 0, 240, 241, 0, 242, 0, 0, 0, 0, 0, 0, 243, 244, 245, 0, 246, 247, 248, 249, 250, 0, 0, 251, 252, 253, 254, 255, 0, 0, 256, 257, 258, 259, 260, 0, 0, 0, 261, 262, 263, 264, 265, 0, 266, 267, 268, 269, 270, 0, 0, 0, 0, 0, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 0, 718, 104, 0, 0, 127, 128, 0, 0, 283, 0, 0, 129, 0, 0, 0, 0, 284, 0, 285, 130, 0, 0, 0, 0, 20, 0, 0, 0, 131, 0, 132, 0, 133, 0, 0, 28, 29, 30, 31, 134, 135, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 136, 0, 0, 0, 137, 138, 0, 0, 0, 0, 0, 139, 0, 140, 0, 141, 142, 143, 144, 145, 146, 147, 148, 0, 0, 149, 150, 151, 152, 153, 154, 155, 0, 0, 156, 50, 157, 158, 159, 160, 161, 162, 163, 164, 0, 0, 165, 166, 167, 0, 168, 169, 53, 170, 171, 0, 0, 172, 173, 174, 175, 176, 177, 178, 179, 180, 0, 181, 182, 183, 184, 185, 0, 0, 0, 0, 0, 186, 187, 0, 0, 0, 0, 188, 189, 190, 191, 192, 193, 0, 0, 194, 0, 0, 195, 196, 197, 198, 199, 0, 200, 201, 202, 203, 204, 205, 0, 206, 207, 208, 209, 0, 210, 71, 0, 211, 212, 213, 0, 0, 0, 0, 0, 214, 215, 216, 217, 218, 219, 220, 221, 0, 222, 223, 224, 0, 0, 0, 225, 0, 0, 0, 226, 227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 229, 230, 0, 231, 232, 233, 0, 234, 235, 236, 237, 0, 238, 239, 0, 0, 240, 241, 0, 242, 0, 0, 0, 0, 0, 0, 243, 244, 245, 0, 246, 247, 248, 249, 250, 0, 0, 251, 252, 253, 254, 255, 0, 0, 256, 257, 258, 259, 260, 0, 0, 0, 261, 262, 263, 264, 265, 0, 266, 267, 268, 269, 270, 0, 0, 0, 0, 0, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 0, 928, 104, 0, 0, -425, -425, 0, 0, 283, 0, 0, -425, 0, 0, 0, 0, 284, 0, 285, -425, 0, 0, 0, 0, -425, 0, 0, 0, -425, 0, -425, 0, -425, 0, 0, -425, -425, -425, -425, -425, -425, 0, 0, 0, 0, 0, 0, 0, -425, 0, 0, -425, 0, 0, 0, -425, -425, 0, 0, 0, 0, 0, -425, 0, -425, 0, -425, -425, -425, -425, -425, -425, -425, -425, 0, 0, -425, -425, -425, -425, -425, -425, -425, 0, 0, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, 0, 0, -425, -425, -425, 0, -425, -425, -425, -425, -425, 0, 0, -425, -425, -425, -425, -425, -425, -425, -425, -425, 0, -425, -425, -425, -425, -425, 0, 0, 0, 0, 0, -425, -425, 0, 0, 0, 0, -425, -425, -425, -425, -425, -425, 0, 0, -425, 0, 0, -425, -425, -425, -425, -425, 0, -425, -425, -425, -425, -425, -425, 0, -425, -425, -425, -425, 0, -425, -425, 0, -425, -425, -425, 0, 0, 0, 0, 0, -425, -425, -425, -425, -425, -425, -425, -425, 0, -425, -425, -425, 0, 0, 0, -425, 0, 0, 0, -425, -425, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -425, -425, -425, 0, -425, -425, -425, 0, -425, -425, -425, -425, 0, -425, -425, 0, 0, -425, -425, 0, -425, 0, 0, 0, 0, 0, 0, -425, -425, -425, 0, -425, -425, -425, -425, -425, 0, 0, -425, -425, -425, -425, -425, 0, 0, -425, -425, -425, -425, -425, 0, 0, 0, -425, -425, -425, -425, -425, 0, -425, -425, -425, -425, -425, 0, 0, 0, 0, 0, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, -425, 126, 0, -425, 0, 127, 0, 0, 0, 0, -425, 0, 0, 0, 0, 0, 0, 0, -425, 0, -425, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 132, 0, 133, 0, 0, 28, 29, 30, 31, 134, 135, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 136, 0, 0, 0, 137, 982, 0, 0, 0, 0, 0, 139, 0, 140, 0, 141, 142, 143, 144, 145, 146, 147, 148, 0, 0, 0, 150, 151, 152, 153, 154, 155, 0, 0, 0, 50, 157, 158, 159, 160, 161, 162, 163, 164, 0, 0, 165, 983, 167, 0, 168, 169, 53, 170, 171, 0, 0, 172, 173, 174, 175, 176, 0, 178, 179, 180, 0, 181, 0, 183, 184, 185, 0, 0, 0, 0, 0, 186, 187, 0, 0, 0, 0, 188, 189, 0, 0, 192, 193, 0, 0, 0, 0, 0, 195, 0, 197, 198, 199, 0, 200, 201, 202, 203, 204, 205, 0, 206, 207, 208, 209, 0, 210, 71, 0, 211, 212, 213, 0, 0, 0, 0, 0, 984, 215, 0, 217, 218, 219, 0, 221, 0, 222, 223, 985, 0, 0, 0, 225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -58, 491, 0, 0, 0, 0, 0, 0, 229, 230, 0, 231, 986, 233, 0, 0, 235, 236, 237, 417, 238, 987, 0, 0, 240, 241, 0, 242, 0, 0, 0, -66, 555, 0, 0, 244, 245, 0, 246, 247, 248, 249, 250, 0, 0, 251, 252, 253, 254, 255, 417, 418, 256, 257, 258, 259, 260, 0, -803, 664, 261, 97, 263, 264, 265, 0, 266, 267, 268, 269, 270, 0, 0, -296, 632, 0, 271, 272, 273, 274, 275, 276, 418, -801, 419, 420, 281, 0, 0, 104, 0, 0, 417, 0, -801, -801, -801, -801, 0, 0, 0, 0, 0, 0, 284, 0, 285, -801, 0, 0, 0, 999, 0, 0, 1000, 419, 420, 0, 0, 1001, 0, 1002, 0, 0, 418, 0, 0, 0, 0, 0, 0, 1003, 421, 0, 1004, 1005, 0, 1006, 0, 0, 0, 1007, 0, 0, 0, -801, 1008, 1009, 1010, 1011, 0, 0, 0, 0, 0, 0, 0, 0, 422, 0, 0, 1259, 0, 421, 0, 0, 423, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 424, 0, 0, 0, 0, 0, 425, 0, -58, 0, 0, 1305, 422, 0, 0, 0, 0, 0, 426, 0, 423, 0, -58, 0, 0, 0, 0, 0, 421, 0, 0, 424, 0, 0, 0, 0, 0, 425, 0, -66, 0, 0, 0, 0, 0, 0, 0, -58, 0, 426, 0, 1012, 1013, -66, 422, 1014, 0, 0, -58, 0, 0, 1015, 423, 0, 0, 0, -803, 0, 0, 0, 0, 0, 0, 424, 1308, 0, 0, 0, -66, 425, -803, -296, 0, 0, 0, 0, 0, 0, 0, -66, 0, 426, 0, 0, 0, -296, 0, 0, 0, 0, 0, 0, 1016, 1017, 0, -803, 0, 0, 0, 0, 0, 0, 0, 0, 1018, 0, -803, 0, -58, 0, -296, 0, -58, 0, -58, 0, 0, -58, 0, -58, 0, -296, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1019, -801, 0, 0, 0, 0, -66, 0, 0, 0, -66, 0, -66, 0, 0, -66, 0, -66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -803, 0, 0, 0, -803, 0, -803, 0, 0, -803, 999, -803, 0, 1000, 0, -296, 0, 0, 1001, -296, 1002, -296, 0, 0, -296, 0, -296, 0, 0, 0, 1003, 0, 0, 1004, 1005, 0, 1006, 0, 0, 0, 1007, 0, 0, 0, 1031, 1008, 1009, 1010, 1011, 0, 1029, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 1032, 0, 0, 0, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 0, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 1033, 1034, 391, 392, 393, 394, 395, 0, 396, 397, 398, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1035, 0, 0, 0, 0, 0, 0, 0, 1012, 1013, 0, 0, 1014, 0, 1036, 0, 0, 0, 1015, 0, 0, 0, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 1037, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 0, 461, 0, 0, 0, 0, 1038, 0, 1039, 0, 1040, 1016, 1017, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1049, 1018, 0, -548, 0, 1050, 0, 0, 0, 1041, 0, 0, 0, 0, -548, -548, -548, -548, 1042, 1051, 1052, 1053, 1054, 0, 0, 1029, 0, -548, 1043, 0, 1019, 462, 0, 0, 0, 0, 0, 0, 1055, 463, 0, 971, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 381, 382, 464, 383, 384, 0, 385, 386, 387, 388, 389, 390, -548, 0, 0, 1056, 1057, 391, 392, 393, 394, 395, 0, 396, 397, 398, 465, 0, 0, 0, 0, 0, 0, 0, 0, 1044, 0, 0, 1045, 0, 0, 1058, 0, 0, 0, 0, 0, 0, 0, 1059, 0, 0, 0, 0, 0, 1060, 0, 0, 0, 466, 0, 0, 0, 0, 0, 0, 467, 0, 0, 0, 0, 0, 468, 0, 1115, 0, 0, 0, 1061, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1062, 0, 1063, 0, 1064, 1065, 1066, 0, 0, 0, 0, 0, 381, 382, 0, 383, 384, 1067, 385, 386, 387, 388, 389, 390, 0, 1068, 0, 0, 0, 391, 392, 393, 394, 395, 1069, 396, 397, 398, 0, 0, 0, 0, 469, 0, 1070, 0, 1135, 0, 0, 0, 0, 470, 0, 0, 808, 0, 0, 0, 1071, 1072, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 471, 472, 391, 392, 393, 394, 395, 473, 396, 397, 398, 1136, 0, 0, 0, 0, 0, 0, -548, 808, 0, 0, 0, 0, 0, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 380, 0, 0, 0, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 918, 0, 0, 0, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 1077, 0, 0, 0, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 1081, 0, 0, 0, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 1085, 0, 0, 0, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 1092, 0, 0, 0, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 1134, 0, 0, 0, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 1142, 0, 0, 0, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 1153, 0, 0, 0, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 1154, 0, 0, 0, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 1155, 0, 0, 0, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 1270, 0, 0, 0, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 1088, 391, 392, 393, 394, 395, 0, 396, 397, 398, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 1098, 391, 392, 393, 394, 395, 0, 396, 397, 398, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 1163, 391, 392, 393, 394, 395, 0, 396, 397, 398, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 1206, 391, 392, 393, 394, 395, 0, 396, 397, 398, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 1241, 391, 392, 393, 394, 395, 0, 396, 397, 398, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 808, 396, 397, 398, 0, 0, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 1076, 396, 397, 398, 0, 0, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 1093, 396, 397, 398, 0, 0, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 1094, 396, 397, 398, 0, 0, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 1157, 396, 397, 398, 0, 0, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 1266, 396, 397, 398, 0, 0, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 1267, 396, 397, 398, 0, 0, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 1273, 396, 397, 398, 0, 0, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 1274, 396, 397, 398, 0, 0, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 1275, 396, 397, 398, 0, 0, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 1277, 396, 397, 398, 0, 0, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 1294, 396, 397, 398, 0, 0, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 1295, 396, 397, 398, 0, 0, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 1296, 396, 397, 398, 0, 0, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 1316, 396, 397, 398, 0, 0, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 1324, 396, 397, 398, 0, 0, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 0, 1104, 381, 382, 1205, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 393, 394, 395, 0, 396, 397, 398, 381, 382, 0, 383, 384, 0, 385, 386, 387, 388, 389, 390, 0, 0, 0, 0, 0, 391, 392, 1159, 394, 395, 0, 396, 397, 398 }; static const short int yycheck[] = { 4, 120, 1, 1, 122, 120, 128, 191, 130, 340, 1, 1, 294, 344, 296, 1, 117, 299, 300, 320, 321, 25, 1, 1, 500, 501, 148, 1, 504, 1, 12, 1, 1, 0, 156, 64, 1, 766, 1, 1, 1, 64, 64, 1, 228, 27, 1228, 51, 64, 164, 12, 161, 5, 1, 1, 1, 1, 97, 1, 243, 1, 12, 1, 1, 12, 1, 0, 1, 190, 27, 1, 0, 174, 1, 1, 84, 85, 1, 29, 1, 5, 296, 295, 174, 174, 298, 1, 903, 904, 905, 624, 34, 626, 174, 1276, 3, 4, 1, 1, 1, 58, 105, 1, 107, 108, 109, 110, 0, 5, 113, 1, 0, 300, 1, 329, 1, 1, 239, 77, 1, 304, 294, 306, 307, 300, 296, 1, 296, 1, 1, 232, 1, 97, 137, 1, 1, 294, 1, 294, 1, 1, 1, 232, 296, 292, 294, 9, 151, 188, 175, 77, 155, 5, 157, 174, 324, 325, 174, 1, 232, 329, 174, 166, 1, 117, 169, 306, 293, 172, 173, 186, 175, 176, 293, 178, 174, 293, 332, 182, 235, 138, 294, 97, 174, 188, 189, 296, 296, 306, 296, 194, 195, 117, 197, 300, 199, 200, 201, 293, 138, 51, 147, 317, 1, 319, 320, 321, 274, 174, 199, 214, 174, 303, 274, 12, 296, 185, 147, 222, 223, 117, 77, 303, 188, 175, 101, 230, 318, 232, 174, 199, 203, 199, 232, 224, 239, 351, 318, 36, 37, 38, 39, 293, 203, 248, 12, 1, 1, 196, 185, 208, 49, 199, 299, 296, 224, 299, 224, 199, 263, 203, 204, 29, 299, 117, 199, 270, 296, 235, 208, 293, 293, 203, 188, 205, 279, 280, 224, 36, 37, 38, 39, 225, 224, 1, 247, 290, 301, 235, 300, 224, 49, 293, 301, 298, 301, 301, 301, 302, 303, 329, 235, 300, 294, 605, 309, 310, 293, 199, 291, 314, 301, 303, 301, 304, 301, 1045, 296, 296, 318, 324, 325, 296, 296, 199, 301, 295, 318, 295, 660, 300, 64, 299, 224, 301, 304, 301, 304, 342, 300, 303, 294, 624, 296, 626, 349, 299, 300, 295, 224, 329, 329, 356, 293, 358, 318, 360, 304, 303, 301, 301, 295, 300, 304, 665, 299, 319, 320, 321, 294, 304, 296, 300, 318, 299, 300, 300, 381, 300, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 300, 294, 300, 296, 404, 300, 299, 300, 295, 0, 1, 301, 175, 740, 741, 742, 300, 304, 300, 300, 297, 314, 300, 274, 295, 174, 319, 320, 321, 274, 708, 300, 300, 304, 300, 296, 300, 300, 300, 0, 300, 441, 300, 300, 300, 294, 446, 296, 186, 293, 299, 300, 452, 299, 454, 455, 456, 305, 297, 756, 353, 354, 175, 463, 301, 314, 298, 467, 307, 308, 296, 310, 311, 77, 313, 314, 315, 316, 317, 318, 64, 64, 147, 196, 64, 324, 325, 326, 327, 328, 490, 330, 331, 332, 605, 334, 64, 291, 64, 77, 147, 64, 64, 299, 353, 354, 97, 400, 64, 299, 510, 326, 327, 328, 514, 330, 331, 332, 64, 1, 64, 521, 0, 1, 524, 64, 64, 297, 301, 293, 12, 294, 301, 299, 247, 862, 536, 735, 294, 301, 540, 301, 391, 392, 174, 1293, 297, 29, 1141, 1276, 926, 400, 1089, 754, 665, -1, 307, 308, 558, 310, 311, 858, 313, 314, 315, 316, 317, 318, 1, -1, -1, 0, 1, 324, 325, 326, 327, 328, -1, 330, 331, 332, -1, -1, -1, -1, 64, -1, -1, -1, 590, -1, 25, -1, 185, 77, -1, 188, -1, -1, 697, 0, 1, 36, 37, 38, 39, -1, 199, -1, 9, 1, -1, 613, -1, -1, 49, -1, 618, -1, 0, 1, -1, -1, 902, -1, -1, -1, -1, 9, -1, 631, -1, 224, -1, 25, -1, 915, 199, 754, -1, 919, -1, 754, 235, 756, 36, 37, 38, 39, -1, 929, 763, -1, 87, -1, 605, -1, 1130, 49, -1, 1133, -1, 224, -1, 147, 296, 945, 668, 19, 670, 104, 672, 303, 235, 624, 1148, 626, -1, -1, -1, -1, -1, 165, 684, 685, 686, -1, 318, -1, 690, -1, -1, 175, -1, 177, -1, 87, -1, -1, -1, 51, -1, 624, 295, 626, -1, 185, 299, -1, 301, 711, 605, 304, 196, 306, 665, 897, 898, 899, 900, 199, -1, -1, 1021, 725, -1, 727, 728, -1, 730, 624, 165, 626, 295, -1, -1, 917, 299, -1, 301, -1, -1, 304, -1, 61, 224, 858, 697, -1, -1, -1, 1030, -1, 1081, 1, 1083, 235, 1230, 708, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, 199, -1, 665, -1, -1, 624, 697, 626, -1, 25, -1, -1, 132, 29, -1, 678, 185, 708, -1, -1, 36, 37, 38, 39, -1, 224, -1, -1, 114, -1, 199, -1, 118, 49, 697, 185, 235, 756, 158, -1, -1, -1, 812, -1, -1, 708, 166, 295, 296, 199, -1, 299, 138, 301, -1, 224, 304, 177, 306, 678, 263, 77, -1, 183, -1, -1, 235, -1, -1, -1, -1, 87, 735, 12, 224, 195, 324, 325, 697, 849, -1, 329, 852, -1, -1, 235, -1, 857, 291, 708, -1, -1, -1, 756, -1, -1, 295, 1145, -1, 987, 299, -1, -1, 263, 874, 304, -1, -1, 727, 879, 880, 881, -1, 997, 981, -1, 735, -1, 888, -1, 890, -1, 61, 990, -1, -1, 295, -1, -1, -1, 299, -1, 301, 1, 1, 304, -1, 306, 77, 858, 1021, -1, -1, -1, -1, 295, -1, -1, -1, 299, 236, 301, 0, 1, 304, -1, 306, -1, 25, 245, 1044, -1, -1, -1, 1048, -1, 34, 1, -1, 36, 37, 38, 39, -1, 19, 114, -1, -1, 948, 118, -1, -1, 49, 902, -1, -1, -1, -1, -1, 1, 58, 25, -1, -1, -1, 858, 915, -1, -1, 138, 919, -1, 36, 37, 38, 39, 51, 1, 147, 979, 929, 902, -1, -1, 984, 49, 986, 987, 1168, -1, 87, -1, 34, -1, 915, -1, 945, -1, 919, -1, -1, 25, -1, -1, -1, -1, -1, 104, 929, 902, -1, -1, 36, 37, 38, 39, 58, 263, 60, -1, -1, -1, 915, 87, 945, 49, 919, -1, -1, 1029, -1, -1, 981, -1, 1, -1, 929, -1, -1, -1, 104, 990, -1, -1, 1044, 12, -1, -1, 1048, -1, -1, -1, 945, 902, 1054, 1169, -1, 132, -1, 1059, 981, -1, 29, 87, -1, 1245, 915, 236, 165, 990, 919, -1, 1021, 171, 172, 173, 245, 1077, -1, -1, 929, 1030, -1, 158, -1, 1085, -1, -1, 981, 1089, 165, 166, 1092, -1, -1, -1, 945, 990, -1, -1, 1100, 165, 177, -1, 1104, 203, 204, 185, 183, 1030, 77, -1, -1, -1, -1, -1, -1, 0, 1, -1, 195, 199, 1122, 1123, 1124, 1125, -1, 225, 1021, 171, 172, 173, 981, -1, 1134, 1135, 1136, 1030, 1138, -1, -1, 990, 1142, 1240, -1, -1, 224, -1, -1, -1, -1, 0, 1, 1153, 1154, 1155, -1, 235, -1, 1159, -1, 203, 204, 205, -1, -1, 263, -1, -1, -1, 19, 0, 1, -1, 1174, 1175, -1, -1, -1, 1179, 147, 1030, 1182, 225, 1184, -1, 1186, -1, 1188, -1, 19, 1191, 1192, 1193, 291, 1044, 1145, 1090, 165, 263, -1, -1, 51, -1, -1, -1, -1, -1, 175, -1, 177, 1211, -1, -1, -1, -1, 291, 295, 296, -1, -1, 299, 51, 301, 1145, -1, 304, 291, 306, 196, -1, -1, -1, -1, -1, 84, 85, -1, -1, 263, -1, 1090, -1, -1, 1244, -1, 1246, -1, -1, 1249, 1250, 1251, 1145, 1253, -1, 1255, -1, 1257, -1, 1259, -1, 1261, 1262, 1156, -1, -1, -1, 1160, 1161, -1, -1, 1271, -1, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, 132, -1, -1, -1, 128, -1, 130, -1, 1240, -1, -1, 135, -1, 1145, 0, 1, -1, 1300, -1, 185, 132, -1, 1305, -1, 1156, 1308, 158, 1159, 1160, 1161, 154, -1, 156, 199, 166, -1, 1240, 161, -1, -1, -1, -1, -1, -1, -1, 177, 158, 171, -1, -1, 174, 183, -1, 185, 166, -1, -1, -1, 224, 183, 184, -1, 1, 195, 1240, 177, 190, 199, -1, 235, -1, 183, -1, 185, -1, -1, -1, -1, 202, 203, 204, 205, -1, 195, -1, -1, 25, 199, -1, 213, -1, -1, 224, -1, -1, -1, -1, 36, 37, 38, 39, -1, 25, 235, -1, 229, -1, 231, 1240, -1, 49, 235, 224, 36, 37, 38, 39, -1, -1, -1, -1, 1297, 1, 235, -1, -1, 49, -1, -1, 295, -1, -1, -1, 299, 300, 301, -1, -1, 304, -1, 306, -1, 266, -1, -1, -1, 25, -1, 87, -1, -1, 275, -1, -1, -1, -1, -1, 36, 37, 38, 39, -1, -1, 295, 87, 1297, -1, 299, -1, 301, 49, -1, 304, -1, 306, -1, -1, 301, -1, -1, -1, -1, -1, 295, -1, 309, 310, 299, -1, 301, 314, -1, 304, -1, 306, -1, -1, -1, -1, 185, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, 334, -1, -1, 199, -1, -1, -1, -1, 0, 1, -1, -1, -1, 347, -1, 104, 8, 9, 10, 11, 12, -1, 14, 15, -1, 17, 18, -1, 224, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 235, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, 54, 55, -1, -1, 58, 59, 60, 61, 62, -1, 64, -1, 66, -1, 165, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, 422, 1, 324, 325, 326, 327, 328, 87, 330, 331, 332, 295, -1, -1, -1, 299, 96, 301, -1, 99, 304, -1, 306, -1, 104, 25, 263, 107, 108, 29, -1, -1, -1, -1, -1, -1, 36, 37, 38, 39, -1, -1, 263, -1, 124, 125, 126, 127, 128, 49, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, 141, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, 77, -1, -1, -1, -1, -1, 163, -1, 165, 263, 87, -1, -1, 170, 0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 185, 186, 187, -1, 189, 1, 191, 192, 193, 291, -1, 196, -1, -1, 199, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, 212, -1, 25, -1, 216, 217, -1, -1, -1, 221, -1, -1, 224, 36, 37, 38, 39, -1, 230, 231, -1, 233, 576, 235, 236, -1, 49, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, 61, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, 613, -1, 273, 274, -1, 87, 277, -1, -1, -1, -1, -1, -1, 284, 627, -1, 629, -1, 289, 290, 291, -1, 293, 294, 295, 296, 297, -1, 299, 300, 301, 302, -1, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, -1, 330, 331, 332, 333, 334, -1, -1, -1, -1, -1, -1, -1, -1, 263, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 185, -1, 700, 701, -1, 703, 8, 9, 10, 11, 12, -1, 14, 15, 199, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, 1, 34, -1, 36, 37, 38, 39, -1, 224, 42, 43, 44, 45, 46, 47, 48, 49, 50, 19, 235, -1, 54, 55, -1, -1, 58, 59, 60, 61, 62, -1, 64, -1, 66, -1, -1, -1, -1, -1, 1, -1, -1, 75, 76, 77, -1, -1, 245, -1, -1, 51, -1, -1, -1, 87, -1, -1, 19, -1, -1, -1, -1, -1, 96, -1, 263, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, 295, -1, -1, 280, 299, -1, 301, -1, -1, 304, 51, 306, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, 141, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, 0, 1, -1, -1, -1, -1, -1, -1, -1, -1, 163, 132, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 185, 186, 187, -1, 189, 158, 191, 192, 193, -1, -1, 196, -1, 166, 199, 200, 201, 202, 132, 204, -1, 174, 207, -1, 177, -1, -1, 212, -1, -1, 183, 216, 217, -1, -1, -1, 221, 918, -1, 224, -1, -1, 195, -1, 158, 230, 231, -1, 233, -1, 235, 236, 166, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, 177, -1, -1, -1, -1, 253, 183, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, 195, 267, -1, -1, -1, -1, -1, 273, 274, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, 293, 294, 295, 296, 297, -1, 299, 300, 301, 302, -1, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, -1, 330, 331, 332, 333, 334, -1, 0, 1, 185, -1, -1, -1, -1, -1, 8, 9, 10, 11, 12, -1, 14, 15, 199, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, 224, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, 235, -1, 54, 55, -1, -1, 58, 59, 60, 61, 62, -1, 64, -1, 66, -1, -1, -1, -1, -1, 1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, 19, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, 295, -1, -1, -1, 299, -1, 301, -1, -1, 304, 51, 306, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, 1, 141, 142, -1, 144, 145, -1, 147, 25, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, 36, 37, 38, 39, 163, 25, 165, -1, -1, -1, -1, 170, -1, 49, -1, -1, 36, 37, 38, 39, -1, -1, -1, -1, -1, -1, 185, 186, 187, 49, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, 199, 200, 201, 202, 132, 204, -1, -1, 207, -1, -1, 87, -1, 212, -1, -1, -1, 216, 217, -1, -1, -1, 221, -1, -1, 224, -1, 87, 104, -1, 158, 230, 231, -1, 233, -1, 235, 236, 166, 1270, 239, -1, -1, -1, -1, -1, 245, 246, -1, 177, -1, -1, -1, -1, 253, 183, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, 195, 267, -1, -1, -1, -1, -1, 273, 274, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, 165, 289, 290, 291, -1, 293, 294, 295, -1, 297, -1, 299, 300, 301, 302, -1, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, -1, 330, 331, 332, 333, 334, 0, 1, -1, -1, -1, -1, -1, -1, 8, 9, 10, 11, 12, -1, 14, 15, -1, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, 263, -1, -1, 1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, 263, -1, -1, -1, -1, -1, -1, 1, 75, 76, 77, 25, 291, -1, -1, -1, -1, 84, 85, -1, 87, -1, 36, 37, 38, 39, -1, -1, -1, 96, -1, 25, 99, -1, -1, 49, -1, 104, -1, -1, 107, 108, 36, 37, 38, 39, -1, -1, -1, -1, -1, -1, 1, -1, 1, 49, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, 19, 87, -1, 142, 25, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, 36, 37, 38, 39, -1, -1, 87, -1, -1, 163, -1, 165, -1, 49, -1, -1, 170, 51, 0, 1, 174, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 185, -1, 187, -1, 189, 1, 191, 192, 193, -1, -1, 196, -1, -1, 199, 200, 201, 202, -1, 204, 87, -1, 207, -1, -1, -1, -1, -1, -1, 25, -1, 216, 217, -1, -1, -1, -1, 104, -1, 224, 36, 37, 38, 39, -1, 230, 231, -1, 233, -1, 235, 236, -1, 49, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, 132, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, 87, 277, 158, -1, -1, -1, -1, 165, 284, -1, 166, 0, 1, 289, 290, 291, -1, -1, -1, 295, -1, 177, -1, 299, 300, 301, 302, 183, 304, -1, 306, -1, -1, -1, -1, -1, 312, -1, -1, 195, 263, 0, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, 263, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, 1, 34, -1, 36, 37, 38, 39, 185, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, 19, -1, -1, 199, 55, -1, -1, 58, 59, 60, -1, 62, -1, 263, -1, 66, -1, -1, -1, -1, -1, -1, -1, 1, 75, 76, 77, -1, 224, -1, -1, -1, 51, -1, -1, -1, 87, -1, -1, 235, -1, 291, -1, -1, -1, 96, -1, 25, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, 36, 37, 38, 39, -1, -1, -1, -1, -1, -1, -1, -1, 1, 49, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, 263, -1, -1, 138, 19, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, 295, 151, 152, -1, 299, 185, 301, -1, -1, 304, 87, 306, -1, 163, 132, 165, -1, -1, -1, 199, 170, 51, 0, 1, 174, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 185, -1, 187, -1, 189, 158, 191, 192, 193, 224, -1, 196, -1, 166, 199, 200, 201, 202, -1, 204, 235, -1, 207, -1, 177, -1, -1, -1, -1, -1, 183, 216, 217, -1, -1, -1, -1, -1, -1, 224, -1, -1, 195, -1, -1, 230, 231, 232, 233, -1, 235, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, 132, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, 295, -1, 267, -1, 299, -1, 301, -1, 273, 304, -1, 306, 277, 158, -1, -1, -1, -1, -1, 284, -1, 166, 0, 1, 289, 290, 291, -1, -1, -1, 295, -1, 177, -1, 299, 300, 301, 302, 183, 304, -1, 306, -1, -1, -1, -1, -1, 312, -1, -1, 195, -1, 0, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, 263, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, 185, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, 199, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, 1, 75, 76, 77, -1, 224, -1, -1, -1, -1, 84, 85, -1, 87, -1, -1, 235, -1, -1, -1, -1, -1, 96, -1, 25, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, 36, 37, 38, 39, -1, -1, -1, -1, -1, -1, -1, -1, 1, 49, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, 19, 1, -1, 142, -1, 144, 145, -1, 147, -1, -1, 295, 151, 152, -1, 299, 185, 301, -1, -1, 304, 87, 306, -1, 163, 25, 165, -1, -1, -1, 199, 170, 51, 0, 1, -1, 36, 37, 38, 39, -1, -1, -1, -1, -1, 1, 185, -1, 187, 49, 189, -1, 191, 192, 193, 224, -1, 196, -1, -1, 199, 200, 201, 202, -1, 204, 235, -1, 207, 25, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, 36, 37, 38, 39, -1, 224, -1, 87, -1, -1, -1, 230, 231, 49, 233, -1, 235, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, 132, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, 295, -1, 267, -1, 299, 87, 301, -1, 273, 304, -1, 306, 277, 158, -1, -1, -1, -1, -1, 284, -1, 166, 0, 1, 289, 290, 291, -1, -1, -1, 295, -1, 177, -1, 299, 300, 301, 302, 183, 304, -1, 306, -1, -1, -1, -1, -1, 312, -1, -1, 195, -1, 0, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, 13, 14, 15, 333, 17, 18, 263, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, 185, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, 199, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, 1, 75, 76, 77, -1, 224, -1, -1, -1, -1, -1, 263, -1, 87, -1, -1, 235, -1, -1, -1, -1, -1, 96, -1, 25, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, 36, 37, 38, 39, -1, -1, -1, -1, -1, -1, -1, -1, 1, 49, -1, 124, 125, 126, 127, 128, 263, -1, 131, -1, 133, -1, -1, -1, -1, 138, 19, 1, -1, 142, -1, 144, 145, -1, 147, -1, -1, 295, 151, 152, -1, 299, 185, 301, -1, -1, 304, 87, 306, -1, 163, 25, 165, -1, -1, -1, 199, 170, 51, 0, 1, -1, 36, 37, 38, 39, -1, -1, -1, -1, -1, 1, 185, -1, 187, 49, 189, -1, 191, 192, 193, 224, -1, 196, -1, -1, 199, 200, 201, 202, -1, 204, 235, -1, 207, 25, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, 36, 37, 38, 39, -1, 224, -1, 87, -1, -1, -1, 230, 231, 49, 233, -1, 235, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, 132, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, 295, -1, 267, -1, 299, 87, 301, -1, 273, 304, -1, 306, 277, 158, -1, -1, -1, -1, -1, 284, -1, 166, 0, 1, 289, 290, 291, -1, -1, -1, 295, -1, 177, -1, 299, 300, 301, 302, 183, 304, -1, 306, -1, -1, -1, -1, -1, 312, -1, -1, 195, -1, 0, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, 13, 14, 15, 333, 17, 18, 263, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, 185, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, 199, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, 1, 75, 76, 77, -1, 224, -1, -1, -1, -1, -1, 263, -1, 87, -1, -1, 235, -1, -1, -1, -1, -1, 96, -1, 25, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, 36, 37, 38, 39, -1, -1, -1, -1, -1, -1, -1, -1, 1, 49, -1, 124, 125, 126, 127, 128, 263, -1, 131, -1, 133, -1, -1, -1, -1, 138, 19, 1, -1, 142, -1, 144, 145, -1, 147, -1, -1, 295, 151, 152, -1, 299, 185, 301, -1, -1, 304, 87, 306, -1, 163, 25, 165, -1, -1, -1, 199, 170, 51, 0, 1, -1, 36, 37, 38, 39, -1, -1, -1, -1, -1, 1, 185, -1, 187, 49, 189, -1, 191, 192, 193, 224, -1, 196, -1, -1, 199, 200, 201, 202, -1, 204, 235, -1, 207, 25, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, 36, 37, 38, 39, -1, 224, -1, 87, -1, -1, -1, 230, 231, 49, 233, -1, 235, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, 132, 253, -1, -1, -1, 1, -1, 259, 260, 261, -1, 263, -1, 295, -1, 267, -1, 299, 87, 301, -1, 273, 304, 19, 306, 277, 158, -1, -1, -1, -1, -1, 284, -1, 166, -1, -1, 289, 290, 291, -1, -1, -1, 295, -1, 177, -1, 299, 300, 301, 302, 183, 304, -1, 306, 51, -1, -1, -1, -1, 312, -1, -1, 195, -1, 0, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, 13, 14, 15, 333, 17, 18, 263, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, 1, 34, -1, 36, 37, 38, 39, 185, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, 19, -1, -1, 199, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, 132, -1, -1, -1, 75, 76, 77, -1, 224, -1, -1, -1, 51, -1, 263, -1, 87, -1, -1, 235, -1, -1, -1, -1, -1, 96, 158, -1, 99, -1, -1, -1, 12, 104, 166, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, 177, -1, -1, -1, 29, 1, 183, -1, 124, 125, 126, 127, 128, 263, -1, 131, -1, 133, 195, -1, -1, -1, 138, 19, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, 295, 151, 152, -1, 299, -1, 301, -1, -1, 304, -1, 306, -1, 163, 132, 165, -1, -1, 77, -1, 170, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 185, -1, 187, -1, 189, 158, 191, 192, 193, -1, -1, 196, -1, 166, 199, 200, 201, 202, -1, 204, -1, -1, 207, -1, 177, -1, -1, -1, -1, -1, 183, 216, 217, -1, -1, -1, -1, -1, -1, 224, -1, -1, 195, -1, -1, 230, 231, -1, 233, -1, 235, 236, -1, 147, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, 132, 253, -1, -1, 165, -1, 1, 259, 260, 261, -1, 263, -1, -1, 175, 267, 177, -1, -1, -1, -1, 273, -1, -1, 19, 277, 158, -1, -1, -1, -1, -1, 284, -1, 166, 196, -1, 289, 290, 291, -1, -1, -1, 295, -1, 177, -1, 299, 300, 301, 302, 183, 304, -1, 306, -1, 51, -1, -1, -1, 312, -1, -1, 195, -1, 0, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, 245, 246, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, 1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, 19, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, 132, -1, 1, 75, 76, 77, -1, -1, -1, -1, -1, 51, -1, 12, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, 158, 99, -1, -1, 29, -1, 104, -1, 166, 107, 108, 36, 37, 38, 39, -1, -1, -1, -1, 177, -1, -1, -1, 1, 49, 183, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, 61, 195, -1, -1, 138, 19, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, 132, 165, -1, -1, -1, -1, 170, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 185, -1, 187, -1, 189, 158, 191, 192, 193, -1, -1, 196, -1, 166, 199, 200, 201, 202, -1, 204, -1, -1, 207, -1, 177, -1, -1, -1, -1, 141, 183, 216, 217, -1, -1, -1, -1, -1, -1, 224, -1, -1, 195, -1, -1, 230, 231, 232, 233, -1, 235, 236, -1, 165, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, 177, -1, 132, 253, -1, -1, -1, 1, 1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, 19, 19, 277, 158, -1, -1, -1, -1, -1, 284, 212, 166, -1, -1, 289, 290, 291, -1, -1, 221, 295, -1, 177, -1, 299, 300, 301, 302, 183, 304, -1, 306, 51, 51, -1, -1, -1, 312, -1, -1, 195, -1, 0, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, 1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, 19, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, 132, 132, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, 51, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, 158, 158, 99, -1, -1, -1, -1, 104, 166, 166, 107, 108, -1, -1, 1, -1, -1, -1, -1, 177, 177, -1, -1, -1, -1, 183, 183, 124, 125, 126, 127, 128, 19, -1, 131, -1, 133, 195, 195, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, 51, -1, 163, 132, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 185, -1, 187, -1, 189, 158, 191, 192, 193, -1, -1, 196, -1, 166, 199, 200, 201, 202, -1, 204, -1, -1, 207, -1, 177, -1, -1, -1, -1, -1, 183, 216, 217, -1, -1, -1, -1, -1, -1, 224, -1, -1, 195, -1, -1, 230, 231, -1, 233, -1, 235, 236, -1, -1, 239, -1, -1, 132, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, 158, -1, -1, -1, -1, 273, -1, -1, 166, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, 177, -1, 289, 290, 291, -1, 183, -1, 295, -1, -1, -1, 299, 300, 301, 302, -1, 304, 195, 306, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, 0, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, 311, 66, 313, 314, 315, 316, 317, 318, -1, -1, 75, 76, 77, 324, 325, 326, 327, 328, -1, 330, 331, 332, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 185, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, 199, 200, 201, 202, 308, 204, 310, 311, 207, 313, 314, 315, 316, 317, 318, -1, -1, 216, 217, -1, 324, 325, 326, 327, 328, 224, 330, 331, 332, -1, -1, 230, 231, -1, 233, -1, 235, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, 295, -1, -1, -1, 299, 300, 301, 302, -1, 304, -1, 306, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, 0, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, 313, 314, 315, 316, 317, 318, -1, -1, 75, 76, 77, 324, 325, 326, 327, 328, -1, 330, 331, 332, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 185, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, 199, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, 224, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, 235, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, 295, -1, -1, -1, 299, 300, 301, 302, -1, 304, -1, 306, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, 0, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 185, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, 199, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, 224, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, 235, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, 295, -1, -1, -1, 299, 300, 301, 302, -1, 304, -1, 306, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, 0, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 185, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, 199, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, 224, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, 235, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, 295, -1, -1, -1, 299, 300, 301, 302, -1, 304, -1, 306, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, 0, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 185, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, 199, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, 224, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, 235, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, 295, -1, -1, -1, 299, 300, 301, 302, -1, 304, -1, 306, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, 0, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 185, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, 199, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, 224, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, 235, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, 295, -1, -1, -1, 299, 300, 301, 302, -1, 304, -1, 306, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, 0, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 185, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, 199, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, 224, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, 235, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, 295, -1, -1, -1, 299, 300, 301, 302, -1, 304, -1, 306, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, 0, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 185, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, 199, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, 224, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, 235, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, 295, -1, -1, -1, 299, 300, 301, 302, -1, 304, -1, 306, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, -1, 319, 320, 321, 322, 323, -1, -1, -1, 1, -1, -1, -1, 5, -1, 333, 8, 9, 10, 11, 12, -1, 14, 15, -1, 17, 18, 19, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, -1, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, -1, -1, 55, 56, 57, 58, 59, 60, -1, 62, 63, -1, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, -1, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, -1, 98, 99, 100, -1, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, -1, 115, 116, 117, -1, 119, -1, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, -1, 138, 139, 140, -1, 142, -1, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, -1, -1, -1, -1, 175, 176, 177, 178, 179, 180, -1, 182, 183, 184, 185, 186, 187, -1, 189, 190, 191, 192, 193, -1, 195, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, 210, 211, -1, 213, 214, 215, 216, 217, 218, 219, 220, -1, 222, 223, -1, -1, 226, 227, -1, 229, 230, 231, -1, 233, -1, -1, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, -1, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, -1, -1, -1, 277, 278, 279, 280, 281, 282, 283, 284, -1, -1, -1, 288, 289, 290, 291, -1, -1, -1, -1, -1, -1, 298, -1, 300, -1, 302, -1, -1, -1, 306, -1, 308, -1, -1, -1, 312, -1, -1, -1, -1, -1, -1, 319, 320, 321, 322, 323, -1, -1, -1, 1, -1, -1, -1, 5, -1, 333, 8, 9, 10, 11, 12, -1, 14, 15, -1, 17, 18, 19, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, -1, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, -1, -1, 55, 56, 57, 58, 59, 60, -1, 62, 63, -1, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, -1, -1, -1, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, -1, 98, 99, 100, -1, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, -1, 115, 116, 117, -1, 119, -1, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, -1, 135, 136, -1, 138, 139, 140, -1, 142, -1, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, -1, -1, -1, -1, 175, 176, 177, 178, 179, 180, -1, 182, 183, 184, 185, 186, 187, -1, 189, 190, 191, 192, 193, -1, 195, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, 210, 211, -1, 213, 214, 215, 216, 217, 218, 219, 220, -1, 222, 223, -1, -1, 226, 227, -1, 229, 230, 231, -1, 233, -1, -1, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, -1, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, -1, -1, -1, 277, 278, 279, 280, 281, 282, 283, 284, -1, -1, -1, 288, 289, 290, 291, -1, -1, -1, -1, -1, -1, 298, -1, 300, -1, 302, -1, -1, -1, 306, -1, 308, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, 19, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, 88, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, 132, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, 146, 147, -1, -1, 150, 151, 152, -1, -1, -1, -1, -1, 158, -1, -1, -1, -1, 163, -1, 165, 166, -1, -1, -1, 170, -1, -1, -1, -1, 175, -1, 177, -1, -1, -1, -1, -1, 183, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, 195, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, 214, 215, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, 247, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, 19, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, 132, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, 158, -1, -1, -1, -1, 163, -1, 165, 166, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, 177, -1, -1, -1, -1, -1, 183, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, 195, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, 298, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, 19, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, 132, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, 158, -1, -1, -1, -1, 163, -1, 165, 166, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, 177, -1, -1, -1, -1, -1, 183, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, 195, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, 19, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, 132, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, 158, -1, -1, -1, -1, 163, -1, 165, 166, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, 177, -1, -1, -1, -1, -1, 183, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, 195, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, 19, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, 132, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, 158, -1, -1, -1, -1, 163, -1, 165, 166, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, 177, -1, -1, -1, -1, -1, 183, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, 195, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, 19, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, 132, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, 158, -1, -1, -1, -1, 163, -1, 165, 166, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, 177, -1, -1, -1, -1, -1, 183, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, 195, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, 19, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, 132, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, 158, -1, -1, -1, -1, 163, -1, 165, 166, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, 177, -1, -1, -1, -1, -1, 183, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, 195, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, 19, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, 132, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, 158, -1, -1, -1, -1, 163, -1, 165, 166, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, 177, -1, -1, -1, -1, -1, 183, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, 195, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, 275, 276, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, 275, 276, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, 174, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, 295, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, 1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, -1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, 307, 308, -1, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, -1, 330, 331, 332, 333, 8, 9, 10, 11, 12, -1, 14, 15, -1, 17, 18, 19, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, 132, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, 158, -1, -1, -1, -1, 163, -1, 165, 166, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, 177, -1, -1, -1, -1, -1, 183, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, 195, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, 298, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, -1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, 19, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, 132, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, 158, -1, -1, -1, -1, 163, -1, 165, 166, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, 177, -1, -1, -1, -1, -1, 183, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, 195, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, 298, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, -1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, 19, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, 132, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, 158, -1, -1, -1, -1, 163, -1, 165, 166, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, 177, -1, -1, -1, -1, -1, 183, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, 195, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, 301, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, -1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, 19, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, 132, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, 158, -1, -1, -1, -1, 163, -1, 165, 166, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, 177, -1, -1, -1, -1, -1, 183, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, 195, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, 301, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, -1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, 19, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, 132, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, 158, -1, -1, -1, -1, 163, -1, 165, 166, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, 177, -1, -1, -1, -1, -1, 183, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, 195, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, -1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, 84, 85, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, -1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, 275, 276, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, -1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, 296, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, -1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, 296, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, -1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, -1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, -1, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, -1, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, -1, -1, -1, -1, -1, 273, -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, -1, -1, 289, 290, 291, -1, -1, -1, -1, -1, -1, -1, -1, 300, -1, 302, -1, -1, -1, -1, -1, -1, -1, -1, -1, 312, -1, -1, -1, -1, -1, -1, 319, 320, 321, 322, 323, -1, 8, 9, 10, 11, 12, -1, 14, 15, 333, 17, 18, -1, -1, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, -1, 32, -1, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, -1, -1, 66, -1, -1, -1, -1, -1, -1, -1, -1, 75, 76, 77, -1, -1, -1, -1, -1, -1, -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, 96, -1, -1, 99, -1, -1, -1, -1, 104, -1, -1, 107, 108, -1, 0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 124, 125, 126, 127, 128, 19, -1, 131, -1, 133, -1, -1, -1, -1, 138, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, 152, -1, -1, -1, -1, -1, -1, -1, -1, 51, -1, 163, -1, 165, -1, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, 64, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, 84, 85, 196, -1, -1, -1, 200, 201, 202, -1, 204, -1, -1, 207, -1, -1, -1, -1, -1, -1, -1, -1, 216, 217, -1, -1, -1, -1, -1, -1, -1, -1, -1, 64, -1, -1, 230, 231, -1, 233, -1, -1, 236, -1, -1, 239, -1, -1, 132, -1, -1, 245, 246, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, 259, 260, 261, -1, 263, -1, -1, -1, 267, 158, -1, -1, 64, -1, 273, -1, -1, 166, 277, -1, -1, -1, -1, -1, -1, 284, -1, -1, 177, -1, 289, 290, 291, -1, 183, -1, 185, -1, -1, -1, -1, 300, -1, 302, -1, -1, 195, -1, -1, -1, 199, -1, -1, 312, 1, -1, -1, -1, -1, -1, 319, 320, 321, 322, 323, 12, -1, -1, -1, -1, -1, 18, -1, -1, 333, 224, -1, 24, -1, -1, 27, 28, -1, -1, 274, 32, 235, 34, -1, 36, 37, 38, 39, -1, -1, 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, -1, -1, -1, 55, -1, -1, 58, 59, 60, -1, 62, -1, 307, 308, 66, 310, 311, -1, 313, 314, 315, 316, 317, 318, 76, 77, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, -1, -1, -1, 295, -1, -1, -1, 299, -1, 301, -1, -1, 304, -1, 306, -1, -1, 107, 108, -1, -1, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, 133, 330, 331, 332, -1, -1, -1, -1, -1, 142, -1, 144, 145, -1, 147, -1, -1, -1, 151, -1, -1, -1, -1, -1, -1, 307, 308, -1, 310, 311, 163, 313, 314, 315, 316, 317, 318, 170, -1, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, -1, -1, -1, -1, -1, -1, 307, 308, 204, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, -1, -1, -1, 231, -1, 233, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 253, -1, -1, -1, -1, -1, -1, 260, 261, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 273, -1, 0, 1, -1, -1, -1, 5, 6, -1, -1, -1, -1, -1, 12, -1, -1, -1, 291, -1, -1, -1, 20, -1, -1, -1, -1, 25, -1, -1, -1, 29, -1, 31, -1, 33, -1, -1, 36, 37, 38, 39, 40, 41, -1, -1, 319, 320, 321, 322, 323, 49, -1, -1, 52, -1, -1, -1, 56, 57, -1, -1, -1, -1, -1, 63, -1, 65, -1, 67, 68, 69, 70, 71, 72, 73, 74, -1, -1, 77, 78, 79, 80, 81, 82, 83, -1, -1, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, -1, -1, 98, 99, 100, -1, 102, 103, 104, 105, 106, -1, -1, 109, 110, 111, 112, 113, 114, 115, 116, 117, -1, 119, 120, 121, 122, 123, -1, -1, -1, -1, -1, 129, 130, -1, -1, -1, -1, 135, 136, 137, 138, 139, 140, -1, -1, 143, -1, -1, 146, 147, 148, 149, 150, -1, 152, 153, 154, 155, 156, 157, -1, 159, 160, 161, 162, -1, 164, 165, -1, 167, 168, 169, -1, -1, -1, -1, -1, 175, 176, 177, 178, 179, 180, 181, 182, -1, 184, 185, 186, -1, -1, -1, 190, -1, -1, -1, 194, 195, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 209, 210, 211, -1, 213, 214, 215, -1, 217, 218, 219, 220, -1, 222, 223, -1, -1, 226, 227, -1, 229, -1, -1, -1, -1, -1, 235, 236, 237, 238, -1, 240, 241, 242, 243, 244, -1, -1, 247, 248, 249, 250, 251, -1, -1, 254, 255, 256, 257, 258, -1, -1, -1, 262, 263, 264, 265, 266, -1, 268, 269, 270, 271, 272, -1, -1, -1, -1, -1, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, -1, -1, 291, 1, -1, -1, 295, 5, 6, 298, 299, -1, -1, -1, 12, -1, -1, 306, -1, 308, -1, -1, 20, -1, -1, -1, -1, 25, -1, -1, -1, 29, -1, 31, -1, 33, -1, -1, 36, 37, 38, 39, 40, 41, -1, -1, -1, -1, -1, -1, -1, 49, -1, -1, 52, -1, -1, -1, 56, 57, -1, -1, -1, -1, -1, 63, -1, 65, -1, 67, 68, 69, 70, 71, 72, 73, 74, -1, -1, 77, 78, 79, 80, 81, 82, 83, -1, -1, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, -1, -1, 98, 99, 100, -1, 102, 103, 104, 105, 106, -1, -1, 109, 110, 111, 112, 113, 114, 115, 116, 117, -1, 119, 120, 121, 122, 123, -1, -1, -1, -1, -1, 129, 130, -1, -1, -1, -1, 135, 136, 137, 138, 139, 140, -1, -1, 143, -1, -1, 146, 147, 148, 149, 150, -1, 152, 153, 154, 155, 156, 157, -1, 159, 160, 161, 162, -1, 164, 165, -1, 167, 168, 169, -1, -1, -1, -1, -1, 175, 176, 177, 178, 179, 180, 181, 182, -1, 184, 185, 186, -1, -1, -1, 190, -1, -1, -1, 194, 195, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 209, 210, 211, -1, 213, 214, 215, -1, 217, 218, 219, 220, -1, 222, 223, -1, -1, 226, 227, -1, 229, -1, -1, -1, -1, -1, -1, 236, 237, 238, -1, 240, 241, 242, 243, 244, -1, -1, 247, 248, 249, 250, 251, -1, -1, 254, 255, 256, 257, 258, -1, -1, -1, 262, 263, 264, 265, 266, -1, 268, 269, 270, 271, 272, -1, -1, -1, -1, -1, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, -1, -1, 291, 1, -1, -1, 295, 5, 6, 298, 299, -1, -1, -1, 12, -1, -1, 306, -1, 308, -1, -1, 20, -1, -1, -1, -1, 25, -1, -1, -1, 29, -1, 31, -1, 33, -1, -1, 36, 37, 38, 39, 40, 41, -1, -1, -1, -1, -1, -1, -1, 49, -1, -1, 52, -1, -1, -1, 56, 57, -1, -1, -1, -1, -1, 63, -1, 65, -1, 67, 68, 69, 70, 71, 72, 73, 74, -1, -1, 77, 78, 79, 80, 81, 82, 83, -1, -1, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, -1, -1, 98, 99, 100, -1, 102, 103, 104, 105, 106, -1, -1, 109, 110, 111, 112, 113, 114, 115, 116, 117, -1, 119, 120, 121, 122, 123, -1, -1, -1, -1, -1, 129, 130, -1, -1, -1, -1, 135, 136, 137, 138, 139, 140, -1, -1, 143, -1, -1, 146, 147, 148, 149, 150, -1, 152, 153, 154, 155, 156, 157, -1, 159, 160, 161, 162, -1, 164, 165, -1, 167, 168, 169, -1, -1, -1, -1, -1, 175, 176, 177, 178, 179, 180, 181, 182, -1, 184, 185, 186, -1, -1, -1, 190, -1, -1, -1, 194, 195, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 209, 210, 211, -1, 213, 214, 215, -1, 217, 218, 219, 220, -1, 222, 223, -1, -1, 226, 227, -1, 229, -1, -1, -1, -1, -1, -1, 236, 237, 238, -1, 240, 241, 242, 243, 244, -1, -1, 247, 248, 249, 250, 251, -1, -1, 254, 255, 256, 257, 258, -1, -1, -1, 262, 263, 264, 265, 266, -1, 268, 269, 270, 271, 272, -1, -1, -1, -1, -1, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, -1, -1, 291, 1, -1, -1, 295, 5, 6, 298, -1, -1, -1, -1, 12, -1, -1, 306, -1, 308, -1, -1, 20, -1, -1, -1, -1, 25, -1, -1, -1, 29, -1, 31, -1, 33, -1, -1, 36, 37, 38, 39, 40, 41, -1, -1, -1, -1, -1, -1, -1, 49, -1, -1, 52, -1, -1, -1, 56, 57, -1, -1, -1, -1, -1, 63, -1, 65, -1, 67, 68, 69, 70, 71, 72, 73, 74, -1, -1, 77, 78, 79, 80, 81, 82, 83, -1, -1, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, -1, -1, 98, 99, 100, -1, 102, 103, 104, 105, 106, -1, -1, 109, 110, 111, 112, 113, 114, 115, 116, 117, -1, 119, 120, 121, 122, 123, -1, -1, -1, -1, -1, 129, 130, -1, -1, -1, -1, 135, 136, 137, 138, 139, 140, -1, -1, 143, -1, -1, 146, 147, 148, 149, 150, -1, 152, 153, 154, 155, 156, 157, -1, 159, 160, 161, 162, -1, 164, 165, -1, 167, 168, 169, -1, -1, -1, -1, -1, 175, 176, 177, 178, 179, 180, 181, 182, -1, 184, 185, 186, -1, -1, -1, 190, -1, -1, -1, 194, 195, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 209, 210, 211, -1, 213, 214, 215, -1, 217, 218, 219, 220, -1, 222, 223, -1, -1, 226, 227, -1, 229, -1, -1, -1, -1, -1, -1, 236, 237, 238, -1, 240, 241, 242, 243, 244, -1, -1, 247, 248, 249, 250, 251, -1, -1, 254, 255, 256, 257, 258, -1, -1, -1, 262, 263, 264, 265, 266, -1, 268, 269, 270, 271, 272, -1, -1, -1, -1, -1, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, -1, -1, 291, 1, -1, -1, 295, 5, 6, 298, -1, -1, -1, -1, 12, -1, -1, 306, -1, 308, -1, -1, 20, -1, -1, -1, -1, 25, -1, -1, -1, 29, -1, 31, -1, 33, -1, -1, 36, 37, 38, 39, 40, 41, -1, -1, -1, -1, -1, -1, -1, 49, -1, -1, 52, -1, -1, -1, 56, 57, -1, -1, -1, -1, -1, 63, -1, 65, -1, 67, 68, 69, 70, 71, 72, 73, 74, -1, -1, 77, 78, 79, 80, 81, 82, 83, -1, -1, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, -1, -1, 98, 99, 100, -1, 102, 103, 104, 105, 106, -1, -1, 109, 110, 111, 112, 113, 114, 115, 116, 117, -1, 119, 120, 121, 122, 123, -1, -1, -1, -1, -1, 129, 130, -1, -1, -1, -1, 135, 136, 137, 138, 139, 140, -1, -1, 143, -1, -1, 146, 147, 148, 149, 150, -1, 152, 153, 154, 155, 156, 157, -1, 159, 160, 161, 162, -1, 164, 165, -1, 167, 168, 169, -1, -1, -1, -1, -1, 175, 176, 177, 178, 179, 180, 181, 182, -1, 184, 185, 186, -1, -1, -1, 190, -1, -1, -1, 194, 195, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 209, 210, 211, -1, 213, 214, 215, -1, 217, 218, 219, 220, -1, 222, 223, -1, -1, 226, 227, -1, 229, -1, -1, -1, -1, -1, -1, 236, 237, 238, -1, 240, 241, 242, 243, 244, -1, -1, 247, 248, 249, 250, 251, -1, -1, 254, 255, 256, 257, 258, -1, -1, -1, 262, 263, 264, 265, 266, -1, 268, 269, 270, 271, 272, -1, -1, -1, -1, -1, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, -1, -1, 291, 1, -1, -1, 295, 5, 6, 298, -1, -1, -1, -1, 12, -1, -1, 306, -1, 308, -1, -1, 20, -1, -1, -1, -1, 25, -1, -1, -1, 29, -1, 31, -1, 33, -1, -1, 36, 37, 38, 39, 40, 41, -1, -1, -1, -1, -1, -1, -1, 49, -1, -1, 52, -1, -1, -1, 56, 57, -1, -1, -1, -1, -1, 63, -1, 65, -1, 67, 68, 69, 70, 71, 72, 73, 74, -1, -1, 77, 78, 79, 80, 81, 82, 83, -1, -1, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, -1, -1, 98, 99, 100, -1, 102, 103, 104, 105, 106, -1, -1, 109, 110, 111, 112, 113, 114, 115, 116, 117, -1, 119, 120, 121, 122, 123, -1, -1, -1, -1, -1, 129, 130, -1, -1, -1, -1, 135, 136, 137, 138, 139, 140, -1, -1, 143, -1, -1, 146, 147, 148, 149, 150, -1, 152, 153, 154, 155, 156, 157, -1, 159, 160, 161, 162, -1, 164, 165, -1, 167, 168, 169, -1, -1, -1, -1, -1, 175, 176, 177, 178, 179, 180, 181, 182, -1, 184, 185, 186, -1, -1, -1, 190, -1, -1, -1, 194, 195, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 209, 210, 211, -1, 213, 214, 215, -1, 217, 218, 219, 220, -1, 222, 223, -1, -1, 226, 227, -1, 229, -1, -1, -1, -1, -1, -1, 236, 237, 238, -1, 240, 241, 242, 243, 244, -1, -1, 247, 248, 249, 250, 251, -1, -1, 254, 255, 256, 257, 258, -1, -1, -1, 262, 263, 264, 265, 266, -1, 268, 269, 270, 271, 272, -1, -1, -1, -1, -1, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, -1, -1, 291, 1, -1, -1, -1, 5, 6, 298, -1, -1, 301, -1, 12, -1, -1, 306, -1, 308, -1, -1, 20, -1, -1, -1, -1, 25, -1, -1, -1, 29, -1, 31, -1, 33, -1, -1, 36, 37, 38, 39, 40, 41, -1, -1, -1, -1, -1, -1, -1, 49, -1, -1, 52, -1, -1, -1, 56, 57, -1, -1, -1, -1, -1, 63, -1, 65, -1, 67, 68, 69, 70, 71, 72, 73, 74, -1, -1, 77, 78, 79, 80, 81, 82, 83, -1, -1, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, -1, -1, 98, 99, 100, -1, 102, 103, 104, 105, 106, -1, -1, 109, 110, 111, 112, 113, 114, 115, 116, 117, -1, 119, 120, 121, 122, 123, -1, -1, -1, -1, -1, 129, 130, -1, -1, -1, -1, 135, 136, 137, 138, 139, 140, -1, -1, 143, -1, -1, 146, 147, 148, 149, 150, -1, 152, 153, 154, 155, 156, 157, -1, 159, 160, 161, 162, -1, 164, 165, -1, 167, 168, 169, -1, -1, -1, -1, -1, 175, 176, 177, 178, 179, 180, 181, 182, -1, 184, 185, 186, -1, -1, -1, 190, -1, -1, -1, 194, 195, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 209, 210, 211, -1, 213, 214, 215, -1, 217, 218, 219, 220, -1, 222, 223, -1, -1, 226, 227, -1, 229, -1, -1, -1, -1, -1, -1, 236, 237, 238, -1, 240, 241, 242, 243, 244, -1, -1, 247, 248, 249, 250, 251, -1, -1, 254, 255, 256, 257, 258, -1, -1, -1, 262, 263, 264, 265, 266, -1, 268, 269, 270, 271, 272, -1, -1, -1, -1, -1, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, -1, -1, 291, 1, -1, -1, 295, 5, 6, 298, -1, -1, -1, -1, 12, -1, -1, 306, -1, 308, -1, -1, 20, -1, -1, -1, -1, 25, -1, -1, -1, 29, -1, 31, -1, 33, -1, -1, 36, 37, 38, 39, 40, 41, -1, -1, -1, -1, -1, -1, -1, 49, -1, -1, 52, -1, -1, -1, 56, 57, -1, -1, -1, -1, -1, 63, -1, 65, -1, 67, 68, 69, 70, 71, 72, 73, 74, -1, -1, 77, 78, 79, 80, 81, 82, 83, -1, -1, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, -1, -1, 98, 99, 100, -1, 102, 103, 104, 105, 106, -1, -1, 109, 110, 111, 112, 113, 114, 115, 116, 117, -1, 119, 120, 121, 122, 123, -1, -1, -1, -1, -1, 129, 130, -1, -1, -1, -1, 135, 136, 137, 138, 139, 140, -1, -1, 143, -1, -1, 146, 147, 148, 149, 150, -1, 152, 153, 154, 155, 156, 157, -1, 159, 160, 161, 162, -1, 164, 165, -1, 167, 168, 169, -1, -1, -1, -1, -1, 175, 176, 177, 178, 179, 180, 181, 182, -1, 184, 185, 186, -1, -1, -1, 190, -1, -1, -1, 194, 195, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 209, 210, 211, -1, 213, 214, 215, -1, 217, 218, 219, 220, -1, 222, 223, -1, -1, 226, 227, -1, 229, -1, -1, -1, -1, -1, -1, 236, 237, 238, -1, 240, 241, 242, 243, 244, -1, -1, 247, 248, 249, 250, 251, -1, -1, 254, 255, 256, 257, 258, -1, -1, -1, 262, 263, 264, 265, 266, -1, 268, 269, 270, 271, 272, -1, -1, -1, -1, -1, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, -1, 1, 291, -1, -1, 5, 6, -1, -1, 298, -1, -1, 12, -1, -1, -1, -1, 306, -1, 308, 20, -1, -1, -1, -1, 25, -1, -1, -1, 29, -1, 31, -1, 33, -1, -1, 36, 37, 38, 39, 40, 41, -1, -1, -1, -1, -1, -1, -1, 49, -1, -1, 52, -1, -1, -1, 56, 57, -1, -1, -1, -1, -1, 63, -1, 65, -1, 67, 68, 69, 70, 71, 72, 73, 74, -1, -1, 77, 78, 79, 80, 81, 82, 83, -1, -1, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, -1, -1, 98, 99, 100, -1, 102, 103, 104, 105, 106, -1, -1, 109, 110, 111, 112, 113, 114, 115, 116, 117, -1, 119, 120, 121, 122, 123, -1, -1, -1, -1, -1, 129, 130, -1, -1, -1, -1, 135, 136, 137, 138, 139, 140, -1, -1, 143, -1, -1, 146, 147, 148, 149, 150, -1, 152, 153, 154, 155, 156, 157, -1, 159, 160, 161, 162, -1, 164, 165, -1, 167, 168, 169, -1, -1, -1, -1, -1, 175, 176, 177, 178, 179, 180, 181, 182, -1, 184, 185, 186, -1, -1, -1, 190, -1, -1, -1, 194, 195, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 209, 210, 211, -1, 213, 214, 215, -1, 217, 218, 219, 220, -1, 222, 223, -1, -1, 226, 227, -1, 229, -1, -1, -1, -1, -1, -1, 236, 237, 238, -1, 240, 241, 242, 243, 244, -1, -1, 247, 248, 249, 250, 251, -1, -1, 254, 255, 256, 257, 258, -1, -1, -1, 262, 263, 264, 265, 266, -1, 268, 269, 270, 271, 272, -1, -1, -1, -1, -1, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, -1, 1, 291, -1, -1, 5, 6, -1, -1, 298, -1, -1, 12, -1, -1, -1, -1, 306, -1, 308, 20, -1, -1, -1, -1, 25, -1, -1, -1, 29, -1, 31, -1, 33, -1, -1, 36, 37, 38, 39, 40, 41, -1, -1, -1, -1, -1, -1, -1, 49, -1, -1, 52, -1, -1, -1, 56, 57, -1, -1, -1, -1, -1, 63, -1, 65, -1, 67, 68, 69, 70, 71, 72, 73, 74, -1, -1, 77, 78, 79, 80, 81, 82, 83, -1, -1, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, -1, -1, 98, 99, 100, -1, 102, 103, 104, 105, 106, -1, -1, 109, 110, 111, 112, 113, 114, 115, 116, 117, -1, 119, 120, 121, 122, 123, -1, -1, -1, -1, -1, 129, 130, -1, -1, -1, -1, 135, 136, 137, 138, 139, 140, -1, -1, 143, -1, -1, 146, 147, 148, 149, 150, -1, 152, 153, 154, 155, 156, 157, -1, 159, 160, 161, 162, -1, 164, 165, -1, 167, 168, 169, -1, -1, -1, -1, -1, 175, 176, 177, 178, 179, 180, 181, 182, -1, 184, 185, 186, -1, -1, -1, 190, -1, -1, -1, 194, 195, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 209, 210, 211, -1, 213, 214, 215, -1, 217, 218, 219, 220, -1, 222, 223, -1, -1, 226, 227, -1, 229, -1, -1, -1, -1, -1, -1, 236, 237, 238, -1, 240, 241, 242, 243, 244, -1, -1, 247, 248, 249, 250, 251, -1, -1, 254, 255, 256, 257, 258, -1, -1, -1, 262, 263, 264, 265, 266, -1, 268, 269, 270, 271, 272, -1, -1, -1, -1, -1, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 1, -1, 291, -1, 5, -1, -1, -1, -1, 298, -1, -1, -1, -1, -1, -1, -1, 306, -1, 308, -1, -1, -1, -1, 25, -1, -1, -1, -1, -1, 31, -1, 33, -1, -1, 36, 37, 38, 39, 40, 41, -1, -1, -1, -1, -1, -1, -1, 49, -1, -1, 52, -1, -1, -1, 56, 57, -1, -1, -1, -1, -1, 63, -1, 65, -1, 67, 68, 69, 70, 71, 72, 73, 74, -1, -1, -1, 78, 79, 80, 81, 82, 83, -1, -1, -1, 87, 88, 89, 90, 91, 92, 93, 94, 95, -1, -1, 98, 99, 100, -1, 102, 103, 104, 105, 106, -1, -1, 109, 110, 111, 112, 113, -1, 115, 116, 117, -1, 119, -1, 121, 122, 123, -1, -1, -1, -1, -1, 129, 130, -1, -1, -1, -1, 135, 136, -1, -1, 139, 140, -1, -1, -1, -1, -1, 146, -1, 148, 149, 150, -1, 152, 153, 154, 155, 156, 157, -1, 159, 160, 161, 162, -1, 164, 165, -1, 167, 168, 169, -1, -1, -1, -1, -1, 175, 176, -1, 178, 179, 180, -1, 182, -1, 184, 185, 186, -1, -1, -1, 190, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, -1, -1, -1, -1, -1, -1, 210, 211, -1, 213, 214, 215, -1, -1, 218, 219, 220, 19, 222, 223, -1, -1, 226, 227, -1, 229, -1, -1, -1, 0, 1, -1, -1, 237, 238, -1, 240, 241, 242, 243, 244, -1, -1, 247, 248, 249, 250, 251, 19, 51, 254, 255, 256, 257, 258, -1, 0, 1, 262, 263, 264, 265, 266, -1, 268, 269, 270, 271, 272, -1, -1, 0, 1, -1, 278, 279, 280, 281, 282, 283, 51, 25, 84, 85, 288, -1, -1, 291, -1, -1, 19, -1, 36, 37, 38, 39, -1, -1, -1, -1, -1, -1, 306, -1, 308, 49, -1, -1, -1, 24, -1, -1, 27, 84, 85, -1, -1, 32, -1, 34, -1, -1, 51, -1, -1, -1, -1, -1, -1, 44, 132, -1, 47, 48, -1, 50, -1, -1, -1, 54, -1, -1, -1, 87, 59, 60, 61, 62, -1, -1, -1, -1, -1, -1, -1, -1, 158, -1, -1, 64, -1, 132, -1, -1, 166, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 177, -1, -1, -1, -1, -1, 183, -1, 185, -1, -1, 64, 158, -1, -1, -1, -1, -1, 195, -1, 166, -1, 199, -1, -1, -1, -1, -1, 132, -1, -1, 177, -1, -1, -1, -1, -1, 183, -1, 185, -1, -1, -1, -1, -1, -1, -1, 224, -1, 195, -1, 141, 142, 199, 158, 145, -1, -1, 235, -1, -1, 151, 166, -1, -1, -1, 185, -1, -1, -1, -1, -1, -1, 177, 64, -1, -1, -1, 224, 183, 199, 185, -1, -1, -1, -1, -1, -1, -1, 235, -1, 195, -1, -1, -1, 199, -1, -1, -1, -1, -1, -1, 192, 193, -1, 224, -1, -1, -1, -1, -1, -1, -1, -1, 204, -1, 235, -1, 295, -1, 224, -1, 299, -1, 301, -1, -1, 304, -1, 306, -1, 235, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 233, 263, -1, -1, -1, -1, 295, -1, -1, -1, 299, -1, 301, -1, -1, 304, -1, 306, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 295, -1, -1, -1, 299, -1, 301, -1, -1, 304, 24, 306, -1, 27, -1, 295, -1, -1, 32, 299, 34, 301, -1, -1, 304, -1, 306, -1, -1, -1, 44, -1, -1, 47, 48, -1, 50, -1, -1, -1, 54, -1, -1, -1, 58, 59, 60, 61, 62, -1, 64, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, 77, -1, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, -1, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, 107, 108, 324, 325, 326, 327, 328, -1, 330, 331, 332, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 133, -1, -1, -1, -1, -1, -1, -1, 141, 142, -1, -1, 145, -1, 147, -1, -1, -1, 151, -1, -1, -1, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, 170, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, -1, 1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, -1, -1, -1, -1, -1, -1, -1, 39, 204, -1, 25, -1, 44, -1, -1, -1, 212, -1, -1, -1, -1, 36, 37, 38, 39, 221, 58, 59, 60, 61, -1, -1, 64, -1, 49, 231, -1, 233, 53, -1, -1, -1, -1, -1, -1, 77, 61, -1, 295, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 307, 308, 77, 310, 311, -1, 313, 314, 315, 316, 317, 318, 87, -1, -1, 107, 108, 324, 325, 326, 327, 328, -1, 330, 331, 332, 101, -1, -1, -1, -1, -1, -1, -1, -1, 291, -1, -1, 294, -1, -1, 133, -1, -1, -1, -1, -1, -1, -1, 141, -1, -1, -1, -1, -1, 147, -1, -1, -1, 134, -1, -1, -1, -1, -1, -1, 141, -1, -1, -1, -1, -1, 147, -1, 274, -1, -1, -1, 170, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 187, -1, 189, -1, 191, 192, 193, -1, -1, -1, -1, -1, 307, 308, -1, 310, 311, 204, 313, 314, 315, 316, 317, 318, -1, 212, -1, -1, -1, 324, 325, 326, 327, 328, 221, 330, 331, 332, -1, -1, -1, -1, 212, -1, 231, -1, 293, -1, -1, -1, -1, 221, -1, -1, 301, -1, -1, -1, 245, 246, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, 245, 246, 324, 325, 326, 327, 328, 252, 330, 331, 332, 293, -1, -1, -1, -1, -1, -1, 263, 301, -1, -1, -1, -1, -1, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, 293, -1, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, 293, -1, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, 293, -1, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, 293, -1, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, 293, -1, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, 293, -1, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, 293, -1, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, 293, -1, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, 293, -1, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, 293, -1, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, 293, -1, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, 293, -1, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, 297, 324, 325, 326, 327, 328, -1, 330, 331, 332, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, 297, 324, 325, 326, 327, 328, -1, 330, 331, 332, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, 297, 324, 325, 326, 327, 328, -1, 330, 331, 332, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, 297, 324, 325, 326, 327, 328, -1, 330, 331, 332, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, 297, 324, 325, 326, 327, 328, -1, 330, 331, 332, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, 301, 330, 331, 332, -1, -1, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, 301, 330, 331, 332, -1, -1, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, 301, 330, 331, 332, -1, -1, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, 301, 330, 331, 332, -1, -1, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, 301, 330, 331, 332, -1, -1, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, 301, 330, 331, 332, -1, -1, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, 301, 330, 331, 332, -1, -1, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, 301, 330, 331, 332, -1, -1, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, 301, 330, 331, 332, -1, -1, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, 301, 330, 331, 332, -1, -1, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, 301, 330, 331, 332, -1, -1, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, 301, 330, 331, 332, -1, -1, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, 301, 330, 331, 332, -1, -1, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, 301, 330, 331, 332, -1, -1, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, 301, 330, 331, 332, -1, -1, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, 301, 330, 331, 332, -1, -1, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, -1, 334, 307, 308, 309, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332, 307, 308, -1, 310, 311, -1, 313, 314, 315, 316, 317, 318, -1, -1, -1, -1, -1, 324, 325, 326, 327, 328, -1, 330, 331, 332 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const unsigned short int yystos[] = { 0, 3, 4, 336, 358, 337, 0, 8, 9, 10, 11, 12, 14, 15, 17, 18, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 32, 34, 36, 37, 38, 39, 42, 43, 44, 45, 46, 47, 48, 49, 50, 55, 58, 59, 60, 62, 66, 75, 76, 77, 87, 96, 99, 104, 107, 108, 124, 125, 126, 127, 128, 131, 133, 138, 142, 144, 145, 147, 151, 152, 163, 165, 170, 187, 189, 191, 192, 193, 196, 200, 201, 202, 204, 207, 216, 217, 230, 231, 233, 236, 239, 245, 246, 253, 259, 260, 261, 263, 267, 273, 277, 284, 289, 290, 291, 300, 302, 312, 319, 320, 321, 322, 323, 333, 364, 365, 380, 405, 409, 411, 412, 418, 419, 420, 421, 424, 1, 5, 6, 12, 20, 29, 31, 33, 40, 41, 52, 56, 57, 63, 65, 67, 68, 69, 70, 71, 72, 73, 74, 77, 78, 79, 80, 81, 82, 83, 86, 88, 89, 90, 91, 92, 93, 94, 95, 98, 99, 100, 102, 103, 105, 106, 109, 110, 111, 112, 113, 114, 115, 116, 117, 119, 120, 121, 122, 123, 129, 130, 135, 136, 137, 138, 139, 140, 143, 146, 147, 148, 149, 150, 152, 153, 154, 155, 156, 157, 159, 160, 161, 162, 164, 167, 168, 169, 175, 176, 177, 178, 179, 180, 181, 182, 184, 185, 186, 190, 194, 195, 209, 210, 211, 213, 214, 215, 217, 218, 219, 220, 222, 223, 226, 227, 229, 236, 237, 238, 240, 241, 242, 243, 244, 247, 248, 249, 250, 251, 254, 255, 256, 257, 258, 262, 263, 264, 265, 266, 268, 269, 270, 271, 272, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 295, 298, 306, 308, 338, 339, 340, 341, 342, 345, 346, 347, 348, 349, 351, 353, 355, 356, 357, 361, 362, 363, 364, 379, 380, 381, 383, 386, 387, 400, 401, 402, 404, 408, 411, 412, 425, 426, 432, 439, 1, 1, 300, 300, 77, 294, 1, 365, 1, 294, 365, 1, 300, 1, 294, 1, 300, 1, 300, 1, 300, 1, 300, 1, 1, 300, 1, 296, 1, 300, 1, 300, 300, 1, 300, 1, 300, 1, 300, 1, 365, 1, 365, 1, 245, 365, 1, 245, 365, 1, 246, 365, 1, 77, 1, 147, 1, 365, 293, 307, 308, 310, 311, 313, 314, 315, 316, 317, 318, 324, 325, 326, 327, 328, 330, 331, 332, 296, 329, 377, 1, 341, 296, 294, 1, 12, 77, 147, 291, 409, 420, 377, 430, 1, 1, 19, 51, 84, 85, 132, 158, 166, 177, 183, 195, 414, 415, 416, 417, 1, 174, 232, 318, 1, 414, 416, 1, 174, 294, 303, 318, 1, 416, 1, 61, 245, 280, 1, 365, 12, 61, 77, 114, 118, 138, 147, 236, 245, 1, 1, 53, 61, 77, 101, 134, 141, 147, 212, 221, 245, 246, 252, 1, 1, 1, 1, 29, 77, 1, 29, 77, 414, 294, 1, 365, 1, 416, 1, 365, 1, 414, 416, 1, 13, 365, 1, 416, 1, 12, 29, 61, 141, 165, 177, 212, 221, 409, 1, 174, 365, 414, 1, 300, 1, 365, 1, 1, 416, 1, 300, 365, 1, 300, 365, 1, 416, 1, 365, 1, 365, 1, 174, 303, 318, 436, 1, 365, 1, 174, 365, 1, 416, 1, 416, 292, 1, 9, 1, 9, 1, 365, 1, 365, 1, 414, 416, 436, 9, 1, 365, 1, 13, 365, 294, 1, 365, 1, 13, 365, 1, 365, 1, 365, 1, 174, 416, 1, 416, 1, 416, 1, 416, 1, 97, 188, 366, 175, 1, 300, 1, 12, 29, 77, 147, 165, 175, 177, 196, 245, 246, 394, 395, 1, 428, 1, 416, 1, 174, 232, 365, 1, 174, 232, 1, 1, 1, 174, 1, 365, 1, 365, 1, 354, 1, 300, 174, 174, 232, 1, 436, 1, 416, 1, 365, 1, 416, 1, 232, 365, 1, 12, 247, 374, 232, 1, 416, 1, 185, 1, 1, 1, 365, 414, 1, 436, 1, 365, 1, 300, 1, 1, 1, 1, 427, 1, 1, 300, 1, 300, 1, 300, 174, 1, 365, 1, 1, 300, 1, 416, 1, 365, 1, 175, 196, 247, 1, 416, 1, 300, 1, 365, 1, 365, 1, 299, 343, 1, 1, 199, 224, 295, 304, 1, 365, 339, 346, 306, 339, 1, 185, 1, 295, 365, 1, 301, 339, 1, 339, 298, 365, 416, 365, 365, 436, 377, 436, 436, 161, 296, 378, 1, 298, 365, 389, 416, 298, 365, 416, 293, 293, 293, 1, 88, 146, 150, 175, 196, 214, 215, 247, 365, 380, 412, 416, 431, 294, 12, 429, 409, 410, 411, 412, 413, 1, 413, 413, 365, 365, 1, 27, 58, 138, 208, 1, 34, 58, 60, 171, 172, 173, 203, 204, 205, 225, 165, 291, 416, 1, 203, 205, 301, 365, 399, 416, 399, 365, 399, 416, 365, 409, 1, 380, 380, 365, 365, 365, 301, 1, 365, 1, 422, 1, 365, 1, 365, 1, 365, 1, 365, 1, 365, 1, 365, 1, 365, 1, 365, 1, 365, 380, 1, 365, 380, 1, 365, 1, 365, 1, 365, 1, 365, 1, 365, 1, 365, 365, 380, 296, 235, 1, 30, 275, 276, 365, 407, 296, 300, 1, 416, 51, 293, 1, 1, 1, 365, 245, 365, 274, 388, 245, 365, 1, 34, 58, 171, 172, 173, 203, 204, 225, 1, 365, 1, 365, 1, 365, 203, 1, 360, 274, 365, 365, 77, 147, 1, 34, 203, 204, 225, 365, 396, 366, 366, 366, 101, 365, 365, 365, 365, 365, 365, 1, 365, 398, 1, 203, 293, 397, 416, 1, 12, 196, 371, 365, 293, 413, 1, 392, 1, 365, 416, 365, 350, 352, 339, 344, 345, 1, 339, 345, 416, 416, 365, 393, 375, 1, 359, 301, 399, 413, 365, 365, 365, 1, 380, 365, 365, 365, 365, 299, 341, 1, 416, 1, 416, 1, 416, 339, 365, 295, 301, 365, 365, 380, 365, 384, 365, 296, 299, 390, 57, 99, 175, 186, 214, 223, 340, 299, 391, 1, 399, 1, 399, 1, 399, 419, 413, 24, 27, 32, 34, 44, 47, 48, 50, 54, 59, 60, 61, 62, 141, 142, 145, 151, 192, 193, 204, 233, 434, 300, 12, 29, 175, 12, 29, 175, 409, 64, 186, 58, 77, 107, 108, 133, 147, 170, 187, 189, 191, 212, 221, 231, 291, 294, 433, 434, 435, 39, 44, 58, 59, 60, 61, 77, 107, 108, 133, 141, 147, 170, 187, 189, 191, 192, 193, 204, 212, 221, 231, 245, 246, 440, 441, 442, 301, 293, 301, 301, 301, 293, 301, 293, 301, 293, 301, 301, 297, 296, 293, 301, 293, 301, 301, 365, 1, 1, 297, 365, 406, 275, 276, 1, 334, 297, 1, 365, 413, 1, 399, 274, 274, 174, 274, 274, 365, 365, 365, 365, 365, 365, 436, 436, 436, 436, 339, 378, 378, 378, 12, 27, 291, 382, 293, 293, 293, 339, 436, 416, 339, 372, 293, 395, 339, 305, 186, 339, 300, 368, 369, 365, 301, 293, 293, 293, 293, 301, 299, 326, 324, 325, 298, 297, 365, 341, 341, 403, 437, 438, 413, 1, 365, 339, 64, 64, 77, 147, 365, 377, 380, 434, 64, 365, 377, 365, 64, 365, 64, 77, 147, 64, 64, 64, 365, 1, 399, 1, 399, 365, 365, 407, 1, 380, 365, 309, 297, 365, 365, 1, 297, 293, 365, 365, 365, 365, 366, 366, 365, 365, 365, 365, 368, 365, 339, 1, 301, 366, 376, 1, 293, 301, 365, 365, 365, 1, 380, 380, 380, 380, 385, 297, 299, 299, 436, 377, 293, 365, 365, 64, 64, 64, 365, 64, 365, 64, 365, 64, 365, 64, 365, 64, 64, 365, 365, 365, 301, 301, 297, 301, 293, 423, 365, 301, 301, 301, 373, 301, 12, 29, 77, 147, 165, 175, 177, 196, 245, 246, 367, 295, 342, 370, 1, 366, 301, 301, 301, 293, 341, 365, 436, 365, 365, 365, 365, 64, 365, 365, 64, 365, 365, 365, 365, 301, 416, 365, 301, 370, 1, 367, 1, 380, 299, 365, 301, 365, 365, 301, 301 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab /* Like YYERROR except do call yyerror. This remains here temporarily to ease the transition to the new meaning of YYERROR, for GCC. Once GCC version 2 has supplanted version 1, this can go. */ #define YYFAIL goto yyerrlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY && yylen == 1) \ { \ yychar = (Token); \ yylval = (Value); \ yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK; \ goto yybackup; \ } \ else \ { \ yyerror (YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (0) #define YYTERROR 1 #define YYERRCODE 256 /* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. If N is 0, then set CURRENT to the empty location which ends the previous symbol: RHS[0] (always defined). */ #define YYRHSLOC(Rhs, K) ((Rhs)[K]) #ifndef YYLLOC_DEFAULT # define YYLLOC_DEFAULT(Current, Rhs, N) \ do \ if (N) \ { \ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ } \ else \ { \ (Current).first_line = (Current).last_line = \ YYRHSLOC (Rhs, 0).last_line; \ (Current).first_column = (Current).last_column = \ YYRHSLOC (Rhs, 0).last_column; \ } \ while (0) #endif /* YY_LOCATION_PRINT -- Print the location on the stream. This macro was not mandated originally: define only if we know we won't break user code: when these are the locations we know. */ #ifndef YY_LOCATION_PRINT # if YYLTYPE_IS_TRIVIAL # define YY_LOCATION_PRINT(File, Loc) \ fprintf (File, "%d.%d-%d.%d", \ (Loc).first_line, (Loc).first_column, \ (Loc).last_line, (Loc).last_column) # else # define YY_LOCATION_PRINT(File, Loc) ((void) 0) # endif #endif /* YYLEX -- calling `yylex' with the right arguments. */ #ifdef YYLEX_PARAM # define YYLEX yylex (&yylval, YYLEX_PARAM) #else # define YYLEX yylex (&yylval) #endif /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (0) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yysymprint (stderr, \ Type, Value); \ YYFPRINTF (stderr, "\n"); \ } \ } while (0) /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ #if defined (__STDC__) || defined (__cplusplus) static void yy_stack_print (short int *bottom, short int *top) #else static void yy_stack_print (bottom, top) short int *bottom; short int *top; #endif { YYFPRINTF (stderr, "Stack now"); for (/* Nothing. */; bottom <= top; ++bottom) YYFPRINTF (stderr, " %d", *bottom); YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (0) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ #if defined (__STDC__) || defined (__cplusplus) static void yy_reduce_print (int yyrule) #else static void yy_reduce_print (yyrule) int yyrule; #endif { int yyi; unsigned long int yylno = yyrline[yyrule]; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu), ", yyrule - 1, yylno); /* Print the symbols being reduced, and their result. */ for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++) YYFPRINTF (stderr, "%s ", yytname[yyrhs[yyi]]); YYFPRINTF (stderr, "-> %s\n", yytname[yyr1[yyrule]]); } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (Rule); \ } while (0) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined (__GLIBC__) && defined (_STRING_H) # define yystrlen strlen # else /* Return the length of YYSTR. */ static YYSIZE_T # if defined (__STDC__) || defined (__cplusplus) yystrlen (const char *yystr) # else yystrlen (yystr) const char *yystr; # endif { const char *yys = yystr; while (*yys++ != '\0') continue; return yys - yystr - 1; } # endif # endif # ifndef yystpcpy # if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE) # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ static char * # if defined (__STDC__) || defined (__cplusplus) yystpcpy (char *yydest, const char *yysrc) # else yystpcpy (yydest, yysrc) char *yydest; const char *yysrc; # endif { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { size_t yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return yystpcpy (yyres, yystr) - yyres; } # endif #endif /* YYERROR_VERBOSE */ #if YYDEBUG /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ #if defined (__STDC__) || defined (__cplusplus) static void yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep) #else static void yysymprint (yyoutput, yytype, yyvaluep) FILE *yyoutput; int yytype; YYSTYPE *yyvaluep; #endif { /* Pacify ``unused variable'' warnings. */ (void) yyvaluep; if (yytype < YYNTOKENS) YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); else YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); # endif switch (yytype) { default: break; } YYFPRINTF (yyoutput, ")"); } #endif /* ! YYDEBUG */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ #if defined (__STDC__) || defined (__cplusplus) static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) #else static void yydestruct (yymsg, yytype, yyvaluep) const char *yymsg; int yytype; YYSTYPE *yyvaluep; #endif { /* Pacify ``unused variable'' warnings. */ (void) yyvaluep; if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); switch (yytype) { default: break; } } /* Prevent warnings from -Wmissing-prototypes. */ #ifdef YYPARSE_PARAM # if defined (__STDC__) || defined (__cplusplus) int yyparse (void *YYPARSE_PARAM); # else int yyparse (); # endif #else /* ! YYPARSE_PARAM */ #if defined (__STDC__) || defined (__cplusplus) int yyparse (void); #else int yyparse (); #endif #endif /* ! YYPARSE_PARAM */ /*----------. | yyparse. | `----------*/ #ifdef YYPARSE_PARAM # if defined (__STDC__) || defined (__cplusplus) int yyparse (void *YYPARSE_PARAM) # else int yyparse (YYPARSE_PARAM) void *YYPARSE_PARAM; # endif #else /* ! YYPARSE_PARAM */ #if defined (__STDC__) || defined (__cplusplus) int yyparse (void) #else int yyparse () ; #endif #endif { /* The look-ahead symbol. */ int yychar; /* The semantic value of the look-ahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; int yystate; int yyn; int yyresult; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* Look-ahead token as an internal (translated) token number. */ int yytoken = 0; /* Three stacks and their tools: `yyss': related to states, `yyvs': related to semantic values, `yyls': related to locations. Refer to the stacks thru separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ short int yyssa[YYINITDEPTH]; short int *yyss = yyssa; short int *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs = yyvsa; YYSTYPE *yyvsp; #define YYPOPSTACK (yyvsp--, yyssp--) YYSIZE_T yystacksize = YYINITDEPTH; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; /* When reducing, the number of symbols on the RHS of the reduced rule. */ int yylen; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ /* Initialize stack pointers. Waste one element of value and location stack so that they stay on the same level as the state stack. The wasted elements are never initialized. */ yyssp = yyss; yyvsp = yyvs; goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. so pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; short int *yyss1 = yyss; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yystacksize); yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { short int *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss); YYSTACK_RELOCATE (yyvs); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. */ /* Read a look-ahead token if we need one and don't already have one. */ /* yyresume: */ /* First try to decide what to do without reference to look-ahead token. */ yyn = yypact[yystate]; if (yyn == YYPACT_NINF) goto yydefault; /* Not known => get a look-ahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = YYLEX; } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yyn == 0 || yyn == YYTABLE_NINF) goto yyerrlab; yyn = -yyn; goto yyreduce; } if (yyn == YYFINAL) YYACCEPT; /* Shift the look-ahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the token being shifted unless it is eof. */ if (yychar != YYEOF) yychar = YYEMPTY; *++yyvsp = yylval; /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; yystate = yyn; goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: `$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; YY_REDUCE_PRINT (yyn); switch (yyn) { case 3: { begin_local_scope(); ;} break; case 4: { end_local_scope(); ;} break; case 5: { (yyval).i = makenode(CMDLIST_,(yyvsp[0]).i,0); ;} break; case 6: { (yyval).i = (yyvsp[0]).i; /* for commands distinguishable by First tok */;} break; case 7: { int p = makenode(PIPE_,(yyvsp[0]).i,0); subtree_swap(&(yyvsp[-2]).i,&p); /* so pipe executed first */ (yyval).i = makenode(PIPE_END_,p,(yyvsp[-2]).i); ;} break; case 8: {kb_error(2330,"Piping must be to quoted string or string expression.\n",Q_ERROR);;} break; case 9: { int p = makenode(REDIRECT_,(yyvsp[0]).i,0); subtree_swap(&(yyvsp[-2]).i,&p); /* so file openedfirst */ (yyval).i = makenode(REDIRECT_END_,p,(yyvsp[-2]).i); ;} break; case 10: { kb_error(2331, "Redirection must be to quoted string or string expression.\n",Q_ERROR);;} break; case 11: { int p = makenode(REDIRECTOVER_,(yyvsp[0]).i,0); subtree_swap(&(yyvsp[-2]).i,&p); /* so file openedfirst */ (yyval).i = makenode(REDIRECT_END_,p,(yyvsp[-2]).i); ;} break; case 12: { kb_error(2332, "Redirection must be to quoted string or string expression.\n",Q_ERROR);;} break; case 13: { (yyval).i = makenode(SET_BREAKPOINT_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 14: { (yyval).i = makenode(SET_BREAKPOINT_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 15: { (yyval).i = makenode(SET_BREAKPOINT_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 16: { kb_error(5981,"Syntax: breakpoint procedurename linenumber\n", Q_ERROR); ;} break; case 17: { (yyval).i = makenode(UNSET_BREAKPOINT_,0,0); ;} break; case 18: { (yyval).i = makenode(WHEREAMI_COMMAND_,0,0); ;} break; case 19: { kb_error(3988,"Illegal command syntax.\n", Q_ERROR); ;} break; case 20: { (yyval).i = makenode(PROCEDURE_,(yyvsp[0]).i,0); ;} break; case 21: { int init = makenode(REPEAT_INIT_,(yyvsp[0]).i,0); (yyval).i = makenode(PROCEDURE_,(yyvsp[-1]).i,0); (yyval).i = makenode(REPEAT_,init,(yyval).i); ;} break; case 22: { kb_error(3600,"Missing semicolon?",Q_ERROR); ;} break; case 23: { (yyval).i = makenode(PERM_PROCEDURE_,(yyvsp[0]).i,0); ;} break; case 24: { int init = makenode(REPEAT_INIT_,(yyvsp[0]).i,0); (yyval).i = makenode(PERM_PROCEDURE_,(yyvsp[-1]).i,0); (yyval).i = makenode(REPEAT_,init,(yyval).i); ;} break; case 25: { kb_error(3601,"Procedure has no arguments; can be followed by repetition count.",Q_ERROR); ;} break; case 26: { kb_error(2333,"Missing semicolon?\n",Q_ERROR); ;} break; case 27: { (yyval).i = (yyvsp[0]).i; ;} break; case 28: { (yyval).i = (yyvsp[0]).i; ;} break; case 29: { begin_local_scope(); ;} break; case 30: { end_local_scope(); (yyval).i = makenode(COMMAND_BLOCK_,(yyvsp[-1]).i,0); ;} break; case 31: { (yyval).i = makenode(NULLBLOCK_,0,0); ;} break; case 32: { kb_error(3602,"Error following '{'",Q_ERROR); ;} break; case 33: { (yyval).i = (yyvsp[0]).i; ;} break; case 34: { (yyval).i = (yyvsp[0]).i; ;} break; case 35: { (yyval).i = (yyvsp[0]).i; ;} break; case 36: { int init = makenode(REPEAT_INIT_,(yyvsp[0]).i,0); subtree_swap(&(yyvsp[-1]).i,&init); (yyval).i = makenode(REPEAT_,init,(yyvsp[-1]).i); ;} break; case 37: { kb_error(3603, "Error following command block; expected ';' or repetition count or nothing.", Q_ERROR); ;} break; case 38: { (yyval).i = makenode(NULLCMD_,0,0); ;} break; case 39: { (yyval).i = (yyvsp[-1]).i; ;} break; case 40: { (yyval).i = (yyvsp[0]).i; ;} break; case 41: { (yyval).i = (yyvsp[0]).i; ;} break; case 42: { (yyval).i = (yyvsp[0]).i; ;} break; case 43: { (yyval).i = makenode(CMDLIST_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 44: { (yyval).i = makenode(CMDLIST_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 45: { (yyval).i = makenode(IFTEST_,(yyvsp[0]).i,0); ;} break; case 46: { (yyval).i = makenode(IF_,(yyvsp[-2]).i,(yyvsp[0]).i); ;} break; case 47: { kb_error(2334,"Syntax: IF rexpr THEN command [ ELSE command ]\n",Q_ERROR);;} break; case 48: {(yyval).i = makenode(ELSE_,(yyvsp[0]).i,0); ;} break; case 49: { (yyval).i = makenode(ELSE_,(yyvsp[-2]).i,(yyvsp[0]).i); ;} break; case 50: { kb_error(2335,"Illegal ELSE. Don't use semicolon before ELSE.\n",Q_ERROR); ;} break; case 51: { (yyval).i = makenode(SINGLE_LETTER_,'?',0); ;} break; case 52: { (yyval).i = makenode(GEOMVIEW_,(yyvsp[0]).i,0); ;} break; case 53: { (yyval).i = makenode(GEOMVIEW_TOGGLE_,(yyvsp[0]).i,0); ;} break; case 54: { (yyval).i = makenode(GEOMVIEW_TOGGLE_,ON_,0); ;} break; case 55: { kb_error(2336,"Syntax: GEOMVIEW ON|OFF or GEOMVIEW \"geomview command\"\n",Q_ERROR); ;} break; case 56: { (yyval).i = makenode(GEOMPIPE_,(yyvsp[0]).i,0); ;} break; case 57: { (yyval).i = makenode(GEOMPIPE_TOGGLE_,(yyvsp[0]).i,0); ;} break; case 58: { (yyval).i = makenode(GEOMPIPE_TOGGLE_,ON_,0); ;} break; case 59: { kb_error(2337,"Syntax: GEOMPIPE ON|OFF or GEOMPIPE \"shell command\"\n",Q_ERROR); ;} break; case 60: { (yyval).i = makenode(LOGFILE_,(yyvsp[0]).i,0); ;} break; case 61: { (yyval).i = makenode(LOGFILE_TOGGLE_,(yyvsp[0]).i,0); ;} break; case 62: { (yyval).i = makenode(LOGFILE_TOGGLE_,ON_,0); ;} break; case 63: { kb_error(2338,"Syntax: LOGFILE ON|OFF or LOGFILE \"filename\"\n",Q_ERROR); ;} break; case 64: { (yyval).i = makenode(KEYLOGFILE_,(yyvsp[0]).i,0); ;} break; case 65: { (yyval).i = makenode(KEYLOGFILE_TOGGLE_,(yyvsp[0]).i,0); ;} break; case 66: { (yyval).i = makenode(KEYLOGFILE_TOGGLE_,ON_,0); ;} break; case 67: { kb_error(2419,"Syntax: KEYLOGFILE ON|OFF or KEYLOGFILE \"filename\"\n",Q_ERROR); ;} break; case 68: { (yyval).i = makenode( POSTSCRIPT_,(yyvsp[0]).i,0); ;} break; case 69: { kb_error(3361,"Syntax: POSTSCRIPT \"filename\"\n",Q_ERROR); ;} break; case 70: { (yyval).i = makenode( BINARY_OFF_FILE_,(yyvsp[0]).i,0); ;} break; case 71: { kb_error(4339,"Syntax: BINARY_OFF_FILE \"filename\"\n",Q_ERROR); ;} break; case 72: { (yyval).i = makenode( OOGLFILE_,(yyvsp[0]).i,0); ;} break; case 73: { kb_error(2339,"Syntax: OOGLFILE \"filename\"\n",Q_ERROR); ;} break; case 74: { (yyval).i = makenode(HISTORY_,0,0); ;} break; case 75: { kb_error(2340,"Syntax: HISTORY (no arguments)\n",Q_ERROR); ;} break; case 76: { (yyval).i = makenode(RETURN_,0,0); ;} break; case 77: { (yyval).i = makenode(RETURN_,(yyvsp[0]).i,0); ;} break; case 78: { kb_error(2341,"Syntax: RETURN [expr] \n",Q_ERROR); ;} break; case 79: { (yyval).i = makenode(BREAK_,1,0); ;} break; case 80: { (yyval).i = makenode(BREAK_,(yyvsp[0]).i,0); ;} break; case 81: { kb_error(2342,"Syntax: BREAK or BREAK integer (to break multiple levels)\n",Q_ERROR); ;} break; case 82: { (yyval).i = makenode(CONTINUE_,1,0); ;} break; case 83: { (yyval).i = makenode(CONTINUE_,(yyvsp[0]).i,0); ;} break; case 84: { kb_error(2343,"Syntax: CONTINUE or CONTINUE integer (to continue in higher level loop)\n",Q_ERROR); ;} break; case 85: { (yyval).i = makenode(SINGLE_LETTER_,'g',0); ;} break; case 86: { int init,count,g; real_val = (yyvsp[0]).i; count = makenode(PUSHCONST,0,0); init = makenode(REPEAT_INIT_,count,0); g = makenode(SINGLE_LETTER_,'g',0); (yyval).i = makenode(REPEAT_,init,g); ;} break; case 87: { int init,g; init = makenode(REPEAT_INIT_,(yyvsp[0]).i,0); g = makenode(SINGLE_LETTER_,'g',0); (yyval).i = makenode(REPEAT_,init,g); ;} break; case 88: { kb_error(3666,"Syntax: GO count\n",Q_ERROR); ;} break; case 89: { (yyval).i = makenode(WHILE_TOP_,(yyvsp[0]).i,0); ;} break; case 90: { (yyval).i = (yyvsp[-1]).i; ;} break; case 91: { (yyval).i = makenode(WHILE_END_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 92: { kb_error(2344,"Syntax: WHILE rexpr DO command\n",Q_ERROR); ;} break; case 93: { (yyval).i = makenode(DO_ENTRY_,0,0);;} break; case 94: { (yyval).i = makenode(DO_TOP_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 95: { (yyval).i = makenode(DO_END_,(yyvsp[-2]).i,(yyvsp[0]).i); ;} break; case 96: { kb_error(4345,"Missing WHILE at end of DO statement.\n",Q_ERROR); ;} break; case 97: { kb_error(2345,"Syntax: DO command WHILE expr\n",Q_ERROR); ;} break; case 98: { (yyval).i = makenode(FOR_ENTRY_,(yyvsp[0]).i,0); ;} break; case 99: { (yyval).i = makenode(FOR_HEAD_,(yyvsp[-2]).i,(yyvsp[-1]).i); ;} break; case 100: { int tmp; real_val = 1; tmp = makenode(PUSHCONST,0,0); (yyval).i = makenode(FOR_HEAD_,(yyvsp[-1]).i,tmp); ;} break; case 101: { (yyval).i = makenode(FOR_TOP_,(yyvsp[-2]).i,(yyvsp[-1]).i); ;} break; case 102: { int tmp = makenode(NULLCMD_,0,0); (yyval).i = makenode(FOR_TOP_,(yyvsp[-1]).i,tmp); ;} break; case 103: { (yyval).i = makenode(FOR_END_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 104: { kb_error(2514, "Syntax: FOR ( command ; rexpr ; command ) command\n",Q_ERROR); ;} break; case 105: { kb_error(3668, "Error in initializer of FOR loop.\n",Q_ERROR); ;} break; case 106: { kb_error(3669, "Error in test expression of FOR loop.\n",Q_ERROR); ;} break; case 107: { kb_error(3670, "Error in increment part of FOR loop.\n",Q_ERROR); ;} break; case 108: { kb_error(2844, "Bad FOR loop body. Try starting body on same line as FOR header.\n", Q_ERROR); ;} break; case 109: { (yyval).i = makenode(SINGLE_LETTER_,(yyvsp[0]).i,0); ;} break; case 110: { (yyval).i = makenode(SINGLE_REDEFD_,(yyvsp[0]).i,0); ;} break; case 111: { int init = makenode(REPEAT_INIT_,(yyvsp[0]).i,0); (yyval).i = makenode(SINGLE_REDEFD_,(yyvsp[-1]).i,0); (yyval).i = makenode(REPEAT_,init,(yyval).i); ;} break; case 112: { kb_error(3671, "Expected repetition count after redefined single letter.\n",Q_ERROR); ;} break; case 113: { (yyval).i = makenode(SINGLE_LETTER_,(yyvsp[0]).i,0); ;} break; case 114: { int init = makenode(REPEAT_INIT_,(yyvsp[0]).i,0); (yyval).i = makenode(SINGLE_LETTER_,(yyvsp[-1]).i,0); (yyval).i = makenode(REPEAT_,init,(yyval).i); ;} break; case 115: { kb_error(3672, "Expected repetition count after single letter command.\n",Q_ERROR); ;} break; case 116: { assigntype = ASSIGN_; switch ((yyvsp[-1]).i) { case 't': (yyval).i = makenode(EDGEWEED_,(yyvsp[0]).i,0); break; case 'w': (yyval).i = makenode(AREAWEED_,(yyvsp[0]).i,0); break; case 'l': (yyval).i = makenode(EDGEDIVIDE_,(yyvsp[0]).i,0); break; case 'm': (yyval).i = makenode(SET_SCALE_,(yyvsp[0]).i,0); break; case 'n': (yyval).i = makenode(NOTCH_,(yyvsp[0]).i,0); break; case 'j': (yyval).i = makenode(JIGGLE_,(yyvsp[0]).i,0); break; case 'G': (yyval).i = makenode(SET_GRAVITY_,(yyvsp[0]).i,0); break; case 'P': (yyval).i = makenode(INVOKE_P_MENU_,(yyvsp[0]).i,0); break; case 'M': (yyval).i = makenode(SET_MODEL_,(yyvsp[0]).i,0); break; case 'y': (yyval).i = makenode(TORDUP_,(yyvsp[0]).i,0); break; case 'K': (yyval).i = makenode(SKINNY_,(yyvsp[0]).i,0); break; case 'k': (yyval).i = makenode(SET_GAP_CONSTANT_,(yyvsp[0]).i,0); break; case 'p': (yyval).i = makenode(SET_AMBIENT_PRESSURE_,(yyvsp[0]).i,0); break; default: kb_error(1884,"Extra expression after single letter command.\n",Q_ERROR); } ;} break; case 117: { kb_error(3660, "Expected argument after single letter command.\n",Q_ERROR); ;} break; case 118: { (yyval).i = makenode(NOP_,0,0); ;} break; case 119: { (yyval).i = makenode(READ_,(yyvsp[0]).i,0); ;} break; case 120: { kb_error(2346,"Syntax: READ \"filename\" (need quoted string or string expression)\n",Q_ERROR);;} break; case 121: { (yyval).i = makenode(TRANSFORM_DEPTH_,(yyvsp[0]).i,0);;} break; case 122: { (yyval).i = makenode(TRANSFORM_DEPTH_,(yyvsp[0]).i,0);;} break; case 123: { kb_error(2348,"Syntax: TRANSFORM_DEPTH := integer\n",Q_ERROR);;} break; case 124: { (yyval).i = makenode(TRANSFORM_EXPR_,(yyvsp[0]).i,0);;} break; case 125: { (yyval).i = makenode(TRANSFORM_EXPR_,(yyvsp[0]).i,0);;} break; case 126: { kb_error(2349,"Syntax: TRANSFORM_EXPR := string (quoted string or string expression) \n",Q_ERROR); ;} break; case 127: { (yyval).i = makenode(SYSTEM_,(yyvsp[0]).i,0); ;} break; case 128: { kb_error(2350,"Syntax: SYSTEM \"command\" (need quoted string or string expression)\n",Q_ERROR);;} break; case 129: { (yyval).i = makenode(EXEC_,(yyvsp[0]).i,0); ;} break; case 130: { kb_error(2351,"Syntax: EXEC string (need quoted string or string expression)\n",Q_ERROR);;} break; case 131: { (yyval).i = makenode(PARALLEL_EXEC_,(yyvsp[0]).i,0); ;} break; case 132: { kb_error(3115,"Syntax: PARALLEL_EXEC string (need quoted string or string expression)\n",Q_ERROR);;} break; case 133: { (yyval).i = makenode(TASK_EXEC_,(yyvsp[-2]).i,(yyvsp[0]).i); ;} break; case 134: { kb_error(3119,"Syntax: TASK_EXEC nodenumber, string (need quoted string or string expression)\n",Q_ERROR);;} break; case 135: { (yyval).i = makenode(CHDIR_,(yyvsp[0]).i,0); ;} break; case 136: { kb_error(2352,"Syntax: CHDIR \"command\" (need quoted string or string expression)\n",Q_ERROR);;} break; case 137: { (yyval).i = makenode(METIS_,(yyvsp[0]).i,0); ;} break; case 138: { kb_error(3236,"Syntax: METIS numparts\n",Q_ERROR); ;} break; case 139: { (yyval).i = makenode(KMETIS_,(yyvsp[0]).i,0); ;} break; case 140: { kb_error(2354,"Syntax: KMETIS numparts\n",Q_ERROR); ;} break; case 141: { (yyval).i = makenode(METIS_READJUST_,(yyvsp[0]).i,0); ;} break; case 142: { kb_error(3237,"Syntax: METIS_READJUST numparts\n",Q_ERROR); ;} break; case 143: { (yyval).i = makenode(BODY_METIS_,(yyvsp[0]).i,0); ;} break; case 144: { kb_error(3775,"Syntax: BODY_METIS numparts\n",Q_ERROR); ;} break; case 145: { (yyval).i = makenode(OMETIS_,(yyvsp[0]).i,0); ;} break; case 146: { (yyval).i = makenode(OMETIS_,0,0); ;} break; case 147: { kb_error(2355,"Syntax: OMETIS or OMETIS expr\n",Q_ERROR); ;} break; case 148: { (yyval).i = makenode(EDGEWEED_,(yyvsp[0]).i,0); ;} break; case 149: { kb_error(2356,"Syntax: EDGEWEED minlength\n",Q_ERROR);;} break; case 150: { (yyval).i = makenode(AREAWEED_,(yyvsp[0]).i,0); ;} break; case 151: { kb_error(2357,"Syntax: AREAWEED minarea\n",Q_ERROR);;} break; case 152: { (yyval).i = makenode(EDGEDIVIDE_,(yyvsp[0]).i,0); ;} break; case 153: { kb_error(2358,"Syntax: EDGE_DIVIDE maxlength\n",Q_ERROR);;} break; case 154: { (yyval).i = makenode(LANCZOS_,(yyvsp[-3]).i,(yyvsp[-1]).i); ;} break; case 155: { (yyval).i = makenode(LANCZOS_,(yyvsp[0]).i,0); ;} break; case 156: { kb_error(2359,"Syntax: lanczos rexpr or lanczos(expr,count) \n",Q_ERROR);;} break; case 157: { (yyval).i = makenode(RITZ_,(yyvsp[-3]).i,(yyvsp[-1]).i); ;} break; case 158: { kb_error(2360,"Syntax: RITZ(probe_value, number_of_eigenvalues)\n",Q_ERROR); ;} break; case 159: { (yyval).i = makenode(EIGENPROBE_,(yyvsp[-3]).i,(yyvsp[-1]).i); ;} break; case 160: { (yyval).i = makenode(EIGENPROBE_,(yyvsp[0]).i,0); ;} break; case 161: { kb_error(2361, "Syntax: EIGENPROBE probe_value or EIGENPROBE(probe_value, iterationmax)\n",Q_ERROR); ;} break; case 162: { (yyval).i = makenode(MOVE_,(yyvsp[0]).i,0); ;} break; case 163: { kb_error(2362,"Syntax: MOVE stepsize\n",Q_ERROR); ;} break; case 164: { (yyval).i = makenode(HESSIAN_SADDLE_,0,0); ;} break; case 165: { (yyval).i = makenode(HESSIAN_SADDLE_,(yyvsp[0]).i,0); ;} break; case 166: { kb_error(2363,"Syntax: SADDLE or SADDLE maxstepsize\n", Q_ERROR); ;} break; case 167: { (yyval).i = makenode(HESSIAN_SEEK_,0,0); ;} break; case 168: { (yyval).i = makenode(HESSIAN_SEEK_,(yyvsp[0]).i,0); ;} break; case 169: { kb_error(2364, "Syntax: HESSIAN_SEEK or HESSIAN_SEEK maxstepsize\n", Q_ERROR); ;} break; case 170: { (yyval).i = makenode(COUNTS_,0,0); ;} break; case 171: { (yyval).i = makenode(SINGLE_LETTER_,'q',0); ;} break; case 172: { (yyval).i = makenode(SUBCOMMAND_,'q',0); ;} break; case 173: { (yyval).i = makenode(ABORT_,'q',0); ;} break; case 174: { (yyval).i = makenode(SIMPLEX_TO_FE_,'q',0); ;} break; case 175: { (yyval).i = makenode(REORDER_STORAGE_,0,0); ;} break; case 176: { (yyval).i = makenode(RENUMBER_ALL_,0,0); ;} break; case 177: { (yyval).i = makenode(DUMP_MEMLIST_,0,0); ;} break; case 178: { (yyval).i = makenode(FREE_DISCARDS_,0,0); ;} break; case 179: { (yyval).i = makenode(REPARTITION_,0,0); ;} break; case 180: { (yyval).i = makenode(EXTRAPOLATE_,0,0); ;} break; case 181: { (yyval).i = makenode(REBODY_,0,0); ;} break; case 182: { (yyval).i = makenode(ZOOM_,0,0); ;} break; case 183: { (yyval).i = makenode(ZOOM_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 184: { kb_error(2365,"Syntax: ZOOM [ vertex_id radius ]\n",Q_ERROR);;} break; case 185: { (yyval).i = makenode(BURCHARD_,(yyvsp[0]).i,0); ;} break; case 186: { (yyval).i = makenode(LAGRANGE_,(yyvsp[0]).i,0); ;} break; case 187: { kb_error(2366,"Syntax: LAGRANGE order\n",Q_ERROR); ;} break; case 188: { (yyval).i = makenode(tok,0,0); ;} break; case 189: { (yyval).i = makenode(tok,0,0); ;} break; case 190: { (yyval).i = makenode(PRINT_PROFILING_,0,0); ;} break; case 191: { (yyval).i = makenode(tok,0,0); ;} break; case 192: { (yyval).i = makenode(tok,0,0); ;} break; case 193: { (yyval).i = makenode(tok,0,0); ;} break; case 194: { (yyval).i = makenode(tok,0,0); ;} break; case 195: { (yyval).i = makenode(tok,0,0); ;} break; case 196: { (yyval).i = makenode(tok,0,0); ;} break; case 197: { (yyval).i = makenode(tok,0,0); ;} break; case 198: { (yyval).i = makenode(tok,0,0); ;} break; case 199: { (yyval).i = makenode(tok,0,0); ;} break; case 200: { (yyval).i = makenode(tok,0,0); ;} break; case 201: { (yyval).i = makenode(tok,0,0); ;} break; case 202: { (yyval).i = makenode(tok,0,0); ;} break; case 203: { (yyval).i = makenode(tok,0,0); ;} break; case 204: { (yyval).i = makenode(tok,0,0); ;} break; case 205: { (yyval).i = makenode(tok,0,0); ;} break; case 206: { (yyval).i = makenode(tok,0,0); ;} break; case 207: { (yyval).i = makenode(tok,0,0); ;} break; case 208: { (yyval).i = makenode(tok,0,0); ;} break; case 209: { (yyval).i = makenode(tok,0,0); ;} break; case 210: { (yyval).i = makenode(tok,0,0); ;} break; case 211: { (yyval).i = makenode(tok,0,0); ;} break; case 212: { (yyval).i = makenode(tok,0,0); ;} break; case 213: { (yyval).i = makenode(SINGLE_LETTER_,'h',0); ;} break; case 214: { (yyval).i = makenode(tok,0,0); ;} break; case 215: { (yyval).i = makenode(TOPINFO_,0,0); ;} break; case 216: { (yyval).i = makenode(BOTTOMINFO_,0,0); ;} break; case 217: { (yyval).i = makenode(LIST_ATTRIBUTES_,0,0); ;} break; case 218: { (yyval).i = makenode(LIST_PROCS_,0,0); ;} break; case 219: { (yyval).i = makenode(LIST_BOUNDARY_,(yyvsp[0]).i,0);;} break; case 220: { int k = makenode(PUSHCONST,(yyvsp[0]).i,0); (yyval).i = makenode(LIST_BOUNDARY_,k,0); list[(yyval).i].op1.bdry_id = (yyvsp[0]).i; ;} break; case 221: { (yyval).i = makenode(LIST_CONSTRAINT_,(yyvsp[0]).i,0);;} break; case 222: { int k = makenode(PUSHCONST,(yyvsp[0]).i,0); (yyval).i = makenode(LIST_CONSTRAINT_,k,0); list[(yyval).i].op1.con_id = (yyvsp[0]).i; ;} break; case 223: { (yyval).i = makenode(LIST_QUANTITY_,(yyvsp[0]).i,0);;} break; case 224: { (yyval).i = makenode(LIST_QUANTITY_,(yyvsp[0]).i,0);;} break; case 225: { (yyval).i = makenode(LIST_METHOD_INSTANCE_,(yyvsp[0]).i,0);;} break; case 226: { (yyval).i = makenode(LIST_METHOD_INSTANCE_,(yyvsp[0]).i,0);;} break; case 227: { (yyval).i = makenode(CLOSE_SHOW_,0,0); ;} break; case 228: { (yyval).i = makenode((yyvsp[-1]).i,(yyvsp[0]).i,0); ;} break; case 229: { (yyval).i = makenode((yyvsp[0]).i,ON_,0); ;} break; case 230: { (yyval).i = makenode((yyvsp[-1]).i,(yyvsp[0]).i,0); ;} break; case 231: { (yyval).i = makenode((yyvsp[0]).i,ON_,0); ;} break; case 232: { (yyval).i = makenode((yyvsp[-1]).i,(yyvsp[0]).i,0); ;} break; case 233: { (yyval).i = makenode((yyvsp[0]).i,ON_,0); ;} break; case 234: { (yyval).i = makenode((yyvsp[0]).i,ON_,0); ;} break; case 235: {verb_flag=0;;} break; case 236: { YYACCEPT; ;} break; case 237: { YYACCEPT; ;} break; case 238: {verb_flag=0;;} break; case 239: { assigntype = (yyvsp[-2]).i; (yyval).i = makenode(SET_INTERNAL_,(yyvsp[-3]).i,(yyvsp[0]).i); ;} break; case 240: { kb_error(3673,"Expected expression to assign to internal variable.\n",Q_ERROR);;} break; case 241: {verb_flag=0;;} break; case 242: { assigntype = ASSIGN_; (yyval).i = makenode(SET_INTERNAL_,(yyvsp[-2]).i,(yyvsp[0]).i); ;} break; case 243: { kb_error(3661,"Expected expression for setting internal variable.\n",Q_ERROR);;} break; case 244: { (yyval).i = makenode(SET_GRAVITY_,(yyvsp[0]).i,0); ;} break; case 245: { kb_error(3675,"Expected expression for setting gravity.\n",Q_ERROR);;} break; case 246: {assigntype = (yyvsp[-1]).i; (yyval).i = makenode(SET_GRAVITY_,(yyvsp[0]).i,0); ;} break; case 247: { kb_error(2367,"Syntax: GRAVITY := rexpr \n GRAVITY ON|OFF\n",Q_ERROR);;} break; case 248: { (yyval).i = makenode(SET_CONSTRAINT_GLOBAL,(yyvsp[-1]).i,0); ;} break; case 249: { (yyval).i = makenode(UNSET_CONSTRAINT_GLOBAL,(yyvsp[-1]).i,0); ;} break; case 250: { (yyval).i = makenode(SET_CONSTRAINT_NAME_GLOBAL,(yyvsp[-1]).i,0); ;} break; case 251: { (yyval).i = makenode(SET_CONSTRAINT_NAME_GLOBAL,(yyvsp[-1]).i,0); ;} break; case 252: { (yyval).i = makenode(SET_CONSTRAINT_NAME_GLOBAL,(yyvsp[-1]).i,0); ;} break; case 253: { (yyval).i = makenode(SET_CONSTRAINT_NAME_GLOBAL,(yyvsp[-1]).i,0); ;} break; case 254: { assigntype = ASSIGN_; (yyval).i = makenode(SET_INTERNAL_,V_SCALE,(yyvsp[0]).i); ;} break; case 255: { kb_error(3676,"Syntax: SET SCALE expr\n",Q_ERROR);;} break; case 256: { assigntype = (yyvsp[-1]).i; (yyval).i = makenode(SET_INTERNAL_,V_SCALE,(yyvsp[0]).i); ;} break; case 257: { kb_error(3677,"Syntax: SCALE := expr\n",Q_ERROR);;} break; case 258: { assigntype = ASSIGN_; (yyval).i = makenode(SET_INTERNAL_,V_DIFFUSION,(yyvsp[0]).i); ;} break; case 259: { kb_error(3662,"Syntax: SET DIFFUSION expr\n",Q_ERROR);;} break; case 260: { assigntype = (yyvsp[-1]).i; (yyval).i = makenode(SET_INTERNAL_,V_GAP_CONSTANT,(yyvsp[0]).i); ;} break; case 261: { kb_error(2369,"Syntax: GAP_CONSTANT := expr\n",Q_ERROR);;} break; case 262: { (yyval).i = makenode(SET_FIXED_AREA_,(yyvsp[0]).i,0); ;} break; case 263: { (yyval).i = makenode(NOTCH_,(yyvsp[0]).i,0); ;} break; case 264: { kb_error(2371,"Syntax: NOTCH maxangle\n",Q_ERROR);;} break; case 265: { (yyval).i = makenode(SET_AUTOCHOP_,(yyvsp[0]).i,0); ;} break; case 266: { (yyval).i = makenode(SET_AUTOCHOP_,(yyvsp[0]).i,0); ;} break; case 267: { kb_error(2372, "Syntax: AUTOCHOP ON|OFF or AUTOCHOP choplength\n", Q_ERROR); ;} break; case 268: { assigntype = (yyvsp[-1]).i; (yyval).i = makenode(SET_QTARGET_,(yyvsp[0]).i,(yyvsp[-4]).i); ;} break; case 269: { assigntype = (yyvsp[-1]).i; (yyval).i = makenode(SET_QMODULUS_,(yyvsp[0]).i,(yyvsp[-4]).i); ;} break; case 270: { assigntype = (yyvsp[-1]).i; (yyval).i = makenode(SET_QTOLERANCE_,(yyvsp[0]).i,(yyvsp[-4]).i); ;} break; case 271: { assigntype = (yyvsp[-1]).i; (yyval).i = makenode(SET_MMODULUS_,(yyvsp[0]).i,(yyvsp[-4]).i); ;} break; case 272: { assigntype = (yyvsp[-1]).i; (yyval).i = makenode(SET_QVOLCONST_,(yyvsp[0]).i,(yyvsp[-4]).i); ;} break; case 273: { kb_error(3372, "Syntax: QUANTITY_NAME . TARGET|MODULUS|TOLERANCE|VOLCONST := expr\n", Q_ERROR); ;} break; case 274: { kb_error(3379, "Syntax: METHOD_NAME . MODULUS\n", Q_ERROR); ;} break; case 275: { assigntype = ASSIGN_; (yyval).i = makenode(SET_QTARGET_,(yyvsp[0]).i,(yyvsp[-2]).i); ;} break; case 276: { assigntype = ASSIGN_; (yyval).i = makenode(SET_QMODULUS_,(yyvsp[0]).i,(yyvsp[-2]).i); ;} break; case 277: { assigntype = ASSIGN_; (yyval).i = makenode(SET_QTOLERANCE_,(yyvsp[0]).i,(yyvsp[-2]).i); ;} break; case 278: { assigntype = ASSIGN_; (yyval).i = makenode(SET_MMODULUS_,(yyvsp[0]).i,(yyvsp[-2]).i); ;} break; case 279: { assigntype = ASSIGN_; (yyval).i = makenode(SET_QVOLCONST_,(yyvsp[0]).i,(yyvsp[-2]).i); ;} break; case 280: { (yyval).i = makenode(SET_Q_FIXED_,(yyvsp[-1]).i,0); ;} break; case 281: { (yyval).i = makenode(SET_Q_INFO_,(yyvsp[-1]).i,0); ;} break; case 282: { (yyval).i = makenode(SET_Q_ENERGY_,(yyvsp[-1]).i,0); ;} break; case 283: { (yyval).i = makenode(SET_Q_CONSERVED_,(yyvsp[-1]).i,0); ;} break; case 284: { strcpy(errmsg,"Syntax:\n"); strcat(errmsg," SET quantityname TARGET expr\n"); strcat(errmsg," SET quantityname MODULUS expr\n"); strcat(errmsg," SET quantityname TOLERANCE expr\n"); strcat(errmsg," SET quantityname VOLCONST expr\n"); strcat(errmsg," SET quantityname FIXED\n"); strcat(errmsg," SET quantityname INFO_ONLY\n"); strcat(errmsg," SET quantityname ENERGY\n"); strcat(errmsg," SET quantityname CONSERVED\n"); kb_error(3663,errmsg,Q_ERROR); ;} break; case 285: { (yyval).i = makenode(SUPPRESS_WARNING_,(yyvsp[0]).i,0); ;} break; case 286: { kb_error(3456, "Syntax: SUPPRESS_WARNING number\n",Q_ERROR) ;} break; case 287: { (yyval).i = makenode(UNSUPPRESS_WARNING_,(yyvsp[0]).i,0); ;} break; case 288: { kb_error(3457, "Syntax: UNSUPPRESS_WARNING number\n",Q_ERROR) ;} break; case 289: { (yyval).i = makenode(LOAD_,(yyvsp[0]).i,0); ;} break; case 290: { kb_error(2373,"Syntax: LOAD \"filename\" (need quoted string or string expression)\n",Q_ERROR);;} break; case 291: { (yyval).i = makenode(ADDLOAD_,(yyvsp[0]).i,0); ;} break; case 292: { kb_error(3544,"Syntax: ADDLOAD \"filename\" (need quoted string or string expression)\n",Q_ERROR);;} break; case 293: { (yyval).i = makenode(PERMLOAD_,(yyvsp[0]).i,0); ;} break; case 294: { kb_error(2544,"Syntax: PERMLOAD \"filename\" (need quoted string or string expression)\n",Q_ERROR);;} break; case 295: { (yyval).i = makenode(DUMP_,(yyvsp[0]).i,0); ;} break; case 296: { (yyval).i = makenode(DUMP_,0,0); ;} break; case 297: { kb_error(2374,"Syntax: DUMP \"filename\" (need quoted string or string expression)\n",Q_ERROR);;} break; case 298: { (yyval).i = makenode(SET_COLORMAP_,(yyvsp[0]).i,0); ;} break; case 299: {(yyval).i = makenode(SET_OPTIMIZE_,(yyvsp[0]).i,0);;} break; case 300: { kb_error(2375,"Syntax: OPTIMIZE maxscale\n",Q_ERROR); ;} break; case 301: { (yyval).i = (yyvsp[-1]).i; ;} break; case 302: { (yyval).i = (yyvsp[-1]).i; ;} break; case 303: { (yyval).i = makenode(SET_SGLOBAL_,(yyvsp[-2]).i,(yyvsp[0]).i); ;} break; case 304: { (yyval).i = makenode(SET_SGLOBAL_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 305: { (yyval).i = makenode(SET_GLOBAL_,(yyvsp[-2]).i,(yyvsp[0]).i); ;} break; case 306: { (yyval).i = makenode(SET_PERM_SGLOBAL_,(yyvsp[-2]).i,(yyvsp[0]).i); ;} break; case 307: { kb_error(2604,"Cannot make permanent assigment to nonpermanent variable.\n",Q_ERROR) ;} break; case 308: { kb_error(2603,"Cannot make nonpermanent assigment to permanent variable.\n",Q_ERROR) ;} break; case 309: { (yyval).i = makenode(SET_GLOBAL_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 310: { (yyval).i = makenode(SET_PERM_GLOBAL_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 311: { (yyval) = (yyvsp[-1]); ;} break; case 312: { (yyval).i = makenode(SET_ELEMENT_GLOBAL_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 313: { (yyval).i = makenode(PDELTA_LVALUE_,(yyvsp[-2]).i,0); ;} break; case 314: { (yyval).i = makenode(PSCALE_LVALUE_,(yyvsp[-2]).i,0); ;} break; case 315: { subtree_swap(&(yyvsp[-2]).i,&(yyvsp[0]).i); switch ( list[(yyvsp[-2]).i].type ) { case PDELTA_LVALUE_: list[(yyvsp[-2]).i].type = SET_DELTA_; list[(yyvsp[-2]).i].left = (yyvsp[0]).i - (yyvsp[-2]).i; break; case PSCALE_LVALUE_: list[(yyvsp[-2]).i].type = SET_PARAM_SCALE; list[(yyvsp[-2]).i].left = (yyvsp[0]).i - (yyvsp[-2]).i; break; default: sprintf(errmsg,"Internal error: lvalue type %d\n", list[(yyvsp[-2]).i].type); kb_error(2882,errmsg,COMMAND_ERROR); } list[(yyvsp[-2]).i].op2.assigntype = (yyvsp[-1]).i; list[(yyvsp[-2]).i].stack_delta = -1; (yyval) = (yyvsp[-2]); ;} break; case 316: { (yyval).i = makenode(PUSH_PARAM_FIXED,(yyvsp[-2]).i,0); ;} break; case 317: { switch ( list[(yyvsp[0]).i].type ) { case PDELTA_LVALUE_: list[(yyvsp[0]).i].type = PUSHDELTA_; list[(yyvsp[0]).i].datatype = REAL_TYPE; break; case PSCALE_LVALUE_: list[(yyvsp[0]).i].type = PUSH_PARAM_SCALE; list[(yyvsp[0]).i].datatype = REAL_TYPE; break; default: sprintf(errmsg,"Internal error: lvalue type %d\n", list[(yyvsp[0]).i].type); kb_error(2883,errmsg,COMMAND_ERROR); } list[(yyvsp[0]).i].stack_delta = 1; (yyval) = (yyvsp[0]); ;} break; case 318: { kb_error(3380, "Syntax: VARIABLE . PDELTA|PSCALE \n", Q_ERROR); ;} break; case 319: { (yyval).i = makenode((yyvsp[-1]).i,(yyvsp[-2]).i,(yyvsp[0]).i); ;} break; case 320: { kb_error(2376,"Syntax: variable := expr\n",Q_ERROR);;} break; case 321: { kb_error(3422,"Got '=' instead of the assignment operator ':='\n", Q_ERROR); ;} break; case 322: { kb_error(3424,"Got '=' instead of the assignment operator ':='\n", Q_ERROR); ;} break; case 323: { kb_error(2377,"Syntax: variable := expr\n",Q_ERROR);;} break; case 324: { (yyval).i = (yyvsp[0]).datatype; ;} break; case 325: { (yyval).i = STRING_TYPE; ;} break; case 326: { (yyval).i = add_local_var((yyvsp[0]).lexeme,1); ;} break; case 327: { (yyval).i = add_local_var((yyvsp[0]).lexeme,1); if ( shadow_warn_flag ) { sprintf(errmsg, "Argument \"%s\" shadows already declared variable.\n", (yyvsp[0]).lexeme); kb_error(2635,errmsg,WARNING); } ;} break; case 328: { (yyval).i = add_local_var((yyvsp[0]).lexeme,1); if ( shadow_warn_flag ) { sprintf(errmsg, "Argument \"%s\" shadows already declared variable.\n", (yyvsp[0]).lexeme); kb_error(2636,errmsg,WARNING); } ;} break; case 329: { (yyval).i = add_local_var((yyvsp[0]).lexeme,1); if ( shadow_warn_flag ) { sprintf(errmsg, "Argument \"%s\" shadows already declared procedure.\n", (yyvsp[0]).lexeme); kb_error(2637,errmsg,WARNING); } ;} break; case 330: { (yyval).i = add_local_var((yyvsp[0]).lexeme,1); if ( shadow_warn_flag ) { sprintf(errmsg, "Argument \"%s\" shadows already declared function.\n", (yyvsp[0]).lexeme); kb_error(2638,errmsg,WARNING); } ;} break; case 331: { (yyval).i = add_local_var((yyvsp[0]).lexeme,1); if ( shadow_warn_flag ) { sprintf(errmsg, "Argument \"%s\" shadows already declared string variable.\n", (yyvsp[0]).lexeme); kb_error(2639,errmsg,WARNING); } ;} break; case 332: { (yyval).i = add_local_var((yyvsp[0]).lexeme,1); if ( shadow_warn_flag ) { sprintf(errmsg, "Argument \"%s\" shadows already declared quantity name.\n", (yyvsp[0]).lexeme); kb_error(2640,errmsg,WARNING); } ;} break; case 333: {(yyval).i = add_local_var((yyvsp[0]).lexeme,1); if ( shadow_warn_flag ) { sprintf(errmsg, "Argument \"%s\" shadows already declared method name.\n", (yyvsp[0]).lexeme); kb_error(2641,errmsg,WARNING); } ;} break; case 334: {(yyval).i = add_local_var((yyvsp[0]).lexeme,1); if ( shadow_warn_flag ) { sprintf(errmsg, "Argument \"%s\" shadows already declared constraint.\n", (yyvsp[0]).lexeme); kb_error(2642,errmsg,WARNING); } ;} break; case 335: { (yyval).i = add_local_var((yyvsp[0]).lexeme,1); if ( shadow_warn_flag ) { sprintf(errmsg, "Argument \"%s\" shadows already declared boundary.\n", (yyvsp[0]).lexeme); kb_error(2643,errmsg,WARNING); } ;} break; case 336: { (yyval).i = makenode(ARGLIST_,0,0); ;} break; case 337: { int_val = (yyvsp[-1]).i; (yyval).i = makenode(ARGLIST_,0,(yyvsp[0]).i); ;} break; case 338: { int_val = (yyvsp[-1]).i; (yyval).i = makenode(ARGLIST_,(yyvsp[-3]).i,(yyvsp[0]).i); ;} break; case 339: { if ( strcmp(yytext,"int") == 0 ) kb_error(3604,"Expecting datatype or ')' after '('\n ('integer' is the Evolver datatype, not 'int'.\n",Q_ERROR); else kb_error(3636,"Expecting datatype or ')' after '('\n",Q_ERROR); ;} break; case 340: { kb_error(3605,"Expecting identifier after datatype.\n",Q_ERROR); ;} break; case 341: { kb_error(3606,"Expecting datatype after ','\n",Q_ERROR); ;} break; case 342: { kb_error(3625,"Expecting identifier after datatype.\n",Q_ERROR); ;} break; case 343: { kb_error(3525,"Expecting comma or right parenthesis after argument.\n",Q_ERROR); ;} break; case 344: { (yyval).i = (yyvsp[-1]).i ;} break; case 345: { (yyval).i = (yyvsp[0]).i; ;} break; case 346: { (yyval).i = 0; ;} break; case 347: { kb_error(2624,"Missing function body, or ';' after prototype.\n", Q_ERROR); ;} break; case 348: { (yyval).i = (yyvsp[0]).i; ;} break; case 349: { (yyval).i = (yyvsp[0]).i; ;} break; case 350: { in_function = 1; if ( (yyvsp[0]).i == 0 ) (yyvsp[0]).i = add_global((yyvsp[0]).lexeme); init_local_scope((yyvsp[0]).i); begin_local_scope(); (yyval).i = makenode(FUNCTION_DEF_START_,(yyvsp[0]).i,(yyvsp[-1]).i); ;} break; case 351: { (yyval).i = makenode(FUNCTION_HEAD_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 352: { int insize = inputbufferspot - (yyvsp[-6]).qnum; in_function = 0; globals((yyvsp[-4]).i)->attr.procstuff.proc_text = mycalloc(insize+1,1); strncpy(globals((yyvsp[-4]).i)->attr.procstuff.proc_text,inputbuffer+(yyvsp[-4]).qnum,insize); globals((yyvsp[-4]).i)->attr.procstuff.proc_text[insize] = 0; list[(yyvsp[-3]).i].op5.locals = globals((yyvsp[-4]).i)->attr.procstuff.locals = localbase; if ( localbase ) localbase->flags |= LL_IN_USE; int_val = (yyvsp[-5]).i; if ( (yyvsp[0]).i ) (yyval).i = makenode(SET_FUNCTION_,(yyvsp[-1]).i,(yyvsp[0]).i); else { makenode(FUNCTION_PROTO_,(yyvsp[-1]).i,0); (yyval).i = 0; } exit_local_scope(); ;} break; case 353: { (yyval).i = (yyvsp[0]).i; ;} break; case 354: { (yyval).i = (yyvsp[0]).i; ;} break; case 355: { in_function = 1; /* for lex*/ if ( (yyvsp[0]).i == 0 ) (yyvsp[0]).i = add_global((yyvsp[0]).lexeme); init_local_scope((yyvsp[0]).i); begin_local_scope(); (yyval).i = makenode(PROCEDURE_DEF_START_,(yyvsp[0]).i,0); ;} break; case 356: { (yyval).i = makenode(PROCEDURE_HEAD_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 357: { int insize = inputbufferspot - (yyvsp[-5]).qnum; in_function = 0; globals((yyvsp[-4]).i)->attr.procstuff.proc_text = mycalloc(insize+1,1); strncpy(globals((yyvsp[-4]).i)->attr.procstuff.proc_text,inputbuffer+(yyvsp[-4]).qnum,insize); globals((yyvsp[-4]).i)->attr.procstuff.proc_text[insize] = 0; list[(yyvsp[-3]).i].op5.locals = globals((yyvsp[-4]).i)->attr.procstuff.locals = localbase; if ( localbase ) localbase->flags |= LL_IN_USE; if ( (yyvsp[0]).i ) (yyval).i = makenode(SET_ARGSPROC_,(yyvsp[-1]).i,(yyvsp[0]).i); else { makenode(PROCEDURE_PROTO_,(yyvsp[-1]).i,0); (yyval).i = 0; } exit_local_scope(); ;} break; case 358: { kb_error(3704,"Expected function name after datatype.\n",Q_ERROR); ;} break; case 359: { kb_error(3705,"Expected datatype for function.\n",Q_ERROR); ;} break; case 360: { kb_error(3706,"Expected name of procedure.\n",Q_ERROR); ;} break; case 361: { kb_error(3496,"Function returns a value; it's not a stand-alone command.\n",Q_ERROR); ;} break; case 362: { (yyval).i = makenode(DEFINE_IDENT_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 363: {int g; if ( (yyvsp[-1]).i == 0 )g = add_global((yyvsp[-1]).lexeme); else g = (yyvsp[-1]).i; /* local */ (yyval).i = makenode(DEFINE_IDENT_,g,(yyvsp[0]).i); ;} break; case 364: { (yyval).i = makenode(DEFINE_IDENT_,(yyvsp[0]).i,REAL_TYPE); ;} break; case 365: {int g = (yyvsp[0]).i ? (yyvsp[0]).i : add_global((yyvsp[0]).lexeme); (yyval).i = makenode(DEFINE_IDENT_,g,REAL_TYPE); ;} break; case 366: { (yyval).i = makenode(DEFINE_IDENT_,(yyvsp[0]).i,STRING_TYPE); ;} break; case 367: { (yyval).i = makenode(INDEXSET_,0,(yyvsp[-1]).i); ;} break; case 368: { (yyval).i = makenode(INDEXSET_,(yyvsp[-3]).i,(yyvsp[-1]).i); ;} break; case 369: { (yyval).i = makenode(DIMENSIONSET_,0,(yyvsp[-1]).i); ;} break; case 370: { (yyval).i = makenode(DIMENSIONSET_,(yyvsp[-3]).i,(yyvsp[-1]).i); ;} break; case 371: { (yyval).qnum = (yyvsp[-2]).i ; (yyval).datatype = (yyvsp[-1]).i; int_val= (yyval).datatype; (yyval).i = makenode(DEFINE_ARRAY_,(yyvsp[-2]).i,(yyvsp[0]).i); ;} break; case 372: { (yyval).qnum = (yyvsp[-2]).i ? (yyvsp[-2]).i : add_global((yyvsp[-2]).lexeme); (yyval).datatype = (yyvsp[-1]).i; int_val= (yyval).datatype; (yyval).i = makenode(DEFINE_ARRAY_,(yyval).qnum,(yyvsp[0]).i); ;} break; case 373: { (yyval).qnum = (yyvsp[-2]).i ; (yyval).datatype = (yyvsp[-1]).i; int_val= (yyval).datatype; (yyval).i = makenode(DEFINE_ARRAY_,(yyvsp[-2]).i,(yyvsp[0]).i); ;} break; case 374: { (yyval).i = (yyvsp[0]).i; ;} break; case 375: { (yyval).i = makenode(ARRAYIDENT_,(yyvsp[0]).i,0); ;} break; case 376: { /* for implicit generator */ (yyval).i = makenode(ATTRIB_LVALUE_,0,0); list[(yyval).i].op1.localnum = 0; list[(yyval).i].op2.name_id = set_name_eltype(V_NORMAL_ATTR,VERTEX); list[(yyval).i].type = ARRAY_VERTEX_NORMAL_; list[(yyval).i].op3.localnum = add_local_var(NULL,SDIM); ;} break; case 377: { /* for implicit generator */ (yyval).i = makenode(ATTRIB_LVALUE_,0,0); list[(yyval).i].op1.localnum = 0; list[(yyval).i].op2.name_id = set_name_eltype((yyvsp[0]).qnum,(yyvsp[0]).etype); if ( ((yyvsp[0]).etype == VERTEX) && ((yyvsp[0]).qnum == V_NORMAL_ATTR) ) { list[(yyval).i].type = ARRAY_VERTEX_NORMAL_; list[(yyval).i].op3.localnum = add_local_var(NULL,SDIM); list[(yyval).i].flags |= IS_VIRTUAL_ATTR; } else if ( ((yyvsp[0]).etype == EDGE) && ((yyvsp[0]).qnum == E_VECTOR_ATTR) ) { list[(yyval).i].type = ARRAY_EDGE_VECTOR_; list[(yyval).i].op3.localnum = add_local_var(NULL,SDIM); list[(yyval).i].flags |= IS_VIRTUAL_ATTR; } else if ( ((yyvsp[0]).etype == FACET) && ((yyvsp[0]).qnum == F_NORMAL_ATTR) ) { list[(yyval).i].type = ARRAY_FACET_NORMAL_; list[(yyval).i].op3.localnum = add_local_var(NULL,SDIM); list[(yyval).i].flags |= IS_VIRTUAL_ATTR; } ;} break; case 378: { if ( (yyvsp[0]).etype != (yyvsp[-1]).etype ) { sprintf(errmsg, "\"%s\" is a %s attribute, not %s.\n", EXTRAS((yyvsp[0]).etype)[(yyvsp[0]).qnum].name,typenames[(yyvsp[0]).etype], typenames[(yyvsp[-1]).etype]); kb_error(3678,errmsg,Q_ERROR); } (yyval).i = makenode(ATTRIB_LVALUE_,(yyvsp[-1]).i,0); list[(yyval).i].op1.localnum = list[(yyvsp[-1]).i].op2.localnum; list[(yyval).i].op2.name_id = set_name_eltype((yyvsp[0]).qnum,(yyvsp[0]).etype); if ( ((yyvsp[0]).etype == VERTEX) && ((yyvsp[0]).qnum == V_NORMAL_ATTR) ) { list[(yyval).i].type = ARRAY_VERTEX_NORMAL_; list[(yyval).i].op3.localnum = add_local_var(NULL,SDIM); list[(yyval).i].flags |= IS_VIRTUAL_ATTR; } else if ( ((yyvsp[0]).etype == EDGE) && ((yyvsp[0]).qnum == E_VECTOR_ATTR) ) { list[(yyval).i].type = ARRAY_EDGE_VECTOR_; list[(yyval).i].op3.localnum = add_local_var(NULL,SDIM); list[(yyval).i].flags |= IS_VIRTUAL_ATTR; } else if ( ((yyvsp[0]).etype == FACET) && ((yyvsp[0]).qnum == F_NORMAL_ATTR) ) { list[(yyval).i].type = ARRAY_FACET_NORMAL_; list[(yyval).i].op3.localnum = add_local_var(NULL,SDIM); list[(yyval).i].flags |= IS_VIRTUAL_ATTR; } ;} break; case 379: { sprintf(errmsg,"\"%s\" is not an attribute name.\n",(yyvsp[0]).lexeme); kb_error(3573,errmsg,Q_ERROR); ;} break; case 380: { sprintf(errmsg,"Missing attribute.\n"); kb_error(3574,errmsg,Q_ERROR); ;} break; case 381: { (yyval).i = makenode(ARRAY_LVALUE_INDEXED_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 382: { (yyval).i = makenode(ARRAY_ASSIGNOP_SINGLE_,(yyvsp[-2]).i,(yyvsp[0]).i); list[(yyval).i].op1.assigntype = (yyvsp[-1]).i; list[(yyval).i].op2.name_id = list[(yyvsp[-2]).i].op2.name_id; list[(yyval).i].stack_delta = list[(yyvsp[-2]).i].op1.indexcount + 2; ;} break; case 383: { (yyval).i = makenode(ARRAY_ASSIGNOP_ARRAY_,(yyvsp[-2]).i,(yyvsp[0]).i); list[(yyval).i].op1.assigntype = (yyvsp[-1]).i; list[(yyvsp[0]).i].flags |= IS_RVALUE; ;} break; case 384: { (yyval).i = makenode(ARRAY_ASSIGNOP_SCALAR_,(yyvsp[-2]).i,(yyvsp[0]).i); list[(yyval).i].op1.assigntype = (yyvsp[-1]).i; ;} break; case 385: { int k = makenode(ARRAY_RVALUE_,(yyvsp[-2]).i,(yyvsp[0]).i); list[k].op1.intval = '*'; (yyval).i = makenode(ARRAY_ASSIGNOP_S_X_A_,(yyvsp[-4]).i,k); list[(yyval).i].op1.assigntype = (yyvsp[-3]).i; list[(yyval).i].op3.name_id = list[(yyvsp[0]).i].op2.name_id; list[(yyvsp[0]).i].flags |= IS_RVALUE; check_special_attr(list[(yyvsp[0]).i].op2.name_id); /* Can do dimension check now */ if ( check_array_dims_same(list[(yyvsp[-4]).i].op2.name_id, list[(yyvsp[0]).i].op2.name_id ) == 0 ) kb_error(4379,"Arrays don't have same number of dimensions or types are different.\n", COMMAND_ERROR); ;} break; case 386: { int k = makenode(ARRAY_RVALUE_,(yyvsp[-2]).i,(yyvsp[0]).i); list[k].op1.intval = '+'; (yyval).i = makenode(ARRAY_ASSIGNOP_A_P_A_,(yyvsp[-4]).i,k); list[(yyval).i].op1.assigntype = (yyvsp[-3]).i; list[(yyval).i].op3.name_id = list[(yyvsp[-2]).i].op2.name_id; list[(yyval).i].op4.name_id = list[(yyvsp[0]).i].op2.name_id; list[(yyvsp[-2]).i].flags |= IS_RVALUE; list[(yyvsp[0]).i].flags |= IS_RVALUE; check_special_attr(list[(yyvsp[-2]).i].op2.name_id); check_special_attr(list[(yyvsp[0]).i].op2.name_id); /* Can do dimension check now */ if ( check_array_dims_same(list[(yyvsp[-4]).i].op2.name_id, list[(yyvsp[-2]).i].op2.name_id) == 0 ) kb_error(4380,"Arrays don't have same number of dimensions or types are different.\n", COMMAND_ERROR); if ( check_array_dims_same(list[(yyvsp[-4]).i].op2.name_id, list[(yyvsp[0]).i].op2.name_id) == 0 ) kb_error(4381,"Arrays don't have same number of dimensions or types are different.\n", COMMAND_ERROR); ;} break; case 387: { int k = makenode(ARRAY_RVALUE_,(yyvsp[-2]).i,(yyvsp[0]).i); list[k].op1.intval = '-'; (yyval).i = makenode(ARRAY_ASSIGNOP_A_S_A_,(yyvsp[-4]).i,k); list[(yyval).i].op1.assigntype = (yyvsp[-3]).i; list[(yyval).i].op3.name_id = list[(yyvsp[-2]).i].op2.name_id; list[(yyval).i].op4.name_id = list[(yyvsp[0]).i].op2.name_id; list[(yyvsp[-2]).i].flags |= IS_RVALUE; list[(yyvsp[0]).i].flags |= IS_RVALUE; check_special_attr(list[(yyvsp[-2]).i].op2.name_id); check_special_attr(list[(yyvsp[0]).i].op2.name_id); /* Can do dimension check now */ if ( check_array_dims_same(list[(yyvsp[-4]).i].op2.name_id, list[(yyvsp[-2]).i].op2.name_id) == 0 ) kb_error(3030,"Arrays don't have same number of dimensions or types are different.\n", COMMAND_ERROR); if ( check_array_dims_same(list[(yyvsp[-4]).i].op2.name_id, list[(yyvsp[0]).i].op2.name_id) == 0 ) kb_error(4383,"Arrays don't have same number of dimensions or types are different.\n", COMMAND_ERROR); ;} break; case 388: { (yyval).i = makenode(DOT_,(yyvsp[-2]).i,(yyvsp[0]).i); list[(yyvsp[-2]).i].flags |= IS_RVALUE; list[(yyvsp[0]).i].flags |= IS_RVALUE; ;} break; case 389: { (yyval).i = makenode(ARRAY_EVAL_,(yyvsp[-1]).i,(yyvsp[0]).i); list[(yyvsp[-1]).i].flags |= IS_RVALUE; ;} break; case 390: { (yyval).i = (yyvsp[0]).i; ;} break; case 391: { (yyval).i = (yyvsp[0]).i; ;} break; case 392: { struct extra *ex; (yyval).qnum = (yyvsp[-1]).qnum; (yyval).etype = (yyvsp[-1]).etype; if ( (yyvsp[-3]).i != (yyval).etype ) kb_error(1885,"This extra attribute already defined on different element type.\n",COMMAND_ERROR); ex = EXTRAS((yyval).etype) + (yyval).qnum; if ( ex->type != (yyvsp[0]).i ) { sprintf(errmsg, "Attribute %s already defined with different type, %s.\n", ex->name,datatype_name[ex->type]); kb_error(1886,errmsg,COMMAND_ERROR); } (yyval).i = makenode(DEFINE_EXTRA_,0,(yyvsp[-3]).i); list[(yyval).i].op1.extranum = (yyval).qnum; ;} break; case 393: { int attr_type=INTEGER_TYPE; if ( (yyvsp[-1]).i ) { sprintf(errmsg,"Cannot use local variable \"%s\" as attribute.\n", (yyvsp[-1]).lexeme); kb_error(2615,errmsg,COMMAND_ERROR); } attr_type = (yyvsp[0]).i; (yyval).qnum = add_attribute((yyvsp[-3]).i,(yyvsp[-1]).lexeme,attr_type,0,NULL,DUMP_ATTR,NULL); /* being a declaration, has effect when parsed */ (yyval).i = makenode(DEFINE_EXTRA_,0,(yyvsp[-3]).i); list[(yyval).i].op1.extranum = (yyval).qnum; ;} break; case 394: { (yyval).i = makenode(DEFINE_EXTRA_INDEX_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 395: { begin_scope(); /* ended right below */ elsym = symbol_add("self",list[(yyvsp[-1]).i].op2.eltype); (yyval).i = makenode(ATTR_FUNCTION_,(yyvsp[-1]).i,0); ;} break; case 396: { init_local_scope(0); begin_local_scope(); ;} break; case 397: { struct extra *ext; end_local_scope(); (yyval).i = makenode(ATTR_FUNCTION_END_,(yyvsp[-4]).i,(yyvsp[-1]).i); list[(yyval).i].op1.extranum = list[(yyvsp[-6]).i].op1.extranum; /* attr number */ list[(yyval).i].op2.eltype = list[(yyvsp[-6]).i].op2.eltype; /* element type */ list[(yyvsp[-4]).i].op1.skipsize = (yyval).i - (yyvsp[-4]).i; ext = EXTRAS(list[(yyval).i].op2.eltype) + list[(yyval).i].op1.extranum; ext->code.locals = localbase; if ( localbase ) localbase->flags |= LL_IN_USE; exit_local_scope(); ext->flags |= FUNCTION_ATTR; end_scope(); ;} break; case 398: { (yyval).i = (yyvsp[0]).i; ;} break; case 399: { (yyval).i = makenode(DEFINE_QUANTITY_,0,0); ;} break; case 400: { (yyval).i = makenode(DEFINE_METHOD_INSTANCE_,0,0); ;} break; case 401: { (yyval).i = makenode(DEFINE_CONSTRAINT_,0,0); ;} break; case 402: { (yyval).i = makenode(DEFINE_BOUNDARY_,0,0); ;} break; case 403: { kb_error(2379, "Syntax: DEFINE name [REAL|INTEGER]\n or: DEFINE elementtype ATTRIBUTE name REAL|INTEGER [ dimension ] \n",Q_ERROR); ;} break; case 404: { strncpy((yyval).lexeme,(yyvsp[-1]).lexeme,31); /* if ( $$.i == 0 ) ?? */ (yyval).i = add_global((yyvsp[-1]).lexeme); /* else $$.i = $1.i; */ /* local ?? */ (yyval).qnum = assignbacktrack(); ;} break; case 405: { (yyval).i = add_perm_global((yyvsp[-1]).lexeme); perm_globals((yyval).i)->flags |= PERMANENT; perm_flag++; (yyval).qnum = assignbacktrack(); ;} break; case 406: { /* if ( $$.i == 0 ) ?? */ (yyval).i = add_global((yyvsp[0]).lexeme); /* else $$.i = $2.i; ?? */ /* local */ ;} break; case 407: { (yyval).i = (yyvsp[-2]).i;(yyval).qnum = assignbacktrack(); ;} break; case 408: { /* if ( $$.i == 0 ) ?? */ (yyval).i = add_global((yyvsp[0]).lexeme); /* else $$.i = $2.i; ?? */ (yyval).qnum = assignbacktrack(); strcpy((yyval).lexeme,(yyvsp[0]).lexeme); ;} break; case 409: { kb_error(2380,"Syntax: variable := rexpr | {command} \n",Q_ERROR);;} break; case 410: { sprintf(errmsg,"Syntax error: Unexpected new identifier '%s'.\n",(yyvsp[-1]).lexeme); kb_error(2381,errmsg, Q_ERROR);;} break; case 411: { kb_error(2382,"Syntax: variable := rexpr | {command} (braces needed around command) \n",Q_ERROR);;} break; case 412: { (yyval).i = makenode(SET_SGLOBAL_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 413: { (yyval).i = makenode(SET_GLOBAL_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 414: { sprintf(errmsg,"Illegal right side of assignment.\n"); kb_error(3756,errmsg, Q_ERROR);;} break; case 415: { (yyval).i = makenode(SET_PERM_SGLOBAL_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 416: { (yyval).i = makenode(SET_PERM_GLOBAL_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 417: { init_local_scope((yyvsp[0]).i); begin_local_scope(); ;} break; case 418: { int insize = inputbufferspot - (yyvsp[-2]).qnum; globals((yyvsp[-2]).i)->attr.procstuff.proc_text = mycalloc(insize+1,1); strncpy(globals((yyvsp[-2]).i)->attr.procstuff.proc_text,inputbuffer+(yyvsp[-2]).qnum,insize); (yyval).i = makenode(SET_PROCEDURE_,(yyvsp[0]).i,(yyvsp[-2]).i); globals((yyvsp[-2]).i)->attr.procstuff.proc_text[insize] = 0; exit_local_scope(); ;} break; case 419: { init_local_scope((yyvsp[-1]).i); begin_local_scope(); ;} break; case 420: { int k,insize = inputbufferspot - (yyvsp[-4]).qnum; globals((yyvsp[-4]).i)->attr.procstuff.proc_text = mycalloc(insize+1,1); strncpy(globals((yyvsp[-4]).i)->attr.procstuff.proc_text,inputbuffer+(yyvsp[-4]).qnum,insize); globals((yyvsp[-4]).i)->attr.procstuff.proc_text[insize] = 0; k = makenode(COMMAND_BLOCK_,(yyvsp[-1]).i,0); (yyval).i = makenode(SET_PROCEDURE_,k,(yyvsp[-4]).i); exit_local_scope(); ;} break; case 421: { int k = makenode(NULLBLOCK_,0,0); localbase = NULL; (yyval).i = makenode(SET_PROCEDURE_,k,(yyvsp[-2]).i); ;} break; case 422: { init_local_scope((yyvsp[-1]).i); begin_local_scope(); ;} break; case 423: { int k,insize = inputbufferspot - (yyvsp[-4]).qnum; perm_globals((yyvsp[-4]).i)->attr.procstuff.proc_text = calloc(insize+1,1); strncpy(perm_globals((yyvsp[-4]).i)->attr.procstuff.proc_text,inputbuffer+(yyvsp[-4]).qnum,insize); perm_globals((yyvsp[-4]).i)->attr.procstuff.proc_text[insize] = 0; k = makenode(COMMAND_BLOCK_,(yyvsp[-1]).i,0); (yyval).i = makenode(SET_PERM_PROCEDURE_,k,(yyvsp[-4]).i); exit_local_scope(); ;} break; case 424: { int k = makenode(NULLBLOCK_,0,0); localbase = NULL; (yyval).i = makenode(SET_PERM_PROCEDURE_,k,(yyvsp[-2]).i); ;} break; case 425: { (yyval).qnum = assignbacktrack(); init_local_scope((yyvsp[-1]).i); begin_local_scope(); ;} break; case 426: { int insize = inputbufferspot - (yyvsp[-1]).qnum; myfree(globals((yyvsp[-3]).i)->attr.procstuff.proc_text); globals((yyvsp[-3]).i)->attr.procstuff.proc_text = mycalloc(insize+1,1); strncpy(globals((yyvsp[-3]).i)->attr.procstuff.proc_text,inputbuffer+(yyvsp[-1]).qnum,insize); globals((yyvsp[-3]).i)->attr.procstuff.proc_text[insize] = 0; (yyval).i = makenode(SET_PROCEDURE_,(yyvsp[0]).i,(yyvsp[-3]).i); exit_local_scope(); ;} break; case 427: { (yyval).i = perm_flag++; (yyval).qnum = assignbacktrack(); init_local_scope((yyvsp[-1]).i); begin_local_scope(); ;} break; case 428: { int insize = inputbufferspot - (yyvsp[-1]).qnum; free(perm_globals((yyvsp[-3]).i)->attr.procstuff.proc_text); perm_globals((yyvsp[-3]).i)->attr.procstuff.proc_text = calloc(insize+1,1); strncpy(perm_globals((yyvsp[-3]).i)->attr.procstuff.proc_text,inputbuffer+(yyvsp[-1]).qnum,insize); perm_globals((yyvsp[-3]).i)->attr.procstuff.proc_text[insize] = 0; perm_globals((yyvsp[-3]).i)->flags |= PERMANENT; (yyval).i = makenode(SET_PERM_PROCEDURE_,(yyvsp[0]).i,(yyvsp[-3]).i); perm_flag = (yyvsp[-1]).i; exit_local_scope(); ;} break; case 429: { kb_error(2383,"Syntax: procedure_name := {command} \n",Q_ERROR);;} break; case 430: { kb_error(2384,"Syntax: procedure_name ::= {command} \n",Q_ERROR);;} break; case 431: { sprintf(errmsg, "'%s' is a variable; cannot be assigned a procedure.\n", globals((yyvsp[-1]).i)->name); kb_error(3899,errmsg,Q_ERROR); ;} break; case 432: { (yyval).i = makenode(LOCAL_LIST_START_,(yyvsp[0]).i,0); ;} break; case 433: { (yyval).i = (yyvsp[0]).i; ;} break; case 434: { (yyval).i = (yyvsp[0]).i; list[(yyvsp[0]).i].left = -1; ;} break; case 435: { ident_t iid = add_local_var((yyvsp[0]).lexeme,1); (yyval).i = makenode(DECLARE_LOCAL_,iid,0); ;} break; case 436: { ident_t iid = add_local_var((yyvsp[0]).lexeme,1); (yyval).i = makenode(DECLARE_LOCAL_,iid,0); if ( shadow_warn_flag ) { sprintf(errmsg, "Local name \"%s\" shadows already declared variable.\n", (yyvsp[0]).lexeme); kb_error(2625,errmsg,WARNING); } ;} break; case 437: { ident_t iid = add_local_var((yyvsp[0]).lexeme,1); (yyval).i = makenode(DECLARE_LOCAL_,iid,0); if ( shadow_warn_flag ) { sprintf(errmsg, "Local name \"%s\" shadows already declared variable.\n", (yyvsp[0]).lexeme); kb_error(2626,errmsg,WARNING); } ;} break; case 438: { ident_t iid = add_local_var((yyvsp[0]).lexeme,1); (yyval).i = makenode(DECLARE_LOCAL_,iid,0); if ( shadow_warn_flag ) { sprintf(errmsg, "Local name \"%s\" shadows already declared procedure.\n", (yyvsp[0]).lexeme); kb_error(2627,errmsg,WARNING); } ;} break; case 439: { ident_t iid = add_local_var((yyvsp[0]).lexeme,1); (yyval).i = makenode(DECLARE_LOCAL_,iid,0); if ( shadow_warn_flag ) { sprintf(errmsg, "Local name \"%s\" shadows already declared function.\n", (yyvsp[0]).lexeme); kb_error(2628,errmsg,WARNING); } ;} break; case 440: { ident_t iid = add_local_var((yyvsp[0]).lexeme,1); (yyval).i = makenode(DECLARE_LOCAL_,iid,0); if ( shadow_warn_flag ) { sprintf(errmsg, "Local name \"%s\" shadows already declared string variable.\n", (yyvsp[0]).lexeme); kb_error(2629,errmsg,WARNING); } ;} break; case 441: {ident_t iid = add_local_var((yyvsp[0]).lexeme,1); (yyval).i = makenode(DECLARE_LOCAL_,iid,0); if ( shadow_warn_flag ) { sprintf(errmsg, "Local name \"%s\" shadows already declared quantity name.\n", (yyvsp[0]).lexeme); kb_error(2630,errmsg,WARNING); } ;} break; case 442: {ident_t iid = add_local_var((yyvsp[0]).lexeme,1); (yyval).i = makenode(DECLARE_LOCAL_,iid,0); if ( shadow_warn_flag ) { sprintf(errmsg, "Local name \"%s\" shadows already declared method name.\n", (yyvsp[0]).lexeme); kb_error(2631,errmsg,WARNING); } ;} break; case 443: {ident_t iid = add_local_var((yyvsp[0]).lexeme,1); (yyval).i = makenode(DECLARE_LOCAL_,iid,0); if ( shadow_warn_flag ) { sprintf(errmsg, "Local name \"%s\" shadows already declared constraint.\n", (yyvsp[0]).lexeme); kb_error(2632,errmsg,WARNING); } ;} break; case 444: {ident_t iid = add_local_var((yyvsp[0]).lexeme,1); (yyval).i = makenode(DECLARE_LOCAL_,iid,0); if ( shadow_warn_flag ) { sprintf(errmsg, "Local name \"%s\" shadows already declared boundary.\n", (yyvsp[0]).lexeme); kb_error(2633,errmsg,WARNING); } ;} break; case 445: { kb_error(2614,"Syntax: LOCAL varname; \n",Q_ERROR);;} break; case 446: { init_local_scope(0); begin_local_scope(); ;} break; case 447: { (yyval).i = makenode(REDEFINE_SINGLE_,(yyvsp[0]).i,(yyvsp[-3]).i); exit_local_scope(); ;} break; case 448: { init_local_scope(0); begin_local_scope(); ;} break; case 449: { (yyval).i = makenode(REDEFINE_SINGLE_,(yyvsp[0]).i,(yyvsp[-3]).i); exit_local_scope(); ;} break; case 450: { init_local_scope(0); begin_local_scope(); ;} break; case 451: { (yyval).i = makenode(REDEFINE_SINGLE_,(yyvsp[0]).i,(yyvsp[-3]).i); exit_local_scope(); ;} break; case 452: { (yyval).i = makenode(UNREDEFINE_SINGLE_,0,(yyvsp[-1]).i); ;} break; case 453: { (yyval).i = makenode(UNREDEFINE_SINGLE_,0,(yyvsp[-1]).i); ;} break; case 454: { (yyval).i = makenode(EXPRLIST_,(yyvsp[-2]).i,(yyvsp[0]).i); ;} break; case 455: { kb_error(3801,"Missing expression after ','\n", Q_ERROR); ;} break; case 456: { (yyval).i = makenode(EXPRLIST_,(yyvsp[0]).i,0); ;} break; case 457: { (yyval).i = makenode(EXPRLIST_,(yyvsp[-2]).i,(yyvsp[0]).i); ;} break; case 458: { kb_error(3891,"Missing expression after ','\n", Q_ERROR); ;} break; case 459: { (yyval).i = makenode(EXPRLIST_,(yyvsp[0]).i,0); ;} break; case 460: { (yyval).i = makenode(PRINTFHEAD_,(yyvsp[0]).i,0); ;} break; case 461: { kb_error(3892,"Missing format string after printf.\n", Q_ERROR); ;} break; case 462: { (yyval).i = makenode(BINARY_PRINTFHEAD_,(yyvsp[0]).i,0); ;} break; case 463: { kb_error(4892,"Missing format string after printf.\n", Q_ERROR); ;} break; case 464: { (yyval).i = makenode(ERRPRINTFHEAD_,(yyvsp[0]).i,0); ;} break; case 465: { kb_error(3802,"Missing format string after errprintf.\n", Q_ERROR); ;} break; case 466: { (yyval).i = (yyvsp[0]).i; ;} break; case 467: { (yyval).i = makenode(PRINTF_,(yyvsp[-2]).i,(yyvsp[0]).i); ;} break; case 468: { kb_error(3893,"Missing expression after ','\n", Q_ERROR); ;} break; case 469: { (yyval).i = (yyvsp[0]).i; ;} break; case 470: { (yyval).i = makenode(BINARY_PRINTF_,(yyvsp[-2]).i,(yyvsp[0]).i); ;} break; case 471: { kb_error(4893,"Missing expression after ','\n", Q_ERROR); ;} break; case 472: { (yyval).i = (yyvsp[0]).i; ;} break; case 473: { (yyval).i = makenode(ERRPRINTF_,(yyvsp[-2]).i,(yyvsp[0]).i); ;} break; case 474: { (yyval).i = makenode(ERRPRINTF_,(yyvsp[-2]).i,(yyvsp[0]).i); ;} break; case 475: { kb_error(3803,"Missing expression after ','\n", Q_ERROR); ;} break; case 477: { (yyval).i = makenode(LIST_PROCS_,0,0); ;} break; case 478: { (yyval).i = makenode(STRPRINT_,(yyvsp[0]).i,0); ;} break; case 479: { (yyval).i = makenode(PRINT_PROCEDURE_,(yyvsp[0]).i,0); ;} break; case 480: { (yyval).i = makenode(PRINT_PROCEDURE_,(yyvsp[0]).i,0); ;} break; case 481: { (yyval).i = makenode(PRINT_PROCEDURE_,(yyvsp[0]).i,0); ;} break; case 482: { (yyval).i = makenode(PRINT_PERM_PROCEDURE_,(yyvsp[0]).i,0); ;} break; case 483: { (yyval).i = makenode(EXPRINT_PROCEDURE_,(yyvsp[0]).i,0); ;} break; case 484: { (yyval).i = makenode(PRINT_,(yyvsp[0]).i,0); ;} break; case 485: { (yyval).i = makenode(PRINT_ARRAY_LVALUE_,(yyvsp[0]).i,0); list[(yyvsp[0]).i].flags |= IS_RVALUE; ;} break; case 486: { int k; switch ( (yyvsp[0]).i ) { case COORD_: switch ( (yyvsp[-1]).etype ) { case VERTEX: case EDGE: case FACET: k = makenode(ATTRIBUTE,(yyvsp[0]).i,(yyvsp[0]).qnum); list[k].op1.localnum = list[(yyvsp[-1]).i].op2.localnum; list[k].op2.coordnum = (yyvsp[0]).qnum - 1; /* 1-based indexing to 0 */ k = makenode(QUALIFIED_ATTRIBUTE,(yyvsp[-1]).i,k); (yyval).i = makenode(PRINT_,k,0); goto vnexit; default: sprintf(errmsg,"\"x\" is not a %s attribute.\n", typenames[(yyvsp[-1]).etype]); kb_error(2650,errmsg,COMMAND_ERROR); } break; case GET_VERTEXNORMAL_: if ( (yyvsp[-1]).etype != VERTEX ) { sprintf(errmsg,"\"vertexnormal\" is vertex attribute; cannot be on %s.\n", typenames[(yyvsp[-1]).etype]); kb_error(2651,errmsg,COMMAND_ERROR); } (yyval).i = makenode(PRINT_VERTEXNORMAL_,(yyvsp[-1]).i,0); list[(yyval).i].op1.localnum = list[(yyvsp[-1]).i].op2.localnum; goto vnexit; break; case PARAM_: if ( (yyvsp[-1]).etype != VERTEX ) { sprintf(errmsg,"\"p\" is %s attribute; cannot be on %s.\n", typenames[VERTEX], typenames[(yyvsp[-1]).etype]); kb_error(2652,errmsg,COMMAND_ERROR); } int_val = V_PARAM_ATTR; break; case GET_EXTRA_ATTR_: if ( (yyvsp[-1]).etype != (yyvsp[0]).etype ) { sprintf(errmsg,"\"%s\" is %s attribute; cannot be on %s.\n", EXTRAS((yyvsp[0]).etype)[(yyvsp[0]).qnum & YYSHIFTMASK].name, typenames[(yyvsp[0]).etype], typenames[(yyvsp[-1]).etype]); kb_error(2653,errmsg,COMMAND_ERROR); } int_val = (yyvsp[0]).qnum; break; default: k = makenode(ATTRIBUTE,(yyvsp[0]).i,(yyvsp[0]).qnum); list[k].op1.localnum = list[(yyvsp[-1]).i].op2.localnum; k = makenode(QUALIFIED_ATTRIBUTE,(yyvsp[-1]).i,k); (yyval).i = makenode(PRINT_,k,0); goto vnexit; } int_val |= ((yyvsp[-1]).etype << YYTYPESHIFT); (yyval).i = makenode(PRINT_ATTR_ARRAY_,(yyvsp[-1]).i,0); vnexit: ; ;} break; case 487: { (yyval).i = makenode(PRINT_LETTER_,(yyvsp[0]).i,0); ;} break; case 488: { (yyval).i = makenode(PRINT_LETTER_,(yyvsp[0]).i,0); ;} break; case 489: { (yyval).i = makenode(PRINT_LETTER_,(yyvsp[0]).i,0); ;} break; case 490: { kb_error(2385, "Syntax: PRINT procedure | expression | stringexpression \n",Q_ERROR ); ;} break; case 491: { (yyval).i = makenode(SHOW_TRANS_,(yyvsp[0]).i,0); ;} break; case 492: { kb_error(2386,"Syntax: SHOW_TRANS \"string\"\n",Q_ERROR);;} break; case 493: { backquote_flag = 1; (yyval).i = makenode(BACKQUOTE_START_,0,0); if ( local_nest_depth == 0 ) init_local_scope(0); begin_local_scope(); list[listtop++].type = SETUP_FRAME_; ;} break; case 494: { verb_flag = 0; backquote_flag = 0; end_local_scope(); (yyval).i = makenode(BACKQUOTE_END_,(yyvsp[-2]).i,(yyvsp[-1]).i);;} break; case 495: { (yyval).i = makenode(ACOMMANDEXPR_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 496: { backquote_flag = 0; kb_error(3805,"Backquote syntax: ` commands ` , expression\n", Q_ERROR); ;} break; case 497: { (yyval).i = makenode(FUNCTION_CALL_,(yyvsp[-2]).i,0); (yyval).i = makenode(FUNCTION_CALL_RETURN_,(yyval).i,0); ;} break; case 498: { (yyval).i = makenode(FUNCTION_CALL_,(yyvsp[-3]).i,(yyvsp[-1]).i); makenode(FUNCTION_CALL_RETURN_,(yyval).i,0); ;} break; case 499: { kb_error(2870, "Function call needs argument list.\n",Q_ERROR); ;} break; case 500: { (yyval).i = makenode(PROCEDURE_CALL_,(yyvsp[-2]).i,0); (yyval).i = makenode(PROCEDURE_CALL_RETURN_,(yyval).i,0); ;} break; case 501: { (yyval).i = makenode(PROCEDURE_CALL_,(yyvsp[-3]).i,(yyvsp[-1]).i); makenode(PROCEDURE_CALL_RETURN_,(yyval).i,0); ;} break; case 502: { kb_error(2871, "Procedure call needs argument list.\n",Q_ERROR); ;} break; case 503: { (yyval).i = makenode(WRAP_VERTEX_,(yyvsp[-3]).i,(yyvsp[-1]).i); ;} break; case 504: { kb_error(3808,"Syntax: wrap_vertex(vertex_number,wrap_code_number)\n", Q_ERROR); ;} break; case 505: { (yyval).i = makenode(VIEW_TRANSFORM_PARITY_,(yyvsp[-1]).i,0); ;} break; case 506: { kb_error(2602, "view_transform_parity needs one index.\n", Q_ERROR); ;} break; case 507: { int nn = makenode(TEXT_SPOT_,(yyvsp[-5]).i,(yyvsp[-3]).i); (yyval).i = makenode(DISPLAY_TEXT_,nn,(yyvsp[-1]).i); ;} break; case 508: { kb_error(4683,"Syntax: text_id := DISPLAY_TEXT(x,y,string)\n",Q_ERROR ); ;} break; case 509: { kb_error(3254,"Syntax: text_id := DISPLAY_TEXT(x,y,string)\n",Q_ERROR ); ;} break; case 510: { (yyval).i = makenode(DELETE_TEXT_,(yyvsp[-1]).i,0); ;} break; case 511: { kb_error(4684,"Syntax: DELETE_TEXT(text_id)\n",Q_ERROR ); ;} break; case 512: { (yyval).i = makenode(CREATE_VERTEX_,(yyvsp[-1]).i,0); ;} break; case 513: { kb_error(2387,"Syntax: NEW_VERTEX(x,y,...) \n",Q_ERROR); ;} break; case 514: { (yyval).i = makenode(CREATE_EDGE_,(yyvsp[-3]).i,(yyvsp[-1]).i); ;} break; case 515: { kb_error(2388,"Syntax: NEW_EDGE(tail_id,head_id) \n",Q_ERROR); ;} break; case 516: { (yyval).i = makenode(CREATE_FACET_,(yyvsp[-1]).i,0); ;} break; case 517: { kb_error(2389,"Syntax: NEW_FACET(edge1,edge2,...) \n",Q_ERROR); ;} break; case 518: { (yyval).i = makenode(CREATE_BODY_,0,0); ;} break; case 519: { kb_error(2390,"Syntax: NEW_BODY \n",Q_ERROR); ;} break; case 520: { (yyval).i = makenode(ELINDEX_,(yyvsp[0]).i,0); ;} break; case 521: { (yyval).i = makenode(ELINDEX_,(yyvsp[-2]).i,(yyvsp[0]).i); ;} break; case 522: { (yyval).i = makenode(PUSH_ELEMENT_ID_,(yyvsp[0]).i,(yyvsp[0]).qnum); ;} break; case 523: { (yyval).i = makenode(PUSH_ELEMENT_ID_,-(yyvsp[-1]).i,(yyvsp[-1]).qnum); ;} break; case 524: { (yyval).i = makenode(PUSH_ELEMENT_ID_,(yyvsp[0]).i,(yyvsp[0]).qnum); ;} break; case 525: { (yyval).i = makenode(PUSH_ELEMENT_ID_,-(yyvsp[-1]).i,(yyvsp[-1]).qnum); ;} break; case 526: { (yyval).i = makenode(VALID_ELEMENT_,(yyvsp[-4]).i,(yyvsp[-2]).i); ;} break; case 527: { kb_error(3904,"Syntax: valid_element(element_type[expr]) \n",Q_ERROR); ;} break; case 528: { (yyval).i = makenode(VALID_CONSTRAINT_,(yyvsp[-1]).i,0); ;} break; case 529: { kb_error(1901,"Syntax: valid_constraint(expr) \n",Q_ERROR); ;} break; case 530: { (yyval).i = makenode(VALID_BOUNDARY_,(yyvsp[-1]).i,0); ;} break; case 531: { kb_error(3029,"Syntax: valid_boundary(expr) \n",Q_ERROR); ;} break; case 532: { (yyval).i = makenode(MERGE_VERTEX_,(yyvsp[-3]).i,(yyvsp[-1]).i); ;} break; case 533: { kb_error(3885,"Syntax: VERTEX_MERGE(first_id,second_id) \n",Q_ERROR); ;} break; case 534: { (yyval).i = makenode(MERGE_EDGE_,(yyvsp[-3]).i,(yyvsp[-1]).i); ;} break; case 535: { kb_error(3637,"Syntax: EDGE_MERGE(first_oid,second_oid) \n",Q_ERROR); ;} break; case 536: { (yyval).i = makenode(MERGE_FACET_,(yyvsp[-3]).i,(yyvsp[-1]).i); ;} break; case 537: { kb_error(3607,"Syntax: FACET_MERGE(first_oid,second_oid) \n",Q_ERROR); ;} break; case 538: { int_val = (yyvsp[-1]).i; (yyval).i = makenode(MATRIX_MULTIPLY_,(yyvsp[-5]).i,(yyvsp[-3]).i); ;} break; case 539: { kb_error(3790,"matrix_multiply syntax: matrix_multiply(mat1,mat2,mat3)\n", COMMAND_ERROR); ;} break; case 540: { kb_error(3791,"matrix_multiply third argument is not an array.\n", COMMAND_ERROR); ;} break; case 541: { kb_error(3792,"matrix_multiply second argument is not an array.\n", COMMAND_ERROR); ;} break; case 542: { kb_error(3793,"matrix_multiply first argument is not an array.\n", COMMAND_ERROR); ;} break; case 543: { (yyval).i = makenode(MATRIX_DETERMINANT_,(yyvsp[-1]).i,0); ;} break; case 544: { (yyval).i = makenode(MATRIX_INVERSE_,(yyvsp[-3]).i,(yyvsp[-1]).i); ;} break; case 545: { kb_error(3794,"matrix_inverse syntax: matrix_inverse(mat1,mat2)\n", COMMAND_ERROR); ;} break; case 546: { kb_error(3795,"matrix_inverse second argument is not an array.\n", COMMAND_ERROR); ;} break; case 547: { kb_error(3796,"matrix_inverse first argument is not an array.\n", COMMAND_ERROR); ;} break; case 548: { (yyval).i = LIST_; loopdepth++; ;} break; case 549: { erroutstring("Syntax: LIST element_gen [ name ] [ WHERE rexpr ]\n"); erroutstring(" LIST TOPINFO\n"); erroutstring(" LIST BOTTOMINFO\n"); erroutstring(" LIST ATTRIBUTES\n"); erroutstring(" LIST PROCEDURES\n"); erroutstring(" LIST QUANTITY quantityname\n"); erroutstring(" LIST METHOD_INSTANCE instancename\n"); erroutstring(" LIST CONSTRAINT rexpr or name\n"); erroutstring(" LIST BOUNDARY rexpr or name\n"); kb_error(1899,NULL,Q_ERROR); ;} break; case 550: { (yyval).i = DELETE_; loopdepth++; ;} break; case 551: { kb_error(2391,"Syntax: DELETE element_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); ;} break; case 552: { (yyval).i = VERTEX_AVERAGE_; loopdepth++; ;} break; case 553: { (yyval).i = RAW_VERTEX_AVERAGE_; loopdepth++; ;} break; case 554: { (yyval).i = RAWEST_VERTEX_AVERAGE_; loopdepth++; ;} break; case 555: { kb_error(2392,"Syntax: VERTEX_AVERAGE element_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); ;} break; case 556: { kb_error(2393,"Syntax: RAW_VERTEX_AVERAGE element_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); ;} break; case 557: { kb_error(2394,"Syntax: RAWEST_VERTEX_AVERAGE element_gen [ name ] [ WHERE expr ]\n",Q_ERROR); ;} break; case 558: { (yyval).i = DISSOLVE_; loopdepth++; ;} break; case 559: { kb_error(2395,"Syntax: DISSOLVE element_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); ;} break; case 560: { (yyval).i = REVERSE_ORIENTATION_; loopdepth++; ;} break; case 561: { kb_error(3250,"Syntax: REVERSE_ORIENTATION element_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); ;} break; case 562: { (yyval).i = REFINE_; loopdepth++; ;} break; case 563: { kb_error(2396,"Syntax: REFINE element_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); ;} break; case 564: { (yyval).i = EDGESWAP_; loopdepth++; ;} break; case 565: { kb_error(2397,"Syntax: EDGESWAP edge_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); ;} break; case 566: { (yyval).i = T1_EDGESWAP_; loopdepth++; ;} break; case 567: { kb_error(4009,"Syntax: T1_EDGESWAP edge_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); ;} break; case 568: { (yyval).i = EQUIANGULATE_; loopdepth++; ;} break; case 569: { kb_error(2545,"Syntax: EQUIANGULATE_ edge_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); ;} break; case 570: { (yyval).i = POP_; loopdepth++; ;} break; case 571: { kb_error(2431,"Syntax: POP element_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); ;} break; case 572: { (yyval).i = POP_TRI_TO_EDGE_; loopdepth++; ;} break; case 573: { kb_error(2800, "Syntax: POP_TRI_TO_EDGE facet_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); ;} break; case 574: { (yyval).i = POP_EDGE_TO_TRI_; loopdepth++; ;} break; case 575: { kb_error(2801,"Syntax: POP_EDGE_TO_TRI edge_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); ;} break; case 576: { (yyval).i = POP_QUAD_TO_QUAD_; loopdepth++; ;} break; case 577: { kb_error(2802,"Syntax: POP_QUAD_TO_QUAD facet_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR); ;} break; case 578: { (yyval).i = FIX_; loopdepth++; ;} break; case 579: { (yyval).i = UNFIX_; loopdepth++; ;} break; case 580: { (yyval).i = makenode(FIX_PARAMETER_,(yyvsp[0]).i,0); ;} break; case 581: { (yyval).i = makenode(UNFIX_PARAMETER_,(yyvsp[0]).i,0); ;} break; case 582: { (yyval).i = makenode(FIX_QUANTITY_,(yyvsp[0]).i,0);;} break; case 583: { (yyval).i = makenode(UNFIX_QUANTITY_,(yyvsp[0]).i,0);;} break; case 584: { kb_error(2398,"Syntax: FIX element_gen [ name ] [ WHERE rexpr ] or FIX quantity_name\n", Q_ERROR); ;} break; case 585: { kb_error(2399,"Syntax: UNFIX element_gen [ name ] [ WHERE rexpr ] or UNFIX quantity_name\n", Q_ERROR); ;} break; case 586: { if ( const_expr_flag ) YYABORT; (yyval).etype = (yyval).i = VERTEX; ;} break; case 587: { if ( const_expr_flag ) YYABORT; (yyval).etype = (yyval).i = EDGE ; ;} break; case 588: { if ( const_expr_flag ) YYABORT; (yyval).etype = (yyval).i = FACET ; ;} break; case 589: { if ( const_expr_flag ) YYABORT; (yyval).etype = (yyval).i = BODY ; ;} break; case 590: { if ( const_expr_flag ) YYABORT; (yyval).etype = (yyval).i = FACETEDGE; ;} break; case 591: { int next; next = makenode(INIT_ELEMENT_,(yyvsp[0]).i,0); (yyval).i = makenode(NEXT_ELEMENT_,next,0); (yyval).etype = (yyvsp[0]).i; ;} break; case 592: { int next; next = makenode(INIT_SUBELEMENT_,(yyvsp[-1]).i,(yyvsp[0]).i); (yyval).i = makenode(NEXT_ELEMENT_,next,0); (yyval).etype = (yyvsp[0]).etype; ;} break; case 593: { verb_flag = 0; (yyval).i = makenode(INDEXED_ELEMENT_,(yyvsp[-3]).i,(yyvsp[-1]).i); (yyval).etype = (yyvsp[-3]).i; ;} break; case 594: { kb_error(3809,"Missing index of element.\n",Q_ERROR); ;} break; case 595: { kb_error(3832,"Missing right bracket after index expression.\n",Q_ERROR); ;} break; case 596: { verb_flag = 0; (yyval).i = makenode(SYMBOL_ELEMENT_,(yyvsp[0]).i,0); (yyval).etype = list[(yyval).i].op1.eltype; ;} break; case 597: { verb_flag = 0; elsym = symbol_lookup("self"); if ( elsym == NULL ) kb_error(2400,"SELF not defined, since not in attribute function def.\n", COMMAND_ERROR); (yyval).i = makenode(SELF_ELEMENT_,elsym->type,0); (yyval).etype = elsym->type; ;} break; case 598: { verb_flag = 0; (yyval).i = makenode(ELEMENT_IDENT_,(yyvsp[0]).i,0); list[(yyval).i].op1.eltype = (yyvsp[0]).etype; (yyval).etype = (yyvsp[0]).etype; ;} break; case 599: { (yyval) = (yyvsp[-1]); ;} break; case 600: { (yyval) = (yyvsp[0]); ;} break; case 601: { subtype = (yyvsp[-3]).i; (yyval).i = makenode(INDEXED_SUBTYPE_,(yyvsp[-4]).i,(yyvsp[-1]).i); (yyval).etype = (yyvsp[-3]).etype; ;} break; case 602: { kb_error(3810,"Missing index of element.\n",Q_ERROR); ;} break; case 603: { kb_error(3812,"Missing right bracket after index expression.\n",Q_ERROR); ;} break; case 604: { int type = list[(yyvsp[0]).i].op1.eltype; begin_scope(); /* ended at end of aggregate */ elsym = symbol_add(default_name,type); elsym->localnum = list[(yyvsp[0]).i].op2.localnum; strcpy(last_name,default_name); (yyvsp[0]).symptr = elsym; (yyval).i = makenode(SINGLE_ELEMENT_,(yyvsp[0]).i,0); (yyval).symptr = elsym; (yyval).etype = (yyvsp[0]).etype; ;} break; case 605: { int type = list[(yyvsp[-1]).i].op1.eltype; begin_scope(); /* ended at end of aggregate */ elsym = symbol_add((yyvsp[0]).lexeme,type); elsym->localnum = list[(yyvsp[-1]).i].op2.localnum; strcpy(last_name,(yyvsp[0]).lexeme); (yyval).i = makenode(SINGLE_ELEMENT_,(yyvsp[-1]).i,0); list[(yyvsp[-1]).i].op5.string = (char*)mycalloc(strlen(elsym->name)+1,1); list[(yyvsp[-1]).i].flags |= HAS_STRING_5; strcpy(list[(yyvsp[-1]).i].op5.string,elsym->name); (yyvsp[-1]).symptr = elsym; (yyval).symptr = elsym; elsym = symbol_add(default_name,type); /* current id as default */ elsym->localnum = list[(yyvsp[-1]).i].op2.localnum; (yyval).etype = (yyvsp[-1]).etype; ;} break; case 606: { int type = list[(yyvsp[0]).i+list[(yyvsp[0]).i].left].op1.eltype; begin_scope(); /* ended at end of aggregate */ elsym = symbol_add(default_name,type); elsym->localnum = list[(yyvsp[0]).i].op2.localnum; strcpy(last_name,default_name); list[(yyvsp[0]).i].op5.string = (char*)mycalloc(strlen(default_name)+1,1); list[(yyvsp[0]).i].flags |= HAS_STRING_5; strcpy(list[(yyvsp[0]).i].op5.string,default_name); (yyvsp[0]).symptr = elsym; (yyval).symptr = elsym; (yyval).i = (yyvsp[0]).i; (yyval).etype = (yyvsp[0]).etype; ;} break; case 607: { int type = list[(yyvsp[-1]).i+list[(yyvsp[-1]).i].left].op1.eltype; begin_scope(); /* ended at end of aggregate */ elsym = symbol_add((yyvsp[0]).lexeme,type); elsym->localnum = list[(yyvsp[-1]).i].op2.localnum; strcpy(last_name,(yyvsp[0]).lexeme); list[(yyvsp[-1]).i].op5.string = (char*)mycalloc(strlen(elsym->name)+1,1); list[(yyvsp[-1]).i].flags |= HAS_STRING_5; strcpy(list[(yyvsp[-1]).i].op5.string,elsym->name); (yyvsp[-1]).symptr = elsym; (yyval).symptr = elsym; (yyval).i = (yyvsp[-1]).i; elsym = symbol_add(default_name,type); /* current id as default */ elsym->localnum = list[(yyvsp[-1]).i].op2.localnum; (yyval).etype = (yyvsp[-1]).etype; ;} break; case 608: { kb_error(1890,"Name already in use as procedure name.\n",COMMAND_ERROR); ;} break; case 609: { kb_error(1891,"Name already in use as procedure name.\n",COMMAND_ERROR); ;} break; case 610: { kb_error(1892,"Name already in use as variable name.\n",COMMAND_ERROR); ;} break; case 611: { kb_error(1893,"Name already in use as variable name.\n",COMMAND_ERROR); ;} break; case 612: { (yyval).i = makenode(WHERE_,(yyvsp[-2]).i,(yyvsp[0]).i); (yyval).symptr = (yyvsp[-2]).symptr; ;} break; case 613: { kb_error(3901,"Missing boolean expression after WHERE.\n",Q_ERROR); ;} break; case 614: { (yyval).i = ON_; ;} break; case 615: { (yyval).i = OFF_; ;} break; case 616: { (yyval).i = makenode(QUOTATION_,0,0); ;} break; case 617: { int size1 = strlen(list[(yyval).i].op1.string); int size2 = strlen(yytext); (yyval).i = (yyvsp[-1]).i; list[(yyval).i].op1.string = (char*)kb_realloc(list[(yyval).i].op1.string, size1+size2+1); strncpy(list[(yyval).i].op1.string+size1,yytext,size2); ;} break; case 618: { (yyval).i = (yyvsp[0]).i ;} break; case 619: { (yyval).i = makenode(STRINGGLOBAL_,(yyvsp[0]).i,0); ;} break; case 620: { (yyval).i = makenode(PERM_STRINGGLOBAL_,(yyvsp[0]).i,0); ;} break; case 621: { (yyval).i = makenode(DATAFILENAME_,(yyvsp[0]).i,0); ;} break; case 622: { (yyval).i = makenode(WARNING_MESSAGES_,(yyvsp[0]).i,0); ;} break; case 623: { (yyval).i = makenode(DATE_AND_TIME_,0,0); ;} break; case 624: { (yyval).i = makenode(GET_TRANSFORM_EXPR_,0,0); ;} break; case 625: { (yyval).i = makenode(SPRINTFHEAD_,(yyvsp[0]).i,0); ;} break; case 626: { kb_error(3894,"Missing format string after SPRINTF.\n",Q_ERROR); ;} break; case 627: { (yyval).i = (yyvsp[0]).i; ;} break; case 628: { (yyval).i = makenode(SPRINTF_,(yyvsp[-2]).i,(yyvsp[0]).i); ;} break; case 629: { kb_error(3806,"Error in SPRINTF arguments.\n",Q_ERROR); ;} break; case 630: { (yyval).qnum = (yyvsp[0]).i; (yyval).i = COORD_; (yyval).datatype=REAL_TYPE; ;} break; case 631: { (yyval).qnum = (yyvsp[0]).i; (yyval).i = PARAM_; (yyval).datatype=REAL_TYPE; ;} break; case 632: { (yyval).i = GET_LENGTH_; (yyval).datatype=REAL_TYPE; ;} break; case 633: { (yyval).i = GET_MEANCURV_; (yyval).datatype=REAL_TYPE; ;} break; case 634: { (yyval).i = GET_SHOW_; (yyval).datatype=REAL_TYPE; ;} break; case 635: { (yyval).i = GET_ORIENTATION_; (yyval).datatype=REAL_TYPE; ;} break; case 636: { (yyval).i = GET_STAR_; (yyval).datatype=REAL_TYPE; ;} break; case 637: { (yyval).i = GET_SQ_MEAN_CURV_; (yyval).datatype=REAL_TYPE; ;} break; case 638: { (yyval).i = GET_DIHEDRAL_; (yyval).datatype=REAL_TYPE; ;} break; case 639: { (yyval).i = GET_VALENCE_; (yyval).datatype=REAL_TYPE; ;} break; case 640: { (yyval).i = GET_AREA_; (yyval).datatype=REAL_TYPE; ;} break; case 641: { (yyval).i = GET_VOLUME_; (yyval).datatype=REAL_TYPE; ;} break; case 642: { (yyval).i = GET_VOLCONST_; (yyval).datatype=REAL_TYPE; ;} break; case 643: { (yyval).i = GET_TARGET_; (yyval).datatype=REAL_TYPE; ;} break; case 644: { (yyval).i = GET_MPI_TASK_; (yyval).datatype=REAL_TYPE; ;} break; case 645: { (yyval).i = GET_DENSITY_; (yyval).datatype=REAL_TYPE; ;} break; case 646: { (yyval).i = GET_PRESSURE_; (yyval).datatype=REAL_TYPE; ;} break; case 647: { (yyval).i = GET_ID_; (yyval).datatype=REAL_TYPE; ;} break; case 648: { (yyval).i = GET_OID_; (yyval).datatype=REAL_TYPE; ;} break; case 649: { (yyval).i = GET_TAG_; (yyval).datatype=REAL_TYPE; ;} break; case 650: { (yyval).i = GET_COLOR_; (yyval).datatype=REAL_TYPE; ;} break; case 651: { (yyval).i = GET_FRONTCOLOR_; (yyval).datatype=REAL_TYPE; ;} break; case 652: { (yyval).i = GET_BACKCOLOR_; (yyval).datatype=REAL_TYPE; ;} break; case 653: { (yyval).i = GET_BACKBODY_; (yyval).datatype=REAL_TYPE; ;} break; case 654: { (yyval).i = GET_FRONTBODY_; (yyval).datatype=REAL_TYPE; ;} break; case 655: { (yyval).i = GET_ORIGINAL_; (yyval).datatype=REAL_TYPE; ;} break; case 656: { (yyval).i = GET_FIXED_; (yyval).datatype=REAL_TYPE; ;} break; case 657: { (yyval).i = GET_NO_REFINE_; (yyval).datatype=REAL_TYPE; ;} break; case 658: { (yyval).i = GET_HIT_PARTNER_; (yyval).datatype=REAL_TYPE;;} break; case 659: { (yyval).i = GET_NONCONTENT_; (yyval).datatype=REAL_TYPE; ;} break; case 660: { (yyval).i = GET_NO_DISPLAY_; (yyval).datatype=REAL_TYPE; ;} break; case 661: { (yyval).i = GET_FIXEDVOL_; (yyval).datatype=REAL_TYPE; ;} break; case 662: { (yyval).i = GET_AXIAL_POINT_; (yyval).datatype=REAL_TYPE; ;} break; case 663: { (yyval).i = GET_TRIPLE_PT_; (yyval).datatype=REAL_TYPE; ;} break; case 664: { (yyval).i = GET_TETRA_PT_; (yyval).datatype=REAL_TYPE; ;} break; case 665: { (yyval).i = GET_MIDV_; (yyval).datatype=REAL_TYPE; ;} break; case 666: { (yyval).i = GET_WRAP_; (yyval).datatype=REAL_TYPE; ;} break; case 667: { (yyval).i = GET_MID_EDGE_; (yyval).datatype=REAL_TYPE; ;} break; case 668: { (yyval).i = GET_MID_FACET_; (yyval).datatype=REAL_TYPE; ;} break; case 669: { (yyval).i = GET_BARE_; (yyval).datatype=REAL_TYPE; ;} break; case 670: { (yyval).i = GET_PHASE_; (yyval).datatype=REAL_TYPE; ;} break; case 671: { (yyval).qnum = (yyvsp[0]).i; (yyval).i = GET_QUANTITY_; (yyval).datatype=REAL_TYPE; ;} break; case 672: { (yyval).qnum = (yyvsp[0]).i; (yyval).i = GET_INSTANCE_; (yyval).datatype=REAL_TYPE; ;} break; case 673: { struct extra *ex; (yyval).i = GET_EXTRA_ATTR_ ; (yyval).qnum = (yyvsp[0]).qnum + ((yyvsp[0]).etype << YYTYPESHIFT); (yyval).etype = (yyvsp[0]).etype; ex = EXTRAS((yyvsp[0]).etype) + (yyvsp[0]).qnum; (yyval).datatype= (ex->type <= MAX_NUMERIC_TYPE) ? REAL_TYPE : ex->type; ;} break; case 674: { if ( const_expr_flag ) { YYABORT; /* illegal for const rexpr */ } (yyval)= (yyvsp[0]); ;} break; case 675: { if ( (datafile_flag && boundary_expr_flag && ((yyvsp[0]).i==PARAM_)) || ( datafile_flag && ((yyvsp[0]).i==COORD_) ) ) { coord_num = (yyvsp[0]).qnum; (yyval).i = makenode(PUSHPARAM,0,0); } else { (yyval).i = makenode(ATTRIBUTE,(yyvsp[0]).i,(yyvsp[0]).qnum); (yyval).datatype = list[(yyval).i].datatype = (yyvsp[0]).datatype; } ;} break; case 676: { if ( const_expr_flag ) { YYABORT; /* illegal for const rexpr */ } switch ( (yyvsp[-1]).i ) { case COORD_: (yyval).i = makenode(INDEXED_COORD_,(yyvsp[0]).i,0); break; case GET_VERTEXNORMAL_: (yyval).i = makenode(GET_VERTEXNORMAL_,(yyvsp[0]).i,0); break; case PARAM_: if ( (yyvsp[-1]).etype == VERTEX ) { (yyval).qnum = V_PARAM_ATTR; (yyval).i = makenode(INDEXED_ATTRIBUTE,(yyvsp[0]).i,(yyval).qnum+((yyvsp[-1]).etype<type); list[(yyvsp[0]).i].op1.localnum = elsym->localnum; } else kb_error(1896,"\nMissing element for attribute. (Get quantity value with name.value) \n",COMMAND_ERROR); } list[(yyvsp[0]).i].datatype = (yyvsp[0]).datatype; ;} break; case 691: { (yyval).i = makenode(QUALIFIED_ATTRIBUTE,(yyvsp[-1]).i,(yyvsp[0]).i); list[(yyvsp[0]).i].op1.localnum = list[(yyvsp[-1]).i].op2.localnum; list[(yyval).i].datatype = (yyvsp[0]).datatype; ;} break; case 692: { sprintf(errmsg,"\"%s\" is not a attribute name.\n",(yyvsp[0]).lexeme); kb_error(3458,errmsg,Q_ERROR); ;} break; case 693: { (yyval).i = makenode(IS_DEFINED_,(yyvsp[-1]).i,0); ;} break; case 694: { kb_error(3814,"Syntax: IS_DEFINED ( quoted_string )\n",Q_ERROR); ;} break; case 695: { kb_error(3815,"Missing closing parenthesis for IS_DEFINED\n",Q_ERROR); ;} break; case 696: { int etype; (yyval).qnum = (yyvsp[-1]).qnum; etype = (yyvsp[-1]).etype; (yyval).i = makenode(SIZEOF_ATTR_,(yyval).qnum,etype); ;} break; case 697: { (yyval).i = makenode(SIZEOF_ARRAY_,(yyvsp[-1]).i,0); ;} break; case 698: { (yyval).i = makenode(SIZEOF_STRING_,(yyvsp[-1]).i,0); ;} break; case 699: { strcpy(errmsg,"Syntax: SIZEOF ( extra_attribute )\n"); strcat(errmsg," SIZEOF ( array_name ) \n"); strcat(errmsg," SIZEOF ( string_expr ) \n"); kb_error(3816,errmsg,Q_ERROR); ;} break; case 700: { (yyval).i = makenode(TOGGLEVALUE,(yyvsp[0]).i,0); ;} break; case 701: { (yyval).i = makenode(TOGGLEVALUE,AUTOCHOP_,0); ;} break; case 702: { (yyval).i = makenode(TOGGLEVALUE,LAGRANGE_,0); ;} break; case 703: { (yyval).i = makenode(EPRINT_,(yyvsp[0]).i,0); ;} break; case 704: { kb_error(2886,"Syntax: EPRINT expression\n",Q_ERROR); ;} break; case 705: { (yyval).i = (yyvsp[-1]).i; ;} break; case 706: { kb_error(2401,"Missing closing parenthesis?\n",Q_ERROR); ;} break; case 707: { (yyval).i = makenode(GET_INTERNAL_,(yyvsp[0]).i,0); ;} break; case 708: { (yyval).i = makenode(GET_INTERNAL_,V_SCALE,0); ;} break; case 709: { (yyval).i = makenode(PUSHGLOBAL_,(yyvsp[0]).i,0); ;} break; case 710: { (yyval).i = makenode(PUSH_PERM_GLOBAL_,(yyvsp[0]).i,0); ;} break; case 711: { (yyval).i = makenode(PUSH_PARAM_EXTRA_,(yyvsp[-2]).i,(yyvsp[0]).i); ;} break; case 712: { kb_error(3817,"Permitted optimizing parameter attributes: pdelta pscale\n", Q_ERROR); ;} break; case 713: { (yyval).i = makenode(DYNAMIC_LOAD_FUNC_,(yyvsp[0]).i,0); ;} break; case 714: { (yyval).i = makenode(PUSHQVALUE_,(yyvsp[0]).i,0); ;} break; case 715: { (yyval).i = makenode(PUSHQPRESSURE_,(yyvsp[-2]).i,0); ;} break; case 716: { (yyval).i = makenode(PUSHQMODULUS_,(yyvsp[-2]).i,0); ;} break; case 717: { (yyval).i = makenode(PUSHQTOLERANCE_,(yyvsp[-2]).i,0); ;} break; case 718: { (yyval).i = makenode(PUSHMMODULUS_,(yyvsp[-2]).i,0); ;} break; case 719: { (yyval).i = makenode(PUSHQTARGET_,(yyvsp[-2]).i,0); ;} break; case 720: { (yyval).i = makenode(PUSHQVALUE_,(yyvsp[-2]).i,0); ;} break; case 721: { (yyval).i = makenode(PUSHMVALUE_,(yyvsp[-2]).i,0); ;} break; case 722: { (yyval).i = makenode(PUSHQVOLCONST_,(yyvsp[-2]).i,0); ;} break; case 723: { (yyval).i = makenode(PUSHQFIXED_,(yyvsp[-2]).i,0); ;} break; case 724: { (yyval).i = makenode(PUSHQENERGY_,(yyvsp[-2]).i,0); ;} break; case 725: { (yyval).i = makenode(PUSHQINFO_ONLY_,(yyvsp[-2]).i,0); ;} break; case 726: { (yyval).i = makenode(PUSHQCONSERVED_,(yyvsp[-2]).i,0); ;} break; case 727: { strcpy(errmsg, "Quantity name needs attribute. Syntax: quantityname.attribute\n"); strcat(errmsg,"Possible quantity attributes: \n"); strcat(errmsg," value, modulus, pressure, target, tolerance, volconst,\n"); strcat(errmsg," fixed, energy, info_only, conserved\n"); kb_error(3818,errmsg,Q_ERROR); ;} break; case 728: { strcpy(errmsg,"Possible quantity attributes: \n"); strcat(errmsg," value, modulus, pressure, target, tolerance, volconst,\n"); strcat(errmsg," fixed, energy, info_only, conserved\n"); kb_error(3819,errmsg,Q_ERROR); ;} break; case 729: { kb_error(3907,"Possible method instance attributes: value, modulus \n", Q_ERROR); ;} break; case 730: { strcpy(errmsg, "Method instance name needs attribute. Syntax: instancename.attribute\n"); strcat(errmsg,"Possible method instance attributes: value, modulus \n"); kb_error(3820,errmsg,Q_ERROR); ;} break; case 731: { real_val = (REAL)(yyvsp[0]).i; (yyval).i = makenode(PUSHCONST,0,0); ;} break; case 732: { real_val = (REAL)(yyvsp[0]).i; (yyval).i = makenode(PUSHCONST,0,0); ;} break; case 733: { real_val = (yyvsp[0]).r; (yyval).i = makenode(PUSHCONST,0,0); ;} break; case 734: { real_val = (yyvsp[0]).r; (yyval).i = makenode(PUSHCONST,0,0); ;} break; case 735: { (yyval).i = (yyvsp[0]).i; ;} break; case 736: { (yyval).i = makenode(PUSHPI,0,0); ;} break; case 737: { (yyval).i = makenode(PUSHE,0,0); ;} break; case 738: { (yyval).i = makenode(PUSHG,0,0); ;} break; case 739: { int_val = (yyvsp[0]).i; (yyval).i = makenode(USERFUNC,0,0); ;} break; case 740: { (yyval).i = makenode((yyvsp[-3]).i,(NTYPE)(yyvsp[-1]).i,0); ;} break; case 741: { sprintf(errmsg,"Syntax: %s ( rexpr )\n",keywordname((yyvsp[-1]).i)); kb_error(3821,errmsg,Q_ERROR); ;} break; case 742: { (yyval).i = makenode((yyvsp[-5]).i,(NTYPE)(yyvsp[-3]).i,(yyvsp[-1]).i); ;} break; case 743: { sprintf(errmsg,"Syntax: %s ( rexpr , rexpr )\n",keywordname((yyvsp[0]).i)); kb_error(3822,errmsg,Q_ERROR); ;} break; case 744: { (yyval).i = makenode('+',(NTYPE)(yyvsp[-2]).i,(NTYPE)(yyvsp[0]).i); ;} break; case 745: { (yyval).i = makenode('-',(NTYPE)(yyvsp[-2]).i,(NTYPE)(yyvsp[0]).i); ;} break; case 746: { (yyval).i = makenode('=',(NTYPE)(yyvsp[-2]).i,(NTYPE)(yyvsp[0]).i); ;} break; case 747: { (yyval).i = makenode('/',(NTYPE)(yyvsp[-2]).i,(NTYPE)(yyvsp[0]).i); ;} break; case 748: { (yyval).i = makenode('*',(NTYPE)(yyvsp[-2]).i,(NTYPE)(yyvsp[0]).i); ;} break; case 749: { (yyval).i = makenode('%',(NTYPE)(yyvsp[-2]).i,(NTYPE)(yyvsp[0]).i); ;} break; case 750: { (yyval).i = makenode(IMOD_,(NTYPE)(yyvsp[-2]).i,(NTYPE)(yyvsp[0]).i); ;} break; case 751: { (yyval).i = makenode(IDIV_,(NTYPE)(yyvsp[-2]).i,(NTYPE)(yyvsp[0]).i); ;} break; case 752: { (yyval).i = makenode(POW,(NTYPE)(yyvsp[-2]).i,(NTYPE)(yyvsp[0]).i); ;} break; case 753: { (yyval).i = makenode(LT_,(NTYPE)(yyvsp[-2]).i,(NTYPE)(yyvsp[0]).i); ;} break; case 754: { (yyval).i = makenode(GT_,(NTYPE)(yyvsp[-2]).i,(NTYPE)(yyvsp[0]).i); ;} break; case 755: { (yyval).i = makenode(NE_,(NTYPE)(yyvsp[-2]).i,(NTYPE)(yyvsp[0]).i); ;} break; case 756: { (yyval).i = makenode(EQ_,(NTYPE)(yyvsp[-2]).i,(NTYPE)(yyvsp[0]).i); ;} break; case 757: { (yyval).i = makenode(LE_,(NTYPE)(yyvsp[-2]).i,(NTYPE)(yyvsp[0]).i); ;} break; case 758: { (yyval).i = makenode(GE_,(NTYPE)(yyvsp[-2]).i,(NTYPE)(yyvsp[0]).i); ;} break; case 759: { (yyval).i = makenode(AND_,(NTYPE)(yyvsp[-2]).i,(NTYPE)(yyvsp[0]).i); ;} break; case 760: { (yyval).i = makenode(OR_,(NTYPE)(yyvsp[-2]).i,(NTYPE)(yyvsp[0]).i); ;} break; case 761: { kb_error(3031,"Cannot add scalar and array\n",Q_ERROR); ;} break; case 762: { kb_error(3027,"Cannot subtract scalar and array\n",Q_ERROR); ;} break; case 763: { kb_error(3823,"Bad second expression after +\n",Q_ERROR); ;} break; case 764: { kb_error(3825,"Bad second expression after -\n",Q_ERROR); ;} break; case 765: { kb_error(3828,"Bad second expression after =\n",Q_ERROR); ;} break; case 766: { kb_error(3829,"Bad second expression after /\n",Q_ERROR); ;} break; case 767: { kb_error(3830,"Bad second expression after *\n",Q_ERROR); ;} break; case 768: { kb_error(3831,"Bad second expression after %\n",Q_ERROR); ;} break; case 769: { kb_error(2887,"Bad second expression after IMOD\n",Q_ERROR); ;} break; case 770: { kb_error(3919,"Bad second expression after IDIV\n",Q_ERROR); ;} break; case 771: { kb_error(3920,"Bad second expression after ^\n",Q_ERROR); ;} break; case 772: { kb_error(3921,"Bad second expression after <\n",Q_ERROR); ;} break; case 773: { kb_error(3922,"Bad second expression after >\n",Q_ERROR); ;} break; case 774: { kb_error(3923,"Bad second expression after !=\n",Q_ERROR); ;} break; case 775: { kb_error(3924,"Bad second expression after ==\n",Q_ERROR); ;} break; case 776: { kb_error(3925,"Bad second expression after <=\n",Q_ERROR); ;} break; case 777: { kb_error(3926,"Bad second expression after >=\n",Q_ERROR); ;} break; case 778: { kb_error(3927,"Bad second expression after AND\n",Q_ERROR); ;} break; case 779: { kb_error(3928,"Bad second expression after OR\n",Q_ERROR); ;} break; case 780: { (yyval).i = makenode(CHS,(NTYPE)(yyvsp[0]).i,0); ;} break; case 781: { kb_error(3826,"Bad expression after unary minus.\n",Q_ERROR); ;} break; case 782: { (yyval).i = (yyvsp[0]).i; ;} break; case 783: { (yyval).i = makenode(NOT_,(NTYPE)(yyvsp[0]).i,0); ;} break; case 784: { kb_error(3827,"Bad expression after NOT.\n",Q_ERROR); ;} break; case 785: { cond_expr_flag++; (yyval).i = makenode(COND_TEST_,(yyvsp[-1]).i,0); ;} break; case 786: { cond_expr_flag--; (yyval).i = makenode(COND_EXPR_,(yyvsp[-2]).i,(yyvsp[-1]).i); ;} break; case 787: { (yyval).i = makenode(COND_ELSE_,(yyvsp[-1]).i,(yyvsp[0]).i); ;} break; case 788: { kb_error(3824,"Conditional expression syntax: expr1 ? expr2 : expr3\n", Q_ERROR); ;} break; case 789: { (yyval).i = MAX_; ;} break; case 790: { (yyval).i = MIN_; ;} break; case 791: { (yyval).i = SUM_; ;} break; case 792: { (yyval).i = AVG_; ;} break; case 793: { (yyval).i = COUNT_; ;} break; case 794: { (yyval).i = HISTOGRAM_; ;} break; case 795: { (yyval).i = LOGHISTOGRAM_; ;} break; case 796: { kb_error(2402,"Syntax: HISTOGRAM(element_gen,expression)\n", Q_ERROR); ;} break; case 797: { kb_error(2403,"Syntax: LOGHISTOGRAM(element_gen,expression)\n", Q_ERROR); ;} break; case 798: { aggrtype = FOREACH_; loopdepth++; (yyval).i = makenode(AGGREGATE_INIT_,0,0); ;} break; case 799: { int aggr; aggrtype = FOREACH_; aggr = makenode(AGGREGATE_,(yyvsp[-2]).i,(yyvsp[0]).i); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-3]).i,aggr); end_scope(); ;} break; case 800: { kb_error(2404, "Syntax: FOREACH element_gen [ name ] [WHERE expr] DO command\n",Q_ERROR);;} break; case 801: { use_given_id = 1; /* in eval() */ ;} break; case 802: { (yyval).i = makenode(SHOW_,(yyvsp[0]).i,0); use_given_id = 0; end_scope(); ;} break; case 803: { (yyval).i = makenode(SINGLE_LETTER_,'s',0); ;} break; case 804: { kb_error(2405,"Syntax: SHOW element_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR);;} break; case 805: { use_given_id = 1; /* in eval() */ ;} break; case 806: { (yyval).i = makenode(SHOW_EXPR_,(yyvsp[0]).i,0); use_given_id = 0; end_scope(); ;} break; case 807: { kb_error(2406,"Syntax: SHOW_EXPR element_gen [ name ] [ WHERE rexpr ]\n",Q_ERROR);;} break; case 808: { loopdepth++; aggrtype = (yyvsp[0]).i; (yyval).i = makenode(AGGREGATE_INIT_,0,0); ;} break; case 809: { int aggr; aggrtype = (yyvsp[-6]).i; aggr = makenode(AGGREGATE_,(yyvsp[-3]).i,(yyvsp[-1]).i); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-5]).i,aggr); end_scope(); ;} break; case 810: { loopdepth++; aggrtype = (yyvsp[0]).i; (yyval).i = makenode(AGGREGATE_INIT_,0,0); ;} break; case 811: { int aggr; aggrtype = (yyvsp[-6]).i; aggr = makenode(AGGREGATE_,(yyvsp[-3]).i,(yyvsp[-1]).i); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-5]).i,aggr); end_scope(); ;} break; case 812: { aggrtype = (yyvsp[0]).i; (yyval).i = makenode(AGGREGATE_INIT_,0,0); ;} break; case 813: { int aggr; aggrtype = (yyvsp[-2]).i; aggr = makenode(AGGREGATE_,(yyvsp[0]).i,0); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-1]).i,aggr); end_scope(); ;} break; case 814: { (yyval).i = makenode(SET_INIT_,0,0); ;} break; case 815: { (yyval).i = SET_NO_REFINE_ ; ;} break; case 816: { (yyval).i = SET_HIT_PARTNER_ ; ;} break; case 817: { (yyval).i = SET_FIXED_ ; ;} break; case 818: { (yyval).i = SET_BARE_ ; ;} break; case 819: { (yyval).i = SET_NONCONTENT_ ; ;} break; case 820: { (yyval).i = SET_NO_DISPLAY_ ; ;} break; case 821: { (yyval).i = SET_AXIAL_POINT_ ; ;} break; case 822: { (yyval).i = SET_TETRA_PT_; ;} break; case 823: { (yyval).i = SET_TRIPLE_PT_; ;} break; case 824: { int aggr; aggrtype = (yyvsp[0]).i; aggr = makenode(AGGREGATE_,(yyvsp[-1]).i,0); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-2]).i,aggr); end_scope(); ;} break; case 825: { int aggr,where; aggrtype = (yyvsp[-2]).i; where = makenode(WHERE_,(yyvsp[-3]).i,(yyvsp[0]).i); aggr = makenode(AGGREGATE_,where,0); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-4]).i,aggr); end_scope(); ;} break; case 826: { (yyval).i = SET_FRONTBODY_; tok=0; /* for UMINUS */ ;} break; case 827: { (yyval).i = SET_BACKBODY_; tok=0; ;} break; case 828: { (yyval).i = SET_COLOR_; tok=0; ;} break; case 829: { (yyval).i = SET_FRONTCOLOR_; tok=0; ;} break; case 830: { (yyval).i = SET_BACKCOLOR_; tok=0; ;} break; case 831: { (yyval).i = SET_DENSITY_; tok=0; ;} break; case 832: { (yyval).i = SET_ORIGINAL_; tok=0; ;} break; case 833: { (yyval).i = SET_VOLCONST_; tok=0; ;} break; case 834: { (yyval).i = SET_VOLUME_; tok=0; ;} break; case 835: { (yyval).i = SET_TARGET_; tok=0; ;} break; case 836: { (yyval).i = SET_PRESSURE_; tok=0; ;} break; case 837: { (yyval).i = SET_OPACITY_; tok=0; ;} break; case 838: { (yyval).i = SET_CONSTRAINT_; tok=0; ;} break; case 839: { (yyval).i = SET_BOUNDARY_; tok=0; ;} break; case 840: { (yyval).i = SET_TAG_; tok=0; ;} break; case 841: { (yyval).i = SET_COORD_+(yyvsp[0]).i; tok = 0; /* UMINUS bug */ ;} break; case 842: { (yyval).i = SET_PARAM_+(yyvsp[0]).i; tok=0; ;} break; case 843: { (yyval).i = SET_PHASE_; tok=0; ;} break; case 844: { (yyval).i = SET_EXTRA_ATTR_ ; tok=0; (yyval).qnum = (yyvsp[0]).qnum; (yyval).etype = (yyvsp[0]).etype; strcpy(set_extra_name,EXTRAS((yyval).etype)[(yyval).qnum].name); ;} break; case 845: { (yyval).i = SET_ORIENTATION_ ; tok=0; ;} break; case 846: { (yyval).i = SET_WRAP_ ; tok = 0;;} break; case 847: { (yyval).i = (yyvsp[0]).i;;} break; case 848: { (yyval).i = (yyvsp[0]).i;;} break; case 849: { (yyval).i = ASSIGN_; ;} break; case 850: { (yyval).i = (yyvsp[0]).i; ;} break; case 851: { kb_error(3415,"Expected assignment operator, got '='\n", Q_ERROR); ;} break; case 852: { (yyval).i = makenode(SINGLE_ELEMENT_,(yyvsp[-2]).i,0); ;} break; case 853: { int mm; int type = list[(yyvsp[-5]).i].op1.eltype; begin_scope(); /* ended at end of aggregate */ elsym = symbol_add(default_name,type); elsym->localnum = list[(yyvsp[-5]).i].op2.localnum; strcpy(last_name,default_name); (yyvsp[-5]).symptr = elsym; attr_kind = (yyvsp[-3]).i; (yyvsp[-2]).symptr = 0; /* in case of WHERE */ assigntype = (yyvsp[-1]).i; mm = makenode(SET_ATTRIBUTE_A,(yyvsp[0]).i,0); (yyval).i = makenode(SINGLE_ASSIGN_,(yyvsp[-2]).i,mm); end_scope(); ;} break; case 854: { (yyval).i = makenode(SINGLE_ELEMENT_,(yyvsp[-2]).i,0); ;} break; case 855: { int mm; int type = list[(yyvsp[-6]).i].op1.eltype; begin_scope(); /* ended at end of aggregate */ elsym = symbol_add(default_name,type); elsym->localnum = list[(yyvsp[-6]).i].op2.localnum; strcpy(last_name,default_name); (yyvsp[-6]).symptr = elsym; attr_kind = (yyvsp[-4]).i; subtree_swap(&(yyvsp[-2]).i,&(yyvsp[0]).i); (yyvsp[-3]).symptr = 0; /* in case of WHERE */ assigntype = (yyvsp[-1]).i; mm = makenode(SET_ATTRIBUTE_A,(yyvsp[0]).i,(yyvsp[-2]).i); (yyval).i = makenode(SINGLE_ASSIGN_,(yyvsp[-3]).i,mm); end_scope(); ;} break; case 856: { int aggr; int nn; aggrtype = SET_ATTRIBUTE_LOOP_; attr_kind = (yyvsp[-1]).i; elsym = (yyvsp[-2]).symptr; (yyvsp[-2]).symptr = 0; /* in case of WHERE */ nn = makenode(SET_ATTRIBUTE_L,(yyvsp[0]).i,0); aggr = makenode(AGGREGATE_,(yyvsp[-2]).i,nn); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-3]).i,aggr); end_scope(); ;} break; case 857: { int aggr; int nn; aggrtype = SET_ATTRIBUTE_LOOP_; attr_kind = (yyvsp[-2]).i; subtree_swap(&(yyvsp[-1]).i,&(yyvsp[0]).i); /* get index eval in top of stack */ elsym = (yyvsp[-3]).symptr; (yyvsp[-3]).symptr = 0; /* in case of WHERE */ nn = makenode(SET_ATTRIBUTE_L,(yyvsp[0]).i,(yyvsp[-1]).i); aggr = makenode(AGGREGATE_,(yyvsp[-3]).i,nn); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-4]).i,aggr); end_scope(); ;} break; case 858: { int aggr; int nn,mm,kk; aggrtype = SET_ATTRIBUTE_LOOP_; elsym = (yyvsp[-3]).symptr; (yyvsp[-3]).symptr = 0; /* in case of WHERE */ kk = makenode(ATTRIB_LVALUE_,0,0); list[kk].op1.localnum = 0; list[kk].op2.name_id = set_name_eltype((yyvsp[-2]).qnum,(yyvsp[-2]).etype); subtree_swap(&(yyvsp[0]).i,&kk); /* so datastart before rexpr */ subtree_swap(&(yyvsp[-1]).i,&kk); /* so datastart before indexset */ mm = makenode(ARRAY_LVALUE_INDEXED_,kk,(yyvsp[-1]).i); nn = makenode(ARRAY_ASSIGNOP_SINGLE_,mm,(yyvsp[0]).i); list[nn].flags |= SET_ASSIGNOP; list[nn].op1.assigntype = ASSIGN_; list[nn].op2.name_id = set_name_eltype((yyvsp[-2]).qnum,(yyvsp[-2]).etype); aggr = makenode(AGGREGATE_,(yyvsp[-3]).i,nn); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-4]).i,aggr); end_scope(); ;} break; case 859: { int aggr; int nn,kk; aggrtype = SET_ATTRIBUTE_LOOP_; attr_kind = (yyvsp[-1]).i; elsym = (yyvsp[-2]).symptr; (yyvsp[-2]).symptr = 0; /* in case of WHERE */ kk = makenode(ATTRIB_LVALUE_,0,0); list[kk].op1.localnum = 0; list[kk].op2.name_id = set_name_eltype((yyvsp[-1]).qnum,(yyvsp[-1]).etype); subtree_swap(&(yyvsp[0]).i,&kk); /* so lvalue before rvalue */ nn = makenode(ARRAY_ASSIGNOP_ARRAY_,kk,(yyvsp[0]).i); list[nn].flags |= SET_ASSIGNOP; list[nn].op1.assigntype = ASSIGN_; list[nn].op2.name_id = set_name_eltype((yyvsp[-1]).qnum,(yyvsp[-1]).etype); aggr = makenode(AGGREGATE_,(yyvsp[-2]).i,nn); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-3]).i,aggr); end_scope(); ;} break; case 860: { int aggr; int nn,kk; aggrtype = SET_ATTRIBUTE_LOOP_; attr_kind = (yyvsp[-1]).i; elsym = (yyvsp[-2]).symptr; (yyvsp[-2]).symptr = 0; /* in case of WHERE */ kk = makenode(ATTRIB_LVALUE_,0,0); list[kk].op1.localnum = 0; list[kk].op2.name_id = set_name_eltype((yyvsp[-1]).qnum,(yyvsp[-1]).etype); subtree_swap(&(yyvsp[0]).i,&kk); /* so rexpr before datastart */ nn = makenode(ARRAY_ASSIGNOP_SCALAR_,kk,(yyvsp[0]).i); list[nn].op1.assigntype = ASSIGN_; list[nn].op2.name_id = set_name_eltype((yyvsp[-1]).qnum,(yyvsp[-1]).etype); list[nn].flags |= SET_ASSIGNOP; aggr = makenode(AGGREGATE_,(yyvsp[-2]).i,nn); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-3]).i,aggr); end_scope(); ;} break; case 861: { int aggr,where; int nn,kk; aggrtype = SET_ATTRIBUTE_LOOP_; attr_kind = (yyvsp[-3]).i; /* splice in the WHERE clause */ subtree_swap(&(yyvsp[-2]).i,&(yyvsp[0]).i); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&(yyvsp[-2]).i,&where); /* get in proper linear order */ list[where].left = (yyvsp[-4]).i - where; list[where].right = (yyvsp[0]).i - where; elsym = (yyvsp[-4]).symptr; (yyvsp[-4]).symptr = 0; /* in case of WHERE */ kk = makenode(ATTRIB_LVALUE_,0,0); list[kk].op1.localnum = 0; list[kk].op2.name_id = set_name_eltype((yyvsp[-3]).qnum,(yyvsp[-3]).etype); subtree_swap(&(yyvsp[-2]).i,&kk); /* so rexpr before datastart */ nn = makenode(ARRAY_ASSIGNOP_SCALAR_,kk,(yyvsp[-2]).i); list[nn].flags |= SET_ASSIGNOP; list[nn].op2.name_id = set_name_eltype((yyvsp[-3]).qnum,(yyvsp[-3]).etype); list[nn].op1.assigntype = ASSIGN_; aggr = makenode(AGGREGATE_,(yyvsp[-4]).i,nn); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-5]).i,aggr); end_scope(); ;} break; case 862: { int aggr,where; int nn,mm,kk; aggrtype = SET_ATTRIBUTE_LOOP_; /* splice in the WHERE clause */ subtree_swap(&(yyvsp[-2]).i,&(yyvsp[0]).i); subtree_swap(&(yyvsp[-3]).i,&(yyvsp[0]).i); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&(yyvsp[-2]).i,&where); /* get in proper linear order */ subtree_swap(&(yyvsp[-3]).i,&where); /* get in proper linear order */ list[where].left = (yyvsp[-5]).i - where; list[where].right = (yyvsp[0]).i - where; elsym = (yyvsp[-5]).symptr; (yyvsp[-5]).symptr = 0; /* in case of WHERE */ kk = makenode(ATTRIB_LVALUE_,0,0); list[kk].op1.localnum = 0; list[kk].op2.name_id = set_name_eltype((yyvsp[-4]).qnum,(yyvsp[-4]).etype); subtree_swap(&(yyvsp[-2]).i,&kk); /* so datastart before rexpr */ subtree_swap(&(yyvsp[-3]).i,&kk); /* so datastart before indexset */ mm = makenode(ARRAY_LVALUE_INDEXED_,kk,(yyvsp[-3]).i); nn = makenode(ARRAY_ASSIGNOP_SINGLE_,mm,(yyvsp[-2]).i); list[nn].flags |= SET_ASSIGNOP; list[nn].op1.assigntype = ASSIGN_; aggr = makenode(AGGREGATE_,(yyvsp[-5]).i,nn); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-6]).i,aggr); end_scope(); ;} break; case 863: { int aggr,where; int nn,kk; aggrtype = SET_ATTRIBUTE_LOOP_; attr_kind = (yyvsp[-3]).i; /* splice in the WHERE clause */ subtree_swap(&(yyvsp[-2]).i,&(yyvsp[0]).i); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&(yyvsp[-2]).i,&where); /* get in proper linear order */ list[where].left = (yyvsp[-4]).i - where; list[where].right = (yyvsp[0]).i - where; elsym = (yyvsp[-4]).symptr; (yyvsp[-4]).symptr = 0; /* in case of WHERE */ kk = makenode(ATTRIB_LVALUE_,0,0); list[kk].op1.localnum = 0; list[kk].op2.name_id = set_name_eltype((yyvsp[-3]).qnum,(yyvsp[-3]).etype); subtree_swap(&(yyvsp[-2]).i,&kk); /* so lvalue before rvalue */ nn = makenode(ARRAY_ASSIGNOP_ARRAY_,kk,(yyvsp[-2]).i); list[nn].flags |= SET_ASSIGNOP; list[nn].op1.assigntype = ASSIGN_; aggr = makenode(AGGREGATE_,(yyvsp[-4]).i,nn); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-5]).i,aggr); end_scope(); ;} break; case 864: { int aggr,where,nn; aggrtype = SET_ATTRIBUTE_LOOP_; attr_kind = (yyvsp[-3]).i; subtree_swap(&(yyvsp[-2]).i,&(yyvsp[0]).i); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&(yyvsp[-2]).i,&where); /* get in proper linear order */ list[where].left = (yyvsp[-4]).i - where; list[where].right = (yyvsp[0]).i - where; elsym = (yyvsp[-4]).symptr; (yyvsp[-4]).symptr = 0; /* in case of WHERE */ nn = makenode(SET_ATTRIBUTE_L,(yyvsp[-2]).i,0); aggr = makenode(AGGREGATE_,where,nn); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-5]).i,aggr); end_scope(); ;} break; case 865: { int aggr,where,nn; aggrtype = SET_ATTRIBUTE_LOOP_; attr_kind = (yyvsp[-4]).i; subtree_swap(&(yyvsp[-3]).i,&(yyvsp[-2]).i); /* get index eval in top of stack */ subtree_swap(&(yyvsp[-3]).i,&(yyvsp[0]).i); /* get index eval in top of stack */ subtree_swap(&(yyvsp[-2]).i,&(yyvsp[0]).i); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&(yyvsp[-3]).i,&where); /* get in proper linear order */ subtree_swap(&(yyvsp[-2]).i,&where); /* get in proper linear order */ list[where].left = (yyvsp[-5]).i - where; list[where].right = (yyvsp[0]).i - where; elsym = (yyvsp[-5]).symptr; (yyvsp[-5]).symptr = 0; /* in case of WHERE */ nn = makenode(SET_ATTRIBUTE_L,(yyvsp[-2]).i,(yyvsp[-3]).i); aggr = makenode(AGGREGATE_,where,nn); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-6]).i,aggr); end_scope(); ;} break; case 866: { int aggr,idnode; aggrtype = SET_NAMED_QUANTITY_; idnode = makenode(PUSH_NAMED_QUANTITY,(yyvsp[0]).i,0); elsym = (yyvsp[-2]).symptr; (yyvsp[-2]).symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,(yyvsp[-2]).i,idnode); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-3]).i,aggr); end_scope(); ;} break; case 867: { int aggr,idnode; aggrtype = UNSET_NAMED_QUANTITY_; idnode = makenode(PUSH_NAMED_QUANTITY,(yyvsp[0]).i,0); elsym = (yyvsp[-2]).symptr; (yyvsp[-2]).symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,(yyvsp[-2]).i,idnode); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-3]).i,aggr); end_scope(); ;} break; case 868: { int aggr,where,idnode; aggrtype = SET_NAMED_QUANTITY_; idnode = makenode(PUSH_NAMED_QUANTITY,(yyvsp[-2]).i,0); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&idnode,&where); /* get in proper linear order */ list[where].left = (yyvsp[-4]).i - where; list[where].right = (yyvsp[0]).i - where; elsym = (yyvsp[-4]).symptr; (yyvsp[-4]).symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,where,idnode); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-5]).i,aggr); end_scope(); ;} break; case 869: { int aggr,where,idnode; aggrtype = UNSET_NAMED_QUANTITY_; idnode = makenode(PUSH_NAMED_QUANTITY,(yyvsp[-2]).i,0); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&idnode,&where); /* get in proper linear order */ list[where].left = (yyvsp[-4]).i - where; list[where].right = (yyvsp[0]).i - where; elsym = (yyvsp[-4]).symptr; (yyvsp[-4]).symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,where,idnode); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-5]).i,aggr); end_scope(); ;} break; case 870: { int aggr,idnode; aggrtype = SET_METHOD_INSTANCE_; idnode = makenode(PUSH_METHOD_INSTANCE_,(yyvsp[0]).i,0); elsym = (yyvsp[-2]).symptr; (yyvsp[-2]).symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,(yyvsp[-2]).i,idnode); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-3]).i,aggr); end_scope(); ;} break; case 871: { int aggr,idnode; aggrtype = UNSET_METHOD_INSTANCE_; idnode = makenode(PUSH_METHOD_INSTANCE_, (yyvsp[0]).i,0); elsym = (yyvsp[-2]).symptr; (yyvsp[-2]).symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,(yyvsp[-2]).i,idnode); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-3]).i,aggr); end_scope(); ;} break; case 872: { int aggr,where,idnode; aggrtype = SET_METHOD_INSTANCE_; idnode = makenode(PUSH_METHOD_INSTANCE_,(yyvsp[-2]).i,0); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&idnode,&where); /* get in proper linear order */ list[where].left = (yyvsp[-4]).i - where; list[where].right = (yyvsp[0]).i - where; elsym = (yyvsp[-4]).symptr; (yyvsp[-4]).symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,where,idnode); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-5]).i,aggr); end_scope(); ;} break; case 873: { int aggr,where,idnode; aggrtype = UNSET_METHOD_INSTANCE_; idnode = makenode(PUSH_METHOD_INSTANCE_,(yyvsp[-2]).i,0); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&idnode,&where); /* get in proper linear order */ list[where].left = (yyvsp[-4]).i - where; list[where].right = (yyvsp[0]).i - where; elsym = (yyvsp[-4]).symptr; (yyvsp[-4]).symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,where,idnode); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-5]).i,aggr); end_scope(); ;} break; case 874: { int aggr,idnode; aggrtype = SET_NAMED_QUANTITY_; idnode = makenode(PUSH_NAMED_QUANTITY,(yyvsp[0]).i,0); elsym = (yyvsp[-1]).symptr; (yyvsp[-1]).symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,(yyvsp[-1]).i,idnode); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-2]).i,aggr); end_scope(); ;} break; case 875: { int aggr,idnode; aggrtype = UNSET_NAMED_QUANTITY_; idnode = makenode(PUSH_NAMED_QUANTITY,(yyvsp[0]).i,0); elsym = (yyvsp[-1]).symptr; (yyvsp[-1]).symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,(yyvsp[-1]).i,idnode); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-2]).i,aggr); end_scope(); ;} break; case 876: { int aggr,where,idnode; aggrtype = SET_NAMED_QUANTITY_; idnode = makenode(PUSH_NAMED_QUANTITY,(yyvsp[-2]).i,0); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&idnode,&where); /* get in proper linear order */ list[where].left = (yyvsp[-3]).i - where; list[where].right = (yyvsp[0]).i - where; elsym = (yyvsp[-3]).symptr; (yyvsp[-3]).symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,where,idnode); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-4]).i,aggr); end_scope(); ;} break; case 877: { int aggr,where,idnode; aggrtype = UNSET_NAMED_QUANTITY_; idnode = makenode(PUSH_NAMED_QUANTITY,(yyvsp[-2]).i,0); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&idnode,&where); /* get in proper linear order */ list[where].left = (yyvsp[-3]).i - where; list[where].right = (yyvsp[0]).i - where; elsym = (yyvsp[-3]).symptr; (yyvsp[-3]).symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,where,idnode); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-4]).i,aggr); end_scope(); ;} break; case 878: { int aggr,idnode; aggrtype = SET_METHOD_INSTANCE_; idnode = makenode(PUSH_METHOD_INSTANCE_,(yyvsp[0]).i,0); elsym = (yyvsp[-1]).symptr; (yyvsp[-1]).symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,(yyvsp[-1]).i,idnode); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-2]).i,aggr); end_scope(); ;} break; case 879: { int aggr,idnode; aggrtype = UNSET_METHOD_INSTANCE_; idnode = makenode(PUSH_METHOD_INSTANCE_,(yyvsp[0]).i,0); elsym = (yyvsp[-1]).symptr; (yyvsp[-1]).symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,(yyvsp[-1]).i,idnode); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-2]).i,aggr); end_scope(); ;} break; case 880: { int aggr,where,idnode; aggrtype = SET_METHOD_INSTANCE_; idnode = makenode(PUSH_METHOD_INSTANCE_,(yyvsp[-2]).i,0); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&idnode,&where); /* get in proper linear order */ list[where].left = (yyvsp[-3]).i - where; list[where].right = (yyvsp[0]).i - where; elsym = (yyvsp[-3]).symptr; (yyvsp[-3]).symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,where,idnode); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-4]).i,aggr); end_scope(); ;} break; case 881: { int aggr,where,idnode; aggrtype = UNSET_METHOD_INSTANCE_; idnode = makenode(PUSH_METHOD_INSTANCE_,(yyvsp[-2]).i,0); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&idnode,&where); /* get in proper linear order */ list[where].left = (yyvsp[-3]).i - where; list[where].right = (yyvsp[0]).i - where; elsym = (yyvsp[-3]).symptr; (yyvsp[-3]).symptr = 0; /* in case of WHERE */ aggr = makenode(AGGREGATE_,where,idnode); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-4]).i,aggr); end_scope(); ;} break; case 882: { if ( tok == '-' ) kb_error(1897,"Syntax kludge: cannot have leading minus sign after ]. Use parentheses.\n", Q_ERROR); else kb_error(2532, "Syntax: SET element_gen [ name ] attribute rexpr [ WHERE rexpr ]\n",Q_ERROR); ;} break; case 883: { (yyval).i = makenode(SET_INIT_,0,0); ;} break; case 884: { (yyval).i = UNSET_FIXED_; ;} break; case 885: { (yyval).i = UNSET_HIT_PARTNER_; ;} break; case 886: { (yyval).i = UNSET_BARE_; ;} break; case 887: { (yyval).i = UNSET_NO_REFINE_; ;} break; case 888: { (yyval).i = UNSET_NONCONTENT_; ;} break; case 889: { (yyval).i = UNSET_NO_DISPLAY_; ;} break; case 890: { (yyval).i = UNSET_DENSITY_; ;} break; case 891: { (yyval).i = UNSET_TARGET_; ;} break; case 892: { (yyval).i = UNSET_TARGET_; ;} break; case 893: { (yyval).i = UNSET_PRESSURE_; ;} break; case 894: { (yyval).i = UNSET_TRIPLE_PT_; ;} break; case 895: { (yyval).i = UNSET_TETRA_PT_; ;} break; case 896: { (yyval).i = UNSET_AXIAL_POINT_; ;} break; case 897: { (yyval).i = UNSET_FRONTBODY_; ;} break; case 898: { (yyval).i = UNSET_BACKBODY_; ;} break; case 899: { (yyval).i = UNSET_FACET_BODY_; ;} break; case 900: { int aggr; aggrtype = (yyvsp[0]).i; aggr = makenode(AGGREGATE_,(yyvsp[-1]).i,0); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-2]).i,aggr); end_scope(); ;} break; case 901: { int aggr,where; aggrtype = (yyvsp[-2]).i; elsym = (yyvsp[-3]).symptr; where = makenode(WHERE_,(yyvsp[-3]).i,(yyvsp[0]).i); aggr = makenode(AGGREGATE_,where,0); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-4]).i,aggr); end_scope(); ;} break; case 902: { (yyval).i = (yyvsp[0]).i; ;} break; case 903: { int aggr; aggrtype = UNSET_CONSTRAINT_NAME; elsym = (yyvsp[-1]).symptr; aggr = makenode(AGGREGATE_,(yyvsp[-1]).i,0); list[aggr].stack_delta = 0; list[aggr].op3.connum = globals((yyvsp[0]).i)->value.cnum; (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-2]).i,aggr); end_scope(); ;} break; case 904: { real_val = globals((yyvsp[0]).i)->value.cnum; (yyval).i = makenode(PUSHCONST,0,0); ;} break; case 905: { int aggr; aggrtype = UNSET_CONSTRAINT_; elsym = (yyvsp[-2]).symptr; aggr = makenode(AGGREGATE_,(yyvsp[-2]).i,(yyvsp[0]).i); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-3]).i,aggr); end_scope(); ;} break; case 906: { int aggr,where; aggrtype = UNSET_CONSTRAINT_; subtree_swap(&(yyvsp[-2]).i,&(yyvsp[0]).i); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&(yyvsp[-2]).i,&where); /* get in proper linear order */ list[where].left = (yyvsp[-4]).i - where; list[where].right = (yyvsp[0]).i - where; elsym = (yyvsp[-4]).symptr; aggr = makenode(AGGREGATE_,where,(yyvsp[-2]).i); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-5]).i,aggr); end_scope(); ;} break; case 907: { int aggr,where; aggrtype = UNSET_CONSTRAINT_NAME; where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; list[where].left = (yyvsp[-3]).i - where; list[where].right = (yyvsp[0]).i - where; elsym = (yyvsp[-3]).symptr; aggr = makenode(AGGREGATE_,where,0); list[aggr].op3.connum = globals((yyvsp[-2]).i)->value.cnum; (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-4]).i,aggr); end_scope(); ;} break; case 908: { (yyval).i = (yyvsp[0]).i; ;} break; case 909: { int aggr; aggrtype = UNSET_BOUNDARY_NAME; elsym = (yyvsp[-1]).symptr; aggr = makenode(AGGREGATE_,(yyvsp[-1]).i,0); list[aggr].stack_delta = 0; list[aggr].op3.bdrynum = globals((yyvsp[0]).i)->value.bnum; (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-2]).i,aggr); end_scope(); ;} break; case 910: { real_val = globals((yyvsp[0]).i)->value.bnum; (yyval).i = makenode(PUSHCONST,0,0); ;} break; case 911: { int aggr; aggrtype = UNSET_BOUNDARY_; elsym = (yyvsp[-2]).symptr; aggr = makenode(AGGREGATE_,(yyvsp[-2]).i,(yyvsp[0]).i); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-3]).i,aggr); end_scope(); ;} break; case 912: { int aggr,where; aggrtype = UNSET_BOUNDARY_NAME; where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; list[where].left = (yyvsp[-3]).i - where; list[where].right = (yyvsp[0]).i - where; elsym = (yyvsp[-3]).symptr; aggr = makenode(AGGREGATE_,where,0); list[aggr].op3.bdrynum = globals((yyvsp[-2]).i)->value.bnum; (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-4]).i,aggr); end_scope(); ;} break; case 913: { int aggr,where; aggrtype = UNSET_BOUNDARY_; subtree_swap(&(yyvsp[-2]).i,&(yyvsp[0]).i); where = makenode(WHERE_,0,0); list[where].left = 0; list[where].right = 0; subtree_swap(&(yyvsp[-2]).i,&where); /* get in proper linear order */ list[where].left = (yyvsp[-4]).i - where; list[where].right = (yyvsp[0]).i - where; elsym = (yyvsp[-4]).symptr; aggr = makenode(AGGREGATE_,where,(yyvsp[-2]).i); (yyval).i = makenode(AGGREGATE_END_,(yyvsp[-5]).i,aggr); end_scope(); ;} break; case 914: { kb_error(1898, "Syntax: UNSET element_gen attribute [ WHERE rexpr ]\n",Q_ERROR); ;} break; case 915: { YYABORT; /* no expression */ ;} break; case 916: { (yyval).i = makenode(HELP_KEYWORD,0,0); help_flag = 0; tok = 0; yyerrok; yyclearin ; ;} break; case 917: { (yyval).i = makenode(HELP_KEYWORD,0,0); help_flag = 0; tok = 0; yyerrok; yyclearin ; ;} break; default: break; } /* Line 1126 of yacc.c. */ yyvsp -= yylen; yyssp -= yylen; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; /* Now `shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*------------------------------------. | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if YYERROR_VERBOSE yyn = yypact[yystate]; if (YYPACT_NINF < yyn && yyn < YYLAST) { int yytype = YYTRANSLATE (yychar); YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); YYSIZE_T yysize = yysize0; YYSIZE_T yysize1; int yysize_overflow = 0; char *yymsg = 0; # define YYERROR_VERBOSE_ARGS_MAXIMUM 5 char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; int yyx; #if 0 /* This is so xgettext sees the translatable formats that are constructed on the fly. */ YY_("syntax error, unexpected %s"); YY_("syntax error, unexpected %s, expecting %s"); YY_("syntax error, unexpected %s, expecting %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s"); YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); #endif char *yyfmt; char const *yyf; static char const yyunexpected[] = "syntax error, unexpected %s"; static char const yyexpecting[] = ", expecting %s"; static char const yyor[] = " or %s"; char yyformat[sizeof yyunexpected + sizeof yyexpecting - 1 + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) * (sizeof yyor - 1))]; char const *yyprefix = yyexpecting; /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yycount = 1; yyarg[0] = yytname[yytype]; yyfmt = yystpcpy (yyformat, yyunexpected); for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; yyformat[sizeof yyunexpected - 1] = '\0'; break; } yyarg[yycount++] = yytname[yyx]; yysize1 = yysize + yytnamerr (0, yytname[yyx]); yysize_overflow |= yysize1 < yysize; yysize = yysize1; yyfmt = yystpcpy (yyfmt, yyprefix); yyprefix = yyor; } yyf = YY_(yyformat); yysize1 = yysize + yystrlen (yyf); yysize_overflow |= yysize1 < yysize; yysize = yysize1; if (!yysize_overflow && yysize <= YYSTACK_ALLOC_MAXIMUM) yymsg = (char *) YYSTACK_ALLOC (yysize); if (yymsg) { /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ char *yyp = yymsg; int yyi = 0; while ((*yyp = *yyf)) { if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyf += 2; } else { yyp++; yyf++; } } yyerror (yymsg); YYSTACK_FREE (yymsg); } else { yyerror (YY_("syntax error")); goto yyexhaustedlab; } } else #endif /* YYERROR_VERBOSE */ yyerror (YY_("syntax error")); } if (yyerrstatus == 3) { /* If just tried and failed to reuse look-ahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval); yychar = YYEMPTY; } } /* Else will try to reuse look-ahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (0) goto yyerrorlab; yyvsp -= yylen; yyssp -= yylen; yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (yyn != YYPACT_NINF) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yydestruct ("Error: popping", yystos[yystate], yyvsp); YYPOPSTACK; yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } if (yyn == YYFINAL) YYACCEPT; *++yyvsp = yylval; /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #ifndef yyoverflow /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEOF && yychar != YYEMPTY) yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp); YYPOPSTACK; } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif return yyresult; } int yybegin() { int retval; PROF_START(yyparse); parse_errors = 0; perm_flag = 0; cond_expr_flag = 0; use_given_id = 0; parens = brace_depth = in_quote = 0; yylex_init(); reset_inputbuffer(); /* unput command start token */ tok = COMMAND_START_; unput_tok(); retval = yyparse(); PROF_FINISH(yyparse); return retval; } int yyerror(s) char *s; { char modmsg[1000]; if ( help_flag ) return 0; parens = brace_depth = in_quote = 0; strncpy(modmsg,s,998); if ( modmsg[strlen(modmsg)-1] != '\n' ) strcat(modmsg,"\n"); if ( datafile_flag ) { if ( listtop == 2 ) return 0; /* no expression */ kb_error(2407,modmsg,PARSE_ERROR); } else { /* fprintf(stderr,"tok = %d\n",tok); */ kb_error(2408,modmsg,SYNTAX_ERROR); } return 0; } /* to be appended to end of ytab.c so it knows about yytoks */ /****************************************************************** * * function: tokname() * * purpose: find name of given token number. Uses yytoks[] * list used by debugging. * */ char *tokname(toknum) int toknum; { char *name; #ifndef NO_YACC_DEBUG #ifdef YYBISON name = yytname[YYTRANSLATE(toknum)]; return name; #else int yy_i; for ( yy_i = 0; yytoks[yy_i].t_val >= 0; yy_i++ ) if ( yytoks[yy_i].t_val == toknum ) return yytoks[yy_i].t_name; /* unfound */ name = " "; sprintf(name,"%4d (unnamed)",toknum); return name; #endif #else /* unfound */ name = " "; sprintf(name,"%4d (unnamed)",toknum); return name; #endif } evolver-2.30c.dfsg/src/lexinit.c0000644000175300017530000035402711410765113017050 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************* * * file: lexinit.c * * Purpose: Reads in ASCII initial data files. * * lexical analyzer version, using lex defs in datafile.lex */ #include "include.h" #include "lex.h" #include "ytab.h" #define yylex() kb_yylex(NULL) /*********************************************************** * * Function: reset_web() * * Purpose: Zero out and initialize web storage structures * */ void reset_web() { int i,j; int cflag = web.torus_clip_flag; int bflag = web.torus_body_flag; unload_libraries(); /* unload dynamic libraries */ clear_symtable(); clear_globals(); /* need this to reset global name hash table */ dymemsize = 0; dymem = NULL; token_count = 0; display_text_count = 0; memset(text_chunks,0,sizeof(text_chunks)); i = 1; if ( ((char*)&i)[0] ) little_endian_flag = 1; else big_endian_flag = 1; /* cut down overgrown eval stacks */ for ( i = 0 ; i < nprocs ; i++ ) { struct thread_data *td = thread_data_ptrs[i]; int stack_used = td->stack_top - td->eval_stack; if ( td->eval_stack_size > stack_used + 1000 ) { td->eval_stack_size = stack_used + 1000; td->eval_stack = (REAL*)realloc(td->eval_stack,td->eval_stack_size*sizeof(REAL)); td->stack_top = td->eval_stack + stack_used; td->eval_stack[td->eval_stack_size-1] = STACKMAGIC; } td->stack_top = td->eval_stack; } /* reset nonzero flags and stuff */ raw_velocity_attr = -1; lmc_mc_attr = -1; lmc_mobility_attr = -1; quarter_turn_var = -1; edge_diffusion_attr = -1; facet_diffusion_attr = -1; window_aspect_ratio = 0.0; /* signifying not used */ transform_gen_count = 0; view_transform_gens = NULL; view_transform_gens_expr = NULL; view_transforms = NULL; if ( !bflag && !cflag ) torus_display_mode = TORUS_DEFAULT_MODE; slice_view_flag = 0; clip_view_flag = 0; quantity_function_sparse_flag = 1; one_sided_present = 0; one_sided_lagrange_attr = -1; memset(slice_coeff,0,sizeof(slice_coeff)); memset(clip_coeff,0,sizeof(clip_coeff)); clip_coeff[0][0] = 1.0; list_free_all(PERM_BLOCK); /* get rid of everything */ reset_skeleton(); /* cleans out web and resets to 0 */ #ifdef MPI_EVOLVER mpi_reset(); #endif #ifdef MPI_EVOLVER random_seed = this_task; #else random_seed = 1; #endif srand(random_seed); srand48(random_seed); file_no = 0; file_no_max = 12; file_names = (char**)mycalloc(file_no_max,sizeof(char**)); file_names[0] = "stdin"; file_no_used = 1; addload_flag = 0; verbose_flag = 0; estimate_flag = 0; warning_messages = NULL; warning_messages_max = 0; warning_messages_new = 0; memset((char*)pbase,0,sizeof(pbase)); memset((char*)pmax,0,sizeof(pmax)); vhead = NULL; vproj_base = NULL; vproj_space = NULL; conhess_base = NULL; pressures = NULL; conrhs = NULL; optparam_congrads = NULL; rleftside = NULL; ritzvecs = NULL; tgverts = NULL; thread_stages = NULL; mpi_show_corona_flag = 0; edgeshow_flag = 1; /* Following PostScript line width variables relative to page size */ ps_labelsize = 3.0; ps_stringwidth = 0.004; /* default edge width */ ps_fixededgewidth = 0.004; ps_tripleedgewidth = 0.003; ps_conedgewidth = 0.004; ps_bareedgewidth = 0.005; ps_gridedgewidth = 0.002; for ( i = 0 ; i < MAXLAGRANGE ; i++ ) { bezier1invert[i] = NULL; bezier1revert[i] = NULL; bezier_refine_1d[i] = NULL; bezier2invert[i] = NULL; bezier2revert[i] = NULL; bezier_refine_2d[i] = NULL; } string_curve_tolerance = 2.0; /* degrees */ dont_resize_flag = 0; local_nest_depth = 0; extra_bdry_attr = 0; extra_bdry_param_attr = 0; memset((char*)show_command,0,sizeof(show_command)); macro_subs = NULL; macros = NULL; vpicklist = NULL; fpicklist = NULL; cg_hvector = NULL; f_sums = NULL; bi_sums = NULL; listmax = 0; list = NULL; permlist = NULL; if ( option_q == 2 ) option_q = 0; area_method_name[0] = 0; length_method_name[0] = 0; volume_method_name[0] = 0; uminus_flag = 0; inputsave_flag = 0; check_count = 0; hessian_slant_cutoff = 0.0; hessian_epsilon = hessian_epsilon_default; #ifdef MPI_EVOLVER sparse_constraints_flag = 0; augmented_hessian_flag = 0; #else sparse_constraints_flag = 1; augmented_hessian_flag = 1; #endif blas_flag = 0; last_error = 0; check_increase_flag = 0; brightness = DEFAULT_BRIGHTNESS; volgrads_every_flag = 0; zener_drag_flag = 0 ; pickvnum = pickenum = pickfnum = 0; keep_macros_flag = 0; needed_version[0] = 0; new_vertex_id = new_edge_id = new_facet_id = new_body_id = 0; sqcurve_ignore_constr = 0; ackerman_flag = 0; ps_colorflag = -1; boundary_expr_flag = 0; gridflag = -1; crossingflag = -1; labelflag = -1; web.target_tolerance = DEFAULT_TARGET_TOLERANCE; web.highcon = -1; web.highbdry = -1; autorecalc_flag = 1; circular_arc_flag = 0; spherical_arc_flag = 0; rgb_colors_flag = 0; kraynikpopedge_flag = 0; kraynikpopvertex_flag = 1; interp_bdry_param = 0; web.headvnum = 1; thickness = .001; user_thickness_flag = 0; last_eigenvalue = 0.0; last_hessian_scale = 0.0; optparamcount = 0; thickenflag = 0; rotorder_var = -1; quantities_only_flag = everything_quantities_flag = 0; linear_metric_mix = .50; min_square_grad_flag = 0; hessian_linear_metric_flag = 0; hess_move_con_flag = 1; eigen_neg = eigen_pos = eigen_zero = 0; innerflag = outerflag = 1; normal_sq_mean_curvature_mi = -1; eff_area_sq_mean_curvature_mi = -1; sq_mean_curvature_mi = -1; mix_sq_mean_curvature_mi = -1; star_normal_sq_mean_curvature_mi = -1; star_eff_area_sq_mean_curvature_mi = -1; star_sq_mean_curvature_mi = -1; gravity_quantity_num = -1; sq_mean_curv_quantity_num = -1; mean_curv_int_quantity_num = -1; view_4D_flag = 0; hessian_normal_flag = 1; hessian_special_normal_flag = 0; memset((char*)hessian_special_normal_expr,0,MAXCOORD*sizeof(struct expnode)); hessian_normal_perp_flag = 0; hessian_double_normal_flag = 0; hessian_normal_one_flag = 0; hessian_quiet_flag = 1; hessian_by_diff_flag = 0; hess_debug = 0; mindeg_debug_level = 0; dirichlet_flag = 0; sobolev_flag = 0; quiet_go_flag = 0; quiet_flag = 0; pressure_set_flag = 1; self_similar_flag = 0; make_pos_def_flag = 0; datafile_view_flag = 0; post_project_flag = 0; read_command_flag = 0; ribiere_flag = 1; assume_oriented_flag = 0; conj_grad_flag = 0; mobility_flag = mobility_tensor_flag = 0; old_area_flag = 0; area_fixed_flag = 0; sqgauss_flag = 0; square_curvature_flag = 0; mean_curv_int_flag = 0; boundary_curvature_flag = 0; normal_curvature_flag = 0; approx_curve_flag = 0; kusner_flag = 0; klein_metric_flag = 0; conf_edge_curv_flag = 0; effective_area_flag = 0; autochop_flag = 0; autopop_flag = 0; immediate_autopop_flag = 0; autopop_quartic_flag = 0; pop_disjoin_flag = 0; pop_enjoin_flag = 0; pop_to_edge_flag = 0; pop_to_face_flag = 0; runge_kutta_flag = 0; web.dimension = 2; web.representation = SOAPFILM; web.skel[VERTEX].ctrlpts = 1; web.skel[EDGE].ctrlpts = 2; web.skel[FACET].ctrlpts = 3; star_fraction = web.dimension + 1.0; areaname = "area"; web.sdim = DEFAULT_SDIM; total_time = 0.0; homothety_target = 1.0; sym_flags = 0; symmetry_name = NULL; sym_wrap = identity_wrap; sym_form_pullback = identity_form_pullback; sym_inverse = identity_inverse; sym_compose = identity_compose; web.modeltype = LINEAR; web.lagrange_order = 1; bezier_flag = 0; web.hide_flag = 1; web.torus_clip_flag = cflag; web.torus_body_flag = bflag; web.torus_flag = 0; web.full_flag = 0; web.meritfactor = 0.0; web.grav_const = 1.0; web.symmetric_content = 0; web.pressure_flag = 0; web.projection_flag = 0; web.area_norm_flag = 0; web.vol_flag = 0; web.jiggle_flag = 0; web.temperature = 0.05; web.total_area = 0.0; web.total_facets = 0; web.scale = 0.1; web.scale_scale = 1.0; web.maxscale = 1.0; web.pressure = 0.0; web.bodycount = 0; web.wulff_count = web.wulff_flag = 0; web.min_area = 0.1; web.min_length = 0.25; web.max_len = 1.4; web.max_angle = 0.1; web.spring_constant = 1.0; web.gauss1D_order = 3; /* can do 3 degree exactly (need default 7 in quadratic)*/ set_by_user_gauss_1D = 0; web.gauss2D_order = 6; /* can do 6th degree poly */ set_by_user_gauss_2D = 0; memset(gauss_lagrange,0,sizeof(gauss_lagrange)); memset(maxgaussorder,0,sizeof(maxgaussorder)); web.tolerance = DEFAULT_TOLERANCE; reflevel = 0; no_refine = 0; globals(view_matrix_global)->attr.arrayptr->sizes[0] = 0; globals(torus_periods_global)->attr.arrayptr->sizes[0] = 0; globals(inverse_periods_global)->attr.arrayptr->sizes[0] = 0; memset(torus_period_expr,0,sizeof(torus_period_expr)); memset(torus_display_period_expr,0,sizeof(torus_display_period_expr)); zoom_number = 1; web.zoom_v = NULLVERTEX; web.zoom_radius = 99999.0; transform_count = 0; view_transforms = NULL; set_view_transforms_global(); transforms_flag = 1; transform_expr[0] = '\0'; view_transform_det = NULL; transform_colors = NULL; /* special kludge so allocate... doesn't free */ allocate_transform_colors(0); transform_gen_swap = NULL; transform_colors_flag = 0; vertex_normals = NULL; conical_x = NULL; conical_w = NULL; for ( j = 0 ; j < NUMELEMENTS ; j++ ) el_list[j] = NULL; v_procnum = NULL; phase_flag = 0; metric_convert_flag = 0; end_geomview_object(); end_normal_motion(); userfunc_init(); for ( j = 0 ; j < NUMELEMENTS ; j++ ) { show_expr[j] = NULL; } memset((char*)show_expr_table,0,sizeof(show_expr_table)); memset((char*)single_redefine,0,128*sizeof(struct expnode)); gauss1poly = NULL; gauss1polyd = NULL; gpoly = NULL; gpolypartial = NULL; expand_global_hash(); /* to get permanent variables on board */ /* just in case no surface read in, can take commands */ { int sdim = SDIM; expand_attribute(VERTEX,V_COORD_ATTR,&sdim); expand_attribute(VERTEX,V_OLDCOORD_ATTR,&sdim); expand_attribute(VERTEX,V_FORCE_ATTR,&sdim); expand_attribute(VERTEX,V_VELOCITY_ATTR,&sdim); expand_attribute(EDGE,E_VERTICES_ATTR,&web.skel[EDGE].ctrlpts); expand_attribute(FACET,F_VERTICES_ATTR,&web.skel[FACET].ctrlpts); } warnings_suppressed_count = 0; reset_counts(); } /****************************************************************** * * Function: initialize() * * Purpose: read in data file and create initial triangulation. * */ #define MAXLIST 100 #define LINESIZE 1000 /* temporary lists of elements */ vertex_id *vlist = NULL; int vmaxlist; edge_id *elist = NULL; int emaxlist; facet_id *flist = NULL; int fmaxlist; int facecount; body_id *blist = NULL; int bmaxlist; void initialize() /* initialize from data file */ { int f; facetedge_id fe; int i,j,k; int dataflag; int esize; REAL modulus; yylex_init(); /* reset lex */ macro_init(); /* reset macros in parser */ line_no = 1; parse_error_flag = 0; parse_errors = 0; recovery_flag = 0; facecount = 0; bare_edge_count = 0; verb_flag = 0; lists_flag = LISTS_OFF; vlist = NULL; elist = NULL; flist = NULL; blist = NULL; if ( !addload_flag ) quantity_init(); reset_timestamp = top_timestamp = ++global_timestamp; graph_timestamp = ++global_timestamp; /* read in any header information */ topflag = 1; tok = yylex(); while ( (tok != 0) && topflag ) { switch ( tok ) { case SUPPRESS_WARNING_: if ( (tok = gettok(INTEGER_)) != INTEGER_ ) { kb_error(4531,"Missing suppressed warning number.\n",WARNING); break; } if ( warnings_suppressed_count < MAXSUPPRESS ) warnings_suppressed[warnings_suppressed_count++] = yylval.i; else kb_error(4532,"Too many warnings suppressed.\n",WARNING); tok = yylex(); /* eat the number */ break; case UNSUPPRESS_WARNING_: if ( (tok = gettok(INTEGER_)) != INTEGER_ ) { kb_error(4533,"Missing unsuppressed warning number.\n",WARNING); break; } for ( i = 0 ; i < warnings_suppressed_count ; i++ ) if ( warnings_suppressed[i] == yylval.i ) { warnings_suppressed[i] = warnings_suppressed[--warnings_suppressed_count]; break; } tok = yylex(); /* eat the number */ break; case HESSIAN_SPECIAL_NORMAL_VECTOR_: tok = yylex(); for ( i = 0 ; i < SDIM ; i++ ) { if ( (tolower(yytext[0]) != 'c') || (yytext[1] != '1' + i) ) break; esize = exparse(SDIM,hessian_special_normal_expr+i,USERCOPY); sprintf(hessian_special_normal_expr[i].name, "hessian_special_normal component %d",i); tok = yylex(); if ( esize <= 0 ) { sprintf(errmsg, "Bad content component %d definition for hessian_special_normal_vector.\n", i+1); kb_error(2095,errmsg,DATAFILE_ERROR); return; } } break; case LENGTH_METHOD_NAME_: tok = yylex(); if ( tok == QUOTATION_ ) { strncpy(length_method_name,yytext,sizeof(length_method_name)-1); if ( strcmp(yytext,"circular_arc_length")==0 ) circular_arc_flag = 1; else if ( strcmp(yytext,"spherical_arc_length")==0 ) spherical_arc_flag = 1; tok = yylex(); if ( !option_q) option_q = 2; /* convert_to_quantities */ } else kb_error(2485,"length_method_name needs quoted string.\n", DATAFILE_ERROR); break; case AREA_METHOD_NAME_: tok = yylex(); if ( tok == QUOTATION_ ) { strncpy(area_method_name,yytext,sizeof(area_method_name)-1); tok = yylex(); if ( !option_q) option_q = 2; /* convert_to_quantities */ } else kb_error(2590,"area_method_name needs quoted string.\n", DATAFILE_ERROR); break; case VOLUME_METHOD_NAME_: tok = yylex(); if ( tok == QUOTATION_ ) { if ( SDIM == 2 ) strncpy(area_method_name,yytext,sizeof(area_method_name)-1); else strncpy(volume_method_name,yytext,sizeof(volume_method_name)-1); tok = yylex(); if ( !option_q) option_q = 2; /* convert_to_quantities */ } else kb_error(2096,"volume_method_name needs quoted string.\n", DATAFILE_ERROR); break; case KEEP_MACROS_ : keep_macros_flag = 1; tok = yylex(); break; case VERSION_: tok = yylex(); strcpy(needed_version,yytext); tok = yylex(); if ( strcmp(needed_version,evolver_version) > 0 ) { sprintf(errmsg,"\nDatafile %s needs Evolver version at least %s. This is version %s.\n\n",datafilename,needed_version,evolver_version); kb_error(2097,errmsg,RECOVERABLE); } break; case LOAD_LIBRARY_: tok = yylex(); if ( tok == QUOTATION_ ) { load_library(yytext); tok = yylex(); } else kb_error(2098,"LOAD_LIBRARY file name missing.\n",DATAFILE_ERROR); recovery_flag = 0; break; case INTERP_BDRY_PARAM_: interp_bdry_param = 1; tok = yylex(); break; case EVERYTHING_QUANTITIES_: /*everything_quantities_flag = quantities_only_flag = 1;*/ if ( !option_q) option_q = 2; tok = yylex(); /* should warn if anything else done yet */ break; case KEEP_ORIGINALS_: match_id_flag = 1; tok = yylex(); break; case SPACE_DIMENSION_: if ( (tok = gettok(INTEGER_)) != INTEGER_ ) { kb_error(1060,"Dimension of space missing.\n",DATAFILE_ERROR); break; } if ( yylval.i > MAXCOORD ) { sprintf(msg,"Space dimension too high. Recompile with -DMAXCOORD=%d as compiler option.\n",yylval.i); kb_error(1061,msg,RECOVERABLE); } web.sdim = yylval.i; if ( web.sdim != DEFAULT_SDIM ) init_view(); web.skel[BODY].dimension = yylval.i; if ( web.sdim != SDIM ) { sprintf(errmsg, "This Evolver compiled strictly for space dimension %d.\n", SDIM); kb_error(1062,errmsg,RECOVERABLE); } tok = yylex(); break; case SURFACE_DIMENSION_: if ( (tok = gettok(INTEGER_)) != INTEGER_ ) { kb_error(1063,"Dimension of surface missing.\n",DATAFILE_ERROR); break; } web.dimension = yylval.i; if ( yylval.i > web.sdim ) kb_error(1064,"Surface dimension higher than space dimension.\n",RECOVERABLE); star_fraction = web.dimension + 1.0; if ( web.representation == SIMPLEX ) { web.skel[EDGE].ctrlpts = web.dimension; web.skel[EDGE].dimension = web.dimension - 1; web.skel[FACET].ctrlpts = web.dimension+1; web.skel[FACET].dimension = web.dimension; } else if ( web.dimension == 1 ) web.representation = STRING; tok = yylex(); break; case SIMPLEX_REP_: web.representation = SIMPLEX; web.skel[EDGE].ctrlpts = web.dimension; web.skel[EDGE].dimension = web.dimension - 1; web.skel[FACET].ctrlpts = web.dimension+1; web.skel[FACET].dimension = web.dimension; tok = yylex(); break; case DEFINE_: /* extra attribute definition */ { int dim,e_type=0,attr_type=0,anum; int sizes[MAXARRAYDIMS]; char name[ATTR_NAME_SIZE+1]; tok = yylex(); switch ( tok ) { case NEWIDENT_ : define_array(); goto define_exit; case ARRAYIDENT_ : define_array(); goto define_exit; case VERTICES_: e_type = VERTEX; break; case EDGES_: e_type = EDGE; break; case FACES_: e_type = FACET; break; case BODIES_: e_type = BODY; break; case FACETEDGES_: e_type = FACETEDGE; break; default: kb_error(1065,"Bad element type in 'define'.\n", DATAFILE_ERROR); }; tok = yylex(); if ( tok != ATTRIBUTE_ ) kb_error(1066,"Need ATTRIBUTE keyword.\n",DATAFILE_ERROR); tok = yylex(); if ( (tok != NEWIDENT_) && !addload_flag ) kb_error(1067,"Need new identifier. \n",DATAFILE_ERROR); strncpy(name,yytext,ATTR_NAME_SIZE); tok = yylex(); if ( tok == DATATYPE_ ) { attr_type = yylval.datatype; } else { kb_error(1068,"Need attribute datatype.\n",DATAFILE_ERROR); attr_type = REAL_TYPE; /* reasonable default */ } tok = yylex(); dim = 0; sizes[0] = 1; while ( tok == '[' ) { dim += 1; if ( dim > MAXARRAYDIMS ) { sprintf(errmsg, "Extra attribute %s has more dimensions than the allowed %d.\n", name,MAXARRAYDIMS); kb_error(2566,errmsg,RECOVERABLE); } tok = yylex(); if ( tok != INTEGER_ ) kb_error(1069,"Need dimension number.\n",DATAFILE_ERROR); sizes[dim-1] = yylval.i; if ( sizes[dim-1] < 0 ) kb_error(1070,"Attribute dimension must be at least 0.\n", DATAFILE_ERROR); tok = yylex(); tok = yylex(); /* eat ] */ } anum = add_attribute(e_type,name,attr_type,dim,sizes,DUMP_ATTR,NULL); if ( (tok == FUNCTION_) && (dim == 0) ) { /* have calculable attribute */ struct extra *ext = EXTRAS(e_type) + anum; esize = exparse(SDIM,&(ext->code),USERCOPY); sprintf(ext->code.name,"attribute '%s' formula",name); tok = yylex(); if ( esize <= 0 ) { sprintf(errmsg, "Bad function definition for attribute %s.\n",name); kb_error(2099,errmsg,DATAFILE_ERROR); } ext->flags |= FUNCTION_ATTR; } } /* end DEFINE */ define_exit: break; case CONFORMAL_: /* have background metric on domain */ web.metric_flag = 1; web.conformal_flag = 1; recovery_flag = 0; uminus_flag = 0; if (exparse(SDIM,&web.metric[0][0],USERCOPY) <= 0 ) { sprintf(errmsg,"Bad conformal metric definition.\n"); kb_error(1071,errmsg,DATAFILE_ERROR); } sprintf(web.metric[0][0].name,"conformal metric"); metric = dmatrix(0,SDIM-1,0,SDIM-1); metric_partial = dmatrix3(SDIM,SDIM,SDIM); det_array = dmatrix(0,web.dimension-1,0,web.dimension-1); tok = yylex(); break; case KLEIN_METRIC_: klein_metric_flag = 1; web.metric_flag = 1; tok = yylex(); break; case METRIC_: /* have background metric on domain */ web.metric_flag = 1; recovery_flag = 0; lists_flag = LISTS_SOME; uminus_flag = 1; for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { esize = exparse(SDIM,&web.metric[i][j],USERCOPY); if ( esize <= 0 ) { sprintf(errmsg, "Bad metric g[%d][%d] definition.\n",i,j); kb_error(1072,errmsg,DATAFILE_ERROR); } sprintf(web.metric[i][j].name,"metric component [%d][%d]", i+1,j+1); } metric = dmatrix(0,SDIM-1,0,SDIM-1); metric_partial = dmatrix3(SDIM,SDIM,SDIM); det_array = dmatrix(0,web.dimension-1,0,web.dimension-1); lists_flag = LISTS_OFF; tok = yylex(); break; case SYMMETRY_GROUP_: web.symmetry_flag = 1; tok = yylex(); if ( tok == QUOTATION_ ) { struct sym_registry *reg; for ( reg = sym_register ; reg->name != NULL ; reg++ ) if ( stricmp(yytext,reg->name) == 0 ) { symmetry_name = reg->name; sym_wrap = reg->wrapper; sym_form_pullback = reg->pullback; sym_inverse = reg->inverse; sym_compose = reg->compose; sym_flags = reg->flags; break; } if ( reg->name == NULL ) /* search failed */ { sprintf(errmsg, "Symmetry name '%s' not found in registry.c \n", yytext); kb_error(1073,errmsg,DATAFILE_ERROR); } if ( stricmp(yytext,"torus")==0 ) torus_period_init(); tok = yylex(); } else kb_error(1074,"Missing symmetry group name.\n",DATAFILE_ERROR); recovery_flag = 0; break; case TORUS_: web.torus_flag = 1; web.symmetry_flag = 1; sym_wrap = torus_wrap; sym_form_pullback = torus_form_pullback; sym_inverse = torus_inverse; sym_compose = torus_compose; torus_period_init(); sym_flags = 0; tok = yylex(); recovery_flag = 0; break; case TORUS_FILLED_: web.torus_flag = 1; web.symmetry_flag = 1; sym_wrap = torus_wrap; sym_form_pullback = torus_form_pullback; sym_inverse = torus_inverse; sym_compose = torus_compose; sym_flags = 0; torus_period_init(); web.full_flag = 1; tok = yylex(); recovery_flag = 0; break; case STRING_: web.dimension = 1; web.representation = STRING; web.skel[EDGE].dimension = 1; web.skel[FACET].dimension = 2; star_fraction = web.dimension + 1.0; edgeshow_flag = 0; /* for suppressing dummy facet edges */ tok = yylex(); recovery_flag = 0; break; case SOAPFILM_: web.dimension = 2; web.representation = SOAPFILM; star_fraction = web.dimension + 1.0; tok = yylex(); recovery_flag = 0; break; case LINEAR_: if ( addload_flag ) { if ( web.modeltype != LINEAR ) kb_error(5342,"addload datafile not in linear mode.\n", RECOVERABLE); } web.modeltype = LINEAR; tok = yylex(); recovery_flag = 0; break; case QUADRATIC_: if ( addload_flag ) { if ( web.modeltype != QUADRATIC ) kb_error(5343,"addload datafile not in quadratic mode.\n", RECOVERABLE); } else linear_to_quad(); tok = yylex(); recovery_flag = 0; break; case LAGRANGE_: switch ( web.modeltype ) { case LINEAR: linear_to_lagrange(1); break; case QUADRATIC: quad_to_lagrange(1); break; case LAGRANGE: lagrange_to_lagrange(1); break; } tok = yylex(); recovery_flag = 0; break; case LAGRANGE_ORDER_: if ( (tok = gettok(INTEGER_)) != INTEGER_ ) { kb_error(2100,"Lagrange order missing.\n",DATAFILE_ERROR); break; } if ( addload_flag ) { if ( web.modeltype != LAGRANGE ) kb_error(5345,"addload datafile not in Lagrange mode.\n", RECOVERABLE); if ( web.lagrange_order != yylval.i ) { sprintf(errmsg, "addload datafile has different Lagrange order %d, should be %d.\n", yylval.i,web.lagrange_order); kb_error(5346,errmsg, RECOVERABLE); } } switch ( web.modeltype ) { case LINEAR: linear_to_lagrange(yylval.i); break; case QUADRATIC: quad_to_lagrange(yylval.i); break; case LAGRANGE: lagrange_to_lagrange(yylval.i); break; } tok = yylex(); recovery_flag = 0; break; case SYMMETRIC_CONTENT_: web.symmetric_content = 1; tok = yylex(); recovery_flag = 0; break; case MEAN_CURV_: web.area_norm_flag = 1; tok = yylex(); recovery_flag = 0; break; case BOUNDARY_CURVATURE_: boundary_curvature_flag = 1; tok = yylex(); recovery_flag = 0; break; case EFFECTIVE_AREA_: effective_area_flag = 1; tok = yylex(); recovery_flag = 0; break; case JIGGLE_: web.jiggle_flag = 1; tok = yylex(); recovery_flag = 0; break; case WULFF_: tok = yylex(); if ( tok == QUOTATION_ ) { wulff_initialize(yytext); tok = yylex(); } else kb_error(1076,"Wulff file name missing.\n",DATAFILE_ERROR); recovery_flag = 0; break; case PHASEFILE_: tok = yylex(); if ( tok == QUOTATION_ ) { phase_initialize(yytext); tok = yylex(); } else kb_error(1077,"Cannot find phasefile name.\n",DATAFILE_ERROR); recovery_flag = 0; break; case TORUS_PERIODS_: case PERIODS_: recovery_flag = 0; lists_flag = LISTS_SOME; uminus_flag = 1; read_periods(); lists_flag = LISTS_OFF; tok = yylex(); /* lookahead */ break; case DISPLAY_PERIODS_: recovery_flag = 0; lists_flag = LISTS_SOME; uminus_flag = 1; read_display_periods(); lists_flag = LISTS_OFF; tok = yylex(); /* lookahead */ break; case DISPLAY_ORIGIN_: { int n; struct global *g ; struct array *a; recovery_flag = 0; n = lookup_global("display_origin"); if ( n < 0 ) n = add_global("display_origin"); g = globals(n); g->flags |= INTERNAL_NAME|ARRAY_PARAM|RECALC_PARAMETER|ALWAYS_RECALC; a = g->attr.arrayptr = (struct array*)mycalloc(1, sizeof(struct array)+sizeof(int)); a->dim = 1; a->itemsize = sizeof(REAL); a->datatype = REAL_TYPE; a->datacount = SDIM*sizeof(REAL); a->sizes[0] = SDIM; a->datastart = (char*)web.display_origin-(char*)a; lists_flag = LISTS_SOME; uminus_flag = 1; lists_flag = LISTS_OFF; for ( i = 0 ; i < SDIM ; i++ ) { if ( read_const(&web.display_origin[i]) <= 0 ) { kb_error(3861,"Not enough values for display_origin.\n", DATAFILE_ERROR); break; } } if ( i >= SDIM ) tok = yylex(); /* lookahead */ } break; case VIEW_MATRIX_: recovery_flag = 0; lists_flag = LISTS_SOME; uminus_flag = 1; for ( i = 0 ; i <= SDIM ; i++ ) { for ( j = 0 ; j <= SDIM ; j++ ) { if ( read_const(&view[i][j]) <= 0 ) { kb_error(1861,"Not enough values for view matrix.\n", DATAFILE_ERROR); break; } } } datafile_view_flag = 1; lists_flag = LISTS_OFF; if ( i > SDIM ) tok = yylex(); /* lookahead */ break; case VIEW_TRANSFORMS_: recovery_flag = 0; uminus_flag = 1; read_transforms(0); break; case VIEW_TRANSFORM_GENS_: recovery_flag = 0; uminus_flag = 1; read_transform_generators(0); break; case CLIP_COEFF: tok = yylex(); /* eat clip_coeff */ if ( tok != '=' && tok != ASSIGN_ ) kb_error(3441,"clip_coeff initializer missing '='.\n", DATAFILE_ERROR); read_array_initializer(perm_globals(clip_coeff_global)->attr.arrayptr); break; case SLICE_COEFF: tok = yylex(); /* eat clip_coeff */ if ( tok != '=' && tok != ASSIGN_ ) kb_error(3441,"clip_coeff initializer missing '='.\n", DATAFILE_ERROR); read_array_initializer(perm_globals(slice_coeff_global)->attr.arrayptr); break; case PARAMETERS_: recovery_flag = 0; uminus_flag = 0; read_parameter(); break; case OPTIMIZING_PARAMETER_: recovery_flag = 0; uminus_flag = 0; read_parameter(); break; case FUNCTION_: datafile_flag = 0; function_kludge_flag = 1; command("function _anti_line_no_",NO_HISTORY); function_kludge_flag = 0; datafile_flag = 1; verb_flag = 0; tok = yylex(); break; case PROCEDURE_WORD_: datafile_flag = 0; function_kludge_flag = 1; command("procedure _anti_line_no_",NO_HISTORY); function_kludge_flag = 0; datafile_flag = 1; verb_flag = 0; tok = yylex(); break; case BOUNDARY_: recovery_flag = 0; uminus_flag = 0; read_boundary(); break; case CONSTRAINT_: recovery_flag = 0; uminus_flag = 0; read_constraint(); break; case SURFACE_ENERGY_: recovery_flag = 0; uminus_flag = 0; read_surface_energy(); break; case QUANTITY_: recovery_flag = 0; uminus_flag = 0; read_quantity(); break; case METHOD_INSTANCE_: recovery_flag = 0; uminus_flag = 0; read_method_instance(); break; case AREA_FIXED_: kb_error(2101, "Area_fixed is obsolete. Replace with named quantity.\n", DATAFILE_ERROR); area_fixed_flag = 1; uminus_flag = 0; if ( read_const(&area_fixed_target) <= 0 ) { kb_error(1078,"Missing fixed area value.\n",DATAFILE_ERROR); if ( tok == AREA_FIXED_ ) tok = yylex(); /* ensure progress */ } else tok = yylex(); recovery_flag = 0; break; case DIFFUSION_: web.diffusion_flag = 1; web.diffusion_const = 0.0; uminus_flag = 0; if ( read_const(&web.diffusion_const) <= 0 ) { kb_error(1079,"Missing DIFFUSION value.\n",WARNING); if ( tok == DIFFUSION_ ) tok = yylex(); /* ensure progress */ } else tok = yylex(); recovery_flag = 0; break; case HOMOTHETY_: web.homothety = 1; uminus_flag = 0; if ( read_const(&homothety_target) <= 0 ) homothety_target = 1.0; else tok = yylex(); break; case AUTOPOP_: autopop_flag = 1; tok = yylex(); recovery_flag = 0; break; case IMMEDIATE_AUTOPOP_: immediate_autopop_flag = 1; tok = yylex(); recovery_flag = 0; break; case AUTOPOP_QUARTIC_: autopop_quartic_flag = 1; tok = yylex(); recovery_flag = 0; break; case AUTOCHOP_: autochop_flag = 1; autochop_length = 1.0; uminus_flag = 0; if ( read_const(&autochop_length) <= 0 ) { kb_error(1080,"Missing AUTOCHOP length.\n",WARNING); if ( tok == AUTOCHOP_ ) tok = yylex(); /* ensure progress */ } else tok = yylex(); recovery_flag = 0; break; case APPROX_CURV_: approx_curve_flag = 1; tok = yylex(); recovery_flag = 0; break; case CONDUCTING_KNOT_ENERGY_: { tok = yylex(); if ( tok != MODULUS_ ) unput_tok(); uminus_flag = 0; if (read_const(&modulus) <= 0) modulus = 1.0; else tok = yylex(); add_standard_quantity("knot_energy",modulus); recovery_flag = 0; break; } case INSULATING_KNOT_ENERGY_: { tok = yylex(); if ( tok != MODULUS_ ) unput_tok(); uminus_flag = 0; if (read_const(&modulus) <= 0) modulus = 1.0; else tok = yylex(); add_standard_quantity("uniform_knot_energy",modulus); recovery_flag = 0; break; } case MEAN_CURV_INT_: mean_curv_int_flag = 1; mean_curvature_param = lookup_global("mean_curvature_modulus"); if ( mean_curvature_param < 0 ) mean_curvature_param = add_global("mean_curvature_modulus"); globals(mean_curvature_param)->flags |= ORDINARY_PARAM | RECALC_PARAMETER | ALWAYS_RECALC; uminus_flag = 0; if ( read_const(&globals(mean_curvature_param)->value.real) <= 0 ) { kb_error(1081,"Missing integral mean curvature modulus value.\nSyntax: mean_curvature_integral: modulus",WARNING); if ( tok == MEAN_CURV_INT_ ) tok = yylex(); /* ensure progress */ } else tok = yylex(); if ( web.representation != SOAPFILM ) kb_error(1082,"Can do integral of mean curvature only in SOAPFILM model.\n", DATAFILE_ERROR); recovery_flag = 0; break; case GAUSS_CURVATURE_: { tok = yylex(); if ( tok != MODULUS_ ) unput_tok(); uminus_flag = 0; if (read_const(&modulus) <= 0) modulus = 1.0; else tok = yylex(); if ( (web.representation != SOAPFILM) ) kb_error(1083,"Can do gauss curvature only in SOAPFILM model.\n", DATAFILE_ERROR); else add_standard_quantity("gauss_curvature_integral",modulus); recovery_flag = 0; break; } case SQGAUSS_: sqgauss_flag = 1; sqgauss_param = lookup_global("square_gauss_modulus"); if (sqgauss_param < 0 ) sqgauss_param = add_global("square_gauss_modulus"); globals(sqgauss_param)->flags |= ORDINARY_PARAM | RECALC_PARAMETER | ALWAYS_RECALC; uminus_flag = 0; if ( read_const(&globals(sqgauss_param)->value.real) <= 0 ) { kb_error(1084,"Missing square gaussian modulus value.\n",WARNING); if ( tok == SQGAUSS_ ) tok = yylex(); /* ensure progress */ } else tok = yylex(); if ( web.representation != SOAPFILM ) kb_error(1085, "Can do square gauss curvature only in SOAPFILM model.\n", DATAFILE_ERROR); recovery_flag = 0; break; case SQUARE_CURVATURE_: square_curvature_flag = 1; square_curvature_param = lookup_global("sq_curvature_modulus"); if ( square_curvature_param < 0 ) square_curvature_param = add_global("sq_curvature_modulus"); globals(square_curvature_param)->flags |= ORDINARY_PARAM | RECALC_PARAMETER | ALWAYS_RECALC; uminus_flag = 0; if ( read_const(&globals(square_curvature_param)->value.real) <= 0 ) { kb_error(1086,"Missing square curvature modulus value.\n",WARNING); if ( tok == SQUARE_CURVATURE_ ) tok = yylex(); /* ensure progress */ } else tok = yylex(); recovery_flag = 0; break; case NORMAL_CURVATURE_: normal_curvature_flag = 1; tok = yylex(); break; case MOBILITY_: recovery_flag = 0; uminus_flag = 0; esize = exparse(SDIM,&mobility_formula,USERCOPY); sprintf(mobility_formula.name,"mobility formula"); tok = yylex(); if ( esize <= 0 ) kb_error(1087,"Bad mobility definition.\n",DATAFILE_ERROR); else mobility_flag = 1; break; case MOBILITY_TENSOR_: mobility_flag = 1; mobility_tensor_flag = 1; recovery_flag = 0; lists_flag = LISTS_SOME; uminus_flag = 1; for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { esize = exparse(SDIM,&mobility_tensor[i][j],USERCOPY); if ( esize <= 0 ) { sprintf(errmsg, "Bad mobility_tensor[%d][%d] definition.\n",i,j); kb_error(1088,errmsg,DATAFILE_ERROR); } sprintf(mobility_tensor[i][j].name, "mobility tensor component [%d][%d]",i+1,j+1); } lists_flag = LISTS_OFF; tok = yylex(); break; case RUNGE_KUTTA_: runge_kutta_flag = 1; tok = yylex(); break; case SCALE_LIMIT_: uminus_flag = 0; if ( read_const(&web.maxscale) <= 0 ) { kb_error(1089,"Missing SCALE_LIMIT value.\n",WARNING); if ( tok == SCALE_LIMIT_ ) tok = yylex(); /* ensure progress */ } else tok = yylex(); recovery_flag = 0; break; case TOTAL_TIME_: uminus_flag = 0; if ( read_const(&total_time) <= 0 ) { kb_error(1090,"Missing TOTAL_TIME value.\n",WARNING); if ( tok == TOTAL_TIME_ ) tok = yylex(); /* ensure progress */ } else tok = yylex(); recovery_flag = 0; break; case ZOOM_RADIUS_: uminus_flag = 0; if ( read_const(&web.zoom_radius) <= 0 ) { kb_error(1091,"Missing ZOOM RADIUS value.\n",WARNING); if ( tok == ZOOM_RADIUS_ ) tok = yylex(); /* ensure progress */ } else tok = yylex(); recovery_flag = 0; break; case ZOOM_VERTEX_: tok = yylex(); if ( tok != INTEGER_ ) kb_error(1092,"Missing ZOOM VERTEX number.\n",WARNING); else { zoom_number = yylval.i; tok = yylex(); } recovery_flag = 0; break; case V_INTEGRAL_ORDER: tok = yylex(); if ( tok != INTEGER_ ) { kb_error(1093,"Missing INTEGRAL_ORDER value.\n",WARNING); break; } if ( yylval.i < 1 ) { sprintf(errmsg,"Invalid INTEGRAL_ORDER value %d.\n",yylval.i); kb_error(1094,errmsg,WARNING); } else { web.gauss1D_order = 2*yylval.i-1; set_by_user_gauss_1D = web.gauss1D_order; web.gauss2D_order = yylval.i; set_by_user_gauss_2D = web.gauss2D_order; } tok = yylex(); recovery_flag = 0; break; case V_INTEGRAL_ORDER_1D: tok = yylex(); if ( tok != INTEGER_ ) { kb_error(1095,"Missing INTEGRAL_ORDER_1D value.\n",WARNING); break; } if ( yylval.i < 1 ) { sprintf(errmsg,"Invalid INTEGRAL_ORDER_1D value %d.\n",yylval.i); kb_error(1096,errmsg,WARNING); } else { web.gauss1D_order = yylval.i; set_by_user_gauss_1D = web.gauss1D_order; } tok = yylex(); recovery_flag = 0; break; case V_INTEGRAL_ORDER_2D: tok = yylex(); if ( tok != INTEGER_ ) { kb_error(1097,"Missing INTEGRAL_ORDER_2D value.\n",WARNING); break; } if ( yylval.i < 1 ) { sprintf(errmsg,"Invalid INTEGRAL_ORDER_2D value %d.\n",yylval.i); kb_error(1098,errmsg,WARNING); } else { web.gauss2D_order = yylval.i; set_by_user_gauss_2D = web.gauss2D_order; } tok = yylex(); recovery_flag = 0; break; case CONSTRAINT_TOLERANCE_: uminus_flag = 0; if ( read_const(&web.tolerance) <= 0 ) { kb_error(1099,"Missing CONSTRAINT_TOLERANCE value.\n",WARNING); if ( tok == CONSTRAINT_TOLERANCE_ ) tok = yylex(); /* ensure progress */ } else tok = yylex(); if ( web.tolerance <= 0.0 ) { kb_error(2102,"Tolerance must be positive.\n",WARNING); web.tolerance = DEFAULT_TOLERANCE; } recovery_flag = 0; break; case MERITFACTOR_: uminus_flag = 0; if ( read_const(&web.meritfactor) <= 0 ) kb_error(1100,"Missing MERIT FACTOR value.\n",WARNING); else tok = yylex(); recovery_flag = 0; break; case GRAV_CONST_: uminus_flag = 0; if ( read_const(&web.grav_const) <= 0 ) { kb_error(1101,"Missing GRAVITY_CONSTANT value.\n",WARNING); if ( tok == GRAV_CONST_ ) tok = yylex(); /* ensure progress */ } else { if ( web.grav_const != 0.0 ) web.gravflag = 1; tok = yylex(); } recovery_flag = 0; break; case SPRING_CONSTANT_: case GAP_CONSTANT_: uminus_flag = 0; if ( read_const(&web.spring_constant) <= 0 ) { kb_error(1102,"Missing GAP_CONSTANT value.\n",WARNING); if ( tok == SPRING_CONSTANT_ ) tok = yylex(); /* ensure progress */ } else tok = yylex(); recovery_flag = 0; break; case SCALE_: uminus_flag = 0; if ( read_const(&web.scale) <= 0 ) { kb_error(1103,"Missing SCALE value.\n",WARNING); if ( tok == SCALE_ ) tok = yylex(); /* ensure progress */ } else tok = yylex(); if ( tok == FIXED_ ) { web.motion_flag = 1; tok = yylex(); } recovery_flag = 0; break; case TEMPERATURE_: uminus_flag = 0; if ( read_const(&web.temperature) <= 0 ) { kb_error(1104,"Missing TEMPERATURE value.\n",WARNING); if ( tok == TEMPERATURE_ ) tok = yylex(); /* ensure progress */ } else tok = yylex(); recovery_flag = 0; break; case PRESSURE_: uminus_flag = 0; if ( read_const(&web.pressure) <= 0 ) { kb_error(1105,"Missing PRESSURE value.\n",WARNING); if ( tok == PRESSURE_ ) tok = yylex(); /* ensure progress */ } else { tok = yylex(); web.pressure_flag = 1; } recovery_flag = 0; break; case VERTICES_: lists_flag = LISTS_FULL; topflag = 0; break; /* done with top stuff */ default: if ( !recovery_flag ) { sprintf(errmsg,"Illegal token '%s'.\n",yytext); kb_error(1106,errmsg,PARSE_ERROR); } tok = yylex(); break; } } /* see that all forward declarations resolved */ check_forwards(); /* set up gaussian quadrature */ gauss_setup(); if ( web.torus_flag ) calc_periods(ADJUST_VOLUMES); /* adjust torus volume constants */ reset_view(); /* can do this now, since know ambient dimension */ #ifdef HASH_ID elhash_bigger(); #endif dataflag = 1; uminus_flag = 1; while ( (tok != 0) && dataflag ) switch ( tok ) { case VERTICES_: if ( (web.dimension > 2) && (web.representation != SIMPLEX) ) kb_error(1107, "Must have simplex representation for surface dimension over 2.\n", UNRECOVERABLE); if ( elist || flist || blist ) kb_error(2409,"Vertices list must be first element list.\n", RECOVERABLE); recovery_flag = 0; read_vertices(); break; case EDGES_: if ( flist || blist ) kb_error(2410,"Edges list must be second element list.\n", RECOVERABLE); recovery_flag = 0; read_edges(); break; case FACES_: if ( blist ) kb_error(2411,"Faces list must precede body list.\n", RECOVERABLE); recovery_flag = 0; read_faces(); break; case BODIES_: recovery_flag = 0; read_bodies(); break; case READ_: if ( addload_flag ) { pop_commandfd(); lists_flag = LISTS_OFF; dataflag = 0; /* end of element data */ break; } /* read commands from datafile */ lists_flag = LISTS_OFF; read_command_flag = 1; dataflag = 0; /* end of element data */ line_no++; /* kludge */ break; default: if ( !recovery_flag ) { sprintf(errmsg,"Illegal token '%s'.\n",yytext); kb_error(1108,errmsg,PARSE_ERROR); } brace_depth = parens = in_quote = 0; /* just in case */ tok = yylex(); break; } if ( parse_errors ) return; datafile_flag = 1; /* got zeroed by pop_commandfd; need to read x1 */ if ( option_q ) convert_to_quantities(); if ( optparamcount && !everything_quantities_flag ) convert_to_quantities(); if ( (web.modeltype == LAGRANGE) && !everything_quantities_flag ) { outstring("Converting to named quantities for Lagrange model.\n"); option_q = 1; /* so convert_to_quantities doesn't try to calc */ convert_to_quantities(); option_q = 0; } for ( i = 0 ; i < web.bdrymax ; i++ ) if ( (web.boundaries[i].attr & (CON_ENERGY|CON_CONTENT)) && !everything_quantities_flag ) { outstring("Converting to named quantities for boundary integrals.\n"); option_q = 1; /* so convert_to_quantities doesn't try to calc */ convert_to_quantities(); option_q = 0; break; } datafile_flag = 0; if ( web.dimension == 1 ) areaname = "length"; else areaname = "area"; mark_recalc_params(); #ifdef MPI_EVOLVER mpi_task_set_thin_corona(); /* get neighbor elements, with basic corona */ #endif /* create initial triangulation of each face */ if ( web.representation == SOAPFILM ) for ( f = 1 ; f <= facecount ; f++ ) { facetedge_id first_fe; int edgecount = 0; if ( !valid_id(flist[f]) ) continue; /* see how many edges, and if we want to refine */ fe = first_fe = get_facet_fe(flist[f]); if ( valid_id(fe) ) do { edgecount++; fe = get_next_edge(fe); } while ( valid_id(fe) && !equal_id(fe,first_fe) ); if ( edgecount > 3 ) face_triangulate(flist[f],edgecount); } /* straighten out facet order around edges */ if ( web.representation == SOAPFILM ) { edge_id e_id; MFOR_ALL_EDGES(e_id) fe_reorder(e_id); } /* put facet-edges on string network if no facets defined */ if ( web.representation == STRING ) string_fixup(); /* phase boundary energies */ if ( phase_flag && (web.representation == STRING) ) { edge_id e_id; FOR_ALL_EDGES(e_id) set_e_phase_density(e_id); } if ( phase_flag && (web.representation != STRING) ) { facet_id f_id; FOR_ALL_FACETS(f_id) set_f_phase_density(f_id); } /* run preliminary checks */ if ( web.representation != SIMPLEX ) if ( facetedge_check(PRELIMCHECK) || facet_body_check() ) kb_error(1109,"Bad data file.\n",DATAFILE_ERROR); if ( vlist == NULL ) kb_error(1110,"No vertices found in datafile.\n",WARNING); if ( (elist == NULL) && (web.representation != SIMPLEX) ) kb_error(1111,"No edges found in datafile.\n",WARNING); if ( vlist ) myfree((char *)vlist); if ( elist ) myfree((char *)elist); if ( flist ) myfree((char *)flist); if ( blist ) myfree((char *)blist); for ( k = 2, web.simplex_factorial = 1.0 ; k <= web.dimension ; k++ ) web.simplex_factorial *= k; volume_factorial = web.simplex_factorial*SDIM; if ( web.representation == SIMPLEX ) { refine_simplex_init(); } else if ( web.modeltype == LINEAR ) { calc_facet_energy = facet_energy_l; calc_facet_forces = facet_force_l; calc_facet_volume = facet_volume_l; film_grad = film_grad_l; calc_edge_energy = edge_energy_l; calc_edge_forces = edge_force_l; calc_edge_area = edge_area_l; string_grad = string_grad_l; } if ( transform_count == 0 ) /* set up identity transform */ { transform_count = 1; view_transforms = dmatrix3(1,SDIM+1,SDIM+1); for ( j = 0 ; j <= SDIM ; j++ ) view_transforms[0][j][j] = 1.0; set_view_transforms_global(); } if ( autopop_flag ) { int n; if ( web.representation == STRING ) sprintf(msg,"Number of vertices popped: %d\n", n = verpop_str()); else sprintf(msg,"Number of vertices popped: %d\n", n = edgepop_film()); outstring(msg); if ( n > 0 ) update_display(); } if ( web.homothety && (web.skel[BODY].count == 0) ) { web.homothety = 0; kb_error(1112,"Cannot do homothety without bodies. Homothety OFF.\n",RECOVERABLE); } if ( sym_flags & NEED_FORM_UNWRAPPING ) if ( auto_convert_flag ) convert_to_quantities(); } /* end initialize */ /************************************************************************ * * function: read_single_value() * * purpose: read one value of desired type from datafile * * return: 1 for success, 0 for failure */ int read_single_value(type,dest) int type; char *dest; { REAL val; switch (type ) { case CHAR_TYPE : if ( read_const(&val) >= 0 ) *(char*)dest = (char)val; else return 0; break; case UCHAR_TYPE : if ( read_const(&val) >= 0 ) *(unsigned char*)dest = (unsigned char)val; else return 0; break; case SHORT_TYPE : if ( read_const(&val) >= 0 ) *(short*)dest = (short)val; else return 0; break; case USHORT_TYPE : if ( read_const(&val) >= 0 ) *(unsigned short*)dest = (unsigned short)val; else return 0; break; case INTEGER_TYPE : if ( read_const(&val) >= 0 ) *(int*)dest = (int)val; else return 0; break; case REAL_TYPE : if ( read_const(&val) >= 0 ) *(REAL*)dest = val; else return 0; break; case ULONG_TYPE : if ( read_const(&val) >= 0 ) *((unsigned long *)dest) = (unsigned long)val; else return 0; break; case STRING_TYPE: if ( tok == QUOTATION_ ) { *(char**)dest = mycalloc(strlen(yytext)+1,sizeof(char)); strcpy(*(char**)dest,yytext); } else return 0; case ELEMENTID_TYPE: return 0; case VERTEX_TYPE: if ( tok != VERTICES_ ) return 0; tok = yylex(); if ( tok != '[' ) return 0; tok = yylex(); if ( (tok != INTEGER_) && (tok != INTEGER_AT_) ) return 0; *(element_id*)dest = get_ordinal_id(VERTEX,yylval.i); tok = yylex(); if ( tok != ']' ) return 0; break; case EDGE_TYPE: if ( tok != EDGES_ ) return 0; tok = yylex(); if ( tok != '[' ) return 0; tok = yylex(); if ( (tok != INTEGER_) && (tok != INTEGER_AT_) ) return 0; *(element_id*)dest = get_ordinal_id(EDGE,yylval.i); tok = yylex(); if ( tok != ']' ) return 0; break; case FACET_TYPE: if ( tok != FACETS_ ) return 0; tok = yylex(); if ( tok != '[' ) return 0; tok = yylex(); if ( (tok != INTEGER_) && (tok != INTEGER_AT_) ) return 0; *(element_id*)dest = get_ordinal_id(FACET,yylval.i); tok = yylex(); if ( tok != ']' ) return 0; break; case BODY_TYPE: if ( tok != BODIES_ ) return 0; tok = yylex(); if ( tok != '[' ) return 0; tok = yylex(); if ( (tok != INTEGER_) && (tok != INTEGER_AT_) ) return 0; *(element_id*)dest = get_ordinal_id(BODY,yylval.i); tok = yylex(); if ( tok != ']' ) return 0; break; case FACETEDGE_TYPE: if ( tok != FACETEDGES_ ) return 0; tok = yylex(); if ( tok != '[' ) return 0; tok = yylex(); if ( (tok != INTEGER_) && (tok != INTEGER_AT_) ) return 0; *(element_id*)dest = get_ordinal_id(FACETEDGE,yylval.i); tok = yylex(); if ( tok != ']' ) return 0; break; case CONSTRAINT_TYPE: if ( tok == CONSTRAINT_ ) { tok = yylex(); if ( tok == INTEGER_ ) *(int *)dest = yylval.i; else if ( tok == CONSTRAINT_NAME_ ) *(int *)dest = yylval.i; else return 0; } else { if ( tok == CONSTRAINT_NAME_ ) *(int *)dest = yylval.i; else return 0; } break; case BOUNDARY_TYPE: if ( tok == BOUNDARY_ ) { tok = yylex(); if ( tok == INTEGER_ ) *(int *)dest = yylval.i; else if ( tok == BOUNDARY_NAME_ ) *(int *)dest = yylval.i; else return 0; } else { if ( tok == BOUNDARY_NAME_ ) *(int *)dest = yylval.i; else return 0; } break; case QUANTITY_TYPE: if ( tok == QUANTITY_ ) tok = yylex(); if ( tok == QUANTITY_NAME_ ) *(int *)dest = yylval.i; else return 0; break; case INSTANCE_TYPE: if ( tok == METHOD_INSTANCE_ ) tok = yylex(); if ( tok == METHOD_NAME_ ) *(int *)dest = yylval.i; else return 0; break; case PROCEDURE_TYPE: if ( tok == PROCEDURE_WORD_ ) tok = yylex(); if ( tok == PROCEDURE_IDENT_ ) *(int *)dest = yylval.i; else return 0; break; } return 1; } /************************************************************************ * * Function: read_attribute_value() * * Purpose: read list of values for one attribute in element definition. */ void read_attribute_value(ex,datastart) struct extra *ex; /* attribute definition */ void *datastart; /* destination for data */ { char *spot; int depth; int blocksize; char *spots[MAXARRAYDIMS]; int items[MAXARRAYDIMS]; int first_bracket_flag = 0; /* whether seen first bracket, which is optional for 1 dim attribute */ int no_first_bracket = 0; /* record option */ depth = 0; spot = datastart; if ( ex->array_spec.dim == 0 ) /* scalar */ { if ( !read_single_value(ex->type,spot) ) { sprintf(errmsg,"missing value for attribute %s.\n",ex->name); kb_error(2587,errmsg,DATAFILE_ERROR); } tok = yylex(); /* get lookahead */ return; } spots[depth] = spot; items[depth] = 0; blocksize = ex->array_spec.datacount; for (;;) { if ( depth == ex->array_spec.dim ) { int k; for ( k = 0 ; ; k++ ) { /* kludge with LEAD_INTEGER_ here */ if ( tok != LEAD_INTEGER_ ) { if ( read_single_value(ex->type,spot) ) { if ( k < ex->array_spec.sizes[depth-1] ) spot += ex->array_spec.itemsize; else { sprintf(errmsg, "Too many initializers for attribute %s.\n", ex->name); kb_error(2511,errmsg,DATAFILE_ERROR); } } else { tok = yylex(); /* get back token exparse pushed back */ if ( tok != ',' ) break; } } else break; } } if ( !first_bracket_flag ) tok = yylex(); if ( (tok == '{') || !first_bracket_flag ) { if ( tok != '{' ) { unput_tok(); no_first_bracket = 1; } first_bracket_flag = 1; if ( blocksize ) blocksize /= ex->array_spec.sizes[depth]; spots[depth] = spot; items[depth]++; if ( (depth > 0) && (items[depth] > ex->array_spec.sizes[depth-1]) ) kb_error(2133,"Too many initializers.\n",DATAFILE_ERROR); depth++; items[depth] = 0; if ( (depth != ex->array_spec.dim) && !no_first_bracket ) tok = yylex(); } else if ( tok == '}' ) { tok = yylex(); depth--; if ( depth == 0 ) return; blocksize *= ex->array_spec.sizes[depth]; spots[depth] += blocksize*ex->array_spec.itemsize; spot = spots[depth]; } else if ( tok == ',' ) { tok = yylex(); spots[depth] += blocksize*ex->array_spec.itemsize; } else if ( no_first_bracket ) { /* must be end of 1-D attribute */ if ( unput_tok_count ) tok = yylex(); /* get lookahead back */ return; } else { sprintf(errmsg,"Illegal token in initialization of attribute %s.\n", ex->name); kb_error(2134,errmsg, DATAFILE_ERROR); } } } /************************************************************************ * * Function: read_extra() * * purpose: read extra attribute of element, attribute name still in yytext. */ void read_extra(el_id,exnum) element_id el_id; int exnum; { char *spot; int type = id_type(el_id); struct extra *ex = EXTRAS(type) + exnum; spot = get_extra(el_id,exnum); read_attribute_value(ex,spot); } /************************************************************************ * * Function: read_vertices() * */ void read_vertices() { int k; REAL c[MAXCOORD]; /* temporary number buffer */ int cnum,bnum,pcount,qnum; struct boundary *bdry; struct constraint *constr; int more_attr; int sdim = SDIM; /* allocate space in structures */ expand_attribute(VERTEX,V_COORD_ATTR,&sdim); expand_attribute(VERTEX,V_OLDCOORD_ATTR,&sdim); expand_attribute(VERTEX,V_FORCE_ATTR,&sdim); expand_attribute(VERTEX,V_VELOCITY_ATTR,&sdim); if ( web.maxparam > 0 ) { expand_attribute(VERTEX,V_PARAM_ATTR,&web.maxparam); } /* read in vertex coordinates */ vmaxlist = MAXLIST; vlist = (vertex_id *)mycalloc(sizeof(vertex_id),vmaxlist); if ( tok != VERTICES_ ) kb_error(1115,"Cannot find VERTICES section of the datafile.\n", UNRECOVERABLE); tok = yylex(); while ( (tok == LEAD_INTEGER_) || (tok == LEAD_INTEGER_AT_) ) { #ifdef MPI_EVOLVER /* test task number, for MPI */ int task; if ( tok == LEAD_INTEGER_ ) task = 1; /* default to task 1 */ else task = yylval.qnum; if ( task >= mpi_nprocs || task < 1 ) { sprintf(errmsg,"Task number %d must be between 1 and %d, %d\n", task,mpi_nprocs-1); kb_error(5006,errmsg,RECOVERABLE); } if ( task != this_task ) { /* skip this vertex */ int flag = 0; while ( !flag ) { tok = yylex(); switch (tok) { case LEAD_INTEGER_: case LEAD_INTEGER_AT_: case EDGES_: case FACES_: case BODIES_: case READ_: case 0: flag = 1; break; } } continue; /* next vertex */ } #endif k = yylval.i; if ( k < 1 ) kb_error(2103,"Vertex number must be positive.\n",DATAFILE_ERROR); for ( pcount = 0 ; pcount < SDIM ; pcount++ ) { if ( read_const(&c[pcount]) <= 0 ) break; if ( (tok == LEAD_INTEGER_) || (tok == LEAD_INTEGER_AT_) ) { pcount++; break; } } tok = yylex(); /* get lookahead */ while ( k >= vmaxlist ) { int spot = vmaxlist; vlist = (vertex_id *)kb_realloc((char *)vlist, (k+MAXLIST)*sizeof(vertex_id)); vmaxlist = k + MAXLIST; for ( ; spot < vmaxlist ; spot ++ ) vlist[spot] = NULLID; } if ( valid_id(vlist[k]) ) { sprintf(errmsg,"Duplicate vertex number %d\n",k); kb_error(1117,errmsg,DATAFILE_ERROR); } move_to_free_front(VERTEX,k); /* so id will be k */ vlist[k] = new_vertex(c,NULLID); set_original(vlist[k],(k-1)|((element_id)VERTEX<flags & QUANTITY_NAME ) apply_quantity(vlist[k],yylval.i); else { sprintf(errmsg,"Undefined quantity: %s.\n",yytext); kb_error(1119,errmsg,DATAFILE_ERROR); } tok = yylex(); if ( stricmp(yytext,"method")==0 ) { tok = yylex(); kb_error(1120,"Obsolete quantity syntax.\n Methods must be listed in quantity definition.",DATAFILE_ERROR); tok = yylex(); } continue; } else kb_error(1121,"Need quantity name and method.\n",DATAFILE_ERROR); break; case BOUNDARY_: case BOUNDARY_NAME_: { REAL *x,*param; int n; if ( tok == BOUNDARY_ ) tok = gettok(INTEGER_); if ( (tok != INTEGER_) && ( tok != BOUNDARY_NAME_ ) ) { kb_error(1122,"Need boundary number or name.\n", DATAFILE_ERROR); break; } if ( tok == INTEGER_ ) { bnum = abs(yylval.i); if ( (bnum >= web.bdrymax) || !(web.boundaries[bnum].attr & IN_USE) ) { sprintf(errmsg, "Bad boundary number %d for vertex %d.\n",bnum,k); kb_error(1123,errmsg,DATAFILE_ERROR); yylex(); break; } } else { bnum = globals(yylval.i)->value.bnum; } set_attr(vlist[k],BOUNDARY); if ( yylval.i < 0 ) set_attr(vlist[k],NEGBOUNDARY); set_boundary_num(vlist[k],bnum); bdry = get_boundary(vlist[k]); if ( pcount != bdry->pcount ) { sprintf(errmsg, "Wrong number of parameters for vertex %d.\n",k); kb_error(1124,errmsg,DATAFILE_ERROR); } if ( (bdry->attr & CON_ENERGY) && (yytext[0] != '0') ) set_attr(vlist[k], BDRY_ENERGY); if ( bdry->attr & CON_CONTENT ) set_attr(vlist[k], BDRY_CONTENT); param = get_param(vlist[k]); x = get_coord(vlist[k]); for ( n = 0 ; n < web.maxparam ; n++ ) param[n] = x[n]; /* initial coordinate calculation later, after all info */ tok = yylex(); } break; case CONSTRAINT_: case CONSTRAINT_NAME_: if ( tok == CONSTRAINT_ ) tok = gettok(INTEGER_); while ( (tok == INTEGER_) || (tok==CONSTRAINT_NAME_) ) { if ( tok == INTEGER_ ) cnum = abs(yylval.i); else cnum = globals(yylval.i)->value.cnum; constr = get_constraint(cnum); if ( (cnum >= web.maxcon) || !(constr->attr & IN_USE) ) { sprintf(errmsg, "Bad constraint number %d for vertex %d.\n",cnum,k); kb_error(1125,errmsg,DATAFILE_ERROR); tok = yylex(); break; } set_attr(vlist[k],CONSTRAINT); if ( yylval.i < 0 ) set_attr(vlist[k],NEGBOUNDARY); set_v_constraint_map(vlist[k],cnum); if ( (constr->attr & CON_ENERGY) && (yytext[0] != '0') ) set_attr(vlist[k], BDRY_ENERGY); if ( constr->attr & CON_CONTENT ) set_attr(vlist[k], BDRY_CONTENT); tok = gettok(INTEGER_); } /* projection later, after all vertex info read in */ break; case EDGES_: case FACES_: case BODIES_: case READ_: case LEAD_INTEGER_: case LEAD_INTEGER_AT_: case NO_TOKEN: more_attr = 0 ; break; /* error recovery */ case UNPUTTED_: kb_error(3701, "Internal error: forgot to get lookahead token.\n", WARNING); tok = yylex(); break; default: sprintf(errmsg,"Unexpected token: %s\n",yytext); kb_error(2105,errmsg,WARNING); tok = yylex(); break; } if ((get_vattr(vlist[k])&(BOUNDARY|CONSTRAINT)) == (BOUNDARY|CONSTRAINT)) kb_error(1126,"Cannot have constraint and boundary.",DATAFILE_ERROR); if ( !(get_vattr(vlist[k])&BOUNDARY) && (pcount != SDIM) ) { sprintf(errmsg,"Wrong number of coordinates for vertex %d.\n",k); kb_error(1127,errmsg,WARNING); } if ( get_vattr(vlist[k]) & BOUNDARY ) { REAL * x = get_coord(vlist[k]); int n; bdry = get_boundary(vlist[k]); for ( n = 0 ; n < SDIM ; n++ ) if ( bdry->coordf[n]->root != NULL ) x[n] = eval(bdry->coordf[n],get_param(vlist[k]),vlist[k],NULL); } if ( get_vattr(vlist[k]) & CONSTRAINT ) project_v_constr(vlist[k],ACTUAL_MOVE,RESET_ONESIDEDNESS); } web.zoom_v = vlist[zoom_number]; /* vertex for zooming in on */ } /* end read_vertices() */ /************************************************************************ * * Function: read_edges() * */ void read_edges() { int i,k; element_id head,tail; int cnum,bnum; struct boundary *bdry; struct constraint *constr; int more_attr; REAL value; /* for constant expression values */ int compcount; /* proper number of components for integrands */ int numv; /* vertices to read in association with a facet */ int edim = (web.representation==STRING) ? 1 : web.dimension - 1; int one = 1; #ifdef MPI_EVOLVER struct element *vdummy = (struct element *)mycalloc(web.sizes[VERTEX],1); #endif if ( web.representation == SIMPLEX ) compcount = binom_coeff(SDIM,edim); else compcount = SDIM; /* optional attributes */ expand_attribute(EDGE,E_VERTICES_ATTR,&web.skel[EDGE].ctrlpts); if ( web.symmetry_flag ) expand_attribute(EDGE,E_WRAP_ATTR,&one); if ( web.representation == SIMPLEX ) { if ( web.modeltype == LAGRANGE ) numv = binom_coeff(web.lagrange_order+edim,edim); else numv = web.dimension; } else if ( web.modeltype == LAGRANGE ) { numv = binom_coeff(web.lagrange_order+edim,edim); } else if ( web.modeltype == QUADRATIC ) numv = 3; else numv = 2; /* read in edges */ emaxlist = MAXLIST; elist = (edge_id *)mycalloc(sizeof(edge_id),emaxlist); while ( (tok != EDGES_) && (tok != 0 ) ) tok = yylex(); if ( tok != EDGES_ ) return; tok = yylex(); while ( (tok == LEAD_INTEGER_) || (tok == LEAD_INTEGER_AT_) ) { int have_mid = 0; WRAPTYPE wrap = 0; #ifdef MPI_EVOLVER /* test task number, for MPI */ int task; if ( tok == LEAD_INTEGER_ ) task = 1; /* default to task 1 */ else task = yylval.qnum; if ( task >= mpi_nprocs || task < 1 ) { sprintf(errmsg,"Task number %d must be between 1 and %d\n", task,mpi_nprocs-1); kb_error(5007,errmsg,RECOVERABLE); } if ( task != this_task ) { /* skip this edge */ int flag = 0; while ( !flag ) { tok = yylex(); switch (tok) { case LEAD_INTEGER_: case LEAD_INTEGER_AT_: case FACES_: case BODIES_: case READ_: case 0: flag = 1; break; } } continue; /* next edge */ } #endif /* check edge number */ k = yylval.i; if ( k < 1 ) kb_error(2106,"Edge number must be positive.\n",DATAFILE_ERROR); while ( k >= emaxlist ) { int spot = emaxlist; elist = (edge_id *)kb_realloc((char *)elist,(k+MAXLIST)*sizeof(edge_id)); emaxlist= k + MAXLIST; for ( ; spot < emaxlist ; spot ++ ) elist[spot] = NULLID; } if ( valid_id(elist[k]) ) { sprintf(errmsg,"Duplicate edge number %d\n",k); kb_error(1130,errmsg,DATAFILE_ERROR); } { /* read vertex list */ int vercount; vertex_id *v,*vv; move_to_free_front(EDGE,k); /* so id will be k */ elist[k] = new_edge(NULLID,NULLID,NULLID); vv = v = get_edge_vertices(elist[k]); for ( vercount = 0 ; vercount < numv ; vercount++ ) { tok = gettok(INTEGER_); if ( tok != INTEGER_ ) { free_element(elist[k]); elist[k] = NULLID; if ( addload_flag && (vercount == 2) ) { for ( i = 0 ; i < vmaxlist ; i++ ) if ( valid_element(vlist[i]) ) free_element(vlist[i]); if ( web.modeltype == QUADRATIC ) kb_error(5437,"addload datafile is linear model, but current model is quadratic.\n", RECOVERABLE); else kb_error(5438,"addload datafile is linear model, but current model is Lagrange.\n", RECOVERABLE); } else kb_error(1131,"Too few vertices for edge.\n",DATAFILE_ERROR); return; } #ifdef MPI_EVOLVER if ( yylval.qnum == 0 ) yylval.qnum = 1; if ( yylval.qnum >= mpi_nprocs ) { sprintf(errmsg,"Task number %d exceeds number of tasks running, %d\n", yylval.qnum,mpi_nprocs-1); kb_error(5008,errmsg,RECOVERABLE); } if ( yylval.qnum != this_task ) { *v = ((element_id)VERTEX << TYPESHIFT) | VALIDMASK | (yylval.i-1) | ((element_id)yylval.qnum << TASK_ID_SHIFT); /* add spaceholder to remote element list */ memset(vdummy,0,web.sizes[VERTEX]); vdummy->self_id = *v; vdummy->attr = ALLOCATED|NEWELEMENT; mpi_add_remote_element(vdummy); v++; } else #endif if ( (yylval.i >= vmaxlist) || !valid_id(vlist[yylval.i]) ) { sprintf(errmsg,"Edge %d: vertex %d is not defined.\n",k,yylval.i); kb_error(1132,errmsg,DATAFILE_ERROR); return; } else *(v++) = vlist[yylval.i]; } if ( web.modeltype == QUADRATIC ) { head = vv[1]; tail = vv[0]; set_edge_midv(elist[k],vv[2]); } else { head = vv[numv-1]; tail = vv[0]; } set_edge_headv(elist[k],head); set_edge_tailv(elist[k],tail); if ( web.modeltype == QUADRATIC ) { have_mid =1; /* for later */ } else if ( web.modeltype == LAGRANGE ) { for ( i = 1 ; i < numv-1 ; i++ ) { set_attr(vv[i],Q_MIDEDGE); set_vertex_edge(vv[i],elist[k]); } } } tok = yylex(); if ( web.torus_flag ) { read_wrap_flag = 1; for ( i = 0 ; i < SDIM ; i++ ) switch ( tok ) { case WRAP_: if ( read_const(&value) < 0 ) kb_error(4135,"Missing wrap value.\n",DATAFILE_ERROR); else tok = yylex(); wrap = (WRAPTYPE)value; i = SDIM; break; case '+': wrap += POSWRAP << (i*TWRAPBITS); tok = ' '; /* so won't expect more input */ tok = yylex(); break; case '*': tok = ' '; /* so won't expect more input */ tok = yylex(); break; case '-': case UMINUS_: wrap += NEGWRAP << (i*TWRAPBITS); tok = ' '; /* so won't expect more input */ tok = yylex(); break; case POW: /* ** */ i++; tok = ' '; /* so won't expect more input */ tok = yylex(); break; default : kb_error(1133,"Edge wraps must immediately follow endpoints.\n", WARNING); i = SDIM; /* assume wraps are missing */ break; } } read_wrap_flag = 0; set_original(elist[k],(k-1)|((element_id)EDGE<= web.bdrymax) || !(web.boundaries[bnum].attr & IN_USE) ) { sprintf(errmsg, "Bad boundary number %d for edge %d.\n",bnum,k); kb_error(1137,errmsg,DATAFILE_ERROR); yylex(); break; } } else { bnum = globals(yylval.i)->value.bnum; } set_attr(elist[k],BOUNDARY); if ( yylval.i < 0 ) set_attr(vlist[k],NEGBOUNDARY); set_edge_boundary_num(elist[k],bnum); bdry = get_edge_boundary(elist[k]); if ( (bdry->attr & CON_ENERGY) && (yytext[0] == '0') ) set_attr(elist[k], BDRY_ENERGY); if ( bdry->attr & CON_CONTENT ) set_attr(elist[k], BDRY_CONTENT); tok = yylex(); break; case CONSTRAINT_: case CONSTRAINT_NAME_: if ( tok == CONSTRAINT_ ) tok = gettok(INTEGER_); while ( (tok == INTEGER_) || (tok==CONSTRAINT_NAME_) ) { struct constraint *con; if ( tok == INTEGER_ ) cnum = abs(yylval.i); else cnum = globals(yylval.i)->value.cnum; con = get_constraint(cnum); if ( (cnum >= web.maxcon) || !(con->attr & IN_USE) ) { sprintf(errmsg, "Bad constraint number %d for edge %d.\n",cnum,k); kb_error(1138,errmsg,DATAFILE_ERROR); tok=yylex(); break; } /* check consistency of number of components */ if ( (con->attr & CON_ENERGY) || ((con->attr & CON_CONTENT) && web.dimension==SOAPFILM) ) { if ( web.dimension == 1 ) { sprintf(errmsg,"Edge %s is on constraint %s, which has an energy integral. Probably a bad idea in the string model.\n",ELNAME(elist[k]),con->name); kb_error(3918,errmsg,WARNING); } else if ( con->compcount != compcount ) { sprintf(errmsg, "Inconsistent number of components in edge %s constraint %s content or energy integrands.\n", ELNAME(elist[k]),con->name); kb_error(1139,errmsg, WARNING); } } set_attr(elist[k],CONSTRAINT); if ( yylval.i < 0 ) set_attr(elist[k],NEGBOUNDARY); set_e_constraint_map(elist[k],cnum); constr = get_constraint(cnum); if ( (constr->attr & CON_ENERGY) && (yytext[0] != '0') ) set_attr(elist[k], BDRY_ENERGY); if ( constr->attr & CON_CONTENT ) set_attr(elist[k], BDRY_CONTENT); tok = gettok(INTEGER_); } break; case DENSITY_: if ( read_const(&value) <= 0 ) kb_error(1140,"Missing DENSITY value.\n",WARNING); else tok = yylex(); set_attr(elist[k],DENSITY); set_edge_density(elist[k],value); break; case QUANTITY_NAME_: /* name of quantity */ { int qnum = yylval.i; tok = yylex(); if ( tok == '-' || tok == UMINUS_ ) { apply_quantity(inverse_id(elist[k]),qnum); tok = yylex(); } else apply_quantity(elist[k],qnum); break; } case IDENT_: /* maybe method or quantity */ if ( globals(yylval.i)->flags & METHOD_NAME ) apply_method(elist[k],yytext); else if ( globals(yylval.i)->flags & QUANTITY_NAME ) apply_quantity(elist[k],yylval.i); else { sprintf(errmsg,"Illegal use of identifier '%s'.\n",yytext); kb_error(1141,yytext,DATAFILE_ERROR); } tok = yylex(); break; case METHOD_: /* apply method instance to edge */ tok = yylex(); case METHOD_NAME_: { int qnum = yylval.i; tok = yylex(); if ( tok == UMINUS_ || tok == '-' ) { apply_method_num(inverse_id(elist[k]),qnum); tok = yylex(); } else apply_method_num(elist[k],qnum); break; } case QUANTITY_: /* see if quantity */ tok = yylex(); if ( tok == QUANTITY_NAME_ ) { /* have named quantity/method pair */ char qname[32]; strncpy(qname,yytext,sizeof(qname)); apply_quantity(elist[k],yylval.i); tok = yylex(); if ( stricmp(yytext,"method")==0 ) { tok = yylex(); kb_error(1143,"Obsolete quantity syntax.\n Methods must be listed in quantity definition.",DATAFILE_ERROR); tok = yylex(); } continue; } else { sprintf(errmsg,"Undefined quantity: %s\n",yytext); kb_error(1144,errmsg,DATAFILE_ERROR); } break; case ENERGY_: /* obsolete surface energy */ kb_error(1147, "'Energy' obsolete. Implement edge integral energy with named quantity.\n", DATAFILE_ERROR); break; case FACES_: case BODIES_: case READ_: case LEAD_INTEGER_: case LEAD_INTEGER_AT_: case NO_TOKEN: more_attr = 0 ; break; /* error recovery */ case UNPUTTED_: kb_error(3702, "Internal error: forgot to get lookahead token.\n", WARNING); tok = yylex(); break; default: sprintf(errmsg,"Unexpected token: %s\n",yytext); kb_error(2110,errmsg,WARNING); tok = yylex(); break; } if ( web.symmetry_flag ) { set_edge_wrap(elist[k],wrap); if ( (web.modeltype == QUADRATIC) && !have_mid ) { /* have to adjust midpoints */ REAL *x = get_coord(get_edge_midv(elist[k])); REAL *t = get_coord(get_edge_tailv(elist[k])); REAL h[MAXCOORD]; (*sym_wrap)(get_coord(get_edge_headv(elist[k])),h,wrap); for ( i = 0 ; i < SDIM ; i++ ) x[i] = 0.5*(t[i] + h[i]); } } if ((get_eattr(elist[k])&(BOUNDARY|CONSTRAINT)) == (BOUNDARY|CONSTRAINT)) kb_error(1148,"Cannot have constraint and boundary.",DATAFILE_ERROR); } #ifdef MPI_EVOLVER myfree((char*)vdummy); #endif } /* end read_edges() */ /************************************************************************ * * Function: read_faces() * * Purpose: Read face definition lines from datafile and construct * facet-edge links. * * MPI note: In SOAPFILM model, facetedges go to same task * as facets, but go to same task as edges in STRING model. */ void read_faces() { int i,k; int e; facetedge_id old_fe; facet_id this_facet_id; int cnum,bnum; struct constraint *constr; REAL value; /* for constant expression values */ int numv; /* vertices to read with facet */ #ifdef MPI_EVOLVER struct element *vdummy = (struct element *)mycalloc(web.sizes[VERTEX],1); struct element *edummy = (struct element *)mycalloc(web.sizes[EDGE],1); int facet_task,edge_task; int split_loop_flag = 0; /* string, set if task number of any edge is foreign */ #endif /* optional attributes */ expand_attribute(FACET,F_VERTICES_ATTR,&web.skel[FACET].ctrlpts); if ( web.representation == SIMPLEX ) { if ( web.modeltype == LAGRANGE ) numv = binom_coeff(web.lagrange_order+web.dimension,web.dimension); else numv = web.dimension; } else if ( web.modeltype == LAGRANGE ) { numv = binom_coeff(web.lagrange_order+web.dimension,web.dimension); } else if ( web.modeltype == QUADRATIC ) numv = 0; else numv = 0; /* read in faces */ fmaxlist = MAXLIST; flist = (facet_id *)mycalloc(sizeof(facet_id),fmaxlist); tok = yylex(); while ( (tok == LEAD_INTEGER_) || (tok == LEAD_INTEGER_AT_) ) { int more_attr; int edge_count = 0; #ifdef MPI_EVOLVER /* test task number, for MPI */ if ( tok == LEAD_INTEGER_ ) facet_task = 1; /* default to task 1 */ else facet_task = yylval.qnum; if ( facet_task >= mpi_nprocs || facet_task < 1 ) { sprintf(errmsg,"Task number %d must be between 1 and %d\n", facet_task,mpi_nprocs-1); kb_error(5009,errmsg,RECOVERABLE); } if ( facet_task != this_task ) { /* skip this facet */ int flag = 0; while ( !flag ) { tok = yylex(); switch (tok) { case LEAD_INTEGER_: case LEAD_INTEGER_AT_: case BODIES_: case READ_: case 0: flag = 1; break; } } continue; /* next facet */ } #endif k = yylval.i; if ( k < 1 ) kb_error(2111,"Face number must be positive.\n",DATAFILE_ERROR); old_fe = NULLFACETEDGE; { while ( k >= fmaxlist ) { int spot = fmaxlist; flist = (facet_id *)kb_realloc((char *)flist, (k+MAXLIST)*sizeof(facet_id)); fmaxlist = k + MAXLIST; for ( ; spot < fmaxlist ; spot++ ) flist[spot] = NULLID; } if ( valid_id(flist[k]) ) { sprintf(errmsg,"Duplicate face number %d\n",k); kb_error(1152,errmsg,DATAFILE_ERROR); } move_to_free_front(FACET,k); /* so id will be k */ flist[k] = new_facet(); set_original(flist[k],(k-1)|((element_id)FACET< numv ) kb_error(1153,"Too many vertices for facet.\n",DATAFILE_ERROR); #ifdef MPI_EVOLVER if ( yylval.qnum == 0 ) yylval.qnum = 1; if ( yylval.qnum >= mpi_nprocs ) { sprintf(errmsg,"Task number %d exceeds number of tasks running, %d\n", yylval.qnum,mpi_nprocs-1); kb_error(5010,errmsg,RECOVERABLE); } if ( yylval.qnum != this_task ) { *v = ((element_id)VERTEX << TYPESHIFT) | VALIDMASK | (yylval.i-1) | ((element_id)yylval.qnum << TASK_ID_SHIFT); memset(vdummy,0,web.sizes[VERTEX]); vdummy->self_id = *v; vdummy->attr = ALLOCATED|NEWELEMENT; mpi_add_remote_element(vdummy); v++; } else #endif if ( (yylval.i >= vmaxlist) || !valid_id(vlist[yylval.i]) ) { sprintf(errmsg,"Facet %d: vertex %d is not defined.\n",k,yylval.i); kb_error(1154,errmsg,DATAFILE_ERROR); } else *(v++) = vlist[yylval.i]; } if ( vercount < numv ) kb_error(1155,"Too few vertices for facet.\n",DATAFILE_ERROR); } else /* facet_edge representation */ { facetedge_id fe = NULLID; facetedge_id first_fe = NULLID; while ( (tok = gettok(INTEGER_)) == INTEGER_ ) { edge_id e_id; facetedge_id edge_fe; edge_count++; e = yylval.i; #ifdef MPI_EVOLVER edge_task = yylval.qnum; if ( edge_task == 0 ) /* for non-partitioned datafiles */ edge_task = 1; if ( edge_task >= mpi_nprocs ) { sprintf(errmsg,"Edge task number %d exceeds number of tasks running, %d\n", edge_task,mpi_nprocs-1); kb_error(5011,errmsg,RECOVERABLE); } if ( edge_task != this_task ) { if (web.representation==SOAPFILM ) { e_id = ((element_id)EDGE << TYPESHIFT) | VALIDMASK | (abs(e)-1) | ((element_id)edge_task << TASK_ID_SHIFT); memset(edummy,0,web.sizes[EDGE]); edummy->self_id = e_id; edummy->attr = ALLOCATED | NEWELEMENT; mpi_add_remote_element(edummy); if ( e < 0 ) invert(e_id); } else /* string */ { split_loop_flag = 1; /* so don't try to close loop at end */ continue; /* only doing local edges */ } } else #endif { if ( abs(e) >= emaxlist ) { sprintf(errmsg,"Facet %d: edge %d is not defined.\n",k,abs(e)); e = 0; kb_error(1156,errmsg,DATAFILE_ERROR); continue; } e_id = e > 0 ? elist[e] : edge_inverse(elist[-e]); } if ( !valid_id(e_id) ) { sprintf(errmsg,"Facet %d: edge %d is not defined.\n",k,e); kb_error(1157,errmsg,DATAFILE_ERROR); continue; } fe = new_facetedge(this_facet_id,e_id); if ( valid_id(old_fe) ) { #ifndef MPI_EVOLVER vertex_id hh,tt; hh = get_fe_headv(old_fe); tt = get_fe_tailv(fe); if ( !equal_id(hh,tt) ) { sprintf(msg,"Inconsistency in face %d, edge %d tail vertex disagrees with previous head.\n", k,e); kb_error(2112,msg,DATAFILE_ERROR); } #endif set_next_edge(old_fe,fe); } else first_fe = fe; set_prev_edge(fe,old_fe); old_fe = fe; #ifdef MPI_EVOLVER if ( (web.representation == SOAPFILM) || (facet_task == this_task) ) #endif if ( !valid_id(get_facet_fe(this_facet_id)) ) set_facet_fe(this_facet_id,fe); /* add to edge facet list, not in geometric order */ edge_fe = get_edge_fe(e_id); if ( valid_id(edge_fe) ) { /* insert in chain */ set_next_facet(fe,get_next_facet(edge_fe)); set_prev_facet(fe,edge_fe); set_prev_facet(get_next_facet(fe),fe); set_next_facet(edge_fe,fe); } else { set_next_facet(fe,fe); set_prev_facet(fe,fe); set_edge_fe(e_id,fe); /* link edge to rest of world */ } } if ( ((web.representation == STRING) && (edge_count < 1)) || ((web.representation == SOAPFILM) && (edge_count < 3)) ) { sprintf(errmsg,"Face %d has too few edges.\n",k); kb_error(1158,errmsg,DATAFILE_ERROR); } #ifdef MPI_EVOLVER if ( !split_loop_flag ) { set_next_edge(fe,first_fe); /* close up ring */ set_prev_edge(first_fe,fe); } #else { vertex_id hh,tt; tt = get_fe_tailv(first_fe); hh = get_fe_headv(fe); if ( equal_id(tt,hh) ) { set_next_edge(fe,first_fe); /* close up ring */ set_prev_edge(first_fe,fe); } else { if ( web.representation != STRING ) { sprintf(errmsg, "Inconsistency in face %d first and last edges.\n",k); kb_error(1159,errmsg,DATAFILE_ERROR); } } } #endif if ( (web.modeltype == LAGRANGE) && (web.representation == SOAPFILM) ) { /* read vertex list */ int vercount = 0; vertex_id *v; if ( tok != VERTICES_ ) kb_error(1160,"Need facet vertices in Lagrange model.\n",RECOVERABLE); v = get_facet_vertices(this_facet_id); while ( (tok = gettok(INTEGER_)) == INTEGER_ ) { if ( vercount++ >= numv ) kb_error(1161,"Too many vertices for facet.\n",DATAFILE_ERROR); else if ( !valid_id(vlist[yylval.i]) ) { sprintf(errmsg,"Facet %d: vertex %d is not defined.\n",k,yylval.i); kb_error(1162,errmsg,DATAFILE_ERROR); } else *(v++) = vlist[yylval.i]; } if ( vercount < numv ) kb_error(1163,"Too few vertices for facet.\n",DATAFILE_ERROR); } } if ( (web.modeltype == LAGRANGE) && (web.modeltype == SOAPFILM) ) { vertex_id *v = get_facet_vertices(this_facet_id); for ( i = 0 ; i < numv ; i++ ) if ( !(get_vattr(v[i]) & Q_MIDEDGE ) ) { set_attr(v[i],Q_MIDFACET); set_vertex_facet(v[i],this_facet_id); } for ( i = 0 ; i <= web.dimension ; i++ ) unset_attr(v[web.skel[FACET].extreme[i]],Q_MIDFACET); } #ifdef MPI_EVOLVER if ( (web.representation == STRING) && (facet_task != this_task)) { /* skip rest of line */ while ( (tok != LEAD_INTEGER_) && (tok != LEAD_INTEGER_AT_) && (tok != READ_) && (tok != BODIES_) && (tok != 0) ) { tok = yylex(); } continue; } #endif set_facet_density(this_facet_id,1.0); /* have attributes, maybe */ for ( more_attr = 1 ; more_attr ; ) switch ( tok ) { case EXTRA_ATTRIBUTE_: case ARRAY_ATTRIBUTE_: read_extra(this_facet_id,yylval.qnum); break; case ORIENTATION_: if ( (tok = gettok(INTEGER_)) != INTEGER_ ) { kb_error(2113,"ORIENTATION value missing.\n",DATAFILE_ERROR); break; } if ( yylval.i < 0 ) set_attr(this_facet_id,NEGBOUNDARY); tok = yylex(); break; case ORIGINAL_: if ( (tok = gettok(INTEGER_)) != INTEGER_ ) { kb_error(2114,"ORIGINAL value missing.\n",DATAFILE_ERROR); break; } else { set_original(this_facet_id,(yylval.i-1)|((element_id)FACET< phasemax) ) kb_error(1168,"Illegal phase value.\n",DATAFILE_ERROR); set_f_phase(this_facet_id,yylval.i); tok = yylex(); break; case COLOR_: if ( (tok = gettok(INTEGER_)) != INTEGER_ ) { kb_error(1169,"Color missing.\n",DATAFILE_ERROR); break; } set_facet_color(this_facet_id,(short)yylval.i); tok = yylex(); break; case FRONTCOLOR_: if ( (tok = gettok(INTEGER_)) != INTEGER_ ) { kb_error(1170,"Frontcolor missing.\n",DATAFILE_ERROR); break; } set_facet_frontcolor(this_facet_id,(short)yylval.i); tok = yylex(); break; case BACKCOLOR_: if ( (tok = gettok(INTEGER_)) != INTEGER_ ) { kb_error(1171,"Backcolor missing.\n",DATAFILE_ERROR); break; } set_facet_backcolor(this_facet_id,(short)yylval.i); tok = yylex(); break; case TAG_: /* optional inheritable tag */ { int one = 1; F_TAG_ATTR = add_attribute(FACET,"tag",INTEGER_TYPE,0,&one,0, NULL); if ( (tok = gettok(INTEGER_)) != INTEGER_ ) { kb_error(1172,"Tag missing.\n",DATAFILE_ERROR); break; } set_tag(this_facet_id,(tagtype)yylval.i); tok = yylex(); } break; case BOUNDARY_: case BOUNDARY_NAME_: /* see if boundary facet */ if ( tok == BOUNDARY_ ) tok = gettok(INTEGER_); if ( (tok != INTEGER_) && ( tok != BOUNDARY_NAME_ ) ) { kb_error(1173,"Need boundary number or name.\n", DATAFILE_ERROR); break; } if ( tok == INTEGER_ ) { bnum = abs(yylval.i); if ( (bnum >= web.bdrymax) || !(web.boundaries[bnum].attr & IN_USE) ) { sprintf(errmsg, "Bad boundary number %d for facet %d.\n",bnum,k); kb_error(1174,errmsg,DATAFILE_ERROR); yylex(); break; } } else { bnum = globals(yylval.i)->value.bnum; } set_attr(this_facet_id,BOUNDARY); set_facet_boundary_num(this_facet_id,bnum); tok = yylex(); break; case CONSTRAINT_: case CONSTRAINT_NAME_: if ( tok == CONSTRAINT_ ) tok = gettok(INTEGER_); while ( (tok == INTEGER_) || (tok==CONSTRAINT_NAME_) ) { if ( tok == INTEGER_ ) cnum = abs(yylval.i); else cnum = globals(yylval.i)->value.cnum; constr = get_constraint(cnum); if ( (cnum >= web.maxcon) || !(constr->attr & IN_USE) ) { sprintf(errmsg, "Bad constraint number %d for face %d.\n",cnum,k); kb_error(1175,errmsg,DATAFILE_ERROR); tok = yylex(); break; } set_attr(this_facet_id,CONSTRAINT); /* ?? */ if ( yylval.i < 0 ) set_attr(this_facet_id,NEGBOUNDARY); set_f_constraint_map(this_facet_id,cnum); if ( constr->attr & CON_ENERGY ) set_attr(this_facet_id, BDRY_ENERGY); tok = gettok(INTEGER_); } break; case ENERGY_: /* see if surface energy */ tok = yylex(); kb_error(1177,"Surface energies obsolete.\n",DATAFILE_ERROR); if ( tok == INTEGER_ ) tok = yylex(); break; case QUANTITY_NAME_: /* name of quantity */ { int qnum = yylval.i; tok = yylex(); if ( tok == '-' || tok == UMINUS_ ) { apply_quantity(inverse_id(this_facet_id),qnum); tok = yylex(); } else apply_quantity(this_facet_id,qnum); break; } case IDENT_: /* maybe method or quantity */ if ( globals(yylval.i)->flags & METHOD_NAME ) apply_method(this_facet_id,yytext); else if ( globals(yylval.i)->flags & QUANTITY_NAME ) apply_quantity(this_facet_id,yylval.i); else { sprintf(errmsg,"Illegal use of identifier '%s'.\n",yytext); kb_error(1178,errmsg,DATAFILE_ERROR); } tok = yylex(); break; case METHOD_: /* apply method instance to edge */ tok = yylex(); /* fall through */ case METHOD_NAME_: { char name[100]; strcpy(name,yytext); tok = yylex(); if ( (tok == UMINUS_) || (tok == '-') ) { apply_method(inverse_id(this_facet_id),name); tok = yylex(); } else apply_method(this_facet_id,name); break; } case QUANTITY_: tok = yylex(); if ( tok == IDENT_ ) { /* have named quantity/method pair */ char qname[32]; strncpy(qname,yytext,sizeof(qname)); tok = yylex(); if ( globals(yylval.i)->flags & QUANTITY_NAME ) { if ( tok == '-' || tok == UMINUS_ ) { apply_quantity(inverse_id(this_facet_id),yylval.i); tok = yylex(); } else apply_quantity(this_facet_id,yylval.i); } else { sprintf(errmsg,"Undefined quantity '%s'.\n",yytext); kb_error(1179,errmsg,DATAFILE_ERROR); } if ( stricmp(yytext,"method")==0 ) { tok = yylex(); kb_error(1180,"Obsolete quantity syntax.\n Methods must be listed in quantity definition.",DATAFILE_ERROR); tok = yylex(); } continue; } break; case BODIES_: case READ_: case LEAD_INTEGER_: case NO_TOKEN: case LEAD_INTEGER_AT_: more_attr = 0 ; break; /* error recovery */ default: sprintf(errmsg,"Unexpected token: %s\n",yytext); kb_error(2115,errmsg,WARNING); tok = yylex(); break; } if ((get_fattr(this_facet_id)&(BOUNDARY|CONSTRAINT)) == (BOUNDARY|CONSTRAINT)) kb_error(1182,"Cannot have constraint and boundary.",DATAFILE_ERROR); if ( k > facecount ) facecount = k; } #ifdef MPI_EVOLVER myfree((char*)vdummy); myfree((char*)edummy); #endif } /* end read_faces() */ /************************************************************************ * * Function: read_bodies() * */ void read_bodies() { int k; int f=0; facet_id f_id=NULLID; int more_attr; REAL value; /* for constant expression values */ int lagmulflag = 0; body_id b_id = NULLID; /* read in bodies */ bmaxlist = MAXLIST; blist = (body_id *)mycalloc(sizeof(body_id),bmaxlist); tok = yylex(); while ( (tok == LEAD_INTEGER_) || (tok == LEAD_INTEGER_AT_) ) /* body loop */ { REAL den,vol; int face_count = 0; #ifdef MPI_EVOLVER int facet_task,body_task; body_task = yylval.qnum; /* test task number, for MPI */ if ( tok == LEAD_INTEGER_ ) body_task = 1; /* default to task 1 */ else body_task = yylval.qnum; if ( body_task >= mpi_nprocs || body_task < 1 ) { sprintf(errmsg,"Task number %d must be between 1 and %d\n", body_task,mpi_nprocs-1); kb_error(5029,errmsg,RECOVERABLE); } #endif k = yylval.i; if ( k < 1 ) kb_error(2116,"Body number must be positive.\n",DATAFILE_ERROR); while ( k >= bmaxlist ) { int spot = bmaxlist; blist = (body_id *)kb_realloc((char *)blist,(k+MAXLIST)*sizeof(body_id)); bmaxlist = k + MAXLIST; for ( ; spot < bmaxlist ; spot ++ ) blist[spot] = NULLID; } #ifdef MPI_EVOLVER if ( !mpi_local_bodies_flag || (body_task == this_task) ) #endif { if ( valid_id(blist[k]) ) { sprintf(errmsg,"Duplicate body number %d\n",k); kb_error(1187,errmsg,DATAFILE_ERROR); } move_to_free_front(BODY,k); /* so id will be k */ blist[k] = new_body(); set_original(blist[k],(k-1)|((element_id)BODY<= mpi_nprocs ) { sprintf(errmsg,"Task number %d exceeds number of tasks running, %d\n", facet_task,mpi_nprocs-1); kb_error(5012,errmsg,RECOVERABLE); } if ( facet_task != this_task ) continue; else #endif { face_count++; if ( abs(f) >= fmaxlist ) { sprintf(errmsg,"Body %d: face %d is not defined.\n",k,f); kb_error(1188,errmsg,DATAFILE_ERROR); } f_id = f > 0 ? flist[f] : facet_inverse(flist[-f]); if ( !valid_id(f_id) ) { sprintf(errmsg,"Body %d: face %d is not defined.\n",k,f); kb_error(1189,errmsg,DATAFILE_ERROR); } set_facet_body(f_id, b_id); } } #ifndef MPI_EVOLVER if ( (web.representation != STRING) && (face_count < 1) ) { sprintf(errmsg,"Body %d has no faces.\n",k); kb_error(1190,errmsg,WARNING); } #endif #ifdef MPI_EVOLVER if ( mpi_local_bodies_flag && (body_task != this_task ) ) { /* skip until next body */ while ( (tok != LEAD_INTEGER_) && (tok != LEAD_INTEGER_AT_) && (tok != READ_) && (tok != 0) ) { tok = yylex(); } continue; } #endif more_attr = 1; while ( more_attr ) switch ( tok ) { case EXTRA_ATTRIBUTE_: case ARRAY_ATTRIBUTE_: read_extra(blist[k],yylval.qnum); break; case ORIGINAL_: if ( (tok = gettok(INTEGER_)) != INTEGER_ ) { kb_error(2117,"ORIGINAL value missing.\n",DATAFILE_ERROR); break; } set_original(blist[k],(yylval.i-1)|((element_id)BODY< phasemax) ) kb_error(1198,"Illegal phase value.\n",DATAFILE_ERROR); else set_b_phase(blist[k],yylval.i); tok = yylex(); break; case METHOD_: /* apply method instance to edge */ tok = yylex(); case METHOD_NAME_: apply_method(blist[k],yytext); tok = yylex(); break; case IDENT_: /* maybe method or quantity */ if ( globals(yylval.i)->flags & METHOD_NAME ) apply_method(blist[k],yytext); else if ( globals(yylval.i)->flags & QUANTITY_NAME ) apply_quantity(blist[k],yylval.i); else { sprintf(errmsg,"Illegal use of identifier: %s.\n",yytext); kb_error(1199,errmsg,DATAFILE_ERROR); } tok = yylex(); break; case READ_: case LEAD_INTEGER_: case NO_TOKEN: case LEAD_INTEGER_AT_: more_attr = 0 ; break; /* error recovery */ case UNPUTTED_: kb_error(3703, "Internal error: forgot to get lookahead token.\n", WARNING); tok = yylex(); break; default: sprintf(errmsg,"Unexpected token: %s\n",yytext); kb_error(2119,errmsg,WARNING); tok = yylex(); break; } /* can't have both pressure and volume */ if ((get_battr(blist[k]) & (FIXEDVOL|PRESSURE)) == (FIXEDVOL|PRESSURE)) kb_error(1203,"Body can't have fixed volume and fixed pressure.\n",DATAFILE_ERROR); } /* end body loop */ if ( web.bodycount > 0 ) web.projection_flag = 1; if ( !lagmulflag ) pressure_set_flag = 0; if ( web.pressure_flag) { if ( !web.full_flag && !valid_id(web.outside_body) ) add_outside(); } } /* end read_bodies() */ evolver-2.30c.dfsg/src/kusner.c0000644000175300017530000007507411410765113016705 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************** * * File: kusner.c * * Purpose: Does calculations needed for including square curvature * in energy. Linear model only. * Rob Kusner version - curvature energy on edges */ #include "include.h" /******************************************************************** * * Function: kusner_energy() * * Purpose: Does square curvature energy calculation for an edge. * Edge curvature version. * */ void kusner_energy() { REAL s1[MAXCOORD],s2[MAXCOORD],t2[MAXCOORD]; REAL s1s1,s1s2,s1t2,s2s2,t2t2,s2t2; REAL a1,a2; REAL det; facetedge_id fe_s1,fe_s2,fe_t2; edge_id e_id; REAL cos_th; /* cosine of angle between facets */ REAL modulus = 3*globals(square_curvature_param)->value.real; FOR_ALL_EDGES(e_id) { if ( get_attr(e_id) & FIXED ) continue; /* get edge vectors away from tail vertex */ fe_s1 = get_edge_fe(e_id); fe_s2 = get_prev_edge(get_next_facet(fe_s1)); fe_s2 = inverse_id(fe_s2); fe_t2 = get_prev_edge(fe_s1); fe_t2 = inverse_id(fe_t2); get_fe_side(fe_s1,s1); get_fe_side(fe_s2,s2); get_fe_side(fe_t2,t2); s1s1 = SDIM_dot(s1,s1); s1s2 = SDIM_dot(s1,s2); s1t2 = SDIM_dot(s1,t2); t2t2 = SDIM_dot(t2,t2); s2s2 = SDIM_dot(s2,s2); s2t2 = SDIM_dot(s2,t2); det = s1s1*t2t2 - s1t2*s1t2; a1 = sqrt(det); det = s1s1*s2s2 - s1s2*s1s2; a2 = sqrt(det); cos_th = (s1s2*s1t2 - s2t2*s1s1)/a1/a2; binary_tree_add(web.total_energy_addends, modulus*s1s1*(1 - cos_th)/(a1 + a2)); } } /************************************************************************ * * Function: kusner_force() * * Purpose: Does square curvature force calculation. * */ void kusner_force() { REAL s1[MAXCOORD],s2[MAXCOORD],t2[MAXCOORD]; REAL s1s1,s1s2,s1t2,s2s2,t2t2,s2t2; REAL a1,a2; REAL det; facetedge_id fe_s1,fe_s2,fe_t2; edge_id e_id; REAL cos_th; /* cosine of angle between facets */ REAL *of,*s1f,*s2f,*t2f; /* vertex force pointers */ REAL dcosds1[MAXCOORD],dcosds2[MAXCOORD],dcosdt2[MAXCOORD]; REAL da1ds1[MAXCOORD], da1dt2[MAXCOORD]; REAL da2ds1[MAXCOORD], da2ds2[MAXCOORD]; int i; REAL modulus = 3*globals(square_curvature_param)->value.real; FOR_ALL_EDGES(e_id) { if ( get_attr(e_id) & FIXED ) continue; /* get edge vectors away from tail vertex */ fe_s1 = get_edge_fe(e_id); fe_s2 = get_prev_edge(get_next_facet(fe_s1)); fe_s2 = inverse_id(fe_s2); fe_t2 = get_prev_edge(fe_s1); fe_t2 = inverse_id(fe_t2); get_fe_side(fe_s1,s1); get_fe_side(fe_s2,s2); get_fe_side(fe_t2,t2); of = get_force(get_edge_tailv(e_id)); s1f = get_force(get_edge_headv(e_id)); s2f = get_force(get_fe_headv(fe_s2)); t2f = get_force(get_fe_headv(fe_t2)); s1s1 = SDIM_dot(s1,s1); s1s2 = SDIM_dot(s1,s2); s1t2 = SDIM_dot(s1,t2); t2t2 = SDIM_dot(t2,t2); s2s2 = SDIM_dot(s2,s2); s2t2 = SDIM_dot(s2,t2); det = s1s1*t2t2 - s1t2*s1t2; a1 = sqrt(det); det = s1s1*s2s2 - s1s2*s1s2; a2 = sqrt(det); cos_th = (s1s2*s1t2 - s2t2*s1s1)/a1/a2; /* gradients of various terms */ for ( i = 0 ; i < SDIM ; i++ ) { da1ds1[i] = 0.5/a1*(2*s1[i]*t2t2 - 2*s1t2*t2[i]); da1dt2[i] = 0.5/a1*(2*s1s1*t2[i] - 2*s1t2*s1[i]); da2ds1[i] = 0.5/a2*(2*s1[i]*s2s2 - 2*s1s2*s2[i]); da2ds2[i] = 0.5/a2*(2*s1s1*s2[i] - 2*s1s2*s1[i]); dcosds1[i] = (s2[i]*s1t2 + s1s2*t2[i] - 2*s2t2*s1[i])/a1/a2 - cos_th/a1*da1ds1[i] - cos_th/a2*da2ds1[i]; dcosdt2[i] = (s1s2*s1[i] - s2[i]*s1s1)/a1/a2 - cos_th/a1*da1dt2[i]; dcosds2[i] = (s1[i]*s1t2 - t2[i]*s1s1)/a1/a2 - cos_th/a2*da2ds2[i]; } for ( i = 0 ; i < SDIM ; i++ ) { REAL f; /* part of force */ f = modulus*2*s1[i]*(1 - cos_th)/(a1 + a2); s1f[i] -= f; of[i] += f; f = -modulus*s1s1*dcosds1[i]/(a1 + a2); s1f[i] -= f; of[i] += f; f = -modulus*s1s1*(1 - cos_th)/(a1 + a2)/(a1 + a2)*da1ds1[i]; s1f[i] -= f; of[i] += f; f = -modulus*s1s1*(1 - cos_th)/(a1 + a2)/(a1 + a2)*da2ds1[i]; s1f[i] -= f; of[i] += f; f = -modulus*s1s1*dcosds2[i]/(a1 + a2); s2f[i] -= f; of[i] += f; f = -modulus*s1s1*(1 - cos_th)/(a1 + a2)/(a1 + a2)*da2ds2[i]; s2f[i] -= f; of[i] += f; f = -modulus*s1s1*dcosdt2[i]/(a1 + a2); t2f[i] -= f; of[i] += f; f = -modulus*s1s1*(1 - cos_th)/(a1 + a2)/(a1 + a2)*da1dt2[i]; t2f[i] -= f; of[i] += f; } } } /************************************************************************** * * function: approx_curvature() * * purpose: calculate approximate curvature according to Dziuk and Schmidt. * Their definition is that linear interpolation of approx curvature * integrated with linear interpolation of velocity gives rate of * area change. So first need regular vertex force, and then * can solve symmetric system for approximate curvatures. * Coefficient matrix has star areas times 1/6 along diagonal and difacet * areas off diagonal times 1/12 */ /* C declarations of the YSMP routines */ #include "f2c.h" int odrv_ ARGS(( integer *, integer *,integer *,REAL *, integer *,integer *, integer *,integer *, integer *, integer *)); int sdrvmd_ ARGS(( integer *, integer *,integer *, integer *,integer *,REAL *, REAL *,REAL *, integer *,integer *,REAL *,integer *, integer *, integer *, REAL *)); void sdrv_flag_check ARGS((integer , integer , integer )); void odrv_flag_check ARGS((integer , integer )); void approx_curvature() { integer *IA, *JA, *P, *IP, NSP, *ISP; doublereal *A; integer i,j, PATH, FLAG; integer count; doublereal *B; REAL *RSP; integer ESP; REAL EMAX; integer N = web.skel[VERTEX].max_ord+1; int Total_entries = web.skel[VERTEX].count + web.skel[EDGE].count; edge_id e_id; /* allocate storage for arrays to be passed to ysmp */ IA = (integer *)temp_calloc(N+1,sizeof(integer)); JA = (integer *)temp_calloc(Total_entries,sizeof(integer)); A = (REAL *)temp_calloc(Total_entries,sizeof(REAL)); P = (integer *)temp_calloc(N,sizeof(integer)); IP = (integer *)temp_calloc(N,sizeof(integer)); NSP = 8*N + 16*Total_entries; ISP = (integer *)temp_calloc(NSP,sizeof(integer)); B = (REAL *)temp_calloc(N,sizeof(REAL)); /* count entries needed for each row */ FOR_ALL_EDGES(e_id) { int tail = loc_ordinal(get_edge_tailv(e_id)); int head = loc_ordinal(get_edge_headv(e_id)); if ( tail > head ) IA[head]++; else IA[tail]++; } /* set up IA pointers */ count = 0; for ( i = 0 ; i < N ; i++ ) { int temp = IA[i] + 1; /* include diagonal element */ IA[i] = count + 1; /* FORTRAN indexing */ count += temp; JA[IA[i]-1] = i+1; /* diagonal */ } IA[N] = count + 1; /* set up JA column index list for off diagonal */ /* and fill in facet star areas */ FOR_ALL_EDGES(e_id) { int tail = loc_ordinal(get_edge_tailv(e_id)); int head = loc_ordinal(get_edge_headv(e_id)); facetedge_id fe = get_edge_fe(e_id); REAL area1 = get_facet_area(get_fe_facet(fe)); REAL area2; int base,addend; fe = get_next_facet(fe); area2 = get_facet_area(get_fe_facet(fe)); /* add to vertex stars */ A[IA[tail]-1] += (area1 + area2)/12; /* each area will be added twice */ A[IA[head]-1] += (area1 + area2)/12; /* each area will be added twice */ if ( tail > head ) { base = head; addend = tail; } else { base = tail; addend = head; } /* seek column, add if not already there */ for ( j = IA[base]-1 ; j < IA[base+1]-1 ; j++ ) if ( (JA[j] == addend+1) || (JA[j] == 0) ) { JA[j] = addend + 1; /* in case first time */ A[j] += (area1 + area2)/12; break; } if ( j == IA[base+1]-1 ) kb_error(1650,"Internal error: approx_curvature: cannot find edge in JA list.\n",RECOVERABLE); } #ifdef GDEBUG /* some debug printing */ { REAL x; printf("IA: "); for ( i = 0 ; i <= N ; i++ ) printf(" %d",IA[i]); printf("\nJA: "); for ( i = 0 ; i < count ; i++ ) printf(" %d",JA[i]); printf("\n"); for ( i = 0 ; i < N ; i++ ) { int j,k,m; for ( m = 0 ; m < i ; m++ ) printf(" "); for ( m = i, j = 0, k = IA[i]-1 ; m < N /* j < IA[i+1]-IA[i] */; m++ ) if ( (m == JA[k]-1) && (k < IA[i+1]-1) ) { printf(" %f",(DOUBLE)A[k]); k++; j++; } else printf(" %f",0.0); printf("\n"); } } #endif /* Call ODRV to perform the reordering on A */ PATH = 2; odrv_( &N, IA,JA,A, P,IP, &NSP,ISP, &PATH, &FLAG ); odrv_flag_check(FLAG,N); /* Call SDRVMD to compute the solution */ RSP = (REAL *) ISP; PATH = 5; /* SSF and SNF only */ sdrvmd_(&N,P,IP,IA,JA,A,B,B,&NSP,ISP,RSP,&ESP, &PATH,&FLAG,&EMAX); sdrv_flag_check(ESP,FLAG,N); /* each coordinate gives a right side */ for ( j = 0 ; j < SDIM ; j++ ) { vertex_id v_id; FOR_ALL_VERTICES(v_id) { int vnum = loc_ordinal(v_id); B[vnum] = get_force(v_id)[j]; } RSP = (REAL *) ISP; PATH = 3; /* SNS */ sdrvmd_(&N,P,IP,IA,JA,A,B,B,&NSP,ISP,RSP,&ESP, &PATH,&FLAG,&EMAX); sdrv_flag_check(ESP,FLAG,N); FOR_ALL_VERTICES(v_id) { int vnum = loc_ordinal(v_id); get_force(v_id)[j] = B[vnum]; } } /* free stuff */ temp_free((char *)IA); temp_free((char *)A); temp_free((char *)JA); temp_free((char *)B); temp_free((char *)ISP); temp_free((char *)IP); temp_free((char *)P); } /**************************************************************/ /* general mobility routines */ #define FORT #ifdef FORT #define A_OFF 1 #else #define A_OFF 0 #endif /* structure for storing constraint and boundary vertex basis */ struct basis { vertex_id v_id; /* which vertex */ REAL vvec[MAXCOORD][MAXCOORD]; /* basis vectors */ REAL *vec[MAXCOORD]; /* for matrix routines */ } *conbasis; int cb_count; /* how many needed */ struct basis **cb_list; /* indexed by vertex ordinal */ int *dimf; /* degrees of freedom of vertices */ static REAL *A; static int *IA,*JA,*P,*IP,NSP,*ISP; /***************************************************************** * * function: mobility_setup() * * purpose: sets up sparse arrays for mobility matrix for * approximate curvature. Subsequent calls to * mobility_mult() transform a formfield to a vectorfield. */ static REAL *RSP; static integer mobN; static int *IA_INV; void mobility_setup() { integer i,j,k, PATH, FLAG=0; integer count; integer ESP; REAL EMAX; int Total_entries; vertex_id v_id; edge_id e_id; facet_id f_id; REAL **proj; /* normal projection matrix */ REAL **q,**qq,**qqq; /* basis product matrices */ struct basis *cb,*cba,*cbb,*cbh,*cbt; int dim_a; if ( web.modeltype != LINEAR ) kb_error(2088,"approx_curv only for LINEAR model.\n",RECOVERABLE); proj = dmatrix(0,SDIM-1,0,SDIM-1); qq = dmatrix(0,SDIM-1,0,SDIM-1); qqq = dmatrix(0,SDIM-1,0,SDIM-1); /*************************/ /* set up conbasis first */ /*************************/ /* count vertices on constraints and boundaries */ cb_count = 0; FOR_ALL_VERTICES(v_id) { if ( get_vattr(v_id) & (BOUNDARY|HIT_WALL) ) cb_count++; } /* allocate space */ cb_list = (struct basis **)temp_calloc(web.skel[VERTEX].max_ord+1, sizeof(struct basis *)); dimf = (int *)temp_calloc(web.skel[VERTEX].max_ord+1,sizeof(int)); if ( cb_count ) conbasis = (struct basis *)temp_calloc(cb_count,sizeof(struct basis)); for ( j = 0 ; j < cb_count ; j++ ) for( i = 0 ; i < SDIM ; i++ ) conbasis[j].vec[i] = conbasis[j].vvec[i]; /* fill in */ cb_count = 0; mobN = 0; FOR_ALL_VERTICES(v_id) { int ord = loc_ordinal(v_id); if ( get_vattr(v_id) & FIXED ) continue; /* leave dimf as 0 */ if ( get_vattr(v_id) & BOUNDARY ) dimf[ord] = bdry_basis(v_id,conbasis[cb_count].vec); else if ( get_vattr(v_id) & HIT_WALL ) dimf[ord] = constr_basis(v_id,conbasis[cb_count].vec); else { mobN += SDIM; dimf[ord] = SDIM; continue; } conbasis[cb_count].v_id = v_id; mobN += dimf[ord]; cb_list[loc_ordinal(v_id)] = conbasis + cb_count; cb_count++; } Total_entries = (mobN*(SDIM+1))/2+SDIM*SDIM*web.skel[EDGE].count; /* allocate storage for arrays to be passed to ysmp */ IA = (integer *)temp_calloc(mobN+1,sizeof(integer)); JA = (integer *)temp_calloc(Total_entries,sizeof(integer)); A = (REAL *)temp_calloc(Total_entries,sizeof(REAL)); P = (integer *)temp_calloc(mobN,sizeof(integer)); IP = (integer *)temp_calloc(mobN,sizeof(integer)); IA_INV = (int *)temp_calloc(web.skel[VERTEX].max_ord+1,sizeof(integer)); #define EMPTY (-1) for ( i = 0 ; i < Total_entries ; i++ ) JA[i] = EMPTY; /* count entries needed for each row, row size accum in IA */ k = 0; FOR_ALL_VERTICES(v_id) /* diagonal elements */ { int ord = loc_ordinal(v_id); cb = cb_list[ord]; IA_INV[ord] = k; dim_a = dimf[ord]; for ( i = 0 ; i < dim_a ; i++ ) IA[k++] = dim_a - i; } FOR_ALL_EDGES(e_id) { int tord = loc_ordinal(get_edge_tailv(e_id)); int hord = loc_ordinal(get_edge_headv(e_id)); int tail = IA_INV[tord]; int head = IA_INV[hord]; int dim_h = dimf[hord]; int dim_t = dimf[tord]; if ( tail > head ) for ( i = 0 ; i < dim_h ; i++ ) IA[head+i] += dim_t; else for ( i = 0 ; i < dim_t ; i++ ) IA[tail+i] += dim_h; } /* set up IA pointers */ count = 0; FOR_ALL_VERTICES(v_id) { int ord = loc_ordinal(v_id); int dim = dimf[ord]; i = IA_INV[ord]; for ( k = 0 ; k < dim ; k++ ) { int temp = IA[i+k]; IA[i+k] = count + A_OFF; /* FORTRAN indexing */ for ( j = 0 ; j < dim - k ; j++ ) JA[count + j] = i+k+A_OFF+j; /* diagonal */ count += temp; } } IA[mobN] = count + A_OFF; /* set up JA column index list for off diagonal */ /* and fill in star areas */ if ( web.representation == STRING ) FOR_ALL_EDGES(e_id) { int tord = loc_ordinal(get_edge_tailv(e_id)); int hord = loc_ordinal(get_edge_headv(e_id)); int tail = IA_INV[tord]; int head = IA_INV[hord]; int base,addend; REAL length; REAL side[MAXCOORD]; int orda,ordb; length = get_edge_length(e_id); get_edge_side(e_id,side); /* figure normal projection matrix */ for ( i = 0 ; i < SDIM ; i++ ) { if ( effective_area_flag ) for ( j = i ; j < SDIM ; j++ ) { /* not pure projection, to keep pos def */ proj[i][j] = - /* 0.99* */ side[i]*side[j]/length; } else for ( j = i ; j < SDIM ; j++ ) proj[i][j] = 0.0; proj[i][i] += length; } /* add to vertex stars */ cbh = cb_list[hord]; if ( cbh ) { for ( i = 0 ; i < dimf[hord] ; i++ ) for ( j = i ; j < dimf[hord] ; j++ ) A[IA[head+i]-A_OFF+(j-i)] += quadratic_form(cbh->vec[i],proj,cbh->vec[j],SDIM)/3; } else { for ( i = 0 ; i < dimf[hord] ; i++ ) for ( j = i ; j < dimf[hord] ; j++ ) A[IA[head+i]-A_OFF+(j-i)] += proj[i][j]/3; } cbt = cb_list[tord]; if ( cbt ) { for ( i = 0 ; i < dimf[tord] ; i++ ) for ( j = i ; j < dimf[tord] ; j++ ) A[IA[tail+i]-A_OFF+(j-i)] += quadratic_form(cbh->vec[i],proj,cbh->vec[j],SDIM)/3; } else { for ( i = 0 ; i < dimf[tord] ; i++ ) for ( j = i ; j < dimf[tord] ; j++ ) A[IA[tail+i]-A_OFF+(j-i)] += proj[i][j]/3; } if ( tail > head ) { base = head; addend = tail; orda = tord; ordb = hord; } else { base = tail; addend = head; orda = hord; ordb = tord; } if ( (cbb = cb_list[ordb]) == NULL ) q = proj; else { mat_mult(cbb->vec,proj,qq,dimf[ordb],SDIM,SDIM); q = qq; } if ( (cba = cb_list[orda]) != NULL ) { mat_mul_tr(q,cba->vec,qqq,dimf[ordb],SDIM,dimf[orda]); q = qqq; } /* seek column, add if not already there */ for ( i = 0 ; i < dimf[ordb] ; i++ ) { for ( j = IA[base+i]-A_OFF ; j < IA[base+i+1]-A_OFF ; j++ ) if ( (JA[j] == addend+A_OFF) || (JA[j] == EMPTY) ) break; if ( j == IA[base+i+1]-A_OFF ) kb_error(1651,"Internal error: approx_curvature: cannot find edge in JA list.\n",RECOVERABLE); for ( k = 0 ; k < dimf[orda] ; k++ ) { JA[j+k] = addend + A_OFF + k; /* in case first time */ A[j+k] += q[i][k]/6; } } } if ( web.representation == SOAPFILM ) FOR_ALL_FACETS(f_id) { REAL area = get_facet_area(f_id); REAL s1s1,s2s2,s1s2; /* dot products */ facetedge_id fe = get_facet_fe(f_id); edge_id e_id1 = get_fe_edge(fe); edge_id e_id2 = get_fe_edge(get_next_edge(fe)); REAL side1[MAXCOORD],side2[MAXCOORD]; int m; /* calc normal projection matrix */ get_edge_side(e_id1,side1); get_edge_side(e_id2,side2); s1s1 = SDIM_dot(side1,side1); s2s2 = SDIM_dot(side2,side2); s1s2 = SDIM_dot(side1,side2); for ( i = 0 ; i < SDIM ; i++ ) { if ( effective_area_flag ) for ( j = 0 ; j < SDIM ; j++ ) proj[i][j] = -(s2s2*side1[i]*side1[j] - s1s2*side1[i]*side2[j] - s1s2*side2[i]*side1[j] + s1s1*side2[i]*side2[j])/4/area; else for ( j = 0 ; j < SDIM ; j++ ) proj[i][j] = 0.0; proj[i][i] += area; } for ( m = 0 ; m < 3 ; m++, fe = get_next_edge(fe) ) { int tord = loc_ordinal(get_fe_tailv(fe)); int hord = loc_ordinal(get_fe_headv(fe)); int tail = IA_INV[tord]; int head = IA_INV[hord]; int base,addend; int orda,ordb; /* add to vertex stars */ cb = cb_list[tord]; if ( cb ) { for ( i = 0 ; i < dimf[tord] ; i++ ) for ( j = i ; j < dimf[tord] ; j++ ) A[IA[tail+i]-A_OFF+(j-i)] += quadratic_form(cb->vec[i],proj,cb->vec[j],SDIM)/6; } else { for ( i = 0 ; i < dimf[tord] ; i++ ) for ( j = i ; j < dimf[tord] ; j++ ) A[IA[tail+i]-A_OFF+(j-i)] += proj[i][j]/6; } if ( tail > head ) { base = head; addend = tail; orda = tord; ordb = hord; } else { base = tail; addend = head; orda = hord; ordb = tord; } if ( (cbb = cb_list[ordb]) == NULL ) q = proj; else { mat_mult(cbb->vec,proj,qq,dimf[ordb],SDIM,SDIM); q = qq; } if ( (cba = cb_list[orda]) != NULL ) { mat_mul_tr(q,cba->vec,qqq,dimf[ordb],SDIM,dimf[orda]); q = qqq; } /* seek column, add if not already there */ if ( dimf[orda] > 0 ) for ( i = 0 ; i < dimf[ordb] ; i++ ) { for ( j = IA[base+i]-A_OFF ; j < IA[base+i+1]-A_OFF ; j++ ) if ( (JA[j] == addend+A_OFF) || (JA[j] == EMPTY) ) break; if ( j == IA[base+i+1]-A_OFF ) kb_error(1652,"Internal error: approx_curvature: cannot find edge in JA list.\n",RECOVERABLE); for ( k = 0 ; k < dimf[orda] ; k++ ) { JA[j+k] = addend + A_OFF + k; /* in case first time */ A[j+k] += q[i][k]/12; } } } } #ifdef GDEBUG /* some debug printing */ { REAL x; printf("IA: "); for ( i = 0 ; i <= N ; i++ ) printf(" %d",IA[i]); printf("\nJA: "); for ( i = 0 ; i < count ; i++ ) printf(" %d",JA[i]); printf("\n"); for ( i = 0 ; i < N ; i++ ) { int j,k,m; for ( m = 0 ; m < i ; m++ ) printf(" "); for ( m = i, j = 0, k = IA[i]-A_OFF ; m < N /* j < IA[i+1]-IA[i] */; m++ ) if ( (m == JA[k]-A_OFF) && (k < IA[i+1]-A_OFF) ) { printf(" %8.6g",(DOUBLE)A[k]); k++; j++; } else printf(" %8.6g",0.0); printf("\n"); } } #endif #ifdef FORT NSP = 8*mobN + 16*Total_entries; ISP = (integer *)temp_calloc(NSP,sizeof(integer)); /* Call ODRV to perform the reordering on A */ PATH = 2; odrv_( &mobN, IA,JA,A, P,IP, &NSP,ISP, &PATH, &FLAG ); odrv_flag_check(FLAG,mobN); /* Call SDRVMD to compute the factorization */ { RSP = (REAL *) ISP; PATH = 5; /* SSF and SNF only */ sdrvmd_(&mobN,P,IP,IA,JA,A,NULL,NULL,&NSP,ISP,RSP,&ESP, &PATH,&FLAG,&EMAX); sdrv_flag_check(ESP,FLAG,mobN); } #else kb_error(5423,"Internal error: Trying abandoned path in mobility_setup().\n", RECOVERABLE); #endif free_matrix(proj); free_matrix(qq); free_matrix(qqq); } /* end mobility_setup */ /********************************************************************* * * function: conbasis_mult() * * purpose: multiply vector (form) by constraint bases of vertices * */ void conbasis_mult ARGS((REAL*,REAL*)); void conbasis_tmult ARGS((REAL*,REAL*)); void conbasis_mult(X,Y) REAL *X; /* incoming, SDIM coords per vertex */ REAL *Y; /* outgoing, degrees of freedom per vertex */ { vertex_id v_id; int i=0; int j; int k = 0; /* place in X */ struct basis *cb; FOR_ALL_VERTICES(v_id) { int ord = loc_ordinal(v_id); int dim = dimf[ord]; k = ord*SDIM; i = IA_INV[ord]; if ( (cb = cb_list[ord]) != NULL ) { for ( j = 0 ; j < dim ; j++,i++ ) Y[i] = SDIM_dot(X+k,cb->vec[j]); } else /* just copy */ for ( j = 0 ; j < dim ; j++,i++ ) Y[i] = X[k+j]; } } /********************************************************************* * * function: conbasis_tmult() * * purpose: form linear combos of constraint bases of vertices * */ void conbasis_tmult(X,Y) REAL *X; /* incoming, degrees of freedom per vertex */ REAL *Y; /* outgoing, SDIM coords per vertex */ { vertex_id v_id; int i; int j,m; int k = 0; /* place in Y */ struct basis *cb; FOR_ALL_VERTICES(v_id) { int ord = loc_ordinal(v_id); int dim = dimf[ord]; k = ord*SDIM; i = IA_INV[ord]; if ( (cb = cb_list[ord]) != NULL ) { for ( m = 0 ; m < SDIM ; m++ ) { Y[k+m] = 0.0; for ( j = 0 ; j < dim ; j++ ) Y[k+m] += X[i+j]*cb->vec[j][m]; } k += SDIM; } else /* just copy */ for ( j = 0 ; j < dim ; j++,i++ ) Y[k+j] = X[i]; } } /********************************************************************* * * function: mobility_mult() * * purpose: multiply a formfield by the mobility matrix. * actually does just one component at a time. * conversion done in place. * */ void mobility_mult(B) doublereal *B; { integer PATH, FLAG=0; integer ESP; REAL EMAX; REAL *temp = (REAL *)temp_calloc(mobN,sizeof(REAL)); conbasis_mult(B,temp); #ifdef FORT RSP = (REAL *) ISP; PATH = 3; /* SNS */ sdrvmd_(&mobN,P,IP,IA,JA,A,temp,temp,&NSP,ISP,RSP,&ESP, &PATH,&FLAG,&EMAX); sdrv_flag_check(ESP,FLAG,mobN); #else kb_error(5424,"Internal error: Trying abandoned path in mobility_mult().\n", RECOVERABLE); #endif conbasis_tmult(temp,B); } /*********************************************************************** * * function: mobility_cleanup() * */ void mobility_cleanup() { /* free stuff */ free_matrix(vgef); vgef = NULL; free_matrix(vgev); vgev = NULL; temp_free((char *)IA); IA = NULL; temp_free((char *)IA_INV); IA_INV = NULL; temp_free((char *)A); A = NULL; temp_free((char *)JA); JA = NULL; temp_free((char *)IP); IP = NULL; temp_free((char *)P); P = NULL; #ifdef FORT temp_free((char *)ISP); ISP = NULL; #else temp_free((char *)F); F = NULL; temp_free((char *)IW); IW = NULL; #endif temp_free((char *)cb_list); cb_list = NULL; if ( conbasis ) { temp_free((char *)conbasis); conbasis = NULL; } temp_free((char*)dimf); dimf = NULL; } /******************************************************************** * * function: stability_test() * * purpose: find largest eigenvalue of mobility matrix, by starting * with random vector and repeatedly applying mobility * matrix. */ void stability_test() { doublereal *B; int i; REAL oldmag,newmag; mobility_setup(); B = (REAL *)temp_calloc(mobN,sizeof(REAL)); for ( i = 0 ; i < mobN ; i++ ) B[i] = drand48(); /* random vector */ oldmag = dot(B,B,mobN); for ( i = 0 ; i < 20 ; i++ ) { mobility_mult(B); newmag = dot(B,B,mobN); printf("%3d. ratio %f \n",i,(DOUBLE)sqrt(newmag/oldmag)); oldmag = newmag; } mobility_cleanup(); temp_free((char *)B); } /******************************************************************** * * Function: conf_edge_curv_energy() * * Purpose: Does square curvature energy calculation for an edge. * Conformal edge curvature version. Curvature on edge * is curvature of sphere through edge endpoints and * the two other vertices on facets on the edge. * H^2 only; not (H-H_0)^2. * */ void conf_edge_curv_energy() { REAL s1s1,s1s2,s1t2,s2s2,t2t2; REAL det; REAL a1,a2; facetedge_id fe_s1,fe_s2,fe_t2; edge_id e_id; REAL modulus = globals(square_curvature_param)->value.real; REAL **mat = dmatrix(0,SDIM,0,SDIM); REAL squares[MAXCOORD]; /* squares of edges */ REAL r_square; REAL center[MAXCOORD]; int i; FOR_ALL_EDGES(e_id) { if ( get_attr(e_id) & FIXED ) continue; /* get edge vectors away from tail vertex */ fe_s1 = get_edge_fe(e_id); fe_s2 = get_prev_edge(get_next_facet(fe_s1)); fe_s2 = inverse_id(fe_s2); fe_t2 = get_prev_edge(fe_s1); fe_t2 = inverse_id(fe_t2); get_fe_side(fe_s1,mat[0]); get_fe_side(fe_s2,mat[1]); get_fe_side(fe_t2,mat[2]); squares[0] = s1s1 = SDIM_dot(mat[0],mat[0]); s1s2 = SDIM_dot(mat[0],mat[1]); s1t2 = SDIM_dot(mat[0],mat[2]); squares[2] = t2t2 = SDIM_dot(mat[2],mat[2]); squares[1] = s2s2 = SDIM_dot(mat[1],mat[1]); det = s1s1*t2t2 - s1t2*s1t2; a1 = sqrt(det)/2; det = s1s1*s2s2 - s1s2*s1s2; a2 = sqrt(det)/2; det = det_adjoint(mat,SDIM); if ( det == 0.0 ) continue; matvec_mul(mat,squares,center,SDIM,SDIM); for ( i = 0 ; i < SDIM ; i++ ) center[i] /= 2*det; r_square = SDIM_dot(center,center); binary_tree_add(web.total_energy_addends,modulus/r_square*(a1+a2)/3); } free_matrix(mat); } /************************************************************************ * * Function: conf_edge_curv_force() * * Purpose: Does square curvature force calculation. * */ void conf_edge_curv_force() { REAL s1[MAXCOORD],s2[MAXCOORD],t2[MAXCOORD]; REAL s1s1,s1s2,s1t2,s2s2,t2t2; REAL a1,a2; REAL det; facetedge_id fe_s1,fe_s2,fe_t2; edge_id e_id; REAL *of,*s1f,*s2f,*t2f; /* vertex force pointers */ int i; REAL modulus = globals(square_curvature_param)->value.real; REAL squares[MAXCOORD]; /* squares of edges */ REAL r_square; REAL center[MAXCOORD]; REAL **mat = dmatrix(0,SDIM,0,SDIM); REAL coeffs[MAXCOORD]; FOR_ALL_EDGES(e_id) { if ( get_attr(e_id) & FIXED ) continue; /* get edge vectors away from tail vertex */ fe_s1 = get_edge_fe(e_id); fe_s2 = get_prev_edge(get_next_facet(fe_s1)); fe_s2 = inverse_id(fe_s2); fe_t2 = get_prev_edge(fe_s1); fe_t2 = inverse_id(fe_t2); get_fe_side(fe_s1,mat[0]); get_fe_side(fe_s2,mat[1]); get_fe_side(fe_t2,mat[2]); get_fe_side(fe_s1,s1); get_fe_side(fe_s2,s2); get_fe_side(fe_t2,t2); squares[0] = s1s1 = SDIM_dot(mat[0],mat[0]); s1s2 = SDIM_dot(mat[0],mat[1]); s1t2 = SDIM_dot(mat[0],mat[2]); squares[2] = t2t2 = SDIM_dot(mat[2],mat[2]); squares[1] = s2s2 = SDIM_dot(mat[1],mat[1]); of = get_force(get_edge_tailv(e_id)); s1f = get_force(get_edge_headv(e_id)); s2f = get_force(get_fe_headv(fe_s2)); t2f = get_force(get_fe_headv(fe_t2)); det = s1s1*t2t2 - s1t2*s1t2; a1 = sqrt(det)/2; det = s1s1*s2s2 - s1s2*s1s2; a2 = sqrt(det)/2; det = det_adjoint(mat,SDIM); if ( det == 0.0 ) continue; /* flat */ matvec_mul(mat,squares,center,SDIM,SDIM); for ( i = 0 ; i < SDIM ; i++ ) center[i] /= 2*det; /* to complete inverse of mat and calc of center */ r_square = SDIM_dot(center,center); /* gradients of various terms */ vec_mat_mul(center,mat,coeffs,SDIM,SDIM); for ( i = 0 ; i < SDIM ; i++ ) coeffs[i] *= 2/det; /* to complete inverse of mat */ for ( i = 0 ; i < SDIM ; i++ ) { REAL f; /* part of force */ f = -modulus*coeffs[0]*(s1[i]-center[i])*(a1+a2)/3/r_square/r_square; s1f[i] -= f; of[i] += f; f = -modulus*coeffs[1]*(s2[i]-center[i])*(a1+a2)/3/r_square/r_square; s2f[i] -= f; of[i] += f; f = -modulus*coeffs[2]*(t2[i]-center[i])*(a1+a2)/3/r_square/r_square; t2f[i] -= f; of[i] += f; f = modulus*(s2s2*s1[i] - s1s2*s2[i])/4/a1/r_square/3; f += modulus*(t2t2*s1[i] - s1t2*t2[i])/4/a2/r_square/3; s1f[i] -= f; of[i] += f; f = modulus*(s1s1*s2[i] - s1s2*s1[i])/4/a1/r_square/3; s2f[i] -= f; of[i] += f; f = modulus*(s1s1*t2[i] - s1t2*s1[i])/4/a2/r_square/3; t2f[i] -= f; of[i] += f; } } } evolver-2.30c.dfsg/src/metric.c0000644000175300017530000005661411410765113016660 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************************ * * File: metric.c * * Contents: Functions to calculate area, energy and their gradients * according to the LINEAR STRING model. * With background metric. */ #include "include.h" REAL euclidean_area; /* euclidean area for conformal metrics */ /************************************************************************ * * Calculates all forces on control points due to edge and * accumulates them at each control point. */ void edge_force_l_metric(e_id) edge_id e_id; { REAL v[MAXCOORD],len,f,fp,*tforce,*hforce; int i,j,k; vertex_id tv = get_edge_tailv(e_id); vertex_id hv = get_edge_headv(e_id); REAL density = get_edge_density(e_id); REAL g[MAXCOORD][MAXCOORD]; REAL g_partial[MAXCOORD][MAXCOORD][MAXCOORD]; REAL *xt=get_coord(tv); REAL *xh=get_coord(hv); REAL midx[MAXCOORD]; REAL gg=0.0,gg_partial[MAXCOORD]; /* force due to linear tension, metric evaluated at midpoint */ for ( i = 0 ; i < SDIM ; i++ ) { midx[i] = (xt[i] + xh[i])/2; v[i] = xh[i] - xt[i]; } if ( web.conformal_flag ) { eval_all(&web.metric[0][0],midx,SDIM,&gg,gg_partial,e_id); len = gg*SDIM_dot(v,v); } else { for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) eval_all(&web.metric[i][j],midx,SDIM,&g[i][j],g_partial[i][j],e_id); for ( len = 0.0, i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) len += v[i]*g[i][j]*v[j]; } len = sqrt(len); tforce = get_force(tv); hforce = get_force(hv); for ( k = 0 ; k < SDIM ; k++ ) { if ( web.conformal_flag ) { fp = gg_partial[k]*SDIM_dot(v,v); f = gg*v[k]; } else for ( f = fp = 0.0, i = 0 ; i < SDIM ; i++ ) { for ( j = 0 ; j < SDIM ; j++ ) fp += g_partial[i][j][k]*v[i]*v[j]/4; f += g[k][i]*v[i]; } tforce[k] += density*(f-fp)/len; hforce[k] -= density*(f+fp)/len; } set_edge_length(e_id,len); } /************************************************************************ * * Returns energy due to one edge. * */ void edge_energy_l_metric(e_id) edge_id e_id; { REAL energy; REAL midx[MAXCOORD]; int i,j; vertex_id tv = get_edge_tailv(e_id); vertex_id hv = get_edge_headv(e_id); REAL *xt=get_coord(tv); REAL *xh=get_coord(hv); REAL v[MAXCOORD]; REAL euclidean; /* energy due to linear tension, metric evaluated at midpoint */ for ( i = 0 ; i < SDIM ; i++ ) { midx[i] = (xt[i] + xh[i])/2; v[i] = xh[i] - xt[i]; } if ( web.conformal_flag ) { REAL gg = eval(&web.metric[0][0],midx,e_id,NULL); euclidean = SDIM_dot(v,v); energy = gg*euclidean; euclidean_area += sqrt(euclidean); } else for ( energy = 0.0, i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) energy += v[i]*v[j]*eval(&web.metric[i][j],midx,e_id,NULL); energy = sqrt(energy); if ( web.representation == STRING ) /* don't count triple junction as area */ binary_tree_add(web.total_area_addends,energy); energy *= get_edge_density(e_id); binary_tree_add(web.total_energy_addends,energy); } /************************************************************************ * * Function: edge_energy_q_metric() * Purpose: Finds energy due to one edge in metric model. * * Quadratic version. */ void edge_energy_q_metric(e_id) edge_id e_id; { REAL *pt[EDGE_CTRL]; REAL tang[MAXCOORD]; vertex_id v[EDGE_CTRL]; int i,j,k; REAL gpt[MAXCOORD]; REAL len; REAL euclidean; v[0] = get_edge_tailv(e_id); v[1] = get_edge_midv(e_id); v[2] = get_edge_headv(e_id); for ( i = 0 ; i < EDGE_CTRL ; i++ ) pt[i] = get_coord(v[i]); /* calculate tangents at integration points and accumulate */ for ( i = 0 ; i < EDGE_INTERP ; i++ ) { for ( j = 0 ; j < SDIM ; j ++ ) { tang[j] = 0.0; gpt[j] = 0.0; for ( k = 0 ; k < EDGE_CTRL ; k++ ) { tang[j] += sdip[k][i]*pt[k][j]; gpt[j] += gcombo[k][i]*pt[k][j]; } } if ( web.conformal_flag ) { euclidean = eval(&web.metric[0][0],gpt,e_id,NULL); len = euclidean*SDIM_dot(tang,tang); euclidean_area += gauss2wt[i]*sqrt(euclidean); } else for ( len = 0.0, k = 0 ; k < SDIM ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) len += tang[k]*tang[j]*eval(&web.metric[k][j],gpt,e_id,NULL); len = gauss2wt[i]*sqrt(len); if ( web.representation == STRING ) { /* don't count triple junction as area */ binary_tree_add(web.total_area_addends,len); /* accumulate area around each vertex to scale motion */ add_vertex_star(v[0],len); add_vertex_star(v[1],len); add_vertex_star(v[2],len); } len *= get_edge_density(e_id); binary_tree_add(web.total_energy_addends,len); } return; } /************************************************************************ * * Function: edge_force_q_metric() * * Purpose: Finds force due to one edge in metric model. * * Quadratic version. */ void edge_force_q_metric(e_id) edge_id e_id; { REAL *pt[EDGE_CTRL]; REAL tang[MAXCOORD]; vertex_id v[EDGE_CTRL]; int i,j,k,m,n; REAL gpt[MAXCOORD]; REAL g[MAXCOORD][MAXCOORD]; REAL g_partial[MAXCOORD][MAXCOORD][MAXCOORD]; REAL *force[EDGE_CTRL]; REAL f,fp,fudge; REAL density = get_edge_density(e_id); REAL len; REAL vv = 1.0; v[0] = get_edge_tailv(e_id); v[1] = get_edge_midv(e_id); v[2] = get_edge_headv(e_id); for ( i = 0 ; i < EDGE_CTRL ; i++ ) { pt[i] = get_coord(v[i]); force[i] = get_force(v[i]); } /* calculate tangents at integration points and accumulate */ for ( i = 0 ; i < EDGE_INTERP ; i++ ) { for ( j = 0 ; j < SDIM ; j ++ ) { tang[j] = 0.0; gpt[j] = 0.0; for ( k = 0 ; k < EDGE_CTRL ; k++ ) { tang[j] += sdip[k][i]*pt[k][j]; gpt[j] += gcombo[k][i]*pt[k][j]; } } if ( web.conformal_flag ) { eval_all(&web.metric[0][0],gpt,SDIM,&g[0][0],g_partial[0][0],e_id); vv = SDIM_dot(tang,tang); len = vv*g[0][0]; } else { for ( k = 0 ; k < SDIM ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) eval_all(&web.metric[k][j],gpt,SDIM,&g[k][j],g_partial[k][j],e_id); for ( len = 0.0, k = 0 ; k < SDIM ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) len += tang[k]*g[k][j]*tang[j]; } if ( len <= 0.0 ) continue; len = sqrt(len); fudge = density*gauss2wt[i]/len/2; for ( m = 0 ; m < EDGE_CTRL ; m++ ) for ( n = 0 ; n < SDIM ; n++ ) { if ( web.conformal_flag ) { fp = g_partial[0][0][n]*vv; f = g[0][0]*v[n]; } else for ( f = fp = 0.0, k = 0 ; k < SDIM ; k++ ) { for ( j = 0 ; j < SDIM ; j++ ) fp += g_partial[k][j][n]*v[k]*v[j]; f += g[n][k]*v[k]; } force[m][n] -= fudge*(2*sdip[m][i]*f + gcombo[m][i]*fp); } } return; } /********************************************************************** * * function: simplex_energy_metric() * * purpose: universal simplex area calculator. Does both linear * and quadratic models. * * return value: area of simplex. caller must multiply by facet density. * * globals used: web.dimension * SDIM * gauss_wt * gauss_pt * gpoly * gpolypartial * ctrl_num * gauss2D_num * **********************************************************************/ REAL simplex_energy_metric(v,pt) vertex_id *v; /* list of vertices */ REAL **pt; /* pointer to list of vertex coords for simplex */ /* order must agree with that used to set up gauss arrays */ { int i,j,k; REAL gpt[MAXCOORD]; REAL area = 0.0; /* total */ REAL new_area; MAT2D(tang,MAXCOORD,MAXCOORD); REAL det; /* calculate integrands at integration points and accumulate */ for ( k = 0 ; k < gauss2D_num ; k++ ) { /* get gauss pt */ vec_mat_mul(gpoly[k],pt,gpt,ctrl_num,SDIM); /* get tangents */ mat_mult(gpolypartial[k],pt,tang,web.dimension,ctrl_num, SDIM); /* evaluate metric and fill in determinant */ if ( web.conformal_flag ) { metric[0][0] = eval(&web.metric[0][0],gpt,NULLID,NULL); for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j <= i ; j++ ) det_array[i][j] = det_array[j][i] = metric[0][0]*SDIM_dot(tang[i],tang[j]); } else { for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) metric[i][j] = eval(&web.metric[i][j],gpt,NULLID,NULL); for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j <= i ; j++ ) det_array[i][j] = det_array[j][i] = quadratic_form(tang[i],metric,tang[j],SDIM); } /* evaluate determinant and add to total */ det = determinant(det_array,web.dimension); if ( det < 0.0 ) kb_error(2170,"Metric not positive definite.\n",RECOVERABLE); new_area = gauss2Dwt[k]*sqrt(det); area += new_area; if ( web.conformal_flag ) euclidean_area += new_area/pow(metric[0][0],web.dimension/2.0) /web.simplex_factorial; } area /= web.simplex_factorial; return area; } /********************************************************************** * * function: simplex_force_metric() * * purpose: Universal simplex force calculator. Does both linear * and quadratic models. Adds forces to vertices. * * globals used: web.dimension * SDIM * gauss_wt * gauss_pt * gpoly * gpolypartial * ctrl_num * gauss2D_num * **********************************************************************/ #ifdef ANSI_DEF void simplex_force_metric( vertex_id *v, /* pointer to list of vertex ID's for simplex */ /* order must agree with that used to set up gauss arrays */ REAL **pt, /* coords to used (unwrapped ) */ REAL density, /* surface tension of facet */ REAL **forces) #else void simplex_force_metric(v,pt,density,forces) vertex_id *v; /* pointer to list of vertex ID's for simplex */ /* order must agree with that used to set up gauss arrays */ REAL **pt; /* coords to used (unwrapped ) */ REAL density; /* surface tension of facet */ REAL **forces; #endif { int i,j,k,mu,nu,m,n; REAL gpt[MAXCOORD]; REAL area = 0.0; /* total */ REAL tempvec1[MAXCOORD],tempvec2[MAXCOORD]; REAL qsum[MAXCOORD]; REAL vv[MAXCOORD][MAXCOORD]; MAT2D(tang,MAXCOORD,MAXCOORD); /* calculate integrands at integration points and accumulate */ for ( k = 0 ; k < gauss2D_num ; k++ ) { /* get gauss pt */ vec_mat_mul(gpoly[k],pt,gpt,ctrl_num,SDIM); /* get tangents */ mat_mult(gpolypartial[k],pt,tang,web.dimension,ctrl_num,SDIM); /* evaluate metric and fill in determinant */ if ( web.conformal_flag ) { eval_all(&web.metric[0][0],gpt,SDIM,&metric[0][0], metric_partial[0][0],NULLID); for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j <= i ; j++ ) { vv[i][j] = vv[j][i] = SDIM_dot(tang[i],tang[j]); det_array[i][j] = det_array[j][i] = metric[0][0]*vv[i][j]; } } else { for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) eval_all(&web.metric[i][j],gpt,SDIM,&metric[i][j], metric_partial[i][j],NULLID); for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j <= i ; j++ ) det_array[i][j] = det_array[j][i] = quadratic_form(tang[i],metric,tang[j],SDIM); } area = det_adjoint(det_array,web.dimension); /* det_array now has adjoint transpose */ if ( area <= 0.0 ) { kb_error(1581,"Zero area facet.\n",WARNING); for ( m = 0 ; m < ctrl_num ; m++ ) for ( mu = 0 ; mu < SDIM ; mu++ ) forces[m][mu] = 0.0; return; } area = sqrt(area); for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j < web.dimension ; j++ ) { REAL factor = gauss2Dwt[k]*density*det_array[j][i]/2/area/web.simplex_factorial; if ( web.conformal_flag ) { for ( m = 0 ; m < SDIM ; m++ ) { tempvec1[m] = metric[0][0]*tang[i][m]; tempvec2[m] = metric[0][0]*tang[j][m]; } for ( n = 0 ; n < SDIM ; n++ ) qsum[n] = metric_partial[0][0][n]*vv[i][j]; } else { matvec_mul(metric,tang[i],tempvec1,SDIM,SDIM); matvec_mul(metric,tang[j],tempvec2,SDIM,SDIM); for ( n = 0 ; n < SDIM ; n++ ) for ( qsum[n] = 0.0,mu = 0 ; mu < SDIM ; mu++ ) for ( nu = 0 ; nu < SDIM ; nu++ ) qsum[n] += tang[i][mu]*metric_partial[mu][nu][n]*tang[j][nu]; } for ( m = 0 ; m < ctrl_num ; m++ ) for ( mu = 0 ; mu < SDIM ; mu++ ) { forces[m][mu] -= factor* (gpolypartial[k][j][m]*tempvec1[mu] + gpolypartial[k][i][m]*tempvec2[mu] + gpoly[k][m]*qsum[mu]); } } } return; } /********************************************************************* * * function: metric_form_to_vector() * * purpose: convert form to vector using metric tensor. * Does conversion in place. */ void metric_form_to_vector(x,f) REAL *x; /* coordinates */ REAL *f; /* form incoming, vector outgoing */ { int i,j; REAL temp[MAXCOORD]; REAL rr,rf; if ( klein_metric_flag ) { /* M^-1 = (I - rxr)*(1-r^2) */ rr = SDIM_dot(x,x); rf = SDIM_dot(x,f); for ( j = 0 ; j < SDIM ; j++ ) f[j] = (f[j] - x[j]*rf)*(1-rr); return; } if ( web.conformal_flag ) { REAL gg = eval(&web.metric[0][0],x,NULLID,NULL); if ( gg == 0.0 ) kb_error(1343,"Metric evaluates to zero.\n",WARNING); else for ( j = 0 ; j < SDIM ; j++ ) f[j] /= gg; return; } /* if here, have general metric */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) metric[i][j] = eval(&web.metric[i][j],x,NULLID,NULL); mat_inv(metric,SDIM); matvec_mul(metric,f,temp,SDIM,SDIM); memcpy((char*)f,(char*)temp,SDIM*sizeof(REAL)); } /*********************************************************************** Metric_facet_area Method General metric Works for edges, facets, and simplices ***********************************************************************/ REAL metric_area_all ARGS((struct qinfo*,int)); /********************************************************************** * * function: metric_area_init() * * purpose: make sure general metric is in effect. */ void metric_area_init(mode,mi) int mode; struct method_instance *mi; { if ( web.conformal_flag || !web.metric_flag ) kb_error(1583,"Cannot use metric_facet_area or metric_edge_length method without a general metric.\n", RECOVERABLE); } /********************************************************************** * * function: metric_area_hess() * * purpose: Universal simplex metric area hess. Does both linear * and quadratic models. * * globals used: web.dimension * SDIM * gauss_wt * gauss_pt * gpoly * gpolypartial * ctrl_num * gauss2D_num * **********************************************************************/ REAL metric_area_all(q_info,mode) struct qinfo *q_info; int mode; /* gradient or hessian */ { REAL density=0.0; /* surface tension of facet */ REAL **grad = q_info->grad; int i,j,k,mu,nu,m,n; REAL *gpt; REAL area = 0.0; /* total */ REAL value = 0.0; struct gauss_lag *gl = (web.modeltype == LAGRANGE) ? &gauss_lagrange[web.dimension][web.gauss2D_order] : NULL; int cpts = (web.modeltype == LAGRANGE) ? gl->lagpts : ctrl_num; int gpts = (web.modeltype == LAGRANGE) ? gl->gnumpts : gauss2D_num; REAL **gp = (web.modeltype == LAGRANGE) ? gl->gpoly : gpoly; REAL ***gpp = (web.modeltype == LAGRANGE) ? gl->gpolypart : gpolypartial; MAT4D(metric_second,MAXCOORD,MAXCOORD,MAXCOORD,MAXCOORD); REAL det; MAT2D(adjA,MAXCOORD,MAXCOORD); /* adjoint of TGT */ REAL ****adjAdA = NULL; MAT2D(dA,MAXCOORD,MAXCOORD); /* derivative of TGT */ REAL **DdetA = NULL; /* derivs of det */ MAT2D(GT,MAXCOORD,MAXCOORD); /* GT */ MAT3D(DGT,MAXCOORD,MAXCOORD,MAXCOORD); /* derivative of GT */ MAT4D(TDDGT,MAXCOORD,MAXCOORD,MAXCOORD,MAXCOORD); /* 2nd derivative of TGT */ REAL sum,tr1,tr2,dda,factor; int a,b; switch(id_type(q_info->id)) { case EDGE: if ( METH_INSTANCE(q_info->method)->flags & USE_DENSITY ) density = get_edge_density(q_info->id); else density = 1.0; break; case FACET: if ( METH_INSTANCE(q_info->method)->flags & USE_DENSITY ) density = get_facet_density(q_info->id); else density = 1.0; break; default: kb_error(1584,"Metric_area method only for edge and facet.\n", RECOVERABLE); } DdetA = dmatrix(0,cpts-1,0,MAXCOORD-1); /* derivs of det */ adjAdA = dmatrix4(cpts,cpts,MAXCOORD,MAXCOORD); /* adjoint of TGT times dA */ /* calculate integrands at integration points and accumulate */ for ( k = 0 ; k < gpts ; k++ ) { REAL **tang = q_info->sides[k]; REAL wt = (web.modeltype == LAGRANGE) ? gl->gausswt[k] : gauss2Dwt[k]; gpt = q_info->gauss_pt[k]; /* evaluate metric (using symmmetry) and fill in determinant */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j <= i ; j++ ) { switch ( mode ) { case METHOD_VALUE: metric[i][j] = eval(&web.metric[i][j],gpt,q_info->id,NULL); metric[j][i] = metric[i][j]; break; case METHOD_GRADIENT: eval_all(&web.metric[i][j],gpt,SDIM,&metric[i][j], metric_partial[i][j],q_info->id); if ( i != j ) { metric[j][i] = metric[i][j]; for ( mu = 0 ; mu < SDIM ; mu++ ) metric_partial[j][i][mu] = metric_partial[i][j][mu]; } break; case METHOD_HESSIAN: eval_second(&web.metric[i][j],gpt,SDIM,&metric[i][j], metric_partial[i][j],metric_second[i][j],q_info->id); if ( i != j ) { metric[j][i] = metric[i][j]; for ( mu = 0 ; mu < SDIM ; mu++ ) { metric_partial[j][i][mu] = metric_partial[i][j][mu]; for ( nu = 0 ; nu < SDIM ; nu++ ) metric_second[j][i][mu][nu] = metric_second[i][j][mu][nu]; } } break; } } for ( i = 0 ; i < web.dimension ; i++ ) for ( j = 0 ; j <= i ; j++ ) adjA[i][j] = adjA[j][i] = quadratic_form(tang[i],metric,tang[j],SDIM); det = det_adjoint(adjA,web.dimension); if ( det <= 0.0 ) continue; area = sqrt(det); value += wt*area; if ( mode == METHOD_VALUE ) continue; /* adjA now has adjoint transpose (but assuming symmetry) */ /* get some useful products */ for ( i = 0 ; i < SDIM ; i++ ) for ( a = 0 ; a < web.dimension ; a++ ) GT[i][a] = SDIM_dot(metric[i],tang[a]); for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( a = 0 ; a < web.dimension ; a++ ) { for ( n = 0, sum = 0.0 ; n < SDIM ; n++ ) sum += metric_partial[i][n][j]*tang[a][n]; DGT[i][j][a] = sum; } factor = wt*density/2/area/web.simplex_factorial; /* get determinant gradients with respect to control pt coords */ for ( m = 0 ; m < cpts ; m++ ) for ( i = 0 ; i < SDIM ; i++ ) { for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b <= a ; b++ ) { sum = 0.0; for ( mu = 0 ; mu < SDIM ; mu++ ) sum += tang[a][mu]*DGT[mu][i][b]; dA[a][b] = dA[b][a] = sum*gp[k][m] + gpp[k][a][m]*GT[i][b] + gpp[k][b][m]*GT[i][a]; } mat_mult(adjA,dA,adjAdA[m][i],web.dimension,web.dimension, web.dimension); /* gradient is trace */ for ( a = 0, sum = 0.0 ; a < web.dimension ; a++ ) sum += adjAdA[m][i][a][a]; DdetA[m][i] = sum; grad[m][i] += factor*sum; } if ( mode == METHOD_GRADIENT ) continue; /* now, second derivatives and Hessians */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) { sum = 0.0; for ( mu = 0 ; mu < SDIM ; mu++ ) for ( nu = 0 ; nu < SDIM ; nu++ ) sum += tang[a][mu]*metric_second[mu][nu][i][j]*tang[b][nu]; TDDGT[a][i][j][b] = sum; } factor = wt*density/4/area/det/web.simplex_factorial; for ( m = 0 ; m < cpts ; m++ ) for ( i = 0 ; i < SDIM ; i++ ) for ( n = 0 ; n <= m ; n++ ) for ( j = 0 ; j < SDIM ; j++ ) { tr1 = 0.0; tr2 = 0.0; for ( a = 0 ; a < web.dimension ; a++ ) for ( b = 0 ; b < web.dimension ; b++ ) { dda = gpp[k][a][m]*gp[k][n]*DGT[i][j][b]; dda += gpp[k][a][n]*gp[k][m]*DGT[j][i][b]; dda += gpp[k][b][n]*gp[k][m]*DGT[j][i][a]; dda += gpp[k][b][m]*gp[k][n]*DGT[i][j][a]; dda += gpp[k][a][m]*gpp[k][b][n]*metric[i][j]; dda += gpp[k][a][n]*gpp[k][b][m]*metric[j][i]; dda += gp[k][m]*gp[k][n]*TDDGT[a][i][j][b]; tr2 += adjA[a][b]*dda; tr1 += adjAdA[m][i][a][b]*adjAdA[n][j][b][a]; } q_info->hess[m][n][i][j] += factor*(DdetA[m][i]*DdetA[n][j] - 2*tr1 + 2*det*tr2); } } /* end gauss loop */ /* fill in symmetric part */ if ( mode == METHOD_HESSIAN ) for ( m = 0 ; m < cpts ; m++ ) for ( i = 0 ; i < SDIM ; i++ ) for ( n = m+1 ; n < cpts ; n++ ) for ( j = 0 ; j < SDIM ; j++ ) q_info->hess[m][n][i][j] = q_info->hess[n][m][j][i]; area = value/web.simplex_factorial; if ( quantities_only_flag ) { if ( web.representation == STRING ) set_edge_length(q_info->id,area); else set_facet_area(q_info->id,area); #ifdef SHARED_MEMORY if ( nprocs > 1 ) proc_total_area[GET_THREAD_ID] += area; else #endif binary_tree_add(web.total_area_addends,area); } free_matrix4(adjAdA); free_matrix(DdetA); return density*area; } /* end metric_area_all() */ REAL metric_area_value(q_info) struct qinfo *q_info; { return metric_area_all(q_info,METHOD_VALUE); } REAL metric_area_grad(q_info) struct qinfo *q_info; { return metric_area_all(q_info,METHOD_GRADIENT); } REAL metric_area_hess(q_info) struct qinfo *q_info; { return metric_area_all(q_info,METHOD_HESSIAN); } evolver-2.30c.dfsg/src/trirevis.c0000644000175300017530000024144411410765113017241 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************** * * File: trirevise.c * * Purpose: Contains routines for modifying surface * triangulations. * */ #include "include.h" /************************************************************************ * * function: refine() * * purpose: wrapper for local_refine() */ void refine() { #ifdef MPI_EVOLVER mpi_refine(); #else local_refine(); #endif } /**************************************************************** * * Function: local_refine() * * Purpose: Refines current triangulation by subdividing each * triangulation into four congruent triangles. */ void local_refine() /* REAL the resolution of triangulation */ { vertex_id v_id; edge_id e_id; element_id sentinel; facet_id f_id; MAT2D(vx,FACET_CTRL,MAXCOORD); REAL **interp=NULL,**oldx=NULL,**oldp=NULL; facet_id newf[FACET_EDGES]; vertex_id allv[MAXLAGRANGE+1][MAXLAGRANGE+1]; WRAPTYPE wraps[MAXLAGRANGE+1][MAXLAGRANGE+1]; WRAPTYPE w2=0,w3=0,w4=0,w5=0,w6=0; int N = web.lagrange_order; int type; /* check impediments to Lagrange model refining */ if ( web.modeltype == LAGRANGE ) { if ( web.representation == SIMPLEX) kb_error(1338,"No refining in simplex Lagrange model yet.\n",RECOVERABLE ); if ( web.representation == SOAPFILM ) { FOR_ALL_EDGES(e_id) { int attr = get_eattr(e_id); if ( (attr & NO_REFINE) && !(attr & BARE_EDGE) ) kb_error(2195, "Can't refine in Lagrange model with no_refine edge yet.\n", RECOVERABLE); } } } web.vol_flag = 0; top_timestamp = ++global_timestamp; /* clean out NEW flags */ MFOR_ALL_VERTICES(v_id) unset_attr(v_id,NEWVERTEX); MFOR_ALL_EDGES(e_id) unset_attr(e_id,NEWEDGE); MFOR_ALL_FACETS(f_id) unset_attr(f_id,NEWFACET); if ( web.modeltype == LAGRANGE ) { /* save in oldcoord attribute */ MFOR_ALL_VERTICES(v_id) { if ( get_vattr(v_id) & BOUNDARY ) memcpy((char *)(get_force(v_id)),(char *)get_param(v_id), sizeof(REAL)*web.maxparam); memcpy((char *)(get_oldcoord(v_id)),(char *)get_coord(v_id), sizeof(REAL)*SDIM); } } /* allocate room for new elements */ for ( type = VERTEX ; type <= FACETEDGE ; type++ ) extend(type,EXTEND_FOR_REFINE); if ( web.representation == SIMPLEX ) { refine_all_simplices(); if ( web.dimension > 2 ) web.maxscale *= 1 << (web.dimension-2); return; } ENTER_GRAPH_MUTEX; /* first, subdivide all edges */ e_id = NULLEDGE; while ( generate_all(EDGE,&e_id,&sentinel) ) { if ( get_eattr(e_id) & (NEWEDGE|NO_REFINE) ) continue; edge_divide(e_id); } #ifdef MPI_EVOLVER mpi_refine_edge_synch(); #endif if ( web.representation == STRING ) goto windup; /* don't want to subdivide facets */ if ( web.modeltype == LAGRANGE ) { /* interpolation matrix */ int newctrl = (2*N+1)*(N+1); int oldctrl = web.skel[FACET].ctrlpts; int p,q,i,j,k,a,b,c; oldx = dmatrix(0,oldctrl,0,SDIM); oldp = dmatrix(0,oldctrl,0,SDIM); if ( bezier_flag ) interp = bezier_refine_2d[N]; else /* regular lagrange */ { interp = dmatrix(0,newctrl,0,oldctrl); for ( p = 1 ; p < 2*N ; p++ ) for ( q = 1 ; p+q < 2*N ; q++ ) { REAL x,y,z,prod; x = p/2.; y = q/2.; z = N - x - y; for ( i = 0 ; i <= N ; i++ ) for ( j = 0 ; i+j <= N ; j++ ) { k = N - i - j; prod = 1.0; for ( a = 0 ; a < i ; a++ ) prod *= (x-a)/(REAL)(i-a); for ( b = 0 ; b < j ; b++ ) prod *= (y-b)/(REAL)(j-b); for ( c = 0 ; c < k ; c++ ) prod *= (z-c)/(REAL)(k-c); interp[p+2*N*q+(3*q-q*q)/2][i+j*N+(3*j-j*j)/2] = prod; } } } } /* now subdivide each facet */ f_id = NULLFACET; while ( generate_all(FACET,&f_id,&sentinel) ) { facetedge_id fe; facetedge_id next_fe; int i,j,k,n; REAL *x[2*FACET_CTRL],*xmid; if ( get_fattr(f_id) & NEWFACET ) continue; newf[0] = newf[1] = newf[2] = NULLFACET; if ( web.modeltype == QUADRATIC ) { /* will need old vertex coords for quadratic corrections */ int wrap,fewrap; fe = get_facet_fe(f_id); if ( inverted(get_fe_edge(fe)) ) fe = get_prev_edge(fe); for ( i = 0,wrap=0 ; i < 6 ; i++ ) { if ( get_eattr(get_fe_edge(fe)) & NO_REFINE ) { e_id = get_fe_edge(fe); if ( web.symmetry_flag ) { REAL *y = get_coord(get_fe_tailv(fe)); (*sym_wrap)(y,vx[i],wrap); x[i] = x[i+6] = vx[i]; fewrap = get_edge_wrap(e_id); if ( inverted(e_id) ) wrap = (*sym_compose)(wrap,fewrap); y = get_coord(get_fe_midv(fe)); (*sym_wrap)(y,vx[i],wrap); x[i] = x[i+6] = vx[i]; if ( !inverted(e_id) ) wrap = (*sym_compose)(wrap,fewrap); } else { x[i] = x[i+6] = get_coord(get_fe_tailv(fe)); x[i+1] = x[i+1+6] = get_coord(get_fe_midv(fe)); } i++; } else { if ( web.symmetry_flag ) { REAL *y = get_coord(get_fe_tailv(fe)); (*sym_wrap)(y,vx[i],wrap); fewrap = get_fe_wrap(fe); wrap = (*sym_compose)(wrap,fewrap); x[i] = x[i+6] = vx[i]; } else x[i] = x[i+6] = get_coord(get_fe_tailv(fe)); } fe = get_next_edge(fe); } } else if ( web.modeltype == LAGRANGE ) { if ( web.symmetry_flag ) { /* get some handy wraps */ fe = get_facet_fe(f_id); e_id = get_fe_edge(fe); if ( inverted(e_id) ) fe = get_prev_edge(fe); w4 = get_fe_wrap(fe); fe = get_next_edge(fe); w2 = (*sym_compose)(w4,get_fe_wrap(fe)); fe = get_next_edge(fe); w5 = (*sym_compose)(w2,get_fe_wrap(fe)); fe = get_next_edge(fe); w3 = (*sym_compose)(w5,get_fe_wrap(fe)); fe = get_next_edge(fe); w6 = (*sym_compose)(w3,get_fe_wrap(fe)); } } /* walk around subdivided edges, cutting off points as new facets */ fe = get_facet_fe(f_id); if ( inverted(get_fe_edge(fe)) ) fe = get_prev_edge(fe); if ( get_eattr(get_fe_edge(get_prev_edge(fe))) & NO_REFINE ) i = 1; else i = 0; for ( n = 0 ; i < FACET_EDGES ; n++ ) { vertex_id headv,midv; edge_id next_e; next_fe = get_next_edge(fe); headv = get_fe_headv(fe); if ( get_vattr(headv) & NEWVERTEX ) { cross_cut(get_prev_edge(fe),fe); if ( web.modeltype == QUADRATIC ) { /* add quadratic correction to linear interpolation */ next_e = get_next_edge(fe); midv = get_fe_midv(next_e); xmid = get_coord(midv); for ( j = 0 ; j < SDIM ; j++ ) xmid[j] -= (x[n+2][j] - 2*x[n+3][j] + x[n+4][j])/8.0; } newf[i] = get_fe_facet(fe); i++; } if ( get_eattr(get_fe_edge(fe)) & NO_REFINE ) {n++,i++;} fe = next_fe; } if ( web.modeltype == LAGRANGE ) { /* fill in allv array with all vertices needed */ int oldctrl = web.skel[FACET].ctrlpts; vertex_id vv_id, *v,*v0,*v1,*v2,*v3; int facet_bdry_flag = get_fattr(f_id) & BOUNDARY; for ( i = 0 ; i <= 2*N ; i++ ) for ( j = 0 ; i+j <= 2*N ; j++ ) allv[i][j] = NULLID; /* use old interior vertices, in same positions */ v = get_facet_vertices(f_id); for ( i = 1 ; i < N ; i++) for ( j = 1; i+j < N ; j++ ) allv[2*j][2*i] = v[i+j*N+(3*j-j*j)/2]; /* now extract from various edges */ fe = get_facet_fe(newf[0]); e_id = get_fe_edge(fe); v = get_edge_vertices(e_id); if ( inverted(e_id) ) for ( i = 0 ; i <= N ; i++ ) allv[0][N-i] = v[i]; else for ( i = 0 ; i <= N ; i++ ) allv[0][i] = v[i]; fe = get_next_edge(fe); /* crosscut edge */ e_id = get_fe_edge(fe); v = get_edge_vertices(e_id); if ( inverted(e_id) ) for ( i = 1 ; i < N ; i++ ) { vv_id = allv[N-i][i]; if ( valid_id(vv_id) ) /* use old vertex */ { unset_attr(vv_id,Q_MIDFACET); set_attr(vv_id,Q_MIDEDGE); set_vertex_edge(vv_id,e_id); free_element(v[i]); v[i] = vv_id; } else allv[N-i][i] = v[i]; } else for ( i = 1 ; i < N ; i++ ) { vv_id = allv[i][N-i]; if ( valid_id(vv_id) ) /* use old vertex */ { unset_attr(vv_id,Q_MIDFACET); set_attr(vv_id,Q_MIDEDGE); set_vertex_edge(vv_id,e_id); free_element(v[i]); v[i] = vv_id; } else allv[i][N-i] = v[i]; } fe = get_next_edge(fe); e_id = get_fe_edge(fe); v = get_edge_vertices(e_id); if ( inverted(e_id) ) for ( i = 0 ; i <= N ; i++ ) allv[i][0] = v[i]; else for ( i = 0 ; i <= N ; i++ ) allv[N-i][0] = v[i]; fe = get_facet_fe(newf[1]); e_id = get_fe_edge(fe); v = get_edge_vertices(e_id); if ( inverted(e_id) ) for ( i = 0 ; i <= N ; i++ ) allv[N-i][N+i] = v[i]; else for ( i = 0 ; i <= N ; i++ ) allv[i][2*N-i] = v[i]; fe = get_next_edge(fe); /* crosscut edge */ e_id = get_fe_edge(fe); v = get_edge_vertices(e_id); if ( inverted(e_id) ) for ( i = 1 ; i < N ; i++ ) { vv_id = allv[i][N]; if ( valid_id(vv_id) ) /* use old vertex */ { unset_attr(vv_id,Q_MIDFACET); set_attr(vv_id,Q_MIDEDGE); set_vertex_edge(vv_id,e_id); free_element(v[i]); v[i] = vv_id; } else allv[i][N] = v[i]; } else for ( i = 1 ; i < N ; i++ ) { vv_id = allv[N-i][N]; if ( valid_id(vv_id) ) /* use old vertex */ { unset_attr(vv_id,Q_MIDFACET); set_attr(vv_id,Q_MIDEDGE); set_vertex_edge(vv_id,e_id); free_element(v[i]); v[i] = vv_id; } else allv[N-i][N] = v[i]; } fe = get_next_edge(fe); e_id = get_fe_edge(fe); v = get_edge_vertices(e_id); if ( inverted(e_id) ) for ( i = 0 ; i <= N ; i++ ) allv[0][2*N-i] = v[i]; else for ( i = 0 ; i <= N ; i++ ) allv[0][N+i] = v[i]; fe = get_facet_fe(newf[2]); e_id = get_fe_edge(fe); v = get_edge_vertices(e_id); if ( inverted(e_id) ) for ( i = 0 ; i <= N ; i++ ) allv[N+i][0] = v[i]; else for ( i = 0 ; i <= N ; i++ ) allv[2*N-i][0] = v[i]; fe = get_next_edge(fe); /* crosscut edge */ e_id = get_fe_edge(fe); v = get_edge_vertices(e_id); if ( inverted(e_id) ) for ( i = 1 ; i < N ; i++ ) { vv_id = allv[N][N-i]; if ( valid_id(vv_id) ) /* use old vertex */ { unset_attr(vv_id,Q_MIDFACET); set_attr(vv_id,Q_MIDEDGE); set_vertex_edge(vv_id,e_id); free_element(v[i]); v[i] = vv_id; } else allv[N][N-i] = v[i]; } else for ( i = 1 ; i < N ; i++ ) { vv_id = allv[N][i]; if ( valid_id(vv_id) ) /* use old vertex */ { unset_attr(vv_id,Q_MIDFACET); set_attr(vv_id,Q_MIDEDGE); set_vertex_edge(vv_id,e_id); free_element(v[i]); v[i] = vv_id; } else allv[N][i] = v[i]; } fe = get_next_edge(fe); e_id = get_fe_edge(fe); v = get_edge_vertices(e_id); if ( inverted(e_id) ) for ( i = 0 ; i <= N ; i++ ) allv[2*N-i][i] = v[i]; else for ( i = 0 ; i <= N ; i++ ) allv[N+i][N-i] = v[i]; /* unwrap coordinates of old vertices */ v = get_facet_vertices(f_id); for ( i = 0 ; i < oldctrl ; i++) { REAL *vx = get_oldcoord(v[i]); for ( k = 0 ; k < SDIM ; k++ ) oldx[i][k] = vx[k]; if ( get_vattr(v[i]) & BOUNDARY ) { REAL *vp = get_param(v[i]); for ( k = 0 ; k < web.maxparam ; k++ ) oldp[i][k] = vp[k]; } } if ( w2 ) (*sym_wrap)(get_coord(v[N]),oldx[N],w2); if ( w3 ) (*sym_wrap)(get_coord(v[oldctrl-1]),oldx[oldctrl-1],w3); if ( w4 ) for ( i = 1 ; i < N ; i++ ) (*sym_wrap)(get_coord(v[i]),oldx[i],w4); if ( w5 ) for ( i = 1 ; i < N ; i++ ) { j = N-i +i*N+(3*i-i*i)/2; (*sym_wrap)(get_coord(v[j]),oldx[j],w5); } if ( w6 ) for ( i = 1 ; i < N ; i++ ) { j = i*N+(3*i-i*i)/2; (*sym_wrap)(get_coord(v[j]),oldx[j],w6); } /* now allocate new vertices and calculate coordinates as needed */ for ( j = 1 ; j < 2*N ; j++) for ( i = 1; i+j < 2*N ; i++ ) { REAL *vx,sum; if ( !valid_id(allv[j][i]) ) { allv[j][i] = new_vertex(NULL,f_id); set_attr(allv[j][i],Q_MIDFACET); } /* regular coordinates */ { vx = get_coord(allv[j][i]); for ( n = 0 ; n < SDIM ; n++ ) { for ( k = 0, sum = 0.0 ; k < web.skel[FACET].ctrlpts ; k++ ) sum += interp[i+2*N*j+(3*j-j*j)/2][k]*oldx[k][n]; vx[n] = sum; } } /* whole facet boundary parameters */ if ( facet_bdry_flag && (get_vattr(allv[j][i]) & Q_MIDFACET) ) { REAL * vp = get_param(allv[j][i]); for ( n = 0 ; n < 2 ; n++ ) { for ( k = 0, sum = 0.0 ; k < web.skel[FACET].ctrlpts ; k++ ) sum += interp[i+2*N*j+(3*j-j*j)/2][k]*oldp[k][n]; vp[n] = sum; } } } /* wrap coordinates of internal vertices */ if ( web.symmetry_flag ) { for ( i = 1 ; i < N ; i++ ) for ( j = 1 ; i+j < N ; j++ ) wraps[j][i] = 0; for ( i = N+1 ; i < 2*N ; i++ ) for ( j = 1 ; i+j < 2*N ; j++ ) wraps[j][i] = w4; for ( i = 1 ; i < N ; i++ ) for ( j = N+1 ; i+j < 2*N ; j++ ) wraps[j][i] = w6; for ( i = 1 ; i < N ; i++ ) for ( j = N-i+1 ; j < N ; j++ ) wraps[j][i] = w5; for ( i = 1 ; i < N ; i++ ) wraps[N-i][i] = w4; for ( i = 1 ; i < N ; i++ ) wraps[N][i] = w6; for ( j = 1 ; j < N ; j++ ) wraps[j][N] = w5; /* now unwrap coordinates */ for ( i = 1 ; i < 2*N ; i++ ) for ( j = 1 ; i+j < 2*N ; j++ ) { REAL newx[MAXCOORD]; REAL *vx = get_coord(allv[j][i]); (*sym_wrap)(vx,newx,sym_inverse(wraps[j][i])); for ( k = 0 ; k < SDIM ; k++ ) vx[k] = newx[k]; } } /* assign vertices to facets */ v0 = get_facet_vertices(f_id); v1 = get_facet_vertices(newf[0]); v2 = get_facet_vertices(newf[1]); v3 = get_facet_vertices(newf[2]); for ( i = 0 ; i <= N ; i++ ) for ( j = 0 ; i+j <= N ; j++ ) { int spot = i + N*j + (3*j-j*j)/2; v0[spot] = allv[N-j][N-i]; v1[spot] = allv[j][i]; v2[spot] = allv[j][N+i]; v3[spot] = allv[N+j][i]; if ( (i>0) && (j>0) && (i+j min_area ) /* skip big triangles */ continue; /* weed by eliminating shortest side. eliminate obtuse vertex, the one between the two shortest sides, if possible. Follow with equiangulation */ i = (sside[0] < sside[1]) ? 0 : 1; i = (sside[i] < sside[2]) ? i : 2; if ( sside[(i+1)%3] < sside[(i+2)%3] ) e_id = inverse_id(get_fe_edge(fe[i])); else e_id = get_fe_edge(fe[i]); if ( sside[(i+1)%3] < sside[(i+2)%3] ) { e_id = get_fe_edge(fe[(i+1)%3]); elimcount = eliminate_edge(e_id); if ( elimcount ) goto elimdone; e_id = get_fe_edge(fe[(i+2)%3]); elimcount = eliminate_edge(e_id); } else { e_id = get_fe_edge(fe[(i+2)%3]); elimcount = eliminate_edge(e_id); if ( elimcount ) goto elimdone; e_id = get_fe_edge(fe[(i+1)%3]); elimcount = eliminate_edge(e_id); } elimdone: if ( elimcount ) { free_element(e_id); if ( verbose_flag ) { sprintf(msg,"Weeded facet %s\n",ELNAME(f_id)); outstring(msg); } weedcount += elimcount; } else if ( verbose_flag ) { sprintf(msg,"Couldn't weed facet %s\n",ELNAME(f_id)); outstring(msg); } } /* end main sweep loop */ LEAVE_GRAPH_MUTEX; if ( weedcount > 0 ) top_timestamp = ++global_timestamp; return weedcount; } /*********************************************************************** * * Function: eliminate_facet() * * Purpose: Delete facet by finding shortest edge and eliminating it. * * Return: 1 if eliminated. */ int eliminate_facet(f_id) facet_id f_id; { REAL side[MAXCOORD]; /* side vector */ REAL sside[FACET_EDGES]; /* squares of side lengths */ facetedge_id fe[FACET_EDGES]; /* edges of triangle */ edge_id e_id; int i; /* side number */ int elimcount; if ( web.dimension == 1 ) return string_eliminate_facet(f_id); if ( web.representation == SIMPLEX ) return simplex_delete_facet(f_id); ENTER_GRAPH_MUTEX; /* find sides */ fe[0] = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { get_fe_side(fe[i],side); sside[i] = SDIM_dot(side,side); fe[i+1] = get_next_edge(fe[i]); } /* weed by eliminating shortest side. eliminate obtuse vertex, the one between the two shortest sides, if possible. */ i = (sside[0] < sside[1]) ? 0 : 1; i = (sside[i] < sside[2]) ? i : 2; e_id = get_fe_edge(fe[i]); elimcount = eliminate_edge(e_id); if ( elimcount ) goto elimdone; if ( sside[(i+1)%3] < sside[(i+2)%3] ) { e_id = get_fe_edge(fe[(i+1)%3]); elimcount = eliminate_edge(e_id); if ( elimcount ) goto elimdone; e_id = get_fe_edge(fe[(i+2)%3]); elimcount = eliminate_edge(e_id); } else { e_id = get_fe_edge(fe[(i+2)%3]); elimcount = eliminate_edge(e_id); if ( elimcount ) goto elimdone; e_id = get_fe_edge(fe[(i+1)%3]); elimcount = eliminate_edge(e_id); } elimdone: if ( elimcount ) { if ( verbose_flag ) { sprintf(msg,"Deleting facet %s\n",ELNAME(f_id)); outstring(msg); } free_element(e_id); } else if ( verbose_flag ) { sprintf(msg,"Couldn't delete facet %s\n",ELNAME(f_id)); outstring(msg); } LEAVE_GRAPH_MUTEX; return elimcount; } /*********************************************************************** * * Function: string_eliminate_facet() * * Purpose: Delete facet in string model by deleting all its edges. * * Return: 1 if eliminated. */ int string_eliminate_facet(f_id) facet_id f_id; { facetedge_id fe,start_fe,next_fe; edge_id e_id,final_e=NULLID; int done,not_done=0; /* side number */ int elimcount=0; int loop_flag; /* whether edges form closed loop */ /* find starting point */ fe = get_facet_fe(f_id); if ( !valid_id(fe) ) { free_element(f_id); return 1; } ENTER_GRAPH_MUTEX; loop_flag = 1; start_fe = fe; do { if ( equal_element(get_next_edge(fe),fe) ) { /* end of edge chain */ loop_flag = 0; } } while ( !equal_id(fe,start_fe) ); if ( loop_flag == 0 ) { /* find start of chain */ while ( !equal_element(get_prev_edge(start_fe),start_fe) ) start_fe = get_prev_edge(start_fe); } fe = start_fe; do { next_fe = get_next_edge(fe); e_id = get_fe_edge(fe); if ( valid_id(next_fe) ) final_e = get_fe_edge(next_fe); done = eliminate_edge(e_id); if ( done ) { elimcount++; free_element(e_id); } else not_done++; fe = next_fe; } while ( valid_element(fe) ); // clean up dangling edge maybe left by last deletion if ( valid_element(final_e) ) { done = eliminate_edge(final_e); if ( done ) { elimcount++; free_element(final_e); } } LEAVE_GRAPH_MUTEX; if ( not_done == 0 ) { if ( verbose_flag ) { sprintf(msg,"Deleted facet %s\n",ELNAME(f_id)); outstring(msg); } return 1; } else if ( verbose_flag ) { sprintf(msg,"Couldn't entirely delete facet %s\n",ELNAME(f_id)); outstring(msg); } return -elimcount; } /*********************************************************************** * * Function: area_histogram() * * Purpose: Give user a histogram of area lengths to guide areaweeding. * Reports in bins of width powers of two. */ void area_histogram() { facet_id f_id; /* facet being worked on */ int bincount[HISTO_BINS]; int n; REAL ref_area = web.total_area/1000; for ( n = 0 ; n < HISTO_BINS ; n++ ) bincount[n] = 0; /* main loop sweeping over all triangles */ FOR_ALL_FACETS(f_id) { REAL area; /* area of triangle */ area = get_facet_area(f_id); if ( area == 0.0 ) n = 0; else n = HISTO_BINS/2 + 1 + (int)floor(log(area/ref_area)*HISTO_BINSIZE); if ( n < 0 ) n = 0; if ( n >= HISTO_BINS ) n = HISTO_BINS - 1; bincount[n]++; } outstring(" area number\n"); if ( bincount[0] ) { sprintf(msg,"%f - %g %6d \n",0.0, (DOUBLE)(ref_area*exp((-HISTO_BINS/2)/HISTO_BINSIZE)), bincount[0]); outstring(msg); } for ( n = 1 ; n < HISTO_BINS ; n++ ) if ( bincount[n] ) { sprintf(msg,"%g - %g %6d\n", (DOUBLE)(ref_area*exp((n-HISTO_BINS/2-1)/HISTO_BINSIZE)), (DOUBLE)(exp((n-HISTO_BINS/2)/HISTO_BINSIZE)*ref_area),bincount[n]); outstring(msg); } } /*********************************************************** * * Function: skinny() * * Purpose: Subdivide long edges of skinny triangles. * Newly created trianges * are marked as NEWFACET, and are not tested * again in this pass. * * Input: Acutest angle cutoff for skinny triangles. * * Output: Skinny triangles have long edges subdivided. * * Return: Number of facets created. */ int skinny(min_angle) REAL min_angle; /* criterion for weeding out small triangles */ { facet_id f_id; /* facet being worked on */ facet_id sentinel; int weedcount = 0; /* number of facets created */ web.vol_flag = 0; /* first, unmark all NEWFACET attributes */ FOR_ALL_FACETS(f_id) unset_attr(f_id,NEWFACET); ENTER_GRAPH_MUTEX; /* main loop sweeping over all triangles */ f_id = NULLFACET; while ( generate_all(FACET,&f_id,&sentinel) ) { REAL side[MAXCOORD]; /* side vector */ REAL sside[FACET_EDGES]; /* squares of side lengths */ REAL angle; /* area of triangle */ facetedge_id fe[FACET_EDGES+1]; /* edges of triangle, with wrap */ int i; /* side number */ int smallside, mid, big; /* skip already modified triangles */ if ( get_fattr(f_id) & NEWFACET ) continue; /* find sides */ fe[0] = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { get_fe_side(fe[i],side); sside[i] = SDIM_dot(side,side); fe[i+1] = get_next_edge(fe[i]); } /* find shortest side */ smallside = (sside[0] < sside[1]) ? 0 : 1; smallside = (sside[smallside] < sside[2]) ? smallside : 2; /* find longest side */ big = (sside[0] > sside[1]) ? 0 : 1; big = (sside[big] > sside[2]) ? big : 2; /* find middle side */ mid = 3 - (smallside+big); angle = acos( (sside[mid]+sside[big]-sside[smallside])/2/ sqrt(sside[mid]*sside[big]) ); if ( angle > min_angle ) /* skip fat triangles */ continue; edge_refine(get_fe_edge(fe[big])); weedcount++; } /* end main sweep loop */ LEAVE_GRAPH_MUTEX; if ( weedcount > 0 ) top_timestamp = ++global_timestamp; return weedcount; } /*********************************************************************** * * Function: skinny_histogram() * * Purpose: Give user a histogram of acute angles to guide skinnyweeding. * Reports in bins of width powers of two. */ void skinny_histogram() { facet_id f_id; /* facet being worked on */ int bincount[HISTO_BINS]; int n; for ( n = 0 ; n < HISTO_BINS ; n++ ) bincount[n] = 0; /* main loop sweeping over all triangles */ FOR_ALL_FACETS(f_id) { REAL sside[FACET_EDGES]; /* squares of side lengths */ REAL angle; /* area of triangle */ facetedge_id fe[FACET_EDGES+1]; /* edges of triangle */ int i; /* side number */ int smallside,mid,big; /* find sides and area */ fe[0] = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { calc_edge(get_fe_edge(fe[i])); sside[i] = get_edge_length(get_fe_edge(fe[i])); fe[i+1] = get_next_edge(fe[i]); } /* find shortest side */ smallside = (sside[0] < sside[1]) ? 0 : 1; smallside = (sside[smallside] < sside[2]) ? smallside : 2; /* find longest side */ big = (sside[0] > sside[1]) ? 0 : 1; big = (sside[big] > sside[2]) ? big : 2; /* find middle side */ mid = 3 - (smallside+big); angle = acos((sside[mid]+sside[big]-sside[smallside])/2/ sqrt(sside[mid]*sside[big]) ); if ( angle == 0.0 ) n = 0; else n = HISTO_BINS + (int)floor(log(angle/M_PI)*HISTO_BINSIZE); if ( n < 0 ) n = 0; else if ( n >= HISTO_BINS ) n = HISTO_BINS - 1; bincount[n]++; } outstring(" angle number\n"); if ( bincount[0] ) { sprintf(msg,"%f - %g %6d \n",0.0, (DOUBLE)(M_PI*exp((-HISTO_BINS/2)/HISTO_BINSIZE)), bincount[0]); outstring(msg); } for ( n = 1 ; n < HISTO_BINS ; n++ ) if ( bincount[n] ) { sprintf(msg,"%g - %g %6d\n", (DOUBLE)(M_PI*exp((n-HISTO_BINS)/HISTO_BINSIZE)), (DOUBLE)(exp((n-HISTO_BINS+1)/HISTO_BINSIZE)*M_PI),bincount[n]); outstring(msg); } } /****************************************************************** * * Function: edgeweed() * * Purpose: eliminate all unfixed edges whose length is less * than min_length. Will not remove fixed edges. * Only removes free boundary edges with both ends * on same boundary. * * Input: none * * Output: number of facets removed */ int edgeweed(min_length) REAL min_length; /* minimum allowed edge length */ { edge_id e_id,sentinel; int weedcount = 0; if ( web.representation == SIMPLEX ) return simplex_tiny_edges(min_length); web.vol_flag = 0; /* main loop over all edges */ ENTER_GRAPH_MUTEX; e_id = NULLEDGE; while ( generate_all(EDGE,&e_id,&sentinel) ) { REAL side_len; /* actual side length */ int elimcount = 0; calc_edge(e_id); side_len = get_edge_length(e_id); if ( side_len < min_length ) elimcount = eliminate_edge(e_id); if ( elimcount ) { free_element(e_id); weedcount++ ; } } LEAVE_GRAPH_MUTEX; if ( weedcount > 0 ) top_timestamp = ++global_timestamp; return weedcount; } /*********************************************************************** * * Function: edge_histogram() * * Purpose: Give user a histogram of edge lengths to guide edgeweeding. * Reports in bins of width powers of two. */ void edge_histogram() { edge_id e_id; int bincount[HISTO_BINS]; int n; for ( n = 0 ; n < HISTO_BINS ; n++ ) bincount[n] = 0; /* main loop over all edges */ if ( overall_size == 0.0 ) resize(); FOR_ALL_EDGES(e_id) { REAL side_len; /* actual side length */ calc_edge(e_id); side_len = get_edge_length(e_id); if ( side_len <= 0.0 ) n = 0; else n = HISTO_BINS/2 + 1 + (int)floor(log(side_len/overall_size)*HISTO_BINSIZE); if ( n < 0 ) n = 0; if ( n >= HISTO_BINS ) n = HISTO_BINS - 1; bincount[n]++; } outstring(" side length number\n"); if ( bincount[0] ) { sprintf(msg,"%f - %g %6d \n",0.0, (DOUBLE)(overall_size*exp((-HISTO_BINS/2)/HISTO_BINSIZE)), bincount[0]); outstring(msg); } for ( n = 1 ; n < HISTO_BINS ; n++ ) if ( bincount[n] ) { sprintf(msg,"%g - %g %6d\n", (DOUBLE)(overall_size*exp((n-HISTO_BINS/2-1)/HISTO_BINSIZE)), (DOUBLE)(overall_size*exp((n-HISTO_BINS/2)/HISTO_BINSIZE)),bincount[n]); outstring(msg); } } /******************************************************************* * * Function: eliminate_edge() * * Purpose: delete an edge and adjacent facets (if triangles in STRING); * merges head of edge to tail, unless head fixed. * If both ends fixed, will do nothing. * Does not deallocate edge itself; caller must do that * due to use in chaining to next edge. * Favors keeping non-valence-2 edges in SOAPFILM model, * constraints and such permitting. * * Input: edge id of edge to eliminate * * Output: returns number of edges eliminated */ int eliminate_edge(short_edge) edge_id short_edge; { facetedge_id base_fe; /* along edge to be eliminated */ vertex_id headv,tailv; vertex_id elim_v; /* vertex to eliminate */ vertex_id keep_v; /* vertex to keep */ int edge_head_same,edge_tail_same; /* whether same constraints and stuff */ int head_edge_comp,tail_edge_comp,tail_head_comp; body_id b_id; facet_id f_id; facetedge_id first_fe; int retval = 0; if ( web.representation == SIMPLEX ) kb_error(1341,"Cannot eliminate edge in simplex model.\n",RECOVERABLE); if ( web.modeltype == LAGRANGE ) kb_error(1342,"No eliminate_edge() in Lagrange model yet.\n",RECOVERABLE ); if ( !valid_element(short_edge) ) return 0; if ( get_eattr(short_edge) & FIXED ) { if ( verbose_flag ) { sprintf(msg,"Not deleting edge %s since it is FIXED.\n", ELNAME(short_edge)); outstring(msg); } return 0; } web.vol_flag = 0; headv = get_edge_headv(short_edge); tailv = get_edge_tailv(short_edge); /* kludge to overcome problem with change_vertex not finding all edges linked to vertex */ if ( !valid_element(headv) ) unfree_element(headv); if ( !valid_element(tailv) ) unfree_element(tailv); /* Figure out which vertex to eliminate. Keep fixed vertices. */ head_edge_comp = compare_vertex_edge_attr(headv,short_edge); tail_edge_comp = compare_vertex_edge_attr(tailv,short_edge); tail_head_comp = compare_vertex_attr(tailv,headv); edge_head_same = ((head_edge_comp==A_SUB_B) || (head_edge_comp==A_EQ_B)) && !(get_vattr(headv) & FIXED); edge_tail_same = ((tail_edge_comp==A_SUB_B) || (tail_edge_comp==A_EQ_B)) && !(get_vattr(tailv) & FIXED); if ( (edge_head_same || ((head_edge_comp == A_EQ_B || head_edge_comp == A_SUPER_B) && (tail_head_comp == A_EQ_B || tail_head_comp == A_SUPER_B)) ) && (get_boundary(headv) == get_edge_boundary(short_edge) ) && !( get_vattr(headv) & (FIXED|AXIAL_POINT)) ) { /* short_edge ok */ } else if ( (edge_tail_same || ((tail_edge_comp == A_EQ_B || tail_edge_comp == A_SUPER_B) && (tail_head_comp == A_EQ_B || tail_head_comp == A_SUB_B)) ) && (get_boundary(tailv) == get_edge_boundary(short_edge) ) && !( get_vattr(tailv) & (FIXED|AXIAL_POINT)) ) { short_edge = edge_inverse(short_edge); } else { if ( verbose_flag ) { sprintf(msg,"Can't delete edge %s due to conflicting constraints, boundaries, or fixedness.\n",ELNAME(short_edge)); outstring(msg); } return 0; /* can't safely eliminate edge */ } /* check edges that would be merged */ if ( web.representation == SOAPFILM ) { facetedge_id fe; base_fe = get_edge_fe(short_edge); fe = base_fe; if ( valid_id(base_fe) ) for (;;) { facetedge_id fe_a = get_prev_edge(fe); facetedge_id fe_b = get_next_edge(fe); if ( valid_id(get_fe_facet(fe)) ) { int abcomp = compare_edge_attr(get_fe_edge(fe_a),get_fe_edge(fe_b)); if ( abcomp == INCOMPARABLE) { facet_id f_id = get_fe_facet(fe); int acomp = compare_edge_facet_attr(get_fe_edge(fe_a),f_id); int bcomp = compare_edge_facet_attr(get_fe_edge(fe_b),f_id); if ( !(acomp==A_SUB_B || acomp==A_EQ_B || bcomp==A_SUB_B || bcomp==A_EQ_B) ) { if ( verbose_flag ) { sprintf(msg, "Can't delete edge %s due to constraints or methods on edges %s and %s.\n",ELNAME(short_edge),ELNAME1(get_fe_edge(fe_a)),ELNAME2(get_fe_edge(fe_b))); outstring(msg); } return 0; } } } fe = get_next_facet(fe); if ( equal_id(fe,base_fe) ) break; } } elim_v = get_edge_headv(short_edge); keep_v = get_edge_tailv(short_edge); if ( get_vattr(keep_v) & AXIAL_POINT ) { sprintf(errmsg,"Not deleting edge %s due to axial point.\n", ELNAME(short_edge)); /* kb_error(2197,errmsg,WARNING); john doesn't like warning */ if ( verbose_flag ) outstring(errmsg); return 0; } /* check for multiple edges between endpoints */ if ( web.symmetry_flag && (web.representation == SOAPFILM) ) { edge_id e_id = short_edge; e_id = get_next_tail_edge(e_id); do { if ( equal_id(get_edge_headv(e_id) ,elim_v) && (get_edge_wrap(e_id) != get_edge_wrap(short_edge)) ) { if ( verbose_flag ) outstring( "Not deleting edge due to multiple edges between endpoints with different wraps.\n"); return 0; } e_id = get_next_tail_edge(e_id); } while ( !equal_id(short_edge,e_id) ); } /* Go through facets around edge, checking for stars (adjacent triangulated triangle) and unstarring if found. */ if ( web.representation == SOAPFILM ) { int bad_flag = 0; base_fe = first_fe = get_edge_fe(short_edge); while ( valid_id(base_fe) ) { if ( simple_unstar(base_fe) < 0 ) bad_flag = 1; base_fe = get_next_facet(base_fe); if ( equal_id(base_fe,first_fe) ) base_fe = NULLID; } /* end base_fe loop */ /* extra checks for bad configurations */ if ( star_finagling ) if( star_finagle(short_edge) < 0 ) bad_flag = 1; if ( bad_flag ) { if ( force_deletion ) { if ( verbose_flag ) outstring("Risky configuration, but proceeding with deletion since force_deletion is on.\n"); } else return 0; } } if ( verbose_flag ) { sprintf(msg,"Deleting edge %s\n",ELNAME(short_edge)); outstring(msg); } ENTER_GRAPH_MUTEX; /* unwrap edge */ if ( web.torus_flag ) torus_unwrap_edge(short_edge); else if ( web.symmetry_flag ) { edge_id pos_e = positive_id(short_edge); /* in case of quadratic */ int wrap = get_edge_wrap(pos_e); if ( wrap ) wrap_vertex(get_edge_headv(pos_e),wrap); } /* put keep_v at middle of old edge if possible */ if ( (web.modeltype == LINEAR) || (web.modeltype == QUADRATIC) ) { if ( edge_head_same && edge_tail_same ) { if ( web.representation == STRING ) { /* could eliminate either end, so keep high valence vertex */ facetedge_id fe = get_edge_fe(short_edge); facetedge_id ffe; edge_id e1,e2; if ( valid_id(fe) ) { ffe = get_next_facet(fe); e1 = get_fe_edge(get_prev_edge(fe)); e2 = get_fe_edge(get_prev_edge(ffe)); edge_tail_same = equal_id(e1,e2); e1 = get_fe_edge(get_next_edge(fe)); e2 = get_fe_edge(get_next_edge(ffe)); edge_head_same = equal_id(e1,e2); if ( edge_tail_same && !edge_head_same ) { short_edge = edge_inverse(short_edge); elim_v = get_edge_headv(short_edge); keep_v = get_edge_tailv(short_edge); } } } if ( edge_tail_same == edge_head_same ) { REAL *tailx = get_coord(keep_v); REAL *headx = get_coord(elim_v); /* since unwrapped */ int i; for ( i = 0 ; i < SDIM ; i++ ) tailx[i] = (tailx[i]+headx[i])/2; } } } /* change all references to the eliminated vertex to the kept vertex. */ if ( !equal_id(elim_v,keep_v) ) { edge_id e_id; int nn = 0; for (;;) { e_id = get_vertex_edge(elim_v); if ( !valid_id(e_id) ) break; remove_vertex_edge(elim_v,e_id); if ( equal_element(e_id,short_edge) ) continue; set_edge_tailv(e_id,keep_v); if ( ++nn > web.skel[EDGE].count ) { sprintf(errmsg,"Internal error: Bad edge loop on vertex %s.\n", ELNAME(elim_v)); kb_error(1344,errmsg,WARNING); break; } } } remove_vertex_edge(keep_v,short_edge); #ifdef MPI_EVOLVER mpi_note_edge_delete(short_edge,elim_v,keep_v); #endif /* Go through facets around edge, adjusting facet loops of the merged edges, deleting facetedges, and facets */ base_fe = first_fe = get_edge_fe(short_edge); while ( valid_id(base_fe) ) { facetedge_id next_base; /* to get before freeing base_fe */ facetedge_id aa,bb; facetedge_id bbase_fe; facetedge_id next_fe,prev_fe; vertex_id third_v; next_fe = get_next_edge(base_fe); prev_fe = get_prev_edge(base_fe); third_v = get_fe_headv(next_fe); if ( (web.representation == STRING) && ((!valid_id(get_fe_facet(base_fe)) || !valid_id(prev_fe) || (!equal_id(get_next_edge(next_fe), prev_fe)) && !equal_id(prev_fe,next_fe)) || ((web.modeltype == QUADRATIC) && (!equal_id(prev_fe,next_fe)))) ) { /* do not eliminate facets with more than 3 edges */ /* but might have free end */ bbase_fe = base_fe; /* do */ { next_fe = get_next_edge(bbase_fe); if ( equal_element(short_edge,get_fe_edge(next_fe)) ) next_fe = get_next_edge(next_fe); prev_fe = get_prev_edge(bbase_fe); if ( equal_element(short_edge,get_fe_edge(prev_fe)) ) prev_fe = get_prev_edge(prev_fe); set_next_edge(prev_fe,next_fe); set_prev_edge(next_fe,prev_fe); f_id = get_fe_facet(bbase_fe); if ( valid_id(f_id) && equal_id(bbase_fe,get_facet_fe(f_id)) ) { b_id = get_facet_body(f_id); if ( valid_id(next_fe) ) { set_facet_fe(f_id,next_fe); if ( valid_id(b_id) ) set_body_facet(b_id,f_id); } else { set_facet_fe(f_id,prev_fe); if ( valid_id(b_id) ) set_body_facet(b_id,f_id); } } bbase_fe = get_next_facet(bbase_fe); } /*while ( valid_id(bbase_fe) && (!equal_id(bbase_fe,base_fe)) );*/ } else { /* eliminate whole facet */ facetedge_id a=0; /* for edge to be merged */ facetedge_id b=0; /* for edge to be merged with */ facetedge_id a_next=0,a_prev=0,b_next=0,b_prev=0; /* facet chain links around edges a,b */ facetedge_id next_fea; /* next around facet loop of a */ edge_id a_edge=0; /* edge of side a */ edge_id b_edge=0; /* edge of side b */ facet_id facet=0; /* facet to be eliminated */ edge_id keep_edge=0,throw_edge=0; /* label relevant edges and */ /* see if we have an adjacent starred triangle which will give trouble if we don't unstar it */ bbase_fe = base_fe; /* for(;;) */ /* may take multiple passes for unstarring */ { int flag; int unstar_count = 0; int ktcomp; /* first, set up to favor deletion of valence 2 edge */ if ( get_edge_valence(get_fe_edge(get_prev_edge(bbase_fe))) == 2 ) bbase_fe = inverse_id(bbase_fe); /* now check conditions and set up for elimination */ for (flag=0;flag<2;flag++) { facet = get_fe_facet(bbase_fe); if ( !valid_id(facet) ) break; a = get_next_edge(bbase_fe); a_edge = get_fe_edge(a); a_next = get_next_facet(a); a_prev = get_prev_facet(a); b = get_prev_edge(bbase_fe); b_edge = get_fe_edge(b); b_next = get_next_facet(b); b_prev = get_prev_facet(b); keep_edge = b_edge; throw_edge = a_edge; ktcomp = compare_edge_attr(keep_edge,throw_edge); if ( ktcomp == A_SUB_B ) { bbase_fe = inverse_id(bbase_fe); continue; } if ( ktcomp == INCOMPARABLE ) { facet_id f_id = get_fe_facet(bbase_fe); int tcomp = compare_edge_facet_attr(throw_edge,f_id); if ( !(tcomp==A_SUB_B || tcomp==A_EQ_B) ) { bbase_fe = inverse_id(bbase_fe); continue; } } if ( equal_element(a_edge,b_edge) ) break; aa = get_next_edge(a_next); bb = get_prev_edge(b_next); if ( web.representation != SOAPFILM ) break; if ( get_vattr(get_edge_headv(a_edge)) & AXIAL_POINT ) break; if ( equal_id(get_next_facet(aa),inverse_id(bb)) ) { int ret = unstar(aa); if ( ret < 0 ) { sprintf(errmsg, "Edge %s not deleted due to adjacent configuration involving facet %s.\n", ELNAME(short_edge),ELNAME1(facet)); kb_error(1345,errmsg,WARNING); break; } unstar_count = 1; } break; } if ( unstar_count ) continue; /* retry */ if ( flag >= 2 ) { sprintf(errmsg,"Edge amenity failed second time through on edge %s\n", ELNAME(short_edge)); kb_error(2198,errmsg, RECOVERABLE); } /* break; since not doing for loop any more */ } /* put throw_edge constraints on keep_edge */ /* probably don't want to do this wholesale set_attr(keep_edge,get_eattr(throw_edge)); set_e_conmap(keep_edge,get_e_constraint_map(throw_edge)); if ( get_eattr(throw_edge) & BOUNDARY ) set_edge_boundary_num(keep_edge,get_edge_boundary_num(throw_edge)); */ if ( equal_element(a,b) ) { /* dihedron; should be only in quadratic model */ facetedge_id rfe = get_next_facet(a); do { facet_id ff_id = get_fe_facet(rfe); facetedge_id rnext = get_next_edge(rfe); facetedge_id rprev = get_prev_edge(rfe); if ( equal_element(rfe,get_facet_fe(ff_id)) ) { /* reset ff_id facetedge link */ if ( equal_id(rnext,rfe) ) { if ( equal_id(rfe,rprev) ) set_facet_fe(ff_id,NULLID); else set_facet_fe(ff_id,rprev); } else set_facet_fe(ff_id,rnext); } set_next_edge(rprev,rnext); set_prev_edge(rnext,rprev); free_element(rfe); rfe = get_next_facet(rfe); } while ( !equal_element(a,rfe) ); goto wasloop; } if ( equal_id(a_next,a) ) /* single facet on edge a */ { if ( equal_id(b_next,b) ) /* and on edge b */ set_edge_fe(keep_edge,NULLID); else /* have more around b */ { set_next_facet(b_prev,b_next); set_prev_facet(b_next,b_prev); set_edge_fe(keep_edge,b_next); } } else if ( equal_id(b_next,b) ) /* single facet on edge b */ { set_next_facet(a_prev,a_next); set_prev_facet(a_next,a_prev); set_edge_fe(keep_edge,fe_inverse(a_next)); } else if ( equal_element(a_edge,b_edge) ) { /* excise a,b from loop */ set_prev_facet(a_next,a_prev); set_next_facet(a_prev,a_next); b_next = get_next_facet(b); /* in case of change */ b_prev = get_prev_facet(b); if ( equal_id(b,b_next) ) set_edge_fe(keep_edge,NULLEDGE); /* dropped only facet */ else { /* excise b */ set_prev_facet(b_next,b_prev); set_next_facet(b_prev,b_next); set_edge_fe(keep_edge,b_next); } } else /* have to join chains */ { set_prev_facet(a_next,fe_inverse(b_next)); set_next_facet(a_prev,fe_inverse(b_prev)); set_next_facet(b_prev,fe_inverse(a_prev)); set_prev_facet(b_next,fe_inverse(a_next)); set_edge_fe(keep_edge,fe_inverse(a_next)); } /* fix edge references around deleted edge to refer to kept edge */ next_fea = get_edge_fe(keep_edge); if ( valid_id(next_fea) ) do { set_fe_edge(next_fea,keep_edge); next_fea = get_next_facet(next_fea); } while ( next_fea != get_edge_fe(keep_edge) ); /* fix phase tension */ if ( (web.representation == STRING) && phase_flag ) set_e_phase_density(keep_edge); /* fix method instances. Oppositely oriented signed methods cancel; others combined. */ { struct edge *ea_ptr = eptr(keep_edge); struct edge *eb_ptr = eptr(throw_edge); int meth_offset = get_meth_offset(EDGE); int *ameths = (int*)((char*)ea_ptr+meth_offset); int *bmeths = (int*)((char*)eb_ptr+meth_offset); int i,j; for ( i = 0 ; i < (int)eb_ptr->method_count ; i++ ) { int m; int handled = 0; int orientable; m = abs(bmeths[i]); orientable = basic_gen_methods[METH_INSTANCE(m)->gen_method].flags & ORIENTABLE_METHOD; for ( j = 0 ; j < (int)ea_ptr->method_count ; j++ ) { int n; n = abs(ameths[j]); if ( n == m ) { /* see if orientable and cancellable */ if ( orientable ) { /* drop from keep_edge */ ameths[j] = ameths[--ea_ptr->method_count]; } /* else leave alone */ handled = 1; break; } } if ( handled ) continue; /* Not found on keep_edge, so paste onto keep_edge */ if ( orientable && same_sign(keep_edge,throw_edge) ) ameths[ea_ptr->method_count++] = -bmeths[i]; else ameths[ea_ptr->method_count++] = bmeths[i]; } /* end b meth loop */ } /* end method adjust */ wasloop: #ifdef MPI_EVOLVER mpi_note_facet_delete(facet,keep_edge,throw_edge); #endif /* free structures */ if ( !equal_element(a_edge,b_edge) ) free_element(throw_edge); if ( !valid_id(get_edge_fe(keep_edge)) ) /* get rid of bare edge */ free_element(keep_edge); if ( !valid_id(get_vertex_edge(third_v)) ) /* get rid of bare vertex */ free_element(third_v); if ( !equal_element(a,b) ) free_element(a); else { /* get rid of loop edge */ free_element(get_fe_edge(a)); } free_element(b); if ( valid_id(facet) ) free_element(facet); /* also takes care of body facet links */ if ( everything_quantities_flag && (get_eattr(keep_edge) & (BOUNDARY|CONSTRAINT)) ) { /* kludge to correct for getting edge content integral back */ fixup_edge_content_meths(keep_edge); } } next_base = get_next_facet(base_fe); free_element(base_fe); base_fe = next_base; if ( equal_id(base_fe,first_fe) ) base_fe = NULLID; } /* end facets around short edge loop */ if ( !equal_id(elim_v,keep_v) ) /* see note above */ free_element(elim_v); if ( !valid_id(get_vertex_edge(keep_v)) ) /* get rid of bare vertex */ { free_element(keep_v); retval = 1; goto eliminate_edge_exit; } /* note: short_edge cannot be eliminated here since caller has to use it to continue edge generation. Caller cannot save next edge before calling, since this routine frees other edges, which might include the saved one. */ if ( web.representation == STRING && everything_quantities_flag && (get_vattr(keep_v) & (BOUNDARY|CONSTRAINT)) ) { /* kludge to correct for getting edge content integral back */ fixup_vertex_content_meths(keep_v); } if ( web.modeltype == QUADRATIC ) { /* fix up adjacent midpoints */ edge_id start_e = get_vertex_edge(keep_v); edge_id ea = start_e; int bailcount = 0; if ( valid_id(ea) ) { do { new_vertex_average(get_edge_midv(ea),VOLKEEP); ea = get_next_tail_edge(ea); if ( bailcount++ > 1000 ) break; } while ( !equal_id(ea,start_e) ); } } retval = 1; eliminate_edge_exit: LEAVE_GRAPH_MUTEX; top_timestamp = ++global_timestamp; return retval; } /* end eliminate_edge() */ /************************************************************************** * * Function: body_facet_fixup() * purpose: Fix up any body facet links to a removed facet. */ void body_facet_fixup(facet) facet_id facet; { body_id b_id; facet_id bf_id; facet_id f_id; /* fix any body references to facet */ if ( valid_id(facet) ) { b_id = get_facet_body(facet); bf_id = get_body_facet(b_id); if ( equal_element(facet,bf_id) ) { /* need to reset body facet */ set_body_facet(b_id,NULLID); /* in case none found */ FOR_ALL_FACETS(f_id) { if ( equal_element(f_id,facet) ) continue; if ( equal_id(get_facet_body(f_id),b_id) ) { set_body_facet(b_id,f_id); break; } if ( equal_id(get_facet_body(inverse_id(f_id)),b_id) ) { set_body_facet(b_id,inverse_id(f_id)); break; } } } b_id = get_facet_body(inverse_id(facet)); bf_id = get_body_facet(b_id); if ( equal_element(facet,bf_id) ) { /* need to reset body facet */ set_body_facet(b_id,NULLID); /* in case none found */ FOR_ALL_FACETS(f_id) { invert(f_id); if ( equal_element(f_id,facet) ) continue; if ( equal_id(get_facet_body(f_id),b_id) ) { set_body_facet(b_id,f_id); break; } if ( equal_id(get_facet_body(inverse_id(f_id)),b_id) ) { set_body_facet(b_id,inverse_id(f_id)); break; } } } } } /************************************************************ * * Function: change_vertex() * * Purpose: Recursively goes through facetedges with tail * at old vertex and put tail at new vertex. * To seek new edges, looks at facet loop around * edge predecessor. */ void change_vertex(fe,old_v,new_v,wrap) facetedge_id fe; /* known to have tail at old vertex */ vertex_id old_v; /* old vertex */ vertex_id new_v; /* new vertex */ WRAPTYPE wrap; /* wraps to add to edges */ { facetedge_id next_fe; /* looping around current edge */ facetedge_id pre_fe; /* going back around facet to new edge */ edge_id e_id; if ( equal_id(old_v,new_v) ) kb_error(1346,"Internal error: change_vertex: looping edge! ",WARNING); e_id = get_fe_edge(fe); set_edge_tailv(e_id,new_v); if ( web.symmetry_flag ) set_edge_wrap(e_id,(*sym_compose)(wrap,get_edge_wrap(e_id))); next_fe = fe; do { pre_fe = fe_inverse(get_prev_edge(next_fe)); if ( equal_id(get_fe_tailv(pre_fe),old_v) ) change_vertex(pre_fe,old_v,new_v,wrap); next_fe = get_next_facet(next_fe); } while ( !equal_id(next_fe,fe) ); } /******************************************************************* * * Function: articulate() * * Purpose: Subdivides all existing edges longer than a given length, and * re-triangulates accordingly. * * Input: REAL max_len - upper bound for final edges * * Output: all edges less than max_len * * Return value: number of facets created */ #ifdef ANSI_DEF int articulate( REAL max_len) /* maximum allowed edge length */ #else int articulate(max_len) REAL max_len; /* maximum allowed edge length */ #endif { edge_id e_id,sentinel; int new_edge_count = 0; web.vol_flag = 0; /* first, a little error check */ if ( max_len <= 0.0 ) kb_error(1347,"Must have positive minimum length.\n",RECOVERABLE); if ( web.representation == SIMPLEX ) return simplex_long_edges(max_len); ENTER_GRAPH_MUTEX; /* main loop over all edges */ e_id = NULLEDGE; while ( generate_all(EDGE,&e_id,&sentinel) ) { REAL side_len; /* actual side length */ calc_edge(e_id); side_len = get_edge_length(e_id); if ( side_len > max_len ) { edge_refine(e_id); new_edge_count++; } } LEAVE_GRAPH_MUTEX; if ( new_edge_count > 0 ) top_timestamp = ++global_timestamp; return new_edge_count; } /************************************************************* * * Function: edge_refine() * * Purpose: subdivide an edge of a triangulation, and also * subdivide adjacent facets. * * Input: edge id of edge to refine * * Output: ID of new edge half */ edge_id edge_refine(e_id) edge_id e_id; { facetedge_id fe; /* for facet being subdvided */ edge_id new_e; if ( !valid_element(e_id) ) return NULLID; if ( web.modeltype == LAGRANGE ) kb_error(2199,"Cannot refine an edge in Lagrange model. Suggest reverting to quadratic.\n",RECOVERABLE); ENTER_GRAPH_MUTEX; new_e = edge_divide(e_id); /* e_id now tail half of old edge */ if ( web.representation == SOAPFILM ) { facetedge_id first_fe; /* now go around facet loop of edge dividing facets */ fe = first_fe = get_edge_fe(e_id); if ( valid_id(fe) ) do { if ( get_vattr(get_edge_tailv(e_id)) & AXIAL_POINT ) { facetedge_id ffe = get_next_edge(fe); facet_id f_id = get_fe_facet(fe); cross_cut(ffe,get_next_edge(ffe)); if ( inverted(f_id) ) set_facet_fe(inverse_id(f_id),inverse_id(get_prev_edge(fe))); else set_facet_fe(f_id,fe); } else cross_cut(get_prev_edge(fe),fe); fe = get_next_facet(fe); } while ( !equal_id(fe,first_fe) ); } LEAVE_GRAPH_MUTEX; top_timestamp = ++global_timestamp; return new_e; } /*********************************************************************** * * function: equiangulate_edge() * * purpose: Test given edge for equiangulation, and swap it if it passes. * * return: 1 if edge swapped, 0 if not. */ int did_global_edge_calc = 0; /* efficiency measure */ int equiangulate_edge(e_id) edge_id e_id; { facetedge_id fe_a; /* for edge under test */ facetedge_id fe_ai; /* other facetedge of e_id */ REAL a; /* length of e_id */ facetedge_id fe_b,fe_c; /* other sides of one triangle */ REAL b,c; /* lengths of other sides of one triangle */ facetedge_id fe_d,fe_e; /* other sides of other triangle */ REAL d,e; /* lengths of other sides of other triangle */ facet_id f1,f2; if ( get_eattr(e_id) & FIXED ) return 0; /* test to be sure edge has exactly two adjacent facets */ fe_a = get_edge_fe(e_id); if ( !valid_id(fe_a) ) return 0; /* might be bare edge */ fe_ai = get_next_facet(fe_a); if ( equal_id(fe_a,fe_ai) ) return 0; if ( !equal_id(fe_ai,get_prev_facet(fe_a)) ) return 0; f1 = get_fe_facet(fe_a); f2 = get_fe_facet(get_next_facet(fe_a)); /* test for equal density */ if ( (get_fattr(f1)&DENSITY) || (get_fattr(f2)&DENSITY) ) if ( fabs(get_facet_density(f1) - get_facet_density(f2)) > 1e-10 ) return 0; /* test for equal constraints */ if ( !equal_constr(e_id,f1) || !equal_constr(e_id,f2) ) return 0; /* test for equal boundary */ if ( ( get_edge_boundary(e_id) != get_facet_boundary(f1)) ||( get_edge_boundary(e_id) != get_facet_boundary(f2)) ) return 0; /* test equiangularity */ if ( !did_global_edge_calc ) calc_edge(e_id); a = get_edge_length(e_id); fe_b = get_next_edge(fe_a); if ( !did_global_edge_calc ) calc_edge(get_fe_edge(fe_b)); b = get_edge_length(get_fe_edge(fe_b)); fe_c = get_prev_edge(fe_a); if ( !did_global_edge_calc ) calc_edge(get_fe_edge(fe_c)); c = get_edge_length(get_fe_edge(fe_c)); if ( b*c == 0.0 ) return 0; fe_ai = fe_inverse(get_next_facet(fe_a)); fe_d = get_next_edge(fe_ai); if ( !did_global_edge_calc ) calc_edge(get_fe_edge(fe_d)); d = get_edge_length(get_fe_edge(fe_d)); fe_e = get_prev_edge(fe_ai); if ( !did_global_edge_calc ) calc_edge(get_fe_edge(fe_e)); e = get_edge_length(get_fe_edge(fe_e)); if ( e*d == 0.0 ) return 0; if ( (b*b + c*c - a*a)/b/c + (d*d + e*e - a*a)/d/e > -0.001 ) return 0; /* -0.01 prevents cycling */ /* test acuteness ??? (fix 0.0001 to make scale invariant)*/ /* if ( a*a + d*d <= e*e + 0.0001 ) return 0; if ( a*a + e*e <= d*d + 0.0001 ) return 0; if ( a*a + b*b <= c*c + 0.0001 ) return 0; if ( a*a + c*c <= b*b + 0.0001 ) return 0; if ( a*a + d*d <= e*e + 0.0001 ) return 0; */ /* may want to switch, but test that opposite vertices are different */ if ( equal_id(get_fe_tailv(fe_c),get_fe_headv(fe_d)) ) return 0; if ( eartest(get_fe_tailv(fe_c),get_fe_headv(fe_d)) ) return 0; /* if we are here, we want to switch diagonals */ return do_edgeswap(e_id); } /* end equiangulate_edge() */ /************************************************************* * * Function: equiangulate() * * Purpose: Switch diagonals of quadrilaterals around so * that the triangulation becomes more equiangular. * The criterion used is that for a Delauney triangulation: * the sum of the angles opposite a common base of * two triangles should be less than pi. Only * quadrilaterals within a face are examined, and * fixed edges are not affected. Angles next to * diagonal must be acute. * * Input: Triangulation. * * Output: One pass made through all edges, and diagonals * switched if appropriate. * * Return value: Number of edges switched. */ int equiangulate() { int switchcount = 0; edge_id e_id; /* edge being examined */ edge_id sentinel; if ( web.modeltype == LAGRANGE ) kb_error(1348,"Cannot equiangulate LAGRANGE model.\n",RECOVERABLE); if ( web.dimension == 1 ) kb_error(2200,"Cannot equiangulate string model.\n",RECOVERABLE); if ( web.representation == SIMPLEX ) { return simplex_equiangulate(); } web.vol_flag = 0; MFOR_ALL_EDGES(e_id) calc_edge(e_id); did_global_edge_calc = 1; /* main loop through edges */ ENTER_GRAPH_MUTEX; e_id = NULLEDGE; while ( generate_all(EDGE,&e_id,&sentinel) ) switchcount += equiangulate_edge(e_id); LEAVE_GRAPH_MUTEX; did_global_edge_calc = 0; return switchcount; } /************************************************************************* * * function: eartest() * * purpose: see if edge swapping would create an ear. Counts * edges already connecting proposed points. * * Input: The two vertex id's * * Output: 1 if ear would be created. 0 else. */ int eartest(v1,v2) vertex_id v1,v2; { edge_id e_id,start_id; e_id = start_id = get_vertex_edge(v1); do { if ( equal_id(v2,get_edge_headv(e_id)) ) return 1; e_id = get_next_tail_edge(e_id); } while ( e_id != start_id ); return 0; } /************************************************************* * * Function: edgeswap() * * Purpose: Switch diagonal of a quadrilateral as in equiangulation, * but without the test of improving equiangularity. * * Input: Edge to try to swap. * * Output: Number of edges swapped. * */ int edgeswap(e_id) edge_id e_id; { facetedge_id fe_a; /* for edge under test */ facetedge_id fe_ai; /* other facetedge of e_id */ facetedge_id fe_c; /* other side of one triangle */ facetedge_id fe_d; /* other side of other triangle */ facet_id f1,f2; web.vol_flag = 0; if ( get_eattr(e_id) & FIXED ) { if ( verbose_flag ) { sprintf(msg,"Not swapping fixed edge %s\n",ELNAME(e_id)); outstring(msg); } return 0; } /* test to be sure edge has exactly two adjacent facets */ fe_a = get_edge_fe(e_id); if ( !valid_id(fe_a) ) /* might be bare edge */ { if ( verbose_flag ) { sprintf(msg,"Not swapping bare edge %s\n",ELNAME(e_id)); outstring(msg); } return 0; } fe_ai = get_next_facet(fe_a); if ( equal_id(fe_a,fe_ai) ) { if ( verbose_flag ) { sprintf(msg,"Not swapping single-facet edge %s\n",ELNAME(e_id)); outstring(msg); } return 0; } if ( !equal_id(fe_ai,get_prev_facet(fe_a)) ) { if ( verbose_flag ) { sprintf(msg,"Not swapping edge %s; more than two facets.\n",ELNAME(e_id)); outstring(msg); } return 0; } f1 = get_fe_facet(fe_a); f2 = get_fe_facet(get_next_facet(fe_a)); /* test for equal density */ if ( (get_fattr(f1)&DENSITY) || (get_fattr(f2)&DENSITY) ) if ( fabs(get_facet_density(f1) - get_facet_density(f2)) > 1e-10 ) { if ( verbose_flag ) { sprintf(msg,"Not swapping edge %s. Unequal facet densities.\n", ELNAME(e_id)); outstring(msg); } return 0; } /* test for equal no_refine and fixedness */ if ( (get_fattr(f1)&(FIXED)) != (get_fattr(f2)&(FIXED)) ) { if ( verbose_flag ) { sprintf(msg,"Not swapping edge %s, facets have unequal FIXED.\n", ELNAME(e_id)); outstring(msg); } return 0; } if ( (get_fattr(f1)&(NO_REFINE)) != (get_fattr(f2)&(NO_REFINE)) ) { if ( verbose_flag ) { sprintf(msg,"Not swapping edge %s, facets have unequal NO_REFINE.\n", ELNAME(e_id)); outstring(msg); } return 0; } /* test for equal constraints */ if ( !equal_constr(e_id,f1) || !equal_constr(e_id,f2) ) { if ( verbose_flag) { sprintf(msg,"Not swapping edge %s. Unequal edge and facet constraints.\n", ELNAME(e_id)); outstring(msg); } return 0; } /* test for equal boundary */ if ( ( get_edge_boundary(e_id) != get_facet_boundary(f1)) ||( get_edge_boundary(e_id) != get_facet_boundary(f2)) ) { if ( verbose_flag ) { sprintf(msg,"Not swapping edge %s. Unequal facet and edge boundaries.\n", ELNAME(e_id)); outstring(msg); } return 0; } fe_c = get_prev_edge(fe_a); fe_ai = fe_inverse(get_next_facet(fe_a)); fe_d = get_next_edge(fe_ai); /* may want to switch, but test that opposite vertices are different */ if ( equal_id(get_fe_tailv(fe_c),get_fe_headv(fe_d)) ) { if ( verbose_flag ) { sprintf(msg,"Not swapping edge %s. Would be a loop on vertex %s.\n", ELNAME(e_id),ELNAME1(get_fe_tailv(fe_c))); outstring(msg); } return 0; } if ( eartest(get_fe_tailv(fe_c),get_fe_headv(fe_d)) ) { if (verbose_flag ) { sprintf(msg,"Not swapping edge %s. Would create two facets with same vertices.\n", ELNAME(e_id)); outstring(msg); } return 0; } return do_edgeswap(e_id); } /*************************************************************************** * * Function: test_axial_points() * * Purpose: make sure axial point at start of facet. */ void test_axial_points(f_id) facet_id f_id; { facetedge_id fe; int i; if ( inverted(f_id) ) f_id = inverse_id(f_id); fe = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { vertex_id v_id; v_id = get_fe_tailv(fe); if ( get_vattr(v_id) & AXIAL_POINT ) { set_facet_fe(f_id,fe); return; } fe = get_next_edge(fe); } } /*************************************************************************** * * function do_edgeswap() * * purpose: common actual edgeswapper for equiangulation and edgeswap. * * return: 1 if edge swapped, 0 if not. */ int do_edgeswap(e_id) edge_id e_id; { vertex_id v_id; facetedge_id fe_a; /* for edge under test */ facetedge_id fe_ai; /* other facetedge of e_id */ facetedge_id fe_b,fe_c; /* other sides of one triangle */ facetedge_id fe_d,fe_e; /* other sides of other triangle */ fe_a = get_edge_fe(e_id); fe_b = get_next_edge(fe_a); fe_c = get_prev_edge(fe_a); fe_ai = fe_inverse(get_next_facet(fe_a)); fe_d = get_next_edge(fe_ai); fe_e = get_prev_edge(fe_ai); /* special check so axial point does not become head of swapped edge */ if ( get_vattr(get_fe_tailv(fe_c)) & AXIAL_POINT ) { int retval; if ( get_vattr(get_fe_headv(fe_d)) & AXIAL_POINT ) { if ( verbose_flag ) { sprintf(msg,"Not swapping edge %s. Would create edge with both ends axial points.\n", ELNAME(e_id)); outstring(msg); } return 0; } set_edge_fe(e_id,inverse_id(fe_ai)); retval = edgeswap(e_id); return retval; } /* if we are here, we want to switch diagonals */ if ( verbose_flag ) { sprintf(msg,"Swapping edge %s\n",ELNAME(e_id)); outstring(msg); } remove_vertex_edge(get_edge_tailv(e_id),e_id); remove_vertex_edge(get_edge_headv(e_id),inverse_id(e_id)); set_facet_fe(get_fe_facet(fe_a),fe_a); set_facet_fe(get_fe_facet(fe_ai),fe_ai); set_fe_facet(fe_b,get_fe_facet(fe_ai)); set_fe_facet(fe_d,get_fe_facet(fe_a)); v_id = get_fe_tailv(fe_c); set_edge_headv(e_id,v_id); v_id = get_fe_headv(fe_d); set_edge_tailv(e_id,v_id); set_next_edge(fe_a,fe_c); set_prev_edge(fe_a,fe_d); set_next_edge(fe_ai,fe_e); set_prev_edge(fe_ai,fe_b); set_next_edge(fe_b,fe_ai); set_prev_edge(fe_b,fe_e); set_next_edge(fe_c,fe_d); set_prev_edge(fe_c,fe_a); set_next_edge(fe_d,fe_a); set_prev_edge(fe_d,fe_c); set_next_edge(fe_e,fe_b); set_prev_edge(fe_e,fe_ai); calc_edge(e_id); if ( web.symmetry_flag ) { test_axial_points(get_fe_facet(fe_a)); test_axial_points(get_fe_facet(fe_ai)); } if ( web.symmetry_flag ) { edge_id ed,ec; WRAPTYPE w; if ( get_vattr(get_fe_tailv(fe_d)) & AXIAL_POINT ) { ed = get_fe_edge(fe_b); ec = get_fe_edge(fe_e); w = (*sym_compose)(get_edge_wrap(ec),get_edge_wrap(ed)); set_edge_wrap(e_id,w); } else { ed = get_fe_edge(fe_d); ec = get_fe_edge(fe_c); w = (*sym_compose)(get_edge_wrap(ec),get_edge_wrap(ed)); set_edge_wrap(e_id,(*sym_inverse)(w)); } if ( ed && (web.modeltype == QUADRATIC) ) { /* adjust coordinates of midpoint */ REAL *x = get_coord(get_edge_midv(e_id)); torus_wrap(x,x,(*sym_inverse)(get_edge_wrap(ed))); } } if ( web.modeltype == QUADRATIC ) new_vertex_average(get_edge_midv(e_id),VOLKEEP); #ifdef MPI_EVOLVER /* Record changed facet-edges for synchronization */ if ( id_task(fe_a) != this_task ) mpi_add_to_synch_list(fe_a); if ( id_task(fe_ai) != this_task ) mpi_add_to_synch_list(fe_ai); if ( id_task(fe_b) != this_task ) mpi_add_to_synch_list(fe_b); if ( id_task(fe_c) != this_task ) mpi_add_to_synch_list(fe_c); if ( id_task(fe_d) != this_task ) mpi_add_to_synch_list(fe_d); if ( id_task(fe_e) != this_task ) mpi_add_to_synch_list(fe_e); #endif top_timestamp = ++global_timestamp; return 1; } /* end do_edgeswap */ /*********************************************************************** * * function: vertex_angle() * * purpose: calculate angle at vertex in string model. * */ REAL vertex_angle(v_id) vertex_id v_id; { REAL b[MAXCOORD]; /* edge vectors */ REAL angle; REAL bb; edge_id ea,eb; REAL ac; REAL netforce[MAXCOORD]; int n; int count = 0; for ( n = 0 ; n < SDIM ; n++ ) netforce[n] = 0.0; eb = ea = get_vertex_edge(v_id); do { if ( !valid_id(eb) ) return 0.0; get_edge_side(eb,b); bb = sqrt(SDIM_dot(b,b)); if ( bb > 0.0 ) for ( n = 0 ; n < SDIM ; n++ ) netforce[n] += b[n]/bb; count++; eb = get_next_tail_edge(eb); } while ( !equal_element(ea,eb) ); if ( count < 2 ) return 0.0; ac = sqrt(SDIM_dot(netforce,netforce)); angle = (ac >= 2.0) ? M_PI : 2*asin(ac/2); return angle; } /*********************************************************************** * * function: dihedral() * * purpose: calculate dihedral angle between facets on edge. * * return: angle in radians if exactly two facets * 0 otherwise * * method: uses 2-vector dot product for cosine angle */ REAL dihedral(e_id) edge_id e_id; { facetedge_id fe_a; /* for edge under test */ facetedge_id fe_ai; /* other facetedge of e_id */ facetedge_id fe_b; /* other side of one triangle */ facetedge_id fe_d; /* other side of other triangle */ REAL a[MAXCOORD],b[MAXCOORD],d[MAXCOORD]; /* edge vectors */ REAL denom,costh; REAL aa,bb,dd,ad,ab,bd; /* test to be sure edge has exactly two adjacent facets */ if ( get_vattr(get_edge_tailv(e_id)) & AXIAL_POINT ) e_id = inverse_id(e_id); fe_a = get_edge_fe(e_id); if ( !valid_id(fe_a) ) return 0.0; if ( !equal_id(get_next_facet(fe_a),get_prev_facet(fe_a)) ) return 0.0; fe_b = fe_inverse(get_prev_edge(fe_a)); fe_ai = fe_inverse(get_next_facet(fe_a)); fe_d = get_next_edge(fe_ai); if ( equal_id(fe_b,fe_d) ) return 0.0; /* test parallelism */ get_fe_side(fe_a,a); get_fe_side(fe_b,b); get_fe_side(fe_d,d); /* works in general dimension */ aa = SDIM_dot(a,a); bb = SDIM_dot(b,b); dd = SDIM_dot(d,d); ab = SDIM_dot(a,b); ad = SDIM_dot(a,d); bd = SDIM_dot(b,d); denom = (aa*bb - ab*ab)*(aa*dd - ad*ad); if ( denom <= 0.0 ) return 0.0; costh = (ab*ad - aa*bd)/sqrt(denom); if ( costh > 1.0 ) return 0.0; if ( costh < -1.0 ) return M_PI; return acos(costh); } /****************************************************************** * * Function: ridge_notcher() * * Purpose: Subdivide internal edges in a face whose adjacent * faces are non-parallel to a certain degree. * * Input: REAL max_angle - maximum allowed angle of deviation * * Output: offending edges subdivided, with middle vertex set * to the average of the four adjacent vertices * */ int ridge_notcher(max_angle) REAL max_angle; { int notchcount = 0; /* number of edges notched */ edge_id e_id; /* edge to notch */ facet_id *todo; /* for list of facets to refine */ int i; int maxfacet = web.skel[FACET].max_ord+1; edge_id sentinel; web.vol_flag = 0; todo = (facet_id *)temp_calloc(web.skel[FACET].max_ord+1,sizeof(facet_id)); /* first, unmark all NEWEDGE attributes */ FOR_ALL_EDGES(e_id) unset_attr(e_id,NEWEDGE); /* main loop over all edges */ e_id = NULLEDGE; while ( generate_all(EDGE,&e_id,&sentinel) ) { REAL angle; facetedge_id fe_a; facet_id f_id; if ( get_eattr(e_id) & (FIXED | BOUNDARY | NEWEDGE) ) continue; angle = dihedral(e_id); if ( angle < max_angle ) continue; /* record neighboring facets for subdivision */ fe_a = get_edge_fe(e_id); f_id = get_fe_facet(fe_a); todo[loc_ordinal(f_id)] = f_id; f_id = get_fe_facet(get_next_facet(fe_a)); todo[loc_ordinal(f_id)] = f_id; if ( verbose_flag ) { sprintf(msg,"Notching edge %s\n",ELNAME(e_id)); outstring(msg); } } for ( i = 0 ; i < maxfacet ; i++ ) if ( valid_id(todo[i]) ) { face_triangulate(todo[i],FACET_EDGES); notchcount++; } temp_free((char*)todo); if ( notchcount > 0 ) top_timestamp = ++global_timestamp; return notchcount; } /****************************************************************** * * Function: ridge_histogram() * * Purpose: Calculate histogram of ridge angles. * * */ void ridge_histogram() { edge_id e_id; /* edge to notch */ int bincount[HISTO_BINS]; int n; for ( n = 0 ; n < HISTO_BINS ; n++ ) bincount[n] = 0; /* main loop over all edges */ FOR_ALL_EDGES(e_id) { REAL angle; if ( get_eattr(e_id) & (FIXED | BOUNDARY ) ) continue; angle = dihedral(e_id); if ( angle == 0.0 ) n = 0; else n = HISTO_BINS/2 + 1 + (int)floor(log(angle)*HISTO_BINSIZE); if ( n < 0 ) n = 0; if ( n >= HISTO_BINS ) n = HISTO_BINS - 1; bincount[n]++; } /* print histogram */ outstring(" angle number\n"); if ( bincount[0] ) { sprintf(msg,"%f - %f %6d \n",0.0, (DOUBLE)(exp((-HISTO_BINS/2)/HISTO_BINSIZE)), bincount[0]); outstring(msg); } for ( n = 1 ; n < HISTO_BINS ; n++ ) if ( bincount[n] ) { sprintf(msg,"%f - %f %6d\n", (DOUBLE)(exp((n-HISTO_BINS/2-1)/HISTO_BINSIZE)), (DOUBLE)(exp((n-HISTO_BINS/2)/HISTO_BINSIZE)),bincount[n]); outstring(msg); } } #ifdef UNDER_DEVELOPMENT /******************************************************************* * * function: merge_collapsed_facets() * * purpose: Merge facets that have the same three vertices. * */ void merge_collapsed_facets() { facetedge_id fe,next_fe,sentinel; vertex_id v1,v2; fe = NULLFACETEDGE; while ( generate_all(FACETEDGE,&fe,&sentinel) ) { next_fe = get_next_facet(fe); if ( equal_id(fe,next_fe) ) continue; /* only one facet on edge */ v1 = get_fe_headv(get_next_edge(fe)); v2 = get_fe_headv(get_next_edge(next_fe)); if ( equal_id(v1,v2) ) { /* merge */ } } } #endif /*************************************************************************** * * function: fixup_vertex_content_meths() * * purpose: Make sure vertex has right set of content method instances. * To be called during eliminate_edge(). * * algorithm: Delete old content methods, then go through adjacent * facets and look for bodies. */ void fixup_vertex_content_meths(v_id) vertex_id v_id; { int meth_offset = get_meth_offset(VERTEX); struct vertex *v_ptr = (struct vertex*)vptr(v_id); int *instlist = (int*)((char*)v_ptr + meth_offset); facetedge_id start_fe,fe; conmap_t *map; struct boundary *bdry = get_vertex_boundary(v_id); int attr; int i,k; char name[200]; if ( !everything_quantities_flag ) return; attr = get_vattr(v_id); if ( !(attr & (BOUNDARY|CONSTRAINT)) ) return; /* Delete old content methods. */ for ( i = 0 ; i < (int)v_ptr->method_count ; i++ ) { if ( METH_INSTANCE(instlist[i])->flags & BODY_INSTANCE ) instlist[i] = instlist[--v_ptr->method_count]; } map = get_v_constraint_map(v_id); /* Add current content methods */ start_fe = fe = get_vertex_fe(v_id); if ( valid_id(fe) ) do { int sides; facet_id f_id = get_fe_facet(fe); vertex_id vv_id = v_id; for ( sides = 0 ; sides < 2 ; sides++ ) { body_id b_id; b_id = get_facet_body(f_id); if ( valid_id(f_id) && valid_id(b_id) && !(get_fattr(f_id) & NONCONTENT) ) { if ( attr & CONSTRAINT ) for ( k = 1 ; k <= (int)map[0] ; k++ ) { struct constraint *con = get_constraint(map[k]); if ( con->attr & CON_CONTENT ) { if ( con->attr & NAMED_THING ) sprintf(name,"body_%d_%s_meth",ordinal(b_id)+1,con->name); else sprintf(name,"body_%d_con_%d_meth",ordinal(b_id)+1,map[k]); apply_method(inverse_id(vv_id),name); } } if ( bdry && (bdry->attr & CON_CONTENT) ) { sprintf(name,"body_%d_bdry_%d_meth",ordinal(b_id)+1,get_vertex_boundary_num(v_id)); apply_method(inverse_id(vv_id),name); } } f_id = inverse_id(f_id); v_id = inverse_id(vv_id); } fe = get_next_facet(fe); } while ( !equal_id(fe,start_fe) ); } /* end fixup_vertex_content_meths() */ /*************************************************************************** * * function: fixup_edge_content_meths() * * purpose: Make sure edge has right set of content method instances. * To be called during eliminate_edge(). * * algorithm: Delete old content methods, then go through adjacent * facets and look for bodies. */ void fixup_edge_content_meths(e_id) edge_id e_id; { int meth_offset = get_meth_offset(EDGE); struct edge *e_ptr = (struct edge*)eptr(e_id); int *instlist = (int*)((char*)e_ptr + meth_offset); facetedge_id start_fe,fe; conmap_t *map; struct boundary *bdry = get_edge_boundary(e_id); int attr; int i,k; char name[200]; if ( !everything_quantities_flag ) return; attr = get_eattr(e_id); if ( !(attr & (BOUNDARY|CONSTRAINT)) ) return; /* Delete old content methods. */ for ( i = 0 ; i < (int)e_ptr->method_count ; i++ ) { if ( METH_INSTANCE(instlist[i])->flags & BODY_INSTANCE ) instlist[i] = instlist[--e_ptr->method_count]; } map = get_e_constraint_map(e_id); /* Add current content methods */ start_fe = fe = get_edge_fe(e_id); if ( valid_id(fe) ) do { int sides; facet_id f_id = get_fe_facet(fe); edge_id ee_id = e_id; if ( inverted(f_id) ) invert(ee_id); for ( sides = 0 ; sides < 2 ; sides++ ) { body_id b_id; b_id = get_facet_body(f_id); if ( valid_id(f_id) && valid_id(b_id) && !(get_fattr(f_id) & NONCONTENT) ) { if ( attr & CONSTRAINT ) for ( k = 1 ; k <= (int)map[0] ; k++ ) { struct constraint *con = get_constraint(map[k]); if ( con->attr & CON_CONTENT ) { if ( con->attr & NAMED_THING ) sprintf(name,"body_%d_%s_meth",ordinal(b_id)+1,con->name); else sprintf(name,"body_%d_con_%d_meth",ordinal(b_id)+1,map[k]); apply_method(ee_id,name); } } if ( bdry && (bdry->attr & CON_CONTENT) ) { sprintf(name,"body_%d_bdry_%d_meth",ordinal(b_id)+1,get_edge_boundary_num(e_id)); apply_method(ee_id,name); } } f_id = inverse_id(f_id); e_id = inverse_id(ee_id); } fe = get_next_facet(fe); } while ( !equal_id(fe,start_fe) ); } /* end fixup_edge_content_meths() */ evolver-2.30c.dfsg/src/extern.h0000644000175300017530000027570111410765113016707 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /****************************************************************** * * File: extern.h * * Purpose: declare global variables for Evolver program. */ #ifdef __cplusplus /* types for exception handler */ struct loadexcep { int a; }; struct cmd_excep { int a; }; struct graph_excep { int a; }; extern "C" { #endif #define PATHSIZE 1000 #define DEFAULT_EDGE_COLOR BLACK #define DEFAULT_FACET_COLOR WHITE #define DEFAULT_TARGET_TOLERANCE (1e-4) struct teix_gvert { REAL angle; REAL area; REAL star_area; REAL normal[MAXCOORD]; REAL force[MAXCOORD]; /* for mean curvature */ }; extern struct teix_gvert *tgverts; void create_pressure_quant(body_id); void body_fe_fixup(facet_id); /* extern char *sys_errlist[]; */ /* Uncomment this if compile error */ extern int broken_pipe_flag; /* so output routines will know to quit */ extern int match_id_flag; /* to make id match datafile number, option -i */ extern char *cmdfilename; /* for saving command line read file */ extern char *current_prompt; /* prompt string being displayed */ extern int echo_flag; /* whether to echo stdin; for piped input */ extern int auto_convert_flag; /* whether to automatically convert_to_quantities */ extern int exit_flag; /* whether to end this command interpreter */ #define END_COMMANDS 1234551234 extern int hessian_subshell_flag; /* whether in hessian subshell, so can't menu */ extern int subshell_depth; /* nesting depth of subshells */ extern char *curdir; /* current directory */ #define DEFAULT_BRIGHTNESS 0.65 extern REAL brightness; /* midlevel gray for screen display */ extern int markedgedrawflag; /* for single-drawing edges */ extern int verbose_flag; /* for lots of messages */ extern char loadfilename[PATHSIZE]; /* for LOAD command */ extern int addload_flag; /* if doing ADDLOAD command */ extern jmp_buf loadjumpbuf; /* for LOAD command */ extern jmp_buf graphjumpbuf; /* for errors during MS graphing */ #ifdef PTHREADS extern pthread_t draw_thread_id; /* for graphics thread */ #else extern unsigned int draw_thread_id; /* for graphics thread */ #endif extern char *warning_messages; /* for storing warning messages */ extern int warning_messages_new; /* number of new warning messages since last looked */ extern size_t warning_messages_max; /* allocated bytes for warning_messages */ extern int shadow_warn_flag; /* warn if locals shadow existing names */ #define MAXSUPPRESS 100 extern int warnings_suppressed[MAXSUPPRESS]; extern int warnings_suppressed_count; extern int file_no; /* number of current source file */ extern int file_no_used; /* number in list */ extern int file_no_max; /* number allocated */ extern char **file_names; /* names of source files */ extern int function_kludge_flag; /* handling errors in function def in datafile */ /* for binary_printf byte order */ extern int big_endian_flag; extern int little_endian_flag; /* my own ctypes */ extern char kb_upper_array[256]; extern char kb_lower_array[256]; extern int this_task; /* machine identifier for MPI; really task number */ extern int mpi_subtask_command_flag; /* whether command is running on task as subcommand from master instead of running isolated */ extern int mpi_debug; extern int mpi_show_corona_flag; /* whether to display imported elements */ extern int mpi_local_bodies_flag; /* First mpi task is root */ #define MASTER_TASK 0 /* controlling edge and facet deletion options */ extern int star_finagling; /* extra bad configuration detection */ extern int force_deletion; /* even if it seems a bad idea */ /************************************************************/ /* Unified lists of imported elements for MPI version */ /* On each node, there is list for each other node that */ /* shares elements. Should be only a few nontrivial */ /* neighbors. */ /* These are sparse lists to be used for locating elements */ /* by id. For compact lists of imported and exported ids, */ /* see import_elements and export_elements. */ struct remote_list_t { int *count; /* used entries for each task */ int *max; /* pointers allocated for each task */ struct element ***ibase; /* task, ordinal */ }; extern struct remote_list_t remote_elements[NUMELEMENTS]; extern int sparse_ibase_flag; /* for new scheme permitting sparse ibase */ /* types of element storage expansion */ #define EXTEND_BATCH 1 #define EXTEND_FOR_REFINE 2 #define MAXOPTPARAM 100 extern int optparamcount; /* number thereof */ struct optparam_t { int pnum; /* which parameter this is */ REAL grad; /* energy gradients */ REAL velocity; /* adjusted motion */ REAL cg; /* conjugate gradient history */ REAL old_value; /* for restoring */ REAL oldgrad; /* for conjugate gradient */ int rownum; /* row in Hessian */ int vhead_index; }; extern struct optparam_t optparam[MAXOPTPARAM]; extern REAL **optparam_congrads; /* constraint gradients */ #define OPTPARAM_DELTA (1.0e-4) /* My memory block types, also used for corruption detection */ #define PERM_BLOCK 0xABAB #define TEMP_BLOCK 0xDCDC #define ETERNAL_BLOCK 0xEAEA #define GRAPH_BLOCK 0xCBCB /* for per-thread temp memory */ extern int this_thread; #if defined(_WIN32) #define THREADBLOCK(blocktype) blocktype = \ (draw_thread_id==GetCurrentThreadId()) ? GRAPH_BLOCK : TEMP_BLOCK #elif defined(PTHREADS) #define THREADBLOCK(blocktype) blocktype = \ (draw_thread_id==pthread_self()) ? GRAPH_BLOCK : TEMP_BLOCK #else #define THREADBLOCK(blocktype) blocktype = TEMP_BLOCK #endif /* for calc_volgrads() mode */ #define NO_OPTS 0 #define DO_OPTS 1 extern int volgrads_every_flag; /* whether recalc volgrads every projection iteration */ extern int zener_drag_flag; /* whether to do zener drag */ #define ZENER_COEFF_NAME "zener_coeff" extern int backcull_flag; /* 3D graphics backculling */ extern int setting_backcull; /* slicing view */ extern int slice_view_flag; extern int slice_coeff_global; extern REAL slice_coeff[MAXCOORD+2]; /* clipping view */ extern int clip_view_flag; extern int clip_coeff_global; #define MAXCLIPS 10 extern REAL clip_coeff[MAXCLIPS][MAXCOORD+2]; extern REAL **vgev; /* vector vol grads, for approx curvature */ extern REAL **vgef; /* form vol grads, for approx curvature */ /* auxiliary structures needed to hold accumulated data for some squared curvature methods */ /* per vertex structure */ struct v_curve_t { REAL area; /* area around vertex */ REAL a; /* allocated area */ REAL force[MAXCOORD]; /* force on vertex */ REAL f; /* scalar force */ REAL h; /* scalar curvature */ REAL star_force[MAXCOORD]; /* star area gradient */ REAL deriv2[MAXCOORD][MAXCOORD]; /* self second deriv */ REAL normal[MAXCOORD]; /* for effective area */ REAL norm; /* square length of normal */ REAL fpgradf[MAXCOORD]; /* for projection gradient part */ REAL vol; /* allocated volume of vertex */ int sign; /* positive if H outward */ REAL term; /* for squared integrands */ }; extern struct v_curve_t *v_curve; /* per edge structure */ struct e_curve_t { REAL deriv[2][MAXCOORD]; /* dA_head/dv_tail */ REAL aderiv[2][MAXCOORD]; /* extra alloc area derivs */ REAL deriv2[MAXCOORD][MAXCOORD]; REAL volderiv[2][MAXCOORD]; /* [0] is deriv of head vol wrt tail */ }; extern struct e_curve_t *e_curve; /* macro stuff */ extern int keep_macros_flag; /* to preserve macros after datafile. */ #define MACRONAMESIZE 30 struct macro { char name[MACRONAMESIZE]; int offset; /* start of substitute string */ int subsize; /* size of substitute string */ }; extern struct macro *macros; /* dynamically allocated */ extern int macro_count; /* number of macros defined */ extern char *macro_subs; /* string space for substitution strings */ extern int facet_general_flag; extern REAL factorial[20]; extern int everything_quantities_flag; /* for pure quantity version, after conversion */ extern int show_all_quantities; /* to display default quantities also */ extern int random_seed; /* seed for random number generators */ extern int option_q; /* record command line option */ extern int dont_resize_flag; /* set if datafile specified view matrix */ extern int do_show_flag; /* for Mac kludge graphics command prompt */ extern char *VERSION; extern char *evolver_version; /* for version checking */ extern char needed_version[30]; extern REAL machine_eps; /* smallest machine resolution at 1.0 */ extern char *typenames[NUMELEMENTS]; /* silent linesplicing character */ #define MOREIN 1 /* free_discards() mode */ #define DISCARDS_ALL 1 #define DISCARDS_SOME 2 /* element attr bits */ #define ALL_ATTR (ATTR)0xFFFFFFFF #define ALLOCATED (ATTR)0x0001 #define NODISPLAY (ATTR)0x0002 #define NEWELEMENT (ATTR)0x0004 #define NEWVERTEX (ATTR)0x0004 #define NEWEDGE (ATTR)0x0004 #define NEWFACET (ATTR)0x0004 #define NEWBODY (ATTR)0x0004 #define WRAPPED (ATTR)0x0008 #define PINNED_V (ATTR)0x0008 #define DENSITY (ATTR)0x0010 #define FIXEDVOL (ATTR)0x0020 #define FIXED (ATTR)0x0040 #define BOUNDARY (ATTR)0x0080 #define NEGBOUNDARY (ATTR)0x0100 #define BDRY_ENERGY (ATTR)0x0200 #define CONSTRAINT (ATTR)0x0400 #define PRESSURE (ATTR)0x0800 #define BDRY_CONTENT (ATTR)0x1000 #define HIT_WALL (ATTR)0x2000 #define NEGCONSTRAINT (ATTR)0x4000 #define HIT_PARTNER (ATTR)0x8000 #define HAVE_CENTEROFMASS (ATTR)0x8000 #define BARE_NAKED (ATTR)0x10000 #define Q_MIDPOINT (ATTR)0x20000 #define TETRA_PT (ATTR)0x40000 #define TRIPLE_PT (ATTR)0x80000 #define DID_BODYFRONTFACET (ATTR)0x80000 #define DID_BODYBACKFACET (ATTR)0x100000 #define HIT_ONE_SIDED (ATTR)0x100000 #define Q_MIDFACET (ATTR)0x200000 #define Q_MIDEDGE (ATTR)0x400000 #define AXIAL_POINT (ATTR)0x800000 #define NO_REFINE (ATTR)0x1000000 #define DISSOLVED (ATTR)0x2000000 #define EDGE_DRAWN (ATTR)0x4000000 #define ACTUALVOL (ATTR)0x8000000 #define REDUNDANT_BIT (ATTR)0x10000000 #define NONCONTENT (ATTR)0x20000000 #define NO_ORIGINAL (-1) /* modes for move_vertices() and hessian_move() */ #define TEST_MOVE 0 #define ACTUAL_MOVE 1 #define SET_VELOCITY 2 /* modes for project_v_constr() */ #define KEEP_ONESIDEDNESS 0 #define RESET_ONESIDEDNESS 1 extern char *dymem; /* dynamic memory region */ extern int dymemsize; /* size of dynamic memory region */ extern struct constraint null_constraint; /* non-constraint placeholder */ #define GETCONSTR(n) (web.constraints+(n)) /* compare_vertex_edge_attr results */ #define INCOMPARABLE 0 #define A_SUB_B 1 #define A_EQ_B 2 #define A_SUPER_B 3 extern char *areaname; /* length or area */ extern int read_command_flag; /* whether commands at end of datafile */ extern int err_tok_gen_flag; /* whether -E option in effect */ extern int token_count; /* how many tokens parsed for -E option */ extern int verb_flag; /* set if lex looking for a verb */ extern int cond_expr_flag; /* set if parser looking for ':' in conditional expr */ extern int read_wrap_flag; /* set when reading wraps in datafile */ extern int exit_after_error; /* auto exit flag */ extern int exit_after_warning; /* auto exit flag */ extern int break_after_warning; /* auto break flag */ extern int last_error; /* number of last error */ extern int change_flag; /* set during command that changes surface */ extern int assigntype; /* type of assignment operator */ extern int uminus_flag; /* whether to interpret " -" as UMINUS */ extern char *cmdptr; /* current command or input for parsing */ extern int logfile_flag; /* whether logging in progress */ extern char logfilename[PATHSIZE]; extern FILE *logfilefd; extern int keylogfile_flag; /* whether keylogging in progress */ extern char keylogfilename[PATHSIZE]; extern FILE *keylogfilefd; /* structure used in check.c */ struct vvvv { element_id id; /* which element */ element_id v[3]; /* id's of vertices */ }; extern int check_count; /* number of errors returned by run_checks() */ extern int gocount; /* number of iterations left */ extern int go_display_flag; /* display each change */ extern int box_flag; /* whether or not to show outline box */ extern int quiet_flag; /* whether to display normal output */ extern int quiet_go_flag; /* whether to display normal g output */ extern int quiet_load_flag; /* whether to display output while loading file */ extern int shading_flag; /* for facet shading by orientation */ extern int smooth_graph_flag; /* whether to smoothly plot in lagrange mode */ extern int visibility_test; /* whether to do after depth sort */ extern int visdebuglevel; /* visibility debug level */ extern int color_flag; /* facet coloring by user */ extern int rgb_colors_flag; /* enabling rgb color scheme */ extern int edge_rgb_color_attr; /* number of rgb color attribute */ extern int facet_rgb_color_attr; /* number of rgb color attribute */ extern int facet_rgb_backcolor_attr; /* number of rgb color attribute */ extern int edge_alpha_flag; /* whether edges have alpha channel */ extern int facet_alpha_flag; /* whether facets have alpha channel */ extern int facetback_alpha_flag; /* whether facetbacks have alpha channel */ extern int background_color; /* graphics background */ extern int geomview_bug_flag; /* for geomview 1.6.1 picking bug */ extern int gv_binary_flag; /* whether to do geomview in binary */ extern int gv_pipe[2]; /* for pipe for reading geomview pick commands */ extern int pickvnum,pickenum,pickfnum,pickbnum; /* geomview picks */ extern int new_vertex_id,new_edge_id,new_facet_id,new_body_id; /* just created elements */ extern int gv_vect_start; /* vector vertex start in vpicklist */ extern int lazy_transforms_flag; /* let graphics display handle symmetry */ /* for piping geomview output */ #define GEOM_TO_GEOMVIEW 0 #define GEOM_NAMED_PIPE 1 #define GEOM_PIPE_COMMAND 2 extern int parallel_update_flag[NUMELEMENTS]; /* set when element info changed */ extern element_id *el_list[NUMELEMENTS]; /* for list of active elements */ extern long global_timestamp; /* universal clock */ extern long web_timestamp; /* something changes */ extern long graph_timestamp; /* so graph routines know when surface changed */ extern long top_timestamp; /* timestamp for topology changes */ extern long vedge_timestamp; /* for vertex edgelist currency */ extern long vfacet_timestamp; /* for vertex facetlist currency */ extern long bfacet_timestamp; /* for body facetlist currency */ extern long fixed_volume_timestamp; /* for volume calculation */ extern long info_volume_timestamp; /* for volume calculation */ extern long reset_timestamp; /* when surface loaded */ #define NOSETSTAMP 0 #define SETSTAMP 1 extern int lmc_mc_attr; /* vertex mean curvature attribute used by laplacian_mean_curvature */ extern int lmc_mobility_attr; /* edge/facet mobility attribute used by laplacian_mean_curvature */ extern int quarter_turn_var; /* for quarter_turn symmetry */ extern int need_fe_reorder_flag; /* setting causes recalc to fe_reorder */ extern int ackerman_flag; /* whether doing phase space motion */ extern char pix_file_name[150]; /* for P 2 */ extern int labelflag; /* whether ps doing labels */ extern int gridflag; /* whether ps doing gridlines */ extern int ps_colorflag; /* whether ps doing color */ extern int full_bounding_box_flag; /* for window bounding box */ extern int crossingflag; /* whether ps doing crossings */ extern char ps_file_name[1000]; /* ps output file */ extern char *binary_off_filename; /* Following PostScript line width variables relative to page size */ extern REAL ps_labelsize; /* relative size of labels */ extern REAL ps_stringwidth; /* default edge width */ extern REAL ps_fixededgewidth; extern REAL ps_tripleedgewidth; extern REAL ps_conedgewidth; extern REAL ps_bareedgewidth; extern REAL ps_gridedgewidth; extern int ps_widthattr; /* in case user defines widths */ #define PS_WIDTHNAME "ps_linewidth" #define NOLABELS 0 #define LABEL_ID 1 #define LABEL_ORIG 2 extern vertex_id *vpicklist; /* for geomview picking */ extern facet_id *fpicklist; extern REAL **to_focus; /* used only by oglgraph.c at present */ extern REAL **from_focus; #define NESTDEPTH 10 extern element_id junk; /* for MSC bug */ #ifndef PARALLEL_MACHINE extern element_id xx_id; /* for macros to prevent multiple evaluation */ extern element_id x1_id,x2_id,x3_id,x4_id,x5_id,x6_id,x7_id,x8_id,x9_id; extern element_id xa_id,xb_id,xc_id,xd_id,xe_id,xf_id,xg_id,xh_id; /*so nested macros don't tromp each other*/ #endif extern struct treenode *permlist; /* evaluation stack */ #define MAXSTACK 100 struct dstack { REAL value, deriv[2*MAXCOORD]; REAL second[2*MAXCOORD][2*MAXCOORD]; }; /* for redefining single letter commands */ extern struct expnode single_redefine[128]; /* for dynamic load library functions */ #define MAX_DLL 5 typedef void (*dll_func_type) ARGS((int , REAL*, struct dstack *)); struct dll { char *name; /* library name */ void *handle; /* for dl functions */ } ; extern struct dll dll_list[MAX_DLL]; #define FUNC_VALUE 1 #define FUNC_DERIV 2 #define FUNC_SECOND 3 /* global variable list */ #define GLOBAL_NAME_SIZE 31 struct global { char name[GLOBAL_NAME_SIZE + 1]; /* 31 significant characters */ union { REAL real; char *string; struct expnode proc; struct { REAL *values; char *value_file; } file; int quant; /* quantity */ int meth_inst; /* method values*/ dll_func_type funcptr; /* dynamic load function */ int cnum; /* constraint number */ int bnum; /* boundary number */ int offset; /* local variable position on stack */ void *dataptr; /* for permanent internal variables */ element_id id; } value; union { struct { int argcount; /* function argument count */ struct locallist_t *locals; /* local variable symbol table */ int proc_timestamp; /* for ordering procedure defines */ char *proc_text; /* text of procedure definition */ #define ARGTYPENUM 8 unsigned char argtypes[ARGTYPENUM]; /* indices into type table */ } procstuff; struct { REAL delta; /* for optimizing parameter differencing */ REAL pscale; /* for optimizing parameter scale matching */ REAL *gradhess; /* for expressions in constraints etc. */ } varstuff; struct array *arrayptr; } attr; int flags; /* see defines below */ int type; /* datatype for variables */ }; extern struct locallist_t *query_locals; #define LOCALSCOPEMAX 100 extern struct locallist_t *local_scope_bases[LOCALSCOPEMAX]; /* for parsing */ extern int local_nest_depth; /* for local_scope_bases */ /* for global identifier id */ #define GLOBMASK 0x00FFFFFF #define GTYPEMASK 0xF0000000 #define EPHGLOBAL 0x10000000 #define PERMGLOBAL 0x20000000 #define LOCALVAR 0x30000000 /* stuff for using name_id to pass info about extra attributes */ #define ATTRIBNAME 0x40000000 #define ETSHIFT 24 #define ELTYPEMASK (0xF << ETSHIFT) #define set_name_eltype(name_id,eltype) (name_id | ATTRIBNAME | (eltype << ETSHIFT)) #define name_eltype(name_id) (((name_id) & ELTYPEMASK) >> ETSHIFT) typedef unsigned int ident_t; #define ident_type(id) ((id) & ~GLOBMASK) #define ident_num(id) ((id) & GLOBMASK) #define get_localp(id) (localstack + localbase->list[(id)&GLOBMASK].offset) #define get_local(id) (localbase->list[(id)&GLOBMASK]) #define dy_globals web.dy_globals_w #define globals(gid) ( \ ((gid)&(~GLOBMASK))==EPHGLOBAL ? \ (((struct global *)(dymem + dy_globals))+((gid)&GLOBMASK)) : \ (((gid)&(~GLOBMASK))==LOCALVAR ? \ (&(localbase->list[(gid)&GLOBMASK].g)) : \ (((gid)&(~GLOBMASK))==PERMGLOBAL ? \ (dy_perm_globals+((gid)&GLOBMASK)) : \ (((struct global *)(dymem + dy_globals))+((gid)&GLOBMASK)) \ ))) extern struct global *Globals; /* handy for debugging */ #define dy_perm_globals web.dy_perm_globals_w #define perm_globals(gid) (dy_perm_globals+((gid)&GLOBMASK)) extern struct global *perm_Globals; /* handy for debugging */ #define dy_globalshash web.dy_globalshash_w #define globalshash ((int *)(dymem + dy_globalshash)) extern int dy_global_hash_max; /* allocated entries */ extern int dy_global_hash_maxfill; /* entries before expanding */ extern int dy_global_hash_used; /* entries actually in use */ /* Bitfield for type of name */ #define NAMETYPEMASK 0xE0000000 #define INDEXMASK 0x1FFFFFFF #define VARIABLENAME 0x20000000 #define METHODNAME 0x40000000 #define QUANTITYNAME 0x60000000 #define PERM_NAME 0x80000000 /* Hash table actions */ #define HASH_LOOK 0 #define HASH_ADD 1 #define HASH_DELETE 2 /* local variable list for procedure */ struct localvar_t { struct global g; int prev; /* up the list; search order */ int scope_depth; int offset; /* in stack units */ int size; /* in stack units */ }; struct locallist_t { struct localvar_t *list; int maxlist; int count; int totalsize; int scope_depth; int flags; /* see below */ ident_t iid; /* of procedure this list belongs to */ }; extern struct locallist_t *localbase; /* locallist_t flag bits */ #define LL_HAS_ARRAY 1 #define LL_PERMANENT 2 #define LL_IN_USE 4 /* type of listing desired */ #define LIST_PROTO 1 #define LIST_FULL 2 extern int proc_timestamp; /* for ordering procedure definitions */ extern int old_global_count; /* for error recovery */ extern int old_perm_global_count; /* for error recovery */ extern int perm_flag; /* for whether permanent assignment parsing in place */ extern int reading_comp_quant_flag; extern int cur_quant; /* when reading compound quantity */ extern int compound_hess_flag; /* mode while doing compound hessian */ #define CH_GRADS 1 #define CH_HESS 2 extern int quantities_only_flag; /* for using named quantities only */ extern int gravity_quantity_num; /* number of quantity for default gravity */ extern int gap_quantity_num; /* number of quantity for default gap energy */ extern int default_area_quant_num; /* number of quantity for default area */ extern int rotorder_var; /* for flip_rot order */ extern int calc_quant_flag; /* set when quantity calculation underway */ extern char volume_method_name[100]; /* for replacing default */ extern char area_method_name[100]; /* for replacing default */ extern char length_method_name[100]; /* for replacing default */ extern int length_method_number; /* for replacing default */ extern int dirichlet_flag; /* to do area hessian with Dirichlet hessian */ extern int sobolev_flag; /* to do area hessian with Sobolev hessian */ extern REAL **vproj_base; /* vertex projection matrix row pointers */ extern REAL *vproj_space; /* vertex projection matrix arena */ extern REAL ***conhess_base; /* vertex constraint hessian arena */ extern REAL **ritzvecs; /* defines for global variable flags */ #define ORDINARY_PARAM 1 /* ordinary real-valued */ #define FILE_VALUES 2 /* values to be read from file */ #define SUBROUTINE 4 /* value is a parse tree */ #define RECALC_PARAMETER 8 /* causes recalc when variable changed */ #define PERMANENT 0x10 /* do not forget for new surface */ #define GLOB_USED 0x20 /* if allocated */ #define QUANTITY_MODULUS 0x40 /* multiplier for quantity */ #define QUANTITY_NAME 0x80 /* actual value for quantity */ #define QUANTITY_TARGET 0x100 /* target value for quantity */ #define QUANTITY_TYPES (QUANTITY_MODULUS|QUANTITY_NAME|QUANTITY_TARGET) #define METHOD_MODULUS 0x200 #define METHOD_NAME 0x400 #define ARRAY_PARAM 0x800 #define METHOD_TYPES (METHOD_MODULUS|METHOD_NAME) #define ANY_TYPE (FILE_VALUES|SUBROUTINE|QUANTITY_TYPES|METHOD_TYPES) #define STRINGVAL 0x2000 /* string */ #define LEFTOVER 0x4000 /* if permanent left over from prev file */ #define OPTIMIZING_PARAMETER 0x8000 /* variable during optimization */ #define DYNAMIC_LOAD_FUNC 0x10000 /* dynamic load library function */ #define CONSTRAINT_NAME 0x20000 #define BOUNDARY_NAME 0x40000 #define GLOB_LOCALVAR 0x80000 #define FUNCTION_NAME 0x100000 #define PROCEDURE_NAME 0x200000 #define INTERNAL_NAME 0x400000 #define READONLY 0x800000 #define INTVAL 0x1000000 #define REALVAL 0x2000000 #define IN_DATAFILE_TOP 0x4000000 #define ALWAYS_RECALC 0x8000000 #define FIXED_SIZE_ARRAY 0x10000000 #define UNFIXED_SIZE_ARRAY 0x20000000 extern struct expnode torus_period_expr[MAXCOORD][MAXCOORD]; extern struct expnode torus_display_period_expr[MAXCOORD][MAXCOORD]; /* for calc_periods() */ #define NO_ADJUST_VOLUMES 0 #define ADJUST_VOLUMES 1 /* Bits for calc_all_grads() and such */ #define CALC_FORCE 1 #define CALC_VOLGRADS 2 extern int torus_display_mode; /* default, raw, connected, clipped */ /* modes */ #define TORUS_DEFAULT_MODE 0 #define TORUS_RAW_MODE 1 #define TORUS_CONNECTED_MODE 2 #define TORUS_CLIPPED_MODE 3 extern int view_matrix_global; /* global id number */ extern int eigenvalues_list_global; /* for accessing ritz() output list */ extern int torus_periods_global; extern int inverse_periods_global; extern REAL window_aspect_ratio; extern int read_depth,include_depth; extern char datafilename[PATHSIZE]; /* current datafile name */ #define NOTDATAFILENAME 0 /* path_open() mode */ #define SETDATAFILENAME 1 /* path_open() mode */ extern char filename[PATHSIZE]; /* file name in command */ extern FILE *commandfd; /* command input file */ extern FILE *logfd; /* command log file */ extern struct cmdfile { FILE *fd; /* for nested reads */ char filename[PATHSIZE]; /* command file name */ int line; /* for error reporting */ int datafile_flag; /* whether datafile */ int file_no; } cmdfile_stack[NESTDEPTH],datafile_stack[NESTDEPTH]; extern int datafile_flag; /* 1 for datafile, 0 for command so expression parser knows what's up */ extern int datafile_input_flag; /* whether further input available from datafile */ #define NOT_DATAFILE 0 #define IN_DATAFILE 1 #define PRETEND_DATAFILE 2 extern int topflag; /* 1 while in datafile top section */ extern int backquote_flag; /* 1 while in backquoted command; kludge */ extern int lists_flag; /* set when parsing space-separated lists */ #define LISTS_OFF 0 #define LISTS_SOME 1 #define LISTS_FULL 2 extern int use_given_id; /* iterator expr should not use local, for SHOW_ */ extern int const_expr_flag; /* 1 for const_expr, 0 for command, so expression parser knows what's up */ extern int boundary_expr_flag; /* so parser knows when parsing boundary */ extern int reading_elements_flag; /* so parser knows attributes should not be accepted in expressions */ extern FILE *outfd; /* for normal output */ extern int check_increase_flag; /* to detect blowups */ extern int estimate_flag; /* for toggling estimate of energy decrease */ extern REAL estimated_change; /* stored result */ extern int autorecalc_flag; /* for toggling autorecalc after variable assign */ extern int autopop_flag; /* whether to do autopopping */ extern int immediate_autopop_flag; /* set if pop before motion */ extern int autopop_quartic_flag; /* if autopop length = sqrt(sqrt(2*scale)) */ extern int pop_disjoin_flag; /* whether cones to be disjoined rather than merged */ extern int pop_enjoin_flag; /* whether cones to be enjoined rather than disjoined */ extern int pop_to_edge_flag; /* control which way popping goes */ extern int pop_to_face_flag; /* control which way popping goes */ /* some popping modes for various popping functions */ #define POP_TO_BETTER 0 #define POP_TO_OPEN 1 #define POP_TO_TRIPLE 2 #define POP_TO_TWIST 3 #define POP_TO_TRIANGLE 4 #define POP_TO_EDGE 5 #define POP_TO_FACE 6 extern int autochop_flag; /* whether to do autochopping */ extern REAL autochop_length; /* max edge length for autochop */ extern int autopop_count; /* number of edges found */ extern int autochop_count; /* number of edges found */ extern int kraynikpopvertex_flag; /* for special popping of a certain cone */ extern int kraynikpopedge_flag; /* for special popping of certain edges */ extern int parens; /* level of parenthesis nesting */ extern int brace_depth; /* level of brace nesting */ extern int in_quote; /* so lexer knows when in string */ extern int in_function; /* so lexer knows when in function def */ extern int in_comment; /* so lexer knows when in comment */ extern int in_control_structure; /* so lexer knows when in control structure */ extern int effective_area_flag; /* use quadratic form for area around vertex */ extern int old_area_flag; /* on for using old effective area */ extern int runge_kutta_flag; /* whether to use runge-kutta method for motion */ extern REAL total_time; /* total scale factor */ extern REAL star_fraction; /* weighting factor for star around vertices */ extern int area_fixed_flag; /* for fixed area constraint */ extern REAL area_fixed_target; /* target value for fixed area */ extern REAL area_fixed_pressure; /* Lagrange multiplier */ #define AREA_Q_ID 0x1111 /* for constraint identification */ extern int post_project_flag; /* project to quant constr after each motion */ extern int edge_diffusion_attr; extern int facet_diffusion_attr; extern struct expnode mobility_formula; extern int mobility_flag; /* whether mobility in effect */ extern struct expnode mobility_tensor[MAXCOORD][MAXCOORD]; extern int mobility_tensor_flag; /* whether tensor mobility in effect */ extern int check_pinning_flag; /* for vertices changing constraints */ extern int nprocs; /* number of parallel processors */ extern int *v_procnum; /* processors for vertex, index by ord */ extern int procs_requested; /* number of processes desired */ extern REAL proc_total_area[MAXPROCS]; /* for individual processes */ extern int web_checksum; /* to see if web needs sending */ extern int dymem_checksum; /* see if dymem needs sending */ extern int comp_quant_vertex; /* during calc_quant_grad */ extern int comp_quant_vertexi; /* during calc_quant_hess */ extern int comp_quant_vertexj; /* during calc_quant_hess */ extern int comp_quant_type; /* during calc_quant_grad */ extern int comp_quant_stamp; /* coordination with eval_all and eval_sec */ extern vertex_id comp_quant_vi; /* during calc_quant_hess */ extern vertex_id comp_quant_vj; /* during calc_quant_hess */ extern REAL *f_sums; /* facet_knot_energy, for sums to all other vertices */ extern REAL *bi_sums; /* b_surface method, for sums to all other vertices */ /*extern char yytext[];*/ extern char *yytext; extern char *inputbuffer; extern int inputbufferspot; extern int inputsave_flag; extern int yydebug; /* flag for parser debugging */ extern int help_flag; /* avoid error message while doing help */ extern int memdebug; /* flag for memory debugging in run_checks() */ extern int itdebug; /* flag for iteration debugginng */ extern int single_step_debugging; /* flag for debug stop at next line */ #if defined(WIN32) && defined(_DEBUG) extern int _CrtCheckMemory(void); #define HEAPCHECK { if ( (_heapchk() != _HEAPOK ) \ /* || (_CrtCheckMemory() == 0 ) */ ) fprintf(stderr, \ "Bad heap at " __FILE__ " line %s. ",__LINE__ ); } #else #define HEAPCHECK #endif extern int tok; extern int unput_tok_count; /* number of pushed-back tokens */ extern int int_val; extern REAL real_val; extern int attr_etype; /* for ATTRIBUTE parse */ extern int subtype; /* for INDEXED_SUBTYPE parse */ extern int coord_num; extern char idname[35]; /* for saving yytext */ extern char set_extra_name[100]; /* for saving name */ extern int line_no; #define MAXHISTORY 100 #define HISTORYSPACE 8000 extern char *history_space; extern int history_number; /* number of current command */ extern int history_count; /* number in list */ extern int history_offsets[MAXHISTORY]; /* following two are for mode argument of command() */ #define NO_HISTORY 0 #define ADD_TO_HISTORY 1 #define MAXCMDSIZE 2000 extern char fulltext[MAXCMDSIZE+5]; /* for full text of commands */ extern size_t fulltextsize; /* length of command */ extern int aggrtype; /* aggregate type being parsed */ extern int aggregate_depth; /* nesting depth of aggregate loops */ extern int attr_kind; /* kind of attribute being parsed */ extern char *default_name; /* for unnamed elements */ extern char last_name[50]; /* name of last element generator */ #define HISTBINS 21 /* bins in histogram (actually 1 less )*/ /* symbol table stuff */ #define SYMNAMESIZE 31 struct sym { char name[SYMNAMESIZE+1]; int type; /* element type, or see defines below */ int localnum; }; #define SYMTYPE_INT 6 #define SYMTYPE_REAL 7 #define SYMTYPE_INT_ARRAY 8 #define SYMTYPE_REAL_ARRAY 9 #define MAXARRAYDIMS 8 extern struct sym *elsym; /* name of element during parsing */ extern struct sym *yysym; /* name of identifier from lex */ extern struct sym symtable[]; /* symbol table */ struct array { int dim; int datatype; /* REAL_TYPE_, INTEGER_TYPE_, etc. */ int itemsize; /* bytes per item */ int datacount; /* total data elements */ size_t datastart; /* byte offset of start of data */ int sizes[MAXARRAYDIMS]; /* arbitrary length, followed by data */ }; /************************************************************************ * structure for defining extra attributes */ #define ATTR_NAME_SIZE 31 struct extra { char name[ATTR_NAME_SIZE+1]; int offset; /* within allocated space */ int type; /* see below */ struct array array_spec; // int adim; /* number of array dimensions; 0 for scalar */ // int sizes[MAXEXTRADIM]; /* of dimensions */ // int itemsize; /* bytes per item */ // int datacount; /* total elements */ struct expnode code; /* for function attribute */ int flags; /* see below */ }; /* flag bits */ #define DUMP_ATTR 1 #define FUNCTION_ATTR 2 #define DIMENSIONED_ATTR 4 #define RECALC_ATTR 8 /* useful for debugging */ extern struct extra *Extras[NUMELEMENTS]; /* datatype types */ #define NULL_TYPE 0 /* these types will be stored on the eval stack as reals */ #define REAL_TYPE 1 #define INTEGER_TYPE 2 #define ULONG_TYPE 3 #define UCHAR_TYPE 4 #define USHORT_TYPE 5 #define UINT_TYPE 6 #define LONG_TYPE 7 #define CHAR_TYPE 8 #define SHORT_TYPE 9 #define MAX_NUMERIC_TYPE 10 /* end of numeric types */ #define STRING_TYPE 11 #define PTR_TYPE 12 #define VERTEX_TYPE 13 #define EDGE_TYPE 14 #define FACET_TYPE 15 #define BODY_TYPE 16 #define FACETEDGE_TYPE 17 #define ELEMENTID_TYPE 18 #define BOUNDARY_TYPE 19 #define CONSTRAINT_TYPE 20 #define QUANTITY_TYPE 21 #define INSTANCE_TYPE 22 #define PROCEDURE_TYPE 23 #define NUMDATATYPES 24 /* the following are set in skeleton.c to match above defines */ extern char *datatype_name[NUMDATATYPES]; extern int datatype_size[NUMDATATYPES]; /* squared curvature as part of energy */ extern int sqcurve_ignore_constr; /* set if to count fixed and constrained verts */ extern int square_curvature_flag; /* set if to be counted */ extern int square_curvature_param; /* which parameter for modulus */ extern int mean_curvature_param; /* which parameter for modulus */ extern int mean_curv_int_quantity_num; /* for everything quantities */ extern int sq_mean_curv_quantity_num; /* for everything quantities */ extern int kusner_flag; /* set for edge square curvature */ extern int assume_oriented_flag; /* for orientation checking */ extern int boundary_curvature_flag; /* whether to include boundary vertices */ extern int conf_edge_curv_flag; /* set for conformal edge curvature squared */ extern int sqgauss_flag; /* for squared gaussian curvature */ extern int h0_attr; /* attribute number for h_zero, if any */ #define H0_IN_GLOBAL 1 #define H0_IN_ATTR 2 #define TEST_SQ 0 #define PLAIN_SQ 1 #define EFF_SQ 2 #define NORMAL_SQ 3 #define PERP_SQ 4 extern int sqgauss_param; /* which parameter for modulus */ extern int normal_sq_mean_curvature_mi; extern int eff_area_sq_mean_curvature_mi; extern int sq_mean_curvature_mi; extern int mix_sq_mean_curvature_mi; extern int star_normal_sq_mean_curvature_mi; extern int star_perp_sq_mean_curvature_mi; extern int star_eff_area_sq_mean_curvature_mi; extern int star_sq_mean_curvature_mi; extern REAL target_length; /* for string model */ extern int approx_curve_flag; /* if approximate curvature in effect */ extern int mean_curv_int_flag; /* for unsquared mean curvature */ extern int normal_curvature_flag; /* choice of curvature formula */ extern int div_normal_curvature_flag; /* choice of curvature formula */ extern int marked_edge_attr; #define MARKED_EDGE_ATTR_NAME "sqcurve_string_mark" extern int circular_arc_flag; /* whether to draw edges as arcs */ extern int spherical_arc_flag; /* whether to draw edges as spherical arcs */ /* flag bits to say if should be evaluated since modulus nonzero */ #define EVALUATE 0x0002 #define SELFSIM_NAME "self_sim_coeff" extern int self_similar_flag; #define STR_CUR_TOL_NAME "string_curve_tolerance" REAL string_curve_tolerance; /* quadratic string model smoothness, deg */ /* for restricting motion to be along normals */ extern int normal_motion_flag; typedef REAL pt_type[MAXCOORD]; extern pt_type *vertex_normals; /* for storage of normals by ordinal */ /* for Dennis DeTurck unit normal motion */ extern int unit_normal_flag; extern REAL deturck_factor; /* weight for unit normal */ extern REAL **identmat; /* handy identity matrix, set up in init_view */ /* homothety target value, set when homothety toggled on */ extern REAL homothety_target; extern int scrollbuffersize; /* output console lines */ extern char *msg; /* for constructing user messages */ extern int msgmax; /* length allocated */ #define ERRMSGSIZE 2000 extern char errmsg[ERRMSGSIZE]; /* for error() routine */ extern int parse_error_flag; /* set when parser hits error */ extern int parse_errors; /* for counting errors */ extern int recovery_flag; /* set while recovering from parsing error */ #define MAXCMDDEPTH 100 extern jmp_buf jumpbuf[MAXCMDDEPTH]; /* for error recovery */ extern jmp_buf cmdbuf; /* for command error recovery */ extern jmp_buf m_jumpbuf[MAXPROCS]; /* for multiproc error recovery */ #define UNRECOVERABLE 0 #define RECOVERABLE 1 #define WARNING 2 #define PARSE_ERROR 3 #define EXPRESSION_ERROR 4 #define COMMAND_ERROR 5 #define DATAFILE_ERROR 6 #define SYNTAX_ERROR 7 #define Q_ERROR 8 #define RECOVERABLE_QUIET 9 #define RECOVERABLE_ABORT 10 /* for queries */ extern int celement; extern int commandverb; extern int condition_flag; #define ATTRIBUTE 19382 extern struct expnode *show_expr[NUMELEMENTS]; /* for element show exprs */ extern struct expnode show_command[NUMELEMENTS]; /* save for dump */ extern struct expnode show_expr_table[NUMELEMENTS]; /* actual expressions */ extern int query_intval; extern REAL query_realval; extern int set_query_type; extern ATTR set_query_attr; extern int query_coord; /* values for set attribute queries */ #define SET_Q_ATTR 1301 #define SET_DENSITY 1302 #define SET_VOLUME 1303 #define SET_CONSTRAINT 1304 #define SET_COORD 1305 #define SET_PARAM 1306 #define SET_TAG 1307 #define UNSET_Q_ATTR 1308 #define UNSET_CONSTRAINT 1309 #define SET_COLOR 1310 #define SET_TRANS 1311 #define SET_FIXED 1312 #define DID_ 4000 extern REAL volume_factorial; /* simplex volume factor */ extern int subsimplex[1<>24) & 0xFF) #define GET_GREEN_BITS(c) (((unsigned int)(c)>>16) & 0xFF) #define GET_BLUE_BITS(c) (((unsigned int)(c)>>8) & 0xFF) #define GET_ALPHA_BITS(c) (((unsigned int)(c)) & 0xFF) typedef REAL IColor[4]; /* r,g,b,a; not same as OOGL ColorA */ #define IRIS_COLOR_MAX 16 extern IColor rgb_colors[IRIS_COLOR_MAX]; extern REAL facet_alpha; /* global transparency */ /* homogeneous coordinate dimensions */ extern int HOMDIM; /* function pointers for invoking device-specific graphics */ /* first four are for random-order plotting and are fed raw data */ extern void (*graph_start) ARGS((void)); /* called at start of graphing */ extern void (*graph_edge ) ARGS((struct graphdata *,edge_id)); /* called to graph one triangle */ extern void (*graph_facet) ARGS((struct graphdata *,facet_id)); /* called to graph one triangle */ extern void (*graph_end) ARGS((void)); /* called at end of graphics */ /* second four are for painter algorithm output, are fed digested data */ extern void (*display_edge ) ARGS((struct tsort *)); /* hardware edge display */ extern void (*display_facet) ARGS((struct tsort *)); /* hardware facet display */ extern void (*init_graphics) ARGS((void)); /* hardware initialization */ extern void (*finish_graphics) ARGS((void)); /* hardware end of picture */ extern void (*close_graphics) ARGS((void)); /* close graphics window */ /* edge thicknesses for painter algorithm */ /* in order BARE_EDGE, FIXED_EDGE, CONSTRAINT_EDGE, BOUNDARY_EDGE, SINGLE_EDGE, TRIPLE_EDGE, and other */ extern REAL *edgewidths; /* pointer to be set by display() */ extern REAL pswidths[]; /* PostScript; values in variable.c */ extern REAL xwidths[]; /* X windows; values in variable.c */ extern REAL otherwidths[]; /* anything else; values in variable.c */ /* graphing flags */ extern int init_flag; /* whether graphics initialized */ extern int bdry_showflag; /* whether to show facets on boundary */ extern int no_wall_flag; /* whether to suppress wall facets */ extern int normflag; extern int thickenflag; extern int innerflag; extern int outerflag; extern int colorflag; extern int OOGL_flag; /* whether MinneView initialized */ extern int geomview_flag; /* whether geomview initialized */ extern int geompipe_flag; /* for pipe only */ extern REAL thickness; /* for thickening */ extern int user_thickness_flag; /* whether user has specified thickness */ extern int view_4D_flag; /* 0 for doing 3D xyz projection graphics, */ /* 1 for outputting 4D graphics */ extern FILE * savefd; /* file for binary dump of web */ extern FILE * data_fd; /* file for initial data */ extern char cmapname[100]; /* colormap file name */ typedef REAL maprow[4]; extern maprow *colormap; /* rgba colormap, values 0 to 1 */ extern int fillcolor; /* current polygon fill color */ /* gaussian integration coefficients */ extern REAL gcombo[EDGE_CTRL][EDGE_INTERP]; extern REAL sdip[EDGE_CTRL][EDGE_INTERP]; extern REAL ssimp[EDGE_CTRL][EDGE_INTERP]; extern REAL gauss2wt[EDGE_INTERP]; extern REAL poly2partial[FACET_CTRL][2][2]; extern REAL scoeff[EDGE_CTRL][EDGE_CTRL]; /* plane area coeff */ extern REAL vcoeff[FACET_CTRL][FACET_CTRL][FACET_CTRL]; /* volume coeff*/ extern int set_by_user_gauss_1D; /* minimums set by user */ extern int set_by_user_gauss_2D; extern REAL **conical_x; extern REAL *conical_w; /* general parameters */ extern REAL **view; /* transformation matrix */ extern int datafile_view_flag; /* whether datafile had view matrix */ extern int steps; extern int energy_init; /* to keep track if we have current config energy */ /* for vertex popping */ struct verfacet { vertex_id v_id; facet_id f_id; }; #define MAXWULFF 100 extern REAL wulff_vector[MAXWULFF][MAXCOORD]; /* the vector components */ #ifdef NOPROTO extern void (*get_wulff)(); #else extern void (*get_wulff)(REAL *,REAL *); #endif extern int interp_bdry_param; /* flag for interp or extrap bdry param */ extern int extra_bdry_attr; /* when extra boundary for vertices */ extern int extra_bdry_param_attr; /* corresponding parameter values */ #define EXTRA_BDRY_NAME "extra_boundary" #define EXTRA_BDRY_PARAM_NAME "extra_boundary_param" extern REAL **phase_data; /* phase boundary energies */ extern char phase_file_name[PATHSIZE]; /* for dump */ extern int phase_flag; /* if phase boundary data in effect */ extern int phasemax; /* number of phases */ /* for BLAS block factoring */ #define BLAS_BLOCKSIZE 64 extern int fixed_constraint_flag; /* set if restoring force valid */ extern REAL **leftside; /* volume projection matrix */ extern REAL **rleftside; /* volume projection matrix */ extern REAL *rightside; /* right side of volume gradient constraints */ extern REAL *pressures; /* multiples of gradients to subtract from force */ extern REAL *vpressures; extern int pressure_set_flag; /* pressures have been given values */ extern REAL *vol_deficit; /* bodyu volume deficits */ extern REAL *vol_restore; /* volume restoring gradient coefficients */ extern int no_refine; /* keyword NOREFINE in data file header sets this to disable initial triangulation of triangular initial faces. */ /* interpolation structures */ #define MAXLEVEL 30 extern int reflevel; /* refinement level */ extern REAL extrap_val[MAXLEVEL]; /* final value at level */ /* hessian minimization */ /* info for each vertex */ struct hess_verlist { vertex_id v_id; /* which vertex this is */ int flags; /* for marking if done */ int freedom; /* degrees of freedom */ int rownum; /* starting row in sparse matrix */ #ifdef MPI_EVOLVER int global_rownum; #endif REAL **proj; /* projection matrix to constraints */ REAL ***conhess; /* for constraint hessian */ REAL slant; /* cos of angle of normal from constraint */ }; /* flag bits */ #define HV_DONE 1 #define SUPERNODE_DONE 2 extern int vhead_attr; /* number of vertex attribute for vhead index */ extern int bhead_attr; /* number of body attribute for vhead index */ #define VHEAD_ATTR_NAME "__vhead_index" #define BHEAD_ATTR_NAME "__bhead_index" /* sparse hessian matrix entry */ struct hess_entry { REAL value; int col; /* which column */ int row; /* which row */ }; #define HASHEMPTY (-1) extern int hessian_by_diff_flag; /* for crude hessian */ extern int hessian_quiet_flag; /* 1 to suppress hessian warnings */ extern int hessian_normal_flag; /* 1 for hessian constrained to normal */ extern int hessian_special_normal_flag; /* for user spec of Hessian direction */ extern struct expnode hessian_special_normal_expr[MAXCOORD]; extern int hessian_normal_perp_flag; /* 1 for hessian metric to normal */ extern int hessian_normal_one_flag; /* 1 for hessian constrained to 1D normal */ extern int hessian_double_normal_flag; /* 1 for hessian constrained to normal */ extern REAL hessian_slant_cutoff; /* for treating constrained vertices as fixed with hessian_normal */ extern int hessian_linear_metric_flag; /* linear interp dot product */ extern REAL linear_metric_mix; /* proportion of linear interp metric */ extern REAL quadratic_metric_mix; /* proportion of quadratic interp metric */ extern int min_square_grad_flag; /* what to minimize in hessian_line_seek */ extern int hess_move_con_flag; /* whether to project to global constraints in move */ /* projecting seems to be good idea */ extern REAL last_hessian_scale; /* from hessian_line_seek() */ extern REAL last_eigenvalue; /* from eigenprobe and stuff */ extern REAL *conrhs; /* right side of augmented matrix for constraints */ extern int total_entries; /* of hessian */ extern struct hess_verlist *vhead; /* main element list */ extern int vhead_count; /* total number of elements */ #ifdef XXX extern REAL *X; /* solution to augmented matrix */ extern REAL *rhs; /* right side of augmented matrix */ #endif extern int hmode; /* mode of motion */ #define SINGLE_DEGREE 2 #define NORMAL_MOTION 1 #define UNRESTRICTED 0 /* type of solution; YSMP path numbers */ #define HESS_CRITICAL 3 #define HESS_DOWNHILL 7 #define FORT #ifdef FORT #define A_OFF 1 #else #define A_OFF 0 #endif /* for controlling steps */ extern int rhs_flag; /* filling in right hand side */ extern int hess_flag; /* filling in hessian matrix */ extern int negdiag; /* C index of most negative entry on diagonal */ /* Flag for doing sparse rank update for function quantities, to avoid dense hessians */ extern int quantity_function_sparse_flag; /* Lanczos default parameters */ #define KRYLOVDIM 100 #define NPRINT 15 /**************************************************************************/ /* Linear system structure */ /* for indefinite symmetric square systems, with linear constraints */ /**************************************************************************/ /************************************************************************* * The following data structure stores a node of the Metis separator tree **************************************************************************/ struct SepNodeType { int nvtxs; /* The number of vertices in the graph */ int lo; /* Low index of separator (inclusive) */ int hi; /* High index of separator (exclusive) */ int isleaf; /* Indicates if it is a leaf node */ union { struct { REAL opc; /* The opcount for this node */ REAL subopc; /* The opcount for the subtree of this node */ } opc; /* original metis fields */ struct { size_t size; /* size of matrix */ int *vlist; /* list of variable numbers */ REAL *mat; /* triangular matrix */ } info; /* stuff useful in factoring */ } u; }; typedef struct SepNodeType SepNodeType; struct sp_entry { int row,col; REAL value; }; extern int blas_flag; /* whether to use BLAS */ extern int augmented_hessian_flag; /* whether to use augmented Hessian */ /* -1 for unset */ extern int augmented_hessian_mode; /* set during factoring */ extern int sparse_constraints_flag; /* whether to store constraint gradients in sparse format */ extern int BK_flag; /*for enabling Bunch-Kaufman version of sparse factoring*/ extern REAL BKalpha; /* single/REAL pivot ratio */ struct linsys { int flags; /* status bits, see below */ int N; /* number of variables and constraints */ int maxN; /* dimension IA can handle */ int A_rows; /* number of variables */ int optparamrowstart; /* first row of optimizing parameters */ int bodyrowstart; /* where bodies start */ int quanrowstart; /* where quantities start */ int total_rows; /* whole enchilada */ /* hashing stuff, for filling of matrix */ int table_size; /* allocated hash table size */ int max_fill; /* fill limit of hash table */ int hashcount; /* hash entries so far */ struct sp_entry *hashtable; /* the table itself */ int hash_extraprobes; /* for performance measure */ /* sparse storage of original matrix in upper triangle */ int *IA; /* row starts (unpermuted) */ int *JA; /* columns of each entry, starting on diag */ REAL *A; /* entry values */ int maxA; /* entries allocated for A and JA */ int *P; /* permutation in factors; P[0] is first variable */ int *IP; /* inverse permutation */ struct SepNodeType *stree; /* metis separation tree */ int streemax; /* max index of used stree */ int maxsepsize; /* maximum separator size */ int NSP; /* size of work space, for ysmp */ int *ISP; /* work space for ysmp or whoever */ /* permuted storage */ int *pIA; /* row starts */ int *pJA; /* columns of each entry, starting on diag */ REAL *pA; /* entry values */ /* factorization */ REAL lambda; /* shift for A */ int *psize; /* types of pivots on diagonal */ /* for mindegree factorization */ int Lsize; /* number of entries */ int *LIA; /* starting indices in LA */ int *LJA; /* columns for entries */ int *LIJA; /* starts of rows in LJA (overlapped) */ REAL *LA; /* values */ /* constraint stuff */ int concount; /* number of possible constraints */ int CN; /* number of actual constraints */ int *coninx; /* map from possible to actual constraints */ int *coninxinv; /* inverse of coninx */ REAL **C; /* constraint gradients, rowwise, orth'd */ REAL **HinvC; /* H^(-1)*C (transposed, actually) */ REAL **CHinvCinv; /* (C^T*H^(-1)*C)^(-1) */ /* sparse constraint stuff */ int *CIA; /* indexes of constraint starts in CJA,CA */ int *CJA; /* J for constraint gradient entry */ REAL *CA; /* values */ /* inertia */ int pos,neg,zero; int degencon; /* hopefully, degenerate constraints */ /* Approximate inverse diagonal, for metric */ REAL *apinv; /* row magnitudes, for nullity detection */ REAL *rowmag; /* low-rank update stuff; update of form V^T F V */ int low_rank; /* number of vectors */ int low_rank_vecsize; /* length of low rank update vectors */ REAL **low_rank_vectors; /* the vectors V themselves, rowwise */ REAL **low_rank_form; /* F */ REAL **low_rank_inverse_form; /* (I + F V A^-1 V)^-1 */ #ifdef MPI_EVOLVER /* stuff used in MPI distributed linear systems */ int *send_counts; /* counts for general MPI scatter/gather */ int *send_starts; /* offsets for general MPI scatter/gather */ int *recv_counts; /* counts for general MPI scatter/gather */ int *recv_starts; /* offsets for general MPI scatter/gather */ int *rowstarts; /* of globals on all tasks */ int *rowcounts; /* of globals on all tasks */ int local_rows; int local_start; /* of global row numbers */ int local_end; int *domain_tasks; /* domain_tasks[i] is task hosting domain i */ int *task_domains; /* task_domains[i] is domain hosted on task i */ int total_factor_fill; /* just for info */ int LA_spot; /* tracker for allocation in LA workspace */ int *IAtrans; /* translate local row numbers in IA to global rows */ int *IAtrans_elim; /* translate local row numbers to elimination order */ int *domain_IA; /* starts of rows in domain_entries list */ int scount; /* number of snodes actually used (info only) */ int *listspace; /* for lists of rows in supernodes */ int listspacesize; int listspacespot; int *sepsizes; /* sizes of final separators between domains */ int *block_starts; /* interleaved domains and separators in entrylist */ int *block_counts; int *domain_entry_starts; /* in entrylist, sending */ int *domain_entry_ends; /* in entrylist, sending */ int *sep_entry_starts; /* in entrylist, sending */ int *sep_entry_counts; int local_elim; /* rows in local domain */ int *all_elim_starts; /* domains and separators postorder */ int local_elim_start; /* handy to have */ int local_elim_end; int *homeglobal_elim; /* elims for home global rows */ int *domain_elim_starts; /* in terms of elimination order */ int *domain_elim_ends; /* needed since domains and separators */ int *sep_elim_starts; /* interleaved in elimination order */ int *sep_elim_ends; int *row_tasks; /* home row task destinations based on elim */ int *row_tasks_counts; /* counts of those going to each task */ int *row_tasks_starts; /* offsets for each task */ int *rows_from; /* elim numbers of incoming global rows */ int *rows_from_counts; /* counts of incoming global rows */ int *rows_from_starts; /* starts of incoming global rows */ int *domain_sepnum; /* postorder node number of domain */ int *sep_sepnum; /* postorder node number of separator */ int *block_tasks; /* which tasks host domains/seps in interleaved order */ struct snode *slist; /* allocated list of supernodes, enough for each row in domain and the separator */ struct snode *last_snode; /* of domain */ struct snode *sep_snode; /* of separator */ struct snode *csnode[2]; /* of children of separator */ int *clist[2]; /* row nums of children of separator */ int ctask[2]; /* tasks of children, for knowing clist and csnode order */ int *final_list; /* row nums in final separator */ int total_ssize; /* for debug */ REAL *workspace; /* for temp stack of factoring "extra" data */ int workspacesize; int *domain_counts; int domain_entries_size; /* number of entries in matrix for domain */ int sep_entries_size; /* number of entries in matrix for separator */ struct hess_entry *domain_entries; /* matrix entries for domain */ struct hess_entry *sep_entries; /* matrix entries for separator */ #endif }; /* flag bits */ #define S_HESSFILLED 1 #define S_RHSFILLED 2 #define S_FACTORED 4 #define S_PROJECTED 8 #define S_ODRV_REORDERED 0x10 #define S_JA_INCLUDED 0x20 #define S_ORDERFOUND 0x40 #define S_USE_ELEMENTS 0x80 /* psize values */ /* size of diagonal elements, 1 for 1x1, 2 for first of 2x2, 3 for second of 2x2, 0 for zero valued 1x1 */ #define ZEROPIVOT 0 #define ONEBYONE 1 #define FIRSTOFPAIR 2 #define SECONDOFPAIR 3 /* vector-to-form metric */ extern struct linsys Met; extern int ysmp_flag; /* set if doing Yale Sparse Matrix version */ /* sparse matrix function pointers, for easy switching among algorithms */ /* values for ysmp_flag */ #define MINDEG_FACTORING 0 #define YSMP_FACTORING 1 #define METIS_FACTORING 2 /* for mindeg control */ extern int mindeg_debug_level; extern int mindeg_margin; extern int mindeg_min_region_size; /* multiply vector by original sparse matrix */ extern void (*sp_mul_func)ARGS((struct linsys *, REAL*,REAL*)); /* convert raw Hessian data to standard sparse format */ extern void (*sp_AIJ_setup_func)ARGS((int,struct linsys*)); /* set up matrices needed for handling constraints */ extern void (*sp_constraint_setup_func)ARGS((int, struct linsys *)); /* set up projection to constraints using hessian metric */ extern void (*sp_hess_project_setup_func)ARGS((struct linsys *)) ; /* factor matrix */ extern void (*sp_factor_func)ARGS((struct linsys *)) ; /* Matrix inner product using hessian inverse */ extern void (*sp_CHinvC_func)ARGS((struct linsys *)) ; /* return a vector in kernel, supposing nullity > 0 */ extern int (*sp_kernel_func)ARGS((struct linsys *,REAL *)) ; /* solve given rhs */ extern void (*sp_solve_func)ARGS((struct linsys *,REAL *,REAL *)) ; /* solve multiple given rhs */ extern void (*sp_solve_multi_func)ARGS((struct linsys*,REAL**,REAL**,int)); /* optional ordering of vertices */ extern void (*sp_ordering_func)ARGS((struct linsys *)) ; /* to suppress a proliferation of warnings */ extern int pos_def_warning_flag; extern int eigen_pos,eigen_neg,eigen_zero; /* inertia of shifted hessian */ extern int mat_index; /* number of negatives on diagonal */ extern int mat_null; /* number of zeroes on diagonal */ extern REAL hessian_epsilon; /* cutoff for diagonal elements */ extern REAL hessian_epsilon_default; extern int make_pos_def_flag; /* force hessian to positive definiteness */ extern int hess_debug; /* debugging flag */ /* for facet area calculation */ #define AREA_ONLY 1 #define ALL_ENERGIES 2 /* model dependent function pointers */ #ifdef NOPROTO extern REAL (*userfunc[])(); extern REAL (*userfunc_deriv[])(); extern REAL (*userfunc_seconds[])(); extern void (*calc_facet_energy)(); extern void (*calc_facet_forces)(); extern void (*calc_facet_volume)(); extern void (*calc_edge_energy)(); extern void (*calc_edge_forces)(); extern void (*calc_edge_area)(); extern void (*string_grad)(); extern void (*film_grad)(); #else extern REAL (*userfunc[])(REAL*); extern REAL (*userfunc_deriv[])(REAL*,REAL*); extern REAL (*userfunc_seconds[])(REAL*,REAL*,REAL**); extern void (*calc_facet_energy)(facet_id,int); extern void (*calc_facet_forces)(facet_id); extern void (*calc_facet_volume)(facet_id); extern void (*calc_edge_energy)(edge_id); extern void (*calc_edge_forces)(edge_id); extern void (*calc_edge_area)(edge_id); extern void (*string_grad)(void); extern void (*film_grad)(void); #endif /* gaussian integration on [0,1] */ extern REAL *gauss1Dpt; extern REAL *gauss1Dwt; extern int gauss1D_num; extern REAL **gauss1poly; /* interp polys at gauss pts */ extern REAL **gauss1polyd; /* and derivatives */ /* values for linear or quadratic model, whichever in effect */ extern int edge_ctrl; /* control points on edge, 2 linear, 3 quadratic */ /* 2D gauss */ typedef REAL barytype[3]; extern barytype *gauss2Dpt; extern REAL *gauss2Dwt; extern REAL gauss2Dpt1[1][3]; extern REAL gauss2Dwt1[1]; extern REAL gauss2Dpt2[3][3]; extern REAL gauss2Dwt2[3]; extern REAL gauss2Dpt5[7][3]; extern REAL gauss2Dwt5[7]; extern REAL gauss2Dpt6[12][3]; extern REAL gauss2Dwt6[12]; extern REAL gauss2Dpt8[16][3]; extern REAL gauss2Dwt8[16]; extern REAL gauss2Dpt11[28][3]; extern REAL gauss2Dwt11[28]; extern REAL gauss2Dpt13[37][3]; extern REAL gauss2Dwt13[37]; /* general dimension gauss integration variables and arrays */ extern int ctrl_num; /* number of control points */ extern int gauss2D_num; /* number of integration points */ extern REAL **gpoly; /* interpolation polynomial value at integration point k for polynomial of control point j */ extern REAL ***gpolypartial; /* partials of interpolation polynomials */ /* gausspt,dim,ctrlpt */ #define MAXLAGRANGE 20 /* new gaussian-lagrange structure */ #define MAXGAUSSORDER 40 struct gauss_lag { int lagrange_order; /* computed for */ int gnumpts; /* number of gauss points */ REAL **gausspt; /* gaussian integration pts, in barycentric coords add to 1 */ REAL *gausswt; /* weights at gauss pts */ int lagpts; /* number of lagrange pts */ REAL **gpoly; /* basis polynomial of lagrange pt at gauss pt; index [g][l]; mat mult lagrange to get gauss */ REAL ***gpolypart; /* partials of basis polynomials; index [g][dim][l]; mult lagrange to get tangents */ REAL ***lpolypart; /* partials at lagrange pts */ }; extern struct gauss_lag *gauss_lagrange[MAXCOORD]; extern int maxgaussorder[MAXCOORD]; /* allocated for each dimension */ extern int bezier_flag; /* whether to do Bezier basis polynomials in Lagrange */ extern REAL **bezier1invert[MAXLAGRANGE]; extern REAL **bezier1revert[MAXLAGRANGE]; extern REAL **bezier_refine_1d[MAXLAGRANGE]; extern REAL **bezier2invert[MAXLAGRANGE]; extern REAL **bezier2revert[MAXLAGRANGE]; extern REAL **bezier_refine_2d[MAXLAGRANGE]; extern REAL **metric; /* metric values at a point */ extern REAL ***metric_partial; /* partial derivatives of metric */ extern REAL **det_array; /* tangent vector dot products */ /*extern REAL **tang; */ /* tangents to surface at point */ extern REAL euclidean_area; /* euclidean area for conformal metrics */ extern int klein_metric_flag; extern int metric_convert_flag; /* whether to do form-to-vector conversion */ /* for defining pointer-pointer matrices as local variables */ /* need token-pasting, so gets weird with testing style needed */ #define bbxt 1 #define cxtc #define bbxtcxtc 2 #if (__STDC__ == 1) || defined(__SUNPRO_C) #define ddxt 1 #else #define ddxt bbxt/**/cxtc #endif #if (ddxt == 1) #define MAT2D(name,rows,cols) \ REAL *name##qXvS[rows];\ REAL name##xJ[rows][cols];\ REAL **name = mat2d_setup(name##qXvS,(REAL*)name##xJ,rows,cols) #define MAT3D(name,rows,cols,levels) \ REAL **name##qXvS[(rows)+(rows)*(cols)];\ REAL name##xJ[rows][cols][levels];\ REAL *** name=mat3d_setup(name##qXvS,(REAL*)name##xJ,rows,cols,levels) #define MAT4D(name,rows,cols,levels,tiers) \ REAL ***name##qXvS[(rows)+(rows)*(cols)+(rows)*(cols)*(levels)];\ REAL name##xJ[rows][cols][levels][tiers];\ REAL ****name=mat4d_setup(name##qXvS,(REAL*)name##xJ,rows,cols,levels,tiers) #else #define MAT2D(name,rows,cols) \ REAL *name/**/qXvS[rows];\ REAL name/**/xJ[rows][cols];\ REAL ** name = mat2d_setup(name/**/qXvS,(REAL*)name/**/xJ,rows,cols) #define MAT3D(name,rows,cols,levels) \ REAL **name/**/qXvS[(rows)+(rows)*(cols)];\ REAL name/**/xJ[rows][cols][levels];\ REAL *** name = mat3d_setup(name/**/qXvS,(REAL*)name/**/xJ,rows,cols,levels) #define MAT4D(name,rows,cols,levels,tiers) \ REAL ***name/**/qXvS[(rows)+(rows)*(cols)+(rows)*(cols)*(levels)];\ REAL name/**/xJ[rows][cols][levels][tiers];\ REAL **** name=mat4d_setup(name/**/qXvS,(REAL*)name/**/xJ,rows,cols,levels,tiers) #endif /* end matrix defines */ struct veredge { vertex_id v_id; edge_id e_id; }; /* used in verpopst.c */ /* Text display */ #define MAXTEXTS 100 struct text_s { char *text; /* null-terminated string */ int alloc; /* size of text allocation */ REAL start_x,start_y; /* starting position in 0-1 window coords */ REAL vsize; /* vertical size, pixels?? */ }; extern struct text_s text_chunks[MAXTEXTS]; extern int display_text_count; /* symmetry group stuff */ extern char *symmetry_name; /* to be sure datafile matches */ #ifdef NOPROTO typedef void SYM_WRAP(); /* points to user-defined wrap function */ typedef WRAPTYPE SYM_COMP(); /* points to group composition function */ typedef WRAPTYPE SYM_INV (); /* points to group inverse function */ typedef void SYM_FORM(); /* points to form pullback function */ #else typedef void SYM_WRAP(REAL*,REAL*,WRAPTYPE); typedef WRAPTYPE SYM_COMP(WRAPTYPE,WRAPTYPE); typedef WRAPTYPE SYM_INV (WRAPTYPE); typedef void SYM_FORM(REAL*,REAL*,REAL*,WRAPTYPE); #endif extern SYM_WRAP *sym_wrap; extern int sym_flags; extern SYM_FORM *sym_form_pullback; extern SYM_INV *sym_inverse; extern SYM_COMP *sym_compose; /* structure for registry.c */ struct sym_registry { char *name; /* name to match in datafile SYMMETRY_GROUP phrase */ int flags; /* see bits below */ SYM_WRAP *wrapper; /* point wrap function */ SYM_COMP *compose; /* group composition function */ SYM_INV *inverse; /* group inverse function */ SYM_FORM *pullback; /* form pullback function */ }; extern struct sym_registry sym_register[]; /* actual in registry.c */ /* flag bits */ #define HAS_FIXED_PTS 1 #define NEED_FORM_UNWRAPPING 2 #define DOUBLE_AXIAL 4 /* specific case of torus symmetry representation */ #define TWRAPBITS 6 #define POSWRAP 1 #define WRAPMASK 037 #define ALLWRAPMASK 03737373737 #define NEGWRAP WRAPMASK #define WRAPNUM(x) ( (x)>(1<<(TWRAPBITS-2)) ? (x)-(1<<(TWRAPBITS-1)) : (x)) /* for symmetry group "rotate" */ extern int rotorder; /* additional viewing transforms */ extern int transform_count; extern REAL ***view_transforms; extern int view_transforms_global; /* global var number */ extern int *view_transform_det; /* to see if normals need flipping */ extern int transforms_flag; /* whether to show transforms */ extern int transform_gen_count; typedef struct expnode expnodearray[MAXCOORD][MAXCOORD]; extern expnodearray *view_transform_gens_expr; /* generator expressions */ extern REAL ***view_transform_gens; /* generator values */ extern char transform_expr[100]; /* save it */ extern int transform_depth; /* tree depth in transform generation */ extern int *transform_colors; extern int view_transform_swap_colors_global; /* global var number */ extern int *transform_parity; extern int *transform_gen_swap; #define SAME_COLOR 0 #define SWAP_COLORS 1 extern int transform_colors_flag; /* color for transparent facets */ #define CLEAR (-1) /* color for unshown facet */ #define UNSHOWN (-2) /* color for saying color is not set */ #define NOT_A_COLOR (-3) /* from borlandc/graphics.h */ #if !defined(__COLORS) #define __COLORS enum COLORS { BLACK, /* dark colors */ BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, LIGHTGRAY, DARKGRAY, /* light colors */ LIGHTBLUE, LIGHTGREEN, LIGHTCYAN, LIGHTRED, LIGHTMAGENTA, YELLOW, WHITE }; #endif #ifdef SHARED_MEMORY /* process ids */ extern int proc_ids[MAXPROCS]; #define M_ACTIVE 423 #define M_INACTIVE 0 extern int mpflag; /* whether multiprocessing in action */ extern int m_breakflag[MAXPROCS]; /* for user interrupts */ /* locks and stuff */ #ifdef SGI_MULTI extern usptr_t *lock_arena; /* arena where locks are */ extern ulock_t locklist[_MAXLOCKS]; /* only 4096 available */ extern char lock_arena_name[]; /* for usinit() */ #endif #endif /* for keeping multiprocessor force updates from conflicting */ struct procforce { vertex_id v_id; int next; /* link */ REAL f[MAXCOORD]; }; extern struct procforce *pforce; extern int phead[MAXPROCS][MAXPROCS]; /* [owner][calculator] */ extern struct procforce *pbase[MAXPROCS]; /* allocated to calculator */ extern int ptop[MAXPROCS]; /* used per calculator */ extern int pmax[MAXPROCS]; /* available per calculator */ /* Graphing mutual exclusion for multi-threaded version */ #define IMMEDIATE_TIMEOUT 0 #define LONG_TIMEOUT 100000 #ifdef WIN32 extern void * graphmutex; extern void * mem_mutex; extern unsigned int locking_thread; /* so we can tell who has it */ /* Note ENTER_GRAPH_MUTEX and LEAVE_GRAPH_MUTEX form a {} block! */ /* Also set up to be safe to nest within a thread. */ #define ENTER_GRAPH_MUTEX { int did_graphlock_here;\ if ( locking_thread != GetCurrentThreadId() ) \ { MsgWaitForMultipleObjects(1,&graphmutex,0,100000,0); \ locking_thread = GetCurrentThreadId(); \ did_graphlock_here = 1; } \ else did_graphlock_here = 0; #define TRY_GRAPH_MUTEX(timeout) ( \ (MsgWaitForMultipleObjects(1,&graphmutex,0,timeout,0)!=WAIT_TIMEOUT) && \ ((locking_thread = GetCurrentThreadId())!=0) ) #define END_TRY_GRAPH_MUTEX \ {locking_thread = 0; ReleaseMutex(graphmutex);} #define LEAVE_GRAPH_MUTEX if ( did_graphlock_here ) \ {locking_thread = 0; ReleaseMutex(graphmutex); did_graphlock_here = 0; } } #define ABORT_GRAPH_MUTEX \ if ( GetCurrentThreadId() == locking_thread )\ {locking_thread = 0; ReleaseMutex(graphmutex); } else {} #define ENTER_MEM_MUTEX { MsgWaitForMultipleObjects(1,&mem_mutex,0,100000,0); } #define LEAVE_MEM_MUTEX { ReleaseMutex(mem_mutex); } #elif defined(PTHREADS) extern pthread_mutex_t graphmutex; extern pthread_t locking_thread; /* so we can tell who has it */ #define ENTER_GRAPH_MUTEX {int did_graphlock_here;\ if ( locking_thread != pthread_self() ) \ { pthread_mutex_lock(&graphmutex);locking_thread=pthread_self();\ did_graphlock_here = 1;}\ else did_graphlock_here = 0; #define TRY_GRAPH_MUTEX(timeout) ((pthread_mutex_trylock(&graphmutex)!=EBUSY) &&\ (locking_thread=pthread_self())) #define LEAVE_GRAPH_MUTEX if ( did_graphlock_here ) \ {locking_thread=0;pthread_mutex_unlock(&graphmutex);} } #define END_TRY_GRAPH_MUTEX \ {locking_thread=0;pthread_mutex_unlock(&graphmutex);} #define ABORT_GRAPH_MUTEX if ( locking_thread == pthread_self() ) \ {locking_thread=0;pthread_mutex_unlock(&graphmutex);} else {} #define ENTER_GRAPH_MUTEX_PR printf("Lock %s:%d\n",__FILE__,__LINE__);pthread_mutex_lock(&graphmutex); #define LEAVE_GRAPH_MUTEX_PR printf("Unlock %s:%d\n",__FILE__,__LINE__); pthread_mutex_unlock(&graphmutex); extern pthread_mutex_t mem_mutex; #define ENTER_MEM_MUTEX pthread_mutex_lock(&mem_mutex); #define LEAVE_MEM_MUTEX pthread_mutex_unlock(&mem_mutex); #else #define ENTER_GRAPH_MUTEX {} #define LEAVE_GRAPH_MUTEX {} #define ENTER_MEM_MUTEX {} #define LEAVE_MEM_MUTEX {} #define TRY_GRAPH_MUTEX(timeout) 1 #define ABORT_GRAPH_MUTEX {} #endif /* Multithreading with worker threads */ #ifdef WIN32 extern volatile LONG busythreads; /* number of threads that have started on a task */ #else extern int busythreads; /* number of threads that have started on a task */ #endif extern int threadflag; /* 1 if -p option used and worker threads in effect */ extern int thread_task; /* which task to do */ extern element_id global_id; /* global iteration variable */ extern int web_locks; /* informational counters */ extern int thread_locks; extern int element_locks; extern int web_unlocks; extern int thread_unlocks; extern int element_unlocks; /* Task definitions */ #define TH_PROJECT_ALL_TEST 1 #define TH_PROJECT_ALL_ACTUAL 2 #define TH_CALC_FACET_ENERGY 3 #define TH_CALC_FACET_FORCES 4 #define TH_MULTI_CALC_QUANT 5 #define TH_MULTI_QUANT_GRADS 6 #define TH_MULTI_QUANT_HESS 7 #define TH_FIX_GRADS 8 #define TH_MOVE_VERTICES 9 #ifdef PTHREAD_LOG struct thread_event { /* for tracking thread events */ int type; unsigned int time_high; /* from rdtsc() */ unsigned int time_low; }; #define MAXTHREADEVENTS 10000 extern struct thread_event main_events[MAXTHREADEVENTS]; extern int main_eventcount; #endif /* for controlling thread sweep blocks in disjoint stages */ extern int v_partition_coord_attr; /* number of partitioning attribute */ extern int e_partition_coord_attr; /* number of partitioning attribute */ extern int f_partition_coord_attr; /* number of partitioning attribute */ extern int b_partition_coord_attr; /* number of partitioning attribute */ extern int fe_partition_coord_attr; /* number of partitioning attribute */ extern int v_partition_stage_attr; /* which stage element is in */ extern int v_partition_proc_attr; /* which processor element is assigned to */ extern int e_partition_stage_attr; /* which stage element is in */ extern int e_partition_proc_attr; /* which processor element is assigned to */ extern int f_partition_stage_attr; /* which stage element is in */ extern int f_partition_proc_attr; /* which processor element is assigned to */ extern int b_partition_stage_attr; /* which stage element is in */ extern int b_partition_proc_attr; /* which processor element is assigned to */ extern int fe_partition_stage_attr; /* which stage element is in */ extern int fe_partition_proc_attr; /* which processor element is assigned to */ /* Extra stage in proc 0 will be for elements too big for one stage */ #define MAXTHREADSTAGES 9 struct thread_stages_data { element_id *blocks[NUMELEMENTS][MAXTHREADSTAGES]; int counts[NUMELEMENTS][MAXTHREADSTAGES]; int allocated[NUMELEMENTS][MAXTHREADSTAGES]; volatile int stage; /* current stage, can be read by other procs */ int spot; /* in current block */ }; extern struct thread_stages_data *thread_stages; extern int max_thread_stages; /* actual number of stages to do */ extern long partition_timestamp; /* last time partitioning done */ struct thread_data { /* per thread data, pointed to by thread_data_key */ int worker_id; /* number of created thread, 0 to nproc-1 */ int task_serial_number; int task_state; /* for debugging */ int in_crit; /* for debugging */ element_id iteration_id; REAL total_energy; REAL total_area; /* stuff for single-stack-per-thread implementation of eval() */ REAL *eval_stack; /* stack start */ int eval_stack_size; /* allocated size */ REAL *stack_top; /* occupied stack top */ int frame_spot; /* start of topmost frame */ #ifdef _MSC_VER __int64 stagestart[MAXTHREADSTAGES]; __int64 stageend[MAXTHREADSTAGES]; #endif #ifdef PTHREAD_LOG int eventcount; /* for timestamping events */ struct thread_event events[MAXTHREADEVENTS]; #endif }; extern struct thread_data **thread_data_ptrs; /* one per thread to be allocated */ /* Note: all system-specific thread stuff is in tmain.c */ /* in case some header file uses these, like windows.h */ #undef LOCK_ELEMENT #undef UNLOCK_ELEMENT #ifdef PTHREADS #include extern void * element_mutex_ptr; extern void * web_mutex_ptr; #define GET_THREAD_DATA (struct thread_data *)pthread_getspecific(thread_data_key) #define LOCK_ELEMENT(id) (threadflag ? mylock_element(id) : 0) #define UNLOCK_ELEMENT(id) (threadflag ? (elptr(id)->lock = 0) : 0) #define LOCK_WEB (threadflag ? ((pthread_mutex_trylock(web_mutex_ptr)==EBUSY)?\ (pthread_mutex_lock(web_mutex_ptr),web_locks++): web_unlocks++ ): 0) #define UNLOCK_WEB (threadflag ? pthread_mutex_unlock(web_mutex_ptr): 0) extern pthread_key_t thread_data_key; #define GET_THREAD_ID (((struct thread_data*)(GET_THREAD_DATA))->worker_id) #endif #ifdef WINTHREADS #ifndef _WINDOWS_ /* some stuff so windows.h doesn't have to be included everywhere */ extern LPCRITICAL_SECTION element_mutex_ptr; extern LPCRITICAL_SECTION web_mutex_ptr; int __stdcall TryEnterCriticalSection(void *); void __stdcall EnterCriticalSection(void *); void __stdcall LeaveCriticalSection(void *); #endif struct thread_data *win_get_thread_data(unsigned long); #define GET_THREAD_DATA win_get_thread_data(thread_data_key) #define GET_THREAD_ID (((struct thread_data*)(GET_THREAD_DATA))->worker_id) extern unsigned long thread_data_key; #define LOCK_ELEMENT(id) (threadflag?mylock_element(id):0) #define UNLOCK_ELEMENT(id) (threadflag?myunlock_element(id):0) #define LOCK_WEB (threadflag?mylock_web():0) #define UNLOCK_WEB (threadflag?myunlock_web():0) #endif // for systems without threads activated extern struct thread_data default_thread_data; extern struct thread_data *default_thread_data_ptr; #ifndef THREADS #define LOCK_WEB #define UNLOCK_WEB #define LOCK_ELEMENT(id) #define UNLOCK_ELEMENT(id) #define GET_THREAD_DATA (&default_thread_data) #define GET_THREAD_ID 0 #endif /* global communication to threads */ extern int m_hess_mode; extern int m_rhs_mode; extern int m_type; extern int m_mode; extern REAL *m_rhs; extern double cpu_speed; #if defined(_MSC_VER) && !defined(__BORLANDC__) && !(_IA64_==1) && !defined(_WIN64) /*************************************************************************** Some macros for cycle-counting profiling. Usage: Put PROF_START(f) at start of function f, PROF_FINISH(f) at end, define global __int32 f_elapsed_time[2]; and print when done with PROF_PRINT(f); Make sure all return paths go through end of function. ***************************************************************************/ /* always do PROF_NOW, so can get CPU counter even if not profiling */ #define PROF_NOW(fullname) { __asm {rdtsc} __asm {mov fullname,eax} \ __asm { mov fullname[4],edx} } #define PROF_CYCLES(fullname) ((double)*(__int64*)fullname) #ifdef PROF_EVALS #define PROF_EVAL_START(ex) { __asm {rdtsc} __asm {mov eval_elapsed_time,eax} \ __asm {mov eval_elapsed_time[4],edx} \ (ex)->elapsed_time -= (((REAL)*(__int64*)eval_elapsed_time)); } #define PROF_EVAL_END(ex) { __asm {rdtsc} __asm {mov eval_elapsed_time,eax} \ __asm {mov eval_elapsed_time[4],edx} \ (ex)->elapsed_time += (((REAL)*(__int64*)eval_elapsed_time)); } #endif #ifdef PROFILING #define PROFILING_ENABLED #define PROF_START(f) { __asm {rdtsc} __asm {sub f##_elapsed_time,eax} \ __asm {sbb f##_elapsed_time[4],edx} } #define PROF_FINISH(f) { __asm {rdtsc} __asm {add f##_elapsed_time,eax} \ __asm {adc f##_elapsed_time[4],edx} } #define PROF_ELAPSED(f) (((REAL)*(__int64*)f##_elapsed_time)/cpu_speed) #define PROF_RESET(f) f##_elapsed_time[0]=f##_elapsed_time[1]=0; #define PROF_PRINT(f) { fprintf(stderr,"%22s: %13.0f\n",#f,\ (double)*(__int64*)f##_elapsed_time); \ f##_elapsed_time[0]=f##_elapsed_time[1]=0;} #define METHOD_PROFILING_START(mi,mode) {PROF_RESET(mode);PROF_START(mode);} #define METHOD_PROFILING_END(mi,mode) {PROF_FINISH(mode);mi->mode##_elapsed_time += PROF_ELAPSED(mode);mi->mode##_call_count++;} #endif #elif defined(__i386) && defined(__GNUC__) && !defined(__STRICT_ANSI__) /* operands are in source-destination order in gcc asm */ #define PROF_NOW(fullname) { \ asm("rdtsc" : : ); \ asm("movl %%eax,%0" : "=m"(fullname[0]) : ); \ asm("movl %%edx,%0" : "=m"(fullname[1]) : ); } #define PROF_CYCLES(fullname) ((double)*(unsigned long long*)fullname) #ifdef PROF_EVALS #define PROF_EVAL_END { \ asm("push %%eax" : :); \ asm("push %%edx" : :); \ asm("rdtsc" : : ); \ asm("add %%eax,%0" : "=m"(eval_elapsed_time) : ); \ asm("adc %%edx,%0" : "=m"(eval_elapsed_time[1]) : ); \ asm("pop %%edx" : :); \ asm("pop %%eax" : :); \ (ex)->elapsed_time -= (((REAL)*(unsigned long long*)eval_elapsed_time)); } #define PROF_EVAL_END { \ asm("push %%eax" : :); \ asm("push %%edx" : :); \ asm("rdtsc" : : ); \ asm("add %%eax,%0" : "=m"(eval_elapsed_time) : ); \ asm("adc %%edx,%0" : "=m"(eval_elapsed_time[1]) : ); \ asm("pop %%edx" : :); \ asm("pop %%eax" : :); \ (ex)->elapsed_time += (((REAL)*(unsigned long long*)eval_elapsed_time)); } #endif #ifdef PROFILING #define PROFILING_ENABLED #define PROF_START(f) { \ asm("push %%eax" : :); \ asm("push %%edx" : :); \ asm("rdtsc" : : ); \ asm("sub %%eax,%0" : "=m"(f##_elapsed_time) : ); \ asm("sbb %%edx,%0" : "=m"(f##_elapsed_time[1]) : ); \ asm("pop %%edx" : :); \ asm("pop %%eax" : :); } #define PROF_FINISH(f) { \ asm("push %%eax" : :); \ asm("push %%edx" : :); \ asm("rdtsc" : : ); \ asm("add %%eax,%0" : "=m"(f##_elapsed_time) : ); \ asm("adc %%edx,%0" : "=m"(f##_elapsed_time[1]) : ); \ asm("pop %%edx" : :); \ asm("pop %%eax" : :); } #define PROF_ELAPSED(f) (((double)*(unsigned long long*)f##_elapsed_time)/cpu_speed) #define PROF_RESET(f) f##_elapsed_time[0]=f##_elapsed_time[1]=0; #define PROF_PRINT(f) { fprintf(stderr,"%22s: %13.0f\n",#f,\ (double)*(unsigned long long*)f##_elapsed_time); \ f##_elapsed_time[0]=f##_elapsed_time[1]=0;} #define METHOD_PROFILING_START(mi,mode) {PROF_RESET(mode);PROF_START(mode);} #define METHOD_PROFILING_END(mi,mode) { PROF_FINISH(mode); \ mi->mode##_elapsed_time += PROF_ELAPSED(mode);mi->mode##_call_count++;} #endif #endif #ifndef PROF_NOW #define PROF_NOW(f) f[0] = f[1] = 0; #define PROF_CYCLES(f) 0.0 #endif #ifndef PROFILING_ENABLED #define PROF_START(f) #define PROF_FINISH(f) #define PROF_ELAPSED(f) 0.0 #define PROF_RESET(f) #define PROF_PRINT(f) #define METHOD_PROFILING_START(mi,mode) #define METHOD_PROFILING_END(mi,mode) #endif #ifndef PROF_EVAL_START #define PROF_EVAL_START #define PROF_EVAL_END #endif extern int element_setup_elapsed_time[2]; extern int calc_quants_elapsed_time[2]; extern int calc_quant_grads_elapsed_time[2]; extern int calc_quant_hess_elapsed_time[2]; extern int exparse_elapsed_time[2]; extern int yyparse_elapsed_time[2]; extern int yylex_elapsed_time[2]; extern int kblex_elapsed_time[2]; extern int hessian_solve_elapsed_time[2]; extern int hessian_mul_elapsed_time[2]; extern int hessian_AIJ_setup_elapsed_time[2]; extern int hessian_constraint_setup_elapsed_time[2]; extern int hessian_project_setup_elapsed_time[2]; extern int hessian_factor_elapsed_time[2]; extern int hessian_CHinvC_elapsed_time[2]; #ifdef _MSC_VER /* Elaborate macro to make element loop fast and local. Usage: THREAD_FOR_ALL_NEW(elementtype,action) where action is executable code. action should refer to the element id as *idptr. action can be extensive code block, but do not use comma in declaration lists; comma ok in argument lists, but if not in nested parentheses, then looks like end of action argument. */ #define THREAD_FOR_ALL_NEW(elementtype,action) \ { element_id *idptr; \ int proc,nextproc; \ struct thread_stages_data *th; \ struct thread_data *data; \ int maxstage; \ int elnum,maxelnum; \ __int32 now[2]; \ \ data = GET_THREAD_DATA; \ proc = data->worker_id; \ nextproc = (proc == nprocs - 1) ? 0 : proc+1; \ maxstage = (proc == 0) ? max_thread_stages+1 : max_thread_stages; \ th = thread_stages + proc; \ \ for ( th->stage = 0 ; th->stage < maxstage ; th->stage++ ) \ { /* wait for nextproc to reach same stage */ \ while ( thread_stages[nextproc].stage < th->stage ) ; \ \ PROF_NOW(now); \ data->stagestart[th->stage] = *(__int64*)now; \ \ /* do all elements in this stage */ \ idptr = th->blocks[elementtype][th->stage]; \ maxelnum = th->counts[elementtype][th->stage]; \ for ( elnum = 0 ; elnum < maxelnum ; elnum++,idptr++ ) \ action; \ \ PROF_NOW(now); \ data->stageend[th->stage] = *(__int64*)now; \ } \ } /* end THREAD_FOR_ALL_NEW */ extern __int64 thread_launch_start; extern __int64 thread_launch_end; #else #define THREAD_FOR_ALL_NEW(elementtype,action) \ { element_id *idptr; \ int proc,nextproc; \ struct thread_stages_data *th; \ struct thread_data *data; \ int maxstage; \ int elnum,maxelnum; \ int now[2]; \ \ data = GET_THREAD_DATA; \ proc = data->worker_id; \ nextproc = (proc == nprocs - 1) ? 0 : proc+1; \ maxstage = (proc == 0) ? max_thread_stages+1 : max_thread_stages; \ th = thread_stages + proc; \ \ for ( th->stage = 0 ; th->stage < maxstage ; th->stage++ ) \ { /* wait for nextproc to reach same stage */ \ while ( thread_stages[nextproc].stage < th->stage ) ; \ \ /* do all elements in this stage */ \ idptr = th->blocks[elementtype][th->stage]; \ maxelnum = th->counts[elementtype][th->stage]; \ for ( elnum = 0 ; elnum < maxelnum ; elnum++,idptr++ ) \ action; \ \ } \ } /* end THREAD_FOR_ALL_NEW */ #endif /* profiling storage */ extern int q_facet_setup_elapsed_time[2]; #ifdef __cplusplus } #endif evolver-2.30c.dfsg/src/iterate.c0000644000175300017530000021160311410765113017021 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************* * * file: iterate.c * * Purpose: Do one iteration of motion. */ #include "include.h" int volgrads_changed_flag; /* whether volgrads recalced during move */ /************************************************************* * * Function: iterate() * * Purpose: calculate and make one move of vertices */ void iterate() { REAL energy0,energy1,energy2=0.0; int seekcount = 0; /* to prevent infinite loop */ REAL denom; /* denominator of interpolation expression */ REAL scale0,scale1=0.0,scale2=0.0; /* for interpolation */ int old_flag = iterate_flag; REAL old_energy = web.total_energy; /* for estimate */ #ifndef MPI_EVOLVER if ( web.skel[VERTEX].count == 0 ) { kb_error(1051,"No vertices. Did you forget to load a surface?\n",WARNING); return; } #endif iterate_flag = 1; /* for interrupt handler */ if ( web.diffusion_flag ) diffuse(); energy0 = web.total_energy; if ( check_pinning_flag ) check_pinning(); if ( normal_motion_flag ) begin_normal_motion(); calc_all_grads(CALC_FORCE|CALC_VOLGRADS); volgrads_changed_flag = 0; save_coords(&saved,SAVE_IN_ATTR); /* in case of disaster */ if ( conj_grad_flag ) { cg_calc_gamma(); /* find adjustment factor */ cg_direction(); /* fix up motion direction */ } ENTER_GRAPH_MUTEX; if ( !web.motion_flag ) /* want to seek minimum energy along gradient */ { REAL tempscale; if ( web.scale > web.maxscale ) web.scale = web.maxscale; tempscale = web.scale <= 0.0 ? web.maxscale*1e-6 : web.scale; if ( tempscale <= 0.0 ) web.scale = web.maxscale*1e-6; else web.scale = tempscale; if ( itdebug ) printf("First move, scale %g\n",(double)web.scale); move_vertices(TEST_MOVE,web.scale); /* moving by current scale */ energy1 = web.total_energy; if ( !is_finite(energy1) ) energy1 = MAXDOUBLE; scale1 = web.scale; if ( itdebug ) { printf("first move:\nscale1 %g energy1 %20.15g \n", (DOUBLE)scale1,(DOUBLE)energy1); if ( web.skel[BODY].count < 10 ) show_volumes(); printf("\n"); } restore_coords(&saved,SAVE_IN_ATTR); if ( volgrads_changed_flag ) { calc_all_grads(CALC_VOLGRADS); calc_leftside(); volgrads_changed_flag = 0; } web.scale = 0.0; /* to do restoring force */ move_vertices(TEST_MOVE,web.scale); /* moving by current scale */ energy0 = web.total_energy; if ( itdebug ) { printf("0th move:\nscale1 %g energy1 %20.15g \n",0.0,(DOUBLE)energy0); if ( web.skel[BODY].count < 10 ) show_volumes(); printf("\n"); } if ( !is_finite(energy0) ) { kb_error(1863,"Infinite energy. Restoring coordinates.\n",WARNING); goto iterate_error_exit; } scale0 = 0.0; restore_coords(&saved,SAVE_IN_ATTR); if ( volgrads_changed_flag ) { calc_all_grads(CALC_VOLGRADS); calc_leftside(); volgrads_changed_flag = 0; } web.scale = tempscale; if ( energy1 < energy0 ) { do { web.scale *= 2; if ( itdebug ) printf("Doubling scale, scale %g\n",(double)web.scale); move_vertices(TEST_MOVE,web.scale); energy2 = web.total_energy; scale2 = web.scale; if ( itdebug ) { printf("scale2 %g energy2 %20.15g \n",(DOUBLE)scale2,(DOUBLE)energy2); if ( web.skel[BODY].count < 10 ) show_volumes(); printf("\n"); } restore_coords(&saved,SAVE_IN_ATTR); if ( volgrads_changed_flag ) { calc_all_grads(CALC_VOLGRADS); calc_leftside(); volgrads_changed_flag = 0; } if ( !is_finite(energy2) ) { web.scale /= 2; goto have_scale;} /* use finite motion */ if ( energy2 > energy1 ) { web.scale = web.scale/2; break; } energy1 = energy2; scale1 = scale2; } while ( (web.scale < web.maxscale) /* || conj_grad_flag */ ); } else /* have to come down in scale */ { do { seekcount++; if ( seekcount > 20 ) /* looks like energy won't decrease */ { web.scale = 0.0; break; } energy2 = energy1; scale2 = scale1; web.scale /= 2; if ( itdebug ) printf("Halving scale, scale %g\n",(double)web.scale); if ( web.scale < 1e-12*web.maxscale ) { web.scale = 0.0; break; } move_vertices(TEST_MOVE,web.scale); energy1 = web.total_energy; if ( !is_finite(energy1) ) energy1 = MAXDOUBLE; scale1 = web.scale; if ( itdebug ) { printf("scale1 %g energy1 %20.15g \n",(DOUBLE)scale1,(DOUBLE)energy1); if ( web.skel[BODY].count < 10 ) show_volumes(); printf("\n"); } restore_coords(&saved,SAVE_IN_ATTR); if ( volgrads_changed_flag ) { calc_all_grads(CALC_VOLGRADS); calc_leftside(); volgrads_changed_flag = 0; } } while ( energy1 > energy0 ); web.scale *= 2; } if ( web.scale > web.maxscale ) web.scale = web.maxscale; else if ( web.scale > 0.0 ) { /* now quadratic interpolation for minimum energy */ denom = energy0*(scale1-scale2)+energy1*(scale2-scale0) + energy2*(scale0 - scale1); if ( denom == 0.0 ) web.scale = 0.0; else { web.scale = ((energy0-energy2)*scale1*scale1 +(energy1-energy0)*scale2*scale2 +(energy2-energy1)*scale0*scale0)/2/denom; } /* else leave scale as is */ } } else if ( runge_kutta_flag ) { /* only for fixed scale */ runge_kutta(); } have_scale: if ( !web.motion_flag ) if ( web.scale > web.maxscale ) web.scale = web.maxscale; /* max on movement */ if ( itdebug ) printf("Final scale: %g\n",(double)web.scale); move_vertices(ACTUAL_MOVE,web.scale_scale*web.scale); if ( web.jiggle_flag ) jiggle(); if( autopop_flag || autochop_flag ) { autopop_detect(web.scale); if ( autopop_count || autochop_count ) { autopop_pop(); autochop_chop(); } autopop_cleanup(); } if ( fixed_constraint_flag ) calc_content(Q_FIXED); if ( !is_finite(web.total_energy )) { kb_error(1864, "Motion would cause infinite energy. Restoring coordinates.\n",WARNING); goto iterate_error_exit; } if ( check_increase_flag && (web.total_energy > energy0) ) { kb_error(1865, "Motion would have increased energy. Restoring coordinates.\n",WARNING); goto iterate_error_exit; } if ( !web.motion_flag && (web.total_energy > energy0) ) { /* go back and use scale1 */ restore_coords(&saved,SAVE_IN_ATTR); web.scale = scale1; /* known to decrease energy */ move_vertices(ACTUAL_MOVE,web.scale); if ( cg_hvector ) /* restart conjugate gradient */ cg_restart(); } total_time += web.scale; if ( web.area_norm_flag && web.norm_check_flag && (web.representation==SOAPFILM) ) { REAL delta = normal_change_check(); if ( delta > web.norm_check_max ) { sprintf(msg,"Max normal change: %f. Restoring coordinates.\n", (DOUBLE) delta); kb_error(1866,msg,WARNING); goto iterate_error_exit; } } /* if chop here, then instabilities have a chance to get out of control */ /* if ( autopop_flag ) autopop_pop(); if ( autochop_flag ) autochop_chop(); */ /* following good for debugging to see if energy gradients really give change in energy. Estimated decrease should be approx exact for scale much less than optimum; at optimum scale, estimate is twice actual (if energy shape true parabola ) But note estimate does not work if form-to-vector has messed things up. */ if ( estimate_flag ) { #ifdef LONGDOUBLE sprintf(msg,"Estimated energy change: %#*.*Lg\n",DWIDTH,DPREC, estimate_decrease()); outstring(msg); sprintf(msg,"Actual energy change : %#*.*Lg\n",DWIDTH,DPREC, web.total_energy-old_energy); outstring(msg); #else sprintf(msg,"Estimated energy change: %#20.15g\n",estimate_decrease()); outstring(msg); sprintf(msg,"Actual energy change : %#20.15g\n", web.total_energy-old_energy); outstring(msg); #endif } #ifdef LONGDOUBLE sprintf(msg,"%3d. energy: %#*.*Lg scale: %#Lg\n",gocount,DWIDTH,DPREC, web.total_energy,web.scale); #else sprintf(msg,"%3d. %s: %#17.15g energy: %#17.15g scale: %#g\n",gocount, areaname,web.total_area,web.total_energy,web.scale); #endif if ( !quiet_go_flag ) outstring(msg); vgrad_end(); unsave_coords(&saved,SAVE_IN_ATTR); update_display(); iterate_flag = old_flag; /* for interrupt handler */ goto iterate_exit; iterate_error_exit: /* in case optimizing scale didn't work */ vgrad_end(); restore_coords(&saved,SAVE_IN_ATTR); if ( count_fixed_vol() || web.pressure_flag ) calc_content(Q_FIXED); calc_pressure(); calc_energy(); /* energy after motion */ unsave_coords(&saved,SAVE_IN_ATTR); iterate_flag = old_flag; /* for interrupt handler */ update_display(); breakflag = BREAKREPEAT; /* break repeat loop */ iterate_exit: LEAVE_GRAPH_MUTEX; } /* end iterate() */ /************************************************************************** * * function: calc_all_grads() * * purpose: recalculate forces and/or constraint gradients. * */ void calc_all_grads(mode) int mode; /* bits for CALC_FORCE and CALC_VOLGRADS */ { find_fixed(); if ( mode & CALC_VOLGRADS ) calc_volgrads(DO_OPTS); if ( mode & CALC_FORCE ) { calc_force(); pressure_forces(); } partner_shift_grads(mode); /* in case doing partners */ /* Now have energy and constraint gradients as forms. Next convert to vectors using current metric. */ /* lagrange_recalc: */ convert_forms_to_vectors(mode); /* using current metric */ /*one_sided_adjust(mode);*/ /* modify forces and grads on one-sided constraints */ if ( mode & CALC_FORCE ) { calc_lagrange(); /* calculate Lagrange multipliers */ lagrange_adjust(); /* adjust forces using Lagrange multipliers */ pressure_set_flag = 1; } } /* end calc_all_grads() */ /****************************************************************** * * function: burchard() * * purpose: implement Paul Burchard's scheme for accelerating * motion by ramping scale factor so instabilities are damped. * * Starts with current scale factor and ramps up, so use 'm' * command to set small starting scale. Will reset scale at * end to original small value. */ void burchard(maxsteps) int maxsteps; { REAL old_scale = web.scale; int old_motion_flag = web.motion_flag; int i; web.motion_flag = 1; for ( i = 0 ; i < maxsteps ; i++ ) { web.scale =old_scale /(1 - (i*i)/(REAL)(maxsteps*maxsteps)); iterate(); } web.scale = old_scale; web.motion_flag = old_motion_flag; } /*************************************************************** * * Function: fix_vertices() * * Purpose: Fixes vertices by zeroing out force. Also projects * force to be tangent to boundary and constraints. * * only current use is in square_grad() */ void fix_vertices() { vertex_id v_id; REAL *force; int i,j; if ( itdebug ) outstring("fix_vertices(): project forces to level-set constraints\n"); if ( check_pinning_flag ) { edge_id e_id; /* check for vertices that can't move because adjacent vertices are not on same constraint when they could be */ FOR_ALL_VERTICES(v_id) /* clear pinning flag */ vptr(v_id)->attr &= ~PINNED_V; FOR_ALL_EDGES(e_id) { vertex_id headv = get_edge_headv(e_id); vertex_id tailv = get_edge_tailv(e_id); conmap_t * hstat = get_v_constraint_map(headv); conmap_t * tstat = get_v_constraint_map(tailv); for ( i=1, j=0 ; i <= (int)hstat[0] ; i++ ) if (hstat[i] & CON_HIT_BIT) j++; if ( j == 1 ) set_attr(tailv,PINNED_V); for ( i=1, j=0 ; i <= (int)tstat[0] ; i++ ) if (tstat[i] & CON_HIT_BIT) j++; if ( j == 1 ) set_attr(headv,PINNED_V); } FOR_ALL_VERTICES(v_id) /* see if 2 con vertex has 2 con nbrs */ { conmap_t * hit = get_v_constraint_map(v_id); for ( i = 1, j = 0 ; i <= (int)hit[0] ; i++ ) if ( hit[i] & CON_HIT_BIT ) j++; if ( j != 2 ) set_attr(v_id,PINNED_V); } } FOR_ALL_VERTICES(v_id) { ATTR attr = get_vattr(v_id); force = get_force(v_id); if ( attr & FIXED ) { memset((char*)force,0,SDIM*sizeof(REAL)); continue; } if ( mobility_flag ) { if ( mobility_tensor_flag ) { REAL new_force[MAXCOORD]; REAL *x = get_coord(v_id); for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0, new_force[i] = 0.0 ; j < SDIM ; j++ ) new_force[i] += force[j]*eval(&mobility_tensor[i][j],x,v_id,NULL); for ( j = 0 ; j < SDIM ; j++ ) force[j] = new_force[j]; } else { REAL mobility = eval(&mobility_formula,get_coord(v_id),v_id,NULL); for ( j = 0 ; j < SDIM ; j++ ) force[j] *= mobility; } } if ( normal_motion_flag ) { /* project to normal */ REAL d; int vnum = loc_ordinal(v_id); REAL *normal = vertex_normals[vnum]; d = SDIM_dot(force,normal); for ( j = 0 ; j < SDIM ; j++ ) force[j] = d*normal[j]; } if ( (attr & CONSTRAINT) && (!check_pinning_flag || (attr & PINNED_V)) ) { conmap_t * conmap = get_v_constraint_map(v_id); int oncount = 0; struct constraint *con[MAXCONPER]; int conlist[MAXCONPER]; REAL perp[MAXCOORD]; for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { if ( conmap[j] & CON_HIT_BIT ) { conlist[oncount] = conmap[j] & CONMASK; con[oncount] = get_constraint(conmap[j]); /* if ( !(con[oncount]->attr & (NONNEGATIVE|NONPOSITIVE) ) ) */ oncount++; } } if ( oncount > SDIM ) { sprintf(errmsg, "Vertex %s is on more constraints than the dimension of space.\n", ELNAME(v_id)); kb_error(2084,errmsg,WARNING); oncount = SDIM; } if ( oncount ) { constr_proj(TANGPROJ,oncount,con,get_coord(v_id), force,perp,conlist,DETECT,v_id); for ( j = 0 ; j < SDIM ; j++ ) force[j] -= perp[j]; if ( web.area_norm_flag && (web.representation==STRING) ) { /* correction factor for moving vertex along constraint is to divide projected force by sin^2 of contact angle, here crudely estimated. */ REAL sinsq = SDIM_dot(perp,perp); if ( (sinsq != 0.0) && (sinsq < 1.0) ) for ( j = 0 ; j < SDIM ; j++ ) force[j] /= sinsq; } } } } if ( web.h_inverse_metric_flag ) apply_h_inverse_metric(); } /* end fix_vertices() */ /*************************************************************** * * Function: move_vertices() * * Purpose: handle possible MPI invocation of local_move_vertices */ void move_vertices(mode,scale) int mode; /* TEST_MOVE or ACTUAL_MOVE */ REAL scale; { if ( itdebug ) outstring("move_vertices(): by scale factor times velocity\n"); #ifdef MPI_EVOLVER if ( this_task != 0 ) return; mpi_move_vertices(mode,scale); #else local_move_vertices(mode,scale); #endif if ( web.homothety ) homothety(); global_timestamp++; partner_move(); /* in case doing partners */ project_all(1, mode); if ( fixed_constraint_flag || web.pressure_flag || web.pressflag ) calc_pressure(); calc_energy(); /* energy after motion */ } /*************************************************************** * * Function: local_move_vertices() * * Purpose: moves all unfixed vertices by current scale * factor and force. * * Input: uses global variable for scale */ REAL thread_scale; /* for passing scale argument to threads */ void local_move_vertices(mode,scale) int mode; /* TEST_MOVE or ACTUAL_MOVE */ REAL scale; { REAL *velocity; REAL *x; int i; vertex_id v_id; int dim = ackerman_flag ? 2*SDIM : SDIM; if ( optparamcount > 0 ) { for ( i = 0 ; i < optparamcount ; i++ ) globals(optparam[i].pnum)->value.real -= scale*optparam[i].velocity; project_all(0,TEST_MOVE); /* force vertices to constraints */ } /* move by multiple of velocity */ if ( threadflag ) { thread_scale = scale; thread_launch(TH_MOVE_VERTICES,VERTEX); } else FOR_ALL_VERTICES(v_id) { if ( get_vattr(v_id) & FIXED ) continue ; velocity = get_velocity(v_id); x = get_coord(v_id); if ( get_vattr(v_id) & BOUNDARY ) { struct boundary *boundary = get_boundary(v_id); int pcount = boundary->pcount; REAL *param = get_param(v_id); for ( i = 0 ; i < pcount ; i++ ) param[i] += scale*velocity[i]; } else { for ( i = 0 ; i < dim ; i++ ) x[i] += scale*velocity[i]; } } return; } /* end local_move_vertices() */ #ifdef THREADS /***************************************************************** * * function: thread_move_vertices() * * purpose: one thread's part of move_vertices() */ void thread_move_vertices() { int i; int dim = ackerman_flag ? 2*SDIM : SDIM; THREAD_FOR_ALL_NEW(VERTEX, /* following block is macro argument */ { vertex_id v_id = *idptr; REAL *velocity; REAL *x; if ( get_vattr(v_id) & FIXED ) continue ; velocity = get_velocity(v_id); x = get_coord(v_id); if ( get_vattr(v_id) & BOUNDARY ) { struct boundary *boundary = get_boundary(v_id); int pcount = boundary->pcount; REAL *param = get_param(v_id); for ( i = 0 ; i < pcount ; i++ ) param[i] += thread_scale*velocity[i]; } else { for ( i = 0 ; i < dim ; i++ ) x[i] += thread_scale*velocity[i]; } } ) /* end of macro argument */ } #endif /* THREADS */ /*************************************************************************** * * function: project_all() * * purpose: projection to all constraints and boundaries after moving. */ void project_all(mode,mode2) int mode; /* 1 for extensive constraints */ int mode2; /* TEST_MOVE or ACTUAL_MOVE */ { vertex_id v_id; int one_sided_mode = (mode2==TEST_MOVE) ? KEEP_ONESIDEDNESS : RESET_ONESIDEDNESS; /* project to constraints and boundaries */ if ( itdebug ) outstring("project_all(): to level set and extensive constraints\n"); if ( threadflag ) { int task = mode2==TEST_MOVE ? TH_PROJECT_ALL_TEST : TH_PROJECT_ALL_ACTUAL; thread_launch(task,VERTEX); } else FOR_ALL_VERTICES(v_id) { int attr = get_vattr(v_id); if ( attr & CONSTRAINT ) project_v_constr(v_id,mode2,one_sided_mode); else if ( attr & BOUNDARY ) { int i; struct boundary *boundary = get_boundary(v_id); REAL *param = get_param(v_id); REAL *x = get_coord(v_id); for ( i = 0 ; i < SDIM ; i++ ) x[i] = eval(boundary->coordf[i],param,v_id,NULL); } } /* TESTING new feature */ detect_bdry_hits(); /* enforce volume constraints also; keep conjugate gradient under control */ if ( mode && fixed_constraint_flag ) { int calc_count; REAL orig_diff = calc_content(Q_FIXED); REAL old_diff = orig_diff; REAL diff=old_diff; /* total relative constraint diff */ REAL stepsize = 1.0; struct oldcoord psaved; calc_count = 1; psaved.coord = NULL; save_coords(&psaved,SAVE_SEPARATE); do { if ( diff == 0.0 ) break; if ( itdebug ) printf("Diff: %g Old_diff: %g\n", (double)diff,(double)old_diff); if ( diff > 1.01*old_diff ) { if ( itdebug ) printf("Diff increased. Restoring coords.\n"); restore_coords(&psaved,SAVE_SEPARATE); if ( diff > 100*old_diff ) { sprintf(msg,"Total constraint difference would increase by factor of %f.\nAborting constraint adjustment.\n",fabs(diff/old_diff) ); outstring(msg); break; } /* calc_content(Q_FIXED); taken care of by restore_coords */ stepsize *= 0.5; } else stepsize = 1.0; if ((volgrads_every_flag || (diff > 0.5*old_diff)) && (calc_count > 1)) { /* not good improvement, so recalculate */ if ( itdebug ) printf("Recalculating volgrads.\n"); calc_all_grads(CALC_VOLGRADS); calc_leftside(); volgrads_changed_flag = 1; } volume_restore(stepsize,mode2); global_timestamp++; if ( calc_count++ > 10 ) { sprintf(errmsg,"Volume or quantity constraints don't converge in 10 projections.\nTotal difference %g times tolerance\n",(DOUBLE)diff); if ( mode2 == ACTUAL_MOVE ) kb_error(1055,errmsg,WARNING); reset_conj_grad(); break; } old_diff = diff; diff = calc_content(Q_FIXED); if ( itdebug ) printf("Next diff: %g\n",(double)diff); } while ( mode && (diff > 1) ); unsave_coords(&psaved,SAVE_SEPARATE); } /* end enforcing volume constraints */ global_timestamp++; return; } /* end project_all() */ #ifdef THREADS /*************************************************************************** * * function thread_project_all() * * purpose: thread-friendly first part of project_all * */ void thread_project_all(int mode2) { int one_sided_mode = (mode2==TEST_MOVE) ? KEEP_ONESIDEDNESS : RESET_ONESIDEDNESS; /* project to constraints and boundaries */ THREAD_FOR_ALL_NEW(VERTEX, { int attr = get_vattr(*idptr); if ( attr & CONSTRAINT ) project_v_constr(*idptr,mode2,one_sided_mode); else if ( attr & BOUNDARY ) { int i; struct boundary *boundary = get_boundary(*idptr); REAL *param = get_param(*idptr); REAL *x = get_coord(*idptr); for ( i = 0 ; i < SDIM ; i++ ) x[i] = eval(boundary->coordf[i],param,*idptr,NULL); } } ) /* end of THREAD_FOR_ALL macro */ } #endif /* THREADS */ /************************************************************************* * * Function: save_coords() * * Purpose: wrapper for local_save_coords() * NOTE: relies on mode to determine where to save, rather than saver */ void save_coords(saver,mode) struct oldcoord *saver; int mode; /* SAVE_IN_ATTR if use vertex attribute __oldx */ /* SAVE_SEPARATE for separate memory allocation */ { #ifdef MPI_EVOLVER if ( this_task != 0 ) return; mpi_save_coords(mode); #else local_save_coords(saver,mode); #endif } /**************************************************************** * * Function: local_save_coords() * * Purpose: Save current coordinates so they can be restored * after a trial motion. * */ void local_save_coords(saver,mode) struct oldcoord *saver; int mode; /* SAVE_IN_ATTR if use vertex attribute __oldx */ /* SAVE_SEPARATE for separate memory allocation */ { vertex_id v_id; body_id b_id; int n; if ( mode == SAVE_SEPARATE ) { if ( saver->coord ) temp_free((char *)saver->coord); /* in case somebody forgot */ saver->coord = (REAL (*)[MAXCOORD])temp_calloc(web.skel[VERTEX].max_ord+1, sizeof(REAL)*MAXCOORD); FOR_ALL_VERTICES(v_id) { if ( get_vattr(v_id) & BOUNDARY ) memcpy((char *)(saver->coord+loc_ordinal(v_id)),(char *)get_param(v_id), sizeof(REAL)*web.maxparam); else memcpy((char *)(saver->coord+loc_ordinal(v_id)),(char *)get_coord(v_id), sizeof(REAL)*SDIM); } } else { FOR_ALL_VERTICES(v_id) { if ( get_vattr(v_id) & BOUNDARY ) memcpy((char *)(get_oldcoord(v_id)),(char *)get_param(v_id), sizeof(REAL)*web.maxparam); else memcpy((char *)(get_oldcoord(v_id)),(char *)get_coord(v_id), sizeof(REAL)*SDIM); } } /* also save old energy and quantities */ /* always saving separately since in-structure save being used elsewhere */ saver->energy = web.total_energy; saver->bod = (REAL*)temp_calloc(web.skel[BODY].max_ord+1,3*sizeof(REAL)); FOR_ALL_BODIES(b_id) { int spot = 3*loc_ordinal(b_id); saver->bod[spot] = get_body_volume(b_id); saver->bod[spot+1] = get_body_pressure(b_id); saver->bod[spot+2] = get_body_volconst(b_id); } saver->meth = (REAL*)temp_calloc(meth_inst_count,sizeof(REAL)); for ( n = LOW_INST ; n < meth_inst_count ; n++ ) saver->meth[n] = METH_INSTANCE(n)->value; saver->quant = (REAL*)temp_calloc(gen_quant_count,2*sizeof(REAL)); for ( n = 0 ; n < gen_quant_count ; n++ ) { saver->quant[2*n] = GEN_QUANT(n)->value; saver->quant[2*n+1] = GEN_QUANT(n)->pressure; } /* save optimizing parameters */ for ( n = 0 ; n < optparamcount ; n++ ) saver->optparam_values[n] = globals(optparam[n].pnum)->value.real; } /* end save_coords() */ /**************************************************************** * * Function: restore_coords() * * Purpose: wrapper for local_restore_coords() * */ void restore_coords(saver,mode) struct oldcoord *saver; int mode; { #ifdef MPI_EVOLVER if ( this_task != 0 ) return; mpi_restore_coords(mode); #else local_restore_coords(saver,mode); #endif } /**************************************************************** * * Function: local_restore_coords() * * Purpose: Restore current coordinates after a trial motion. * */ void local_restore_coords(saver,mode) struct oldcoord *saver; int mode; { vertex_id v_id; body_id b_id; int n; if ( mode == SAVE_SEPARATE ) { if ( saver->coord == NULL ) kb_error(1056, "Internal error: Cannot restore old coordinates since there aren't any!\n", RECOVERABLE); } /* restore optimizing parameters */ for ( n = 0 ; n < optparamcount ; n++ ) globals(optparam[n].pnum)->value.real = saver->optparam_values[n]; FOR_ALL_VERTICES(v_id) restore_vertex(v_id,saver,mode); web.total_energy = saver->energy; FOR_ALL_BODIES(b_id) { int spot = 3*loc_ordinal(b_id); set_body_volume(b_id,saver->bod[spot],NOSETSTAMP); set_body_pressure(b_id,saver->bod[spot+1]); set_body_volconst(b_id,saver->bod[spot+2]); } for ( n = LOW_INST ; n < meth_inst_count ; n++ ) METH_INSTANCE(n)->value = saver->meth[n]; for ( n = 0 ; n < gen_quant_count ; n++ ) { GEN_QUANT(n)->value = saver->quant[2*n]; GEN_QUANT(n)->pressure = saver->quant[2*n+1]; } global_timestamp++; } /* end restore_coords() */ /****************************************************************** * * Function: restore_vertex() * * Purpose: Put a vertex back where it was. */ void restore_vertex(v_id,saver,mode) vertex_id v_id; struct oldcoord *saver; int mode; /* whether to use _oldx */ { int i; REAL *p,*x; struct boundary *bdry; if ( get_vattr(v_id) & BOUNDARY ) { p = get_param(v_id); x = get_coord(v_id); bdry = get_boundary(v_id); if ( mode == SAVE_SEPARATE ) memcpy((char *)p,(char *)(saver->coord+loc_ordinal(v_id)), sizeof(REAL)*web.maxparam); else memcpy((char *)p,(char *)(get_oldcoord(v_id)), sizeof(REAL)*web.maxparam); for ( i = 0 ; i < SDIM ; i++ ) x[i] = eval(bdry->coordf[i],p,v_id,NULL); } else { if ( mode == SAVE_SEPARATE ) memcpy((char *)get_coord(v_id),(char *)(saver->coord+loc_ordinal(v_id)), sizeof(REAL)*SDIM); else memcpy((char *)get_coord(v_id),(char *)(get_oldcoord(v_id)), sizeof(REAL)*SDIM); } } /* end restore_vertex() */ /**************************************************************** * * Function: unsave_coords() * * Purpose: wrapper for local_unsave_coords() * */ void unsave_coords(saver,mode) struct oldcoord *saver; int mode; { #ifdef MPI_EVOLVER if ( this_task != 0 ) return; mpi_unsave_coords(mode); #else local_unsave_coords(saver,mode); #endif } /******************************************************************** * * Function: local_unsave_coords() * * Purpose: Clean up after all trial motions done. */ void local_unsave_coords(saver,mode) struct oldcoord *saver; int mode; { if ( saver->coord ) temp_free( (char *)saver->coord ); saver->coord = NULL; if ( saver->bod ) temp_free( (char *)saver->bod ); saver->bod = NULL; if ( saver->quant ) temp_free( (char *)saver->quant ); saver->quant = NULL; if ( saver->meth ) temp_free( (char *)saver->meth ); saver->meth = NULL; } /**************************************************************** * * Function: jiggle() * * Purpose: Move each vertex a little bit at random to get * away from metastable equilibria and crystalline * integrand hangups. * * Input: The global variable temperature scales the size * of the jiggles. They are taken to be spherically * symmetric Gaussian distribution with mean size * temperature*max_length. */ void jiggle() { vertex_id v_id; REAL *x; int j; if ( web.max_len == 0.0 ) web.max_len = .1; if ( overall_size == 0.0 ) overall_size = 1.0; FOR_ALL_VERTICES(v_id) { if ( get_vattr(v_id) & FIXED ) continue; x = get_coord(v_id); for ( j = 0 ; j < SDIM ; j++ ) x[j] += gaussian()*web.temperature*web.max_len*overall_size; } outstring("One jiggle done.\n"); } /**************************************************************** * * Function: long_jiggle() * * Purpose: Move each vertex with a long wavelenth perturbation * away from metastable equilibria and crystalline * integrand hangups. * * Input: The global variable temperature scales the size * of the jiggles. The perturbation has a random * amplitude vector chosen from sphere and random * wavevector likewise chosen at random. Amplitude * is multiplied by current temperature. */ /* remembered parameters */ static REAL wavev[MAXCOORD]; static REAL amp[MAXCOORD]; static REAL phase; void long_jiggle() { REAL mag; REAL ww; int j; vertex_id v_id; REAL *x; char response[100]; /* get wave vector */ get_wv: #ifdef LONGDOUBLE sprintf(msg,"Enter wave vector (%Lf,%Lf,%Lf;r): ",wavev[0],wavev[1],wavev[2]); #else sprintf(msg,"Enter wave vector (%f,%f,%f;r): ",wavev[0],wavev[1],wavev[2]); #endif prompt(msg,response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); if ( response[0] == 'r' ) { /* random */ prompt(msg,response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); if ( atoi(response) != 0 ) srand(atoi(response)); /* pick random wavelength in unit sphere */ do { for ( j = 0 ; j < SDIM ; j++ ) wavev[j] = 1 - 2*(REAL)(rand()&0x7FFF)/0x7FFFL; ww = SDIM_dot(wavev,wavev); } while ( ww > 1.0 ); /* invert to wavevector and scale to surface size */ for ( j = 0 ; j < SDIM ; j++ ) wavev[j] /= ww*overall_size; } else if ( isalpha(response[0]) ) return; /* escape without jiggle */ else if ( response[0] ) { REAL val[MAXCOORD]; cmdptr = response; for ( j = 0 ; j < SDIM ; j++ ) if ( read_const(val+j) <= 0 ) { outstring("Wrong number of components.\n"); goto get_wv; } for ( j = 0 ; j < SDIM ; j++ ) wavev[j] = val[j]; } /* pick random phase */ get_phase: sprintf(msg,"Enter phase (%f;r): ",(DOUBLE)phase); prompt(msg,response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); if ( response[0] == 'r' ) phase = 2*M_PI*(REAL)(rand()&0x7FFF)/0x7FFFL; else if ( isalpha(response[0]) ) return; /* escape without jiggle */ else if ( response[0] ) { REAL val; if ( const_expr(response,&val) <= 0 ) goto get_phase; phase = val; } /* amplitude */ get_amp: sprintf(msg,"Enter amplitude (%f,%f,%f;r): ",(DOUBLE)amp[0],(DOUBLE)amp[1],(DOUBLE)amp[2]); prompt(msg,response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); if ( response[0] == 'r' ) { /* random */ /* pick random amplitude */ do for ( j = 0 ; j < SDIM ; j++ ) amp[j] = 1 - 2*(REAL)(rand()&0x7FFF)/0x7FFFL; while ( SDIM_dot(amp,amp) > 1.0 ); for ( j = 0 ; j < SDIM ; j++ ) amp[j] *= web.temperature*overall_size; } else if ( isalpha(response[0]) ) return; /* escape without jiggle */ else if ( response[0] ) { REAL val[MAXCOORD]; cmdptr = response; for ( j = 0 ; j < SDIM ; j++ ) if ( read_const(val+j) <= 0 ) { outstring("Wrong number of components.\n"); goto get_amp; } for ( j = 0 ; j < SDIM ; j++ ) amp[j] = val[j]; } /* move vertices */ FOR_ALL_VERTICES(v_id) { if ( get_vattr(v_id) & FIXED ) continue; x = get_coord(v_id); mag = sin(SDIM_dot(wavev,x) + phase); for ( j = 0 ; j < SDIM ; j++ ) x[j] += amp[j]*mag; } outstring("One long jiggle done.\n"); } /* end long_jiggle() */ /**************************************************************** * * Function: gaussian() * * Purpose: generate gaussian random variables with variance 1. * */ REAL gaussian() { int k; REAL sum = 0.0; for ( k = 0 ; k < 5 ; k++ ) sum += (REAL)(rand()&0x7FFF); return (sum/0x7FFFL - 2.5)/5*sqrt(12.0/5); } /*************************************************************** * * Function: estimate_decrease() * * Purpose: Estimates energy decrease from gradients of all * unfixed vertices using current scale factor and force. * Does not include volume retoration. * * Input: uses global variable for scale */ REAL estimate_decrease() { int i; REAL change = 0.0; for ( i = 0 ; i < optparamcount ; i++ ) change += web.scale*optparam[i].grad*optparam[i].velocity; #ifdef MPI_EVOLVER change += mpi_v_estimate(); #else change += v_estimate(); #endif estimated_change = -change; /* for estimated_change internal variable */ return -change; /* negative since forces are opposite gradients */ } /************************************************************************** * * function: v_estimate() * * purpose: Calculate vertex part of estimated energy change. */ REAL v_estimate() { vertex_id v_id; REAL *force; REAL *velocity; int i,j; REAL change = 0.0; REAL delta; FOR_ALL_VERTICES(v_id) { if ( get_vattr(v_id) & FIXED ) continue; force = get_force(v_id); velocity = get_velocity(v_id); delta = 0.0; if ( get_vattr(v_id) & BOUNDARY ) { REAL temp[MAXCOORD],dummy; struct boundary *boundary = get_boundary(v_id); int pcount = boundary->pcount; REAL *param = get_param(v_id); for ( j = 0 ; j < SDIM ; j++ ) { eval_all(boundary->coordf[j],param,pcount,&dummy,temp,v_id); delta += web.scale*force[j]*dot(temp,velocity,pcount); } } else { for ( i = 0 ; i < SDIM ; i++ ) delta += web.scale*force[i]*velocity[i]; } change += delta; } return change; } /* end v_estimate() */ /******************************************************************** * * Function: homothety() * * Purpose: homothety to scale to fixed area or volume. * Normalizes total volume of all bodies to 1. */ void homothety() { body_id b_id; vertex_id v_id; REAL vol = 0.0; REAL *x; REAL scale; int i; if ( square_curvature_flag ) scale = 1/pow(web.total_area/homothety_target,1.0/web.dimension); else { /* be sure to have current volumes */ calc_content(Q_FIXED); FOR_ALL_BODIES(b_id) vol += get_body_volume(b_id); if ( web.representation == STRING ) scale = 1/sqrt(vol/homothety_target); else scale = 1/pow(vol/homothety_target,1/3.0); } FOR_ALL_VERTICES(v_id) { x = get_coord(v_id); for ( i = 0 ; i < SDIM ; i++ ) x[i] *= scale; } } /*********************************************************************** * * function: cg_calc_gamma() * * purpose: Calculate conjugate gradient direction adjustment factor. * */ void cg_calc_gamma() { REAL sum = 0.0; REAL rsum = 0.0; int i; if ( ribiere_flag ) { #ifdef MPI_EVOLVER rsum = mpi_ribiere_calc(); #else rsum = ribiere_calc(); #endif for ( i = 0 ; i < optparamcount ; i++ ) { rsum += optparam[i].grad*optparam[i].oldgrad; optparam[i].oldgrad = optparam[i].grad; } } #ifdef MPI_EVOLVER sum = mpi_cg_sum_calc(); #else sum = cg_sum_calc(); #endif for ( i = 0 ; i < optparamcount ; i++ ) sum += optparam[i].velocity*optparam[i].grad; if ( cg_oldsum >= 1e-15 ) cg_gamma = (sum-rsum)/cg_oldsum; if ( cg_gamma > 10.0 ) /* something probably changed too much */ cg_gamma = 0.0; /* guarantee convergence, (Shewchuk notes, p. 42) */ if ( ribiere_flag && (cg_gamma < 0.0) ) cg_gamma = 0.0; cg_oldsum = sum; return; } /* end cg_calc_gamma() */ /************************************************************************** * * function: ribiere_calc() * * purpose: calculate vertex contributions to Ribiere conjugate gradient sum. */ REAL ribiere_calc() { int i; REAL rsum = 0.0; vertex_id v_id; int r_attr = find_attribute(VERTEX,RIBIERE_ATTR_NAME); if ( r_attr < 0 ) kb_error(1058,"Internal error: Forces not saved for Ribiere CG.\n", RECOVERABLE); FOR_ALL_VERTICES(v_id) { REAL *f = get_force(v_id); REAL *v = get_velocity(v_id); REAL *g = (REAL*)get_extra(v_id,r_attr); if ( get_vattr(v_id) & (FIXED|BOUNDARY) ) continue; rsum += SDIM_dot(v,g); for ( i = 0 ; i < SDIM ; i++ ) g[i] = f[i]; } return rsum; } /************************************************************************** * * function: cg_sum_calc() * * purpose: calculate vertex contributions to Ribiere conjugate gradient sum. */ REAL cg_sum_calc() { vertex_id v_id; REAL sum = 0.0; FOR_ALL_VERTICES(v_id) { REAL *f = get_force(v_id); REAL *v = get_velocity(v_id); if ( get_vattr(v_id) & (FIXED |BOUNDARY) ) continue; sum += SDIM_dot(v,f); } return sum; } /*********************************************************************** * * function: cg_direction() * * purpose: Adjusts direction of motion for conjugate gradient. * */ void cg_direction() { int i; #ifdef MPI_EVOLVER mpi_cg_direction(); /* do vertices */ #else cg_direction_local(); #endif for ( i = 0 ; i < optparamcount ; i++ ) { optparam[i].grad += cg_gamma*optparam[i].cg; optparam[i].cg = optparam[i].grad; } } /********************************************************************** * * function: cg_direction_local() * * purpose: make conjugate gradient adjustments on vertices. */ void cg_direction_local() { int i; vertex_id v_id; REAL *v,*h; if ( cg_hvector == NULL ) { /* reinitialize */ cg_hvector = (REAL (*)[MAXCOORD])mycalloc(web.skel[VERTEX].max_ord+1, sizeof(REAL [MAXCOORD])); for ( i = 0 ; i < optparamcount ; i++ ) optparam[i].cg = 0.0; } FOR_ALL_VERTICES(v_id) { if ( get_vattr(v_id) & (FIXED|BOUNDARY) ) continue; h = cg_hvector[loc_ordinal(v_id)]; v = get_velocity(v_id); for ( i = 0 ; i < SDIM ; i++ ) h[i] = v[i] += cg_gamma*h[i]; } } /********************************************************************** * * function: cg_restart() * * purpose: reset conjugate gradient */ void cg_restart() { #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) { struct mpi_command message; message.cmd = mpi_CG_RESTART; MPI_Bcast(&message,sizeof(struct mpi_command),MPI_BYTE,MASTER_TASK, MPI_COMM_WORLD); } #endif { myfree((char *)cg_hvector); cg_hvector = NULL; cg_oldsum = 0.0;} } /********************************************************************* * * function: runge_kutta() * * purpose: do runge-kutta method for calculating motion. * Assumes first force evaluation already done. * and vertices saved. Calculates final motion, * but does not actually do it, rather saves in * vertex force array, so that final move can * be done in regular flow of control. * */ void runge_kutta() { REAL **k1,**k2,**k3, **k4; /* saved motions */ int i; vertex_id v_id; k1 = dmatrix(0,web.skel[VERTEX].max_ord,0,SDIM-1); k2 = dmatrix(0,web.skel[VERTEX].max_ord,0,SDIM-1); k3 = dmatrix(0,web.skel[VERTEX].max_ord,0,SDIM-1); k4 = dmatrix(0,web.skel[VERTEX].max_ord,0,SDIM-1); /* save first motion */ FOR_ALL_VERTICES(v_id) { int k = loc_ordinal(v_id); REAL *f = get_force(v_id); for ( i = 0 ; i < SDIM ; i++ ) k1[k][i] = f[i]; } /* second motion */ web.scale /= 2; move_vertices(ACTUAL_MOVE,web.scale); calc_all_grads(CALC_FORCE|CALC_VOLGRADS); volume_restore(1.0,ACTUAL_MOVE); FOR_ALL_VERTICES(v_id) { int k = loc_ordinal(v_id); REAL *f = get_force(v_id); for ( i = 0 ; i < SDIM ; i++ ) k2[k][i] = f[i]; } /* third motion */ restore_coords(&saved,SAVE_IN_ATTR); move_vertices(ACTUAL_MOVE,web.scale); calc_all_grads(CALC_FORCE|CALC_VOLGRADS); volume_restore(1.0,ACTUAL_MOVE); FOR_ALL_VERTICES(v_id) { int k = loc_ordinal(v_id); REAL *f = get_force(v_id); for ( i = 0 ; i < SDIM ; i++ ) k3[k][i] = f[i]; } /* fourth motion */ restore_coords(&saved,SAVE_IN_ATTR); web.scale *= 2; move_vertices(ACTUAL_MOVE,web.scale); calc_all_grads(CALC_FORCE|CALC_VOLGRADS); volume_restore(1.0,ACTUAL_MOVE); FOR_ALL_VERTICES(v_id) { int k = loc_ordinal(v_id); REAL *f = get_force(v_id); for ( i = 0 ; i < SDIM ; i++ ) k4[k][i] = f[i]; } /* runge-kutta combination */ FOR_ALL_VERTICES(v_id) { int k = loc_ordinal(v_id); REAL *f = get_force(v_id); for ( i = 0 ; i < SDIM ; i++ ) f[i] = (k1[k][i] + 2*k2[k][i] + 2*k3[k][i] + k4[k][i])/6; } restore_coords(&saved,SAVE_IN_ATTR); free_matrix(k1); free_matrix(k2); free_matrix(k3); free_matrix(k4); } /* end runge_kutta() */ /************************************************************************ * * function: apply_h_inverse_metric(); * * purpose: Replace force by laplacian of force. For doing motion * by laplacian of mean curvature. * */ void apply_h_inverse_metric() { REAL *scalarh; /* scalar curvature in normal direction */ REAL **normals; /* for soapfilm model */ REAL *stars; /* vertex areas */ edge_id e_id; vertex_id v_id; int i; REAL normal[MAXCOORD]; if ( (web.representation != STRING) && (web.representation != SOAPFILM) ) kb_error(2085,"Can do h_inverse_metric only in STRING and SOAPFILM models.\n", RECOVERABLE); scalarh = (REAL*)temp_calloc(web.skel[VERTEX].max_ord+5,sizeof(REAL)); stars = (REAL*)temp_calloc(web.skel[VERTEX].max_ord+5,sizeof(REAL)); if ( web.representation == STRING ) { /* gather scalar curvatures */ FOR_ALL_VERTICES(v_id) { REAL *f = get_force(v_id); REAL *vel = scalarh+loc_ordinal(v_id); REAL len,star; e_id = get_vertex_edge(v_id); len = get_edge_length(e_id)/2; e_id = get_next_tail_edge(e_id); len += get_edge_length(e_id)/2; /* *vel = sqrt(SDIM_dot(f,f))/len; */ star = calc_vertex_normal(v_id,NULLID,normal)/2; if ( effective_area_flag ) len = star; stars[loc_ordinal(v_id)] = len; *vel = -SDIM_dot(f,normal)/len; } /* compute Laplacian and multiply by normal */ FOR_ALL_VERTICES(v_id) { REAL *f = get_velocity(v_id); REAL len1,len2; vertex_id v1,v2; REAL h1,h,h2; REAL lap; h = scalarh[loc_ordinal(v_id)]; e_id = get_vertex_edge(v_id); v1 = get_edge_headv(e_id); len1 = get_edge_length(e_id); h1 = scalarh[loc_ordinal(v1)]; e_id = get_next_tail_edge(e_id); v2 = get_edge_headv(e_id); len2 = get_edge_length(e_id); h2 = scalarh[loc_ordinal(v2)]; lap = ((h1-h)/len1 + (h2-h)/len2)/stars[loc_ordinal(v_id)]; /* now multiply be normal */ calc_vertex_normal(v_id,NULLID,normal); for ( i = 0 ; i < SDIM ; i++ ) f[i] = lap*normal[i]; } } if ( web.representation == SOAPFILM ) { normals = dmatrix(0,web.skel[VERTEX].max_ord+1,0,MAXCOORD-1); /* gather scalar curvatures */ FOR_ALL_VERTICES(v_id) { REAL *f = get_force(v_id); REAL *vel = scalarh+loc_ordinal(v_id); REAL star; REAL *norm = normals[loc_ordinal(v_id)]; star = calc_vertex_normal(v_id,get_vertex_fe(v_id),norm)/3; if ( !effective_area_flag ) star = get_vertex_area_star(v_id)/3; *vel = SDIM_dot(f,norm)/star; stars[loc_ordinal(v_id)] = star; } /* compute Laplacian and multiply by normal */ FOR_ALL_VERTICES(v_id) { REAL *f = get_velocity(v_id); REAL *norm = normals[loc_ordinal(v_id)]; REAL h1,h,h2; REAL lap; REAL side1[MAXCOORD],side2[MAXCOORD]; facetedge_id fe,start_fe,next_fe; edge_id e1,e2; h = scalarh[loc_ordinal(v_id)]; lap = 0.0; fe = start_fe = get_vertex_fe(v_id); do { REAL s11,s12,s22,det; next_fe = inverse_id(get_prev_edge(fe)); e1 = get_fe_edge(fe); get_edge_side(e1,side1); h1 = scalarh[loc_ordinal(get_edge_headv(e1))]; e2 = get_fe_edge(next_fe); get_edge_side(e2,side2); h2 = scalarh[loc_ordinal(get_edge_headv(e2))]; s11 = SDIM_dot(side1,side1); s12 = SDIM_dot(side1,side2); s22 = SDIM_dot(side2,side2); det = sqrt(s11*s22 - s12*s12); lap += ((s11-2*s12+s22)*h + (s12-s22)*h1 + (s12-s11)*h2)/det; fe = get_next_facet(next_fe); } while ( !equal_id(fe,start_fe) ); lap /= 2*stars[loc_ordinal(v_id)]; /* now multiply by unit normal */ for ( i = 0 ; i < SDIM ; i++ ) f[i] = lap*norm[i]; } free_matrix(normals); } temp_free((char*)scalarh); temp_free((char*)stars); } /* end apply_h_inverse_metric() */ /*************************************************************** * * Function: convert_forms_to_vectors() * * Purpose: wrapper for local_convert_forms_to_vectors() */ void convert_forms_to_vectors(mode) int mode; /* bits for CALC_FORCE and CALC_VOLGRADS */ { if ( itdebug ) { sprintf(msg,"convert_forms_to_vectors(%s %s)\n", (mode&CALC_FORCE)?"CALC_FORCE":"",(mode&CALC_VOLGRADS)?"CALC_VOLGRADS":""); outstring(msg); } #ifdef MPI_EVOLVER if ( this_task != 0 ) return; mpi_convert_forms_to_vectors(mode); #else local_convert_forms_to_vectors(mode); #endif } /*************************************************************** * * Function: local_convert_forms_to_vectors() * * Purpose: Converts energy and constraint gradients to vector * form, using current metric or mobility, and * projects vectors to pointwise constraints. * */ void local_convert_forms_to_vectors(mode) int mode; /* bits for CALC_FORCE and CALC_VOLGRADS */ { vertex_id v_id; REAL *force,*velocity; int i,j,k,m; REAL **weights=NULL; REAL zener_coeff=0.0; MAT2D(a,MAXPARAM,MAXCOORD); int vgrad_attr,vvelocity_attr; /* for debug save of vgrads to attr */ int eltype; if ( zener_drag_flag ) { int zd; zd = lookup_global(ZENER_COEFF_NAME); if ( zd >= 0 ) zener_coeff = globals(zd)->value.real; } if ( approx_curve_flag ) { /* approx_curvature();*/ /* Dzuik and Schmidt version */ mobility_setup(); approx_curv_calc(mode); /* convert form to vector */ mobility_cleanup(); return; } if ( (web.modeltype == LAGRANGE) && web.area_norm_flag ) { /* set up weight matrix and invert */ int ctrl; struct gauss_lag *gl; edge_id e_id; facet_id f_id; /* Notes: This tries to approximate the inverse of the linear interpolation metric. The inverse metric used here has diagonal sum of blocks which are the inverses of the single element metric, with premultiplication of the form by diagonal 1/star, and postmultiplication of the vector by 1/valence. This results in uniform volume gradient for a flat surface, regardless of triangulation. Drawback is that inverse metric is not symmetric if elements not equal size. But probably still positive definite (in sense of guaranteeing downhill in energy) */ if ( web.representation == STRING ) { gl = &gauss_lagrange[web.dimension][web.gauss1D_order]; ctrl = web.skel[EDGE].ctrlpts; } else { gl = &gauss_lagrange[web.dimension][web.gauss2D_order]; ctrl = web.skel[FACET].ctrlpts; } weights = dmatrix(0,ctrl,0,ctrl); for ( i = 0 ; i < ctrl ; i++ ) for ( j = 0 ; j < ctrl ; j++ ) { weights[i][j] = 0.0; for ( m = 0 ; m < gl->gnumpts ; m++ ) weights[i][j] += gl->gausswt[m]*gl->gpoly[m][i]*gl->gpoly[m][j]; } mat_inv(weights,ctrl); if ( web.representation == STRING ) { FOR_ALL_EDGES(e_id) { vertex_id *v_ptr = get_edge_vertices(e_id); ctrl = web.skel[EDGE].ctrlpts; for ( i = 0 ; i < ctrl ; i++ ) { REAL area = get_vertex_length_star(v_ptr[i]); REAL *f = get_force(v_ptr[i]); volgrad *vgptr0 = get_vertex_vgrad(v_ptr[i]); volgrad *vgptr; for ( j = 0 ; j < ctrl ; j++ ) { REAL *vel = get_velocity(v_ptr[j]); REAL fudge = 1.0/get_vertex_evalence(v_ptr[j]); volgrad *vgptri; volgrad *vgptri0 = get_vertex_vgrad(v_ptr[j]); if ( mode & CALC_FORCE ) for ( k = 0 ; k < SDIM ; k++ ) vel[k] += weights[i][j]*f[k]/area; if ( mode & CALC_VOLGRADS ) for ( vgptri=vgptri0; vgptri ; vgptri = vgptri->chain ) for ( vgptr=vgptr0; vgptr ; vgptr = vgptr->chain ) { if ( vgptr->fixnum == vgptri->fixnum ) for ( k = 0 ; k < SDIM ; k++ ) vgptri->velocity[k] += fudge*weights[i][j]*vgptr->grad[k]/area; } } } } } /* end string */ else /* soapfilm */ { FOR_ALL_FACETS(f_id) { vertex_id *v_ptr = get_facet_vertices(f_id); ctrl = web.skel[FACET].ctrlpts; for ( i = 0 ; i < ctrl ; i++ ) { REAL area = get_vertex_area_star(v_ptr[i]); REAL *f = get_force(v_ptr[i]); volgrad *vgptr0 = get_vertex_vgrad(v_ptr[i]); volgrad *vgptr; for ( j = 0 ; j < ctrl ; j++ ) { REAL *vel = get_velocity(v_ptr[j]); REAL fudge = 1.0/get_vertex_fvalence(v_ptr[j]); volgrad *vgptri; volgrad *vgptri0 = get_vertex_vgrad(v_ptr[j]); if ( mode & CALC_FORCE ) for ( k = 0 ; k < SDIM ; k++ ) vel[k] += weights[i][j]*f[k]/area; if ( mode & CALC_VOLGRADS ) for ( vgptri=vgptri0; vgptri ; vgptri = vgptri->chain ) for ( vgptr=vgptr0; vgptr ; vgptr = vgptr->chain ) { if ( vgptr->fixnum == vgptri->fixnum ) for ( k = 0 ; k < SDIM ; k++ ) vgptri->velocity[k] += fudge*weights[i][j]*vgptr->grad[k]/area; } } } } } /* end soapfilm */ } /* end Lagrange area_normalization */ FOR_ALL_VERTICES(v_id) { ATTR attr = get_vattr(v_id); volgrad *vgptr0 = get_vertex_vgrad(v_id); volgrad *vgptr; force = get_force(v_id); velocity = get_velocity(v_id); if ( mode & CALC_FORCE ) if ( attr & FIXED ) { memset((char*)velocity,0,SDIM*sizeof(REAL)); for ( vgptr=vgptr0 ; vgptr ; vgptr = vgptr->chain ) for ( i = 0 ; i < SDIM ; i++ ) vgptr->velocity[i] = 0.0; continue; } /* convert form to vector */ if ( web.metric_flag && metric_convert_flag ) FOR_ALL_VERTICES(v_id) { REAL *x = get_coord(v_id); REAL rr,rf; if ( klein_metric_flag ) { /* M^-1 = (I - rxr)*(1-r^2) */ rr = SDIM_dot(x,x); rf = SDIM_dot(x,force); if ( mode & CALC_FORCE ) for ( j = 0 ; j < SDIM ; j++ ) velocity[j] = (force[j] - x[j]*rf)*(1-rr); if ( mode & CALC_VOLGRADS ) for ( vgptr=vgptr0; vgptr ; vgptr = vgptr->chain ) { rf = SDIM_dot(x,vgptr->grad); for ( i = 0 ; i < SDIM ; i++ ) vgptr->velocity[i] = (vgptr->grad[i] - x[i]*rf)*(1-rr); } } else if ( web.conformal_flag ) { REAL gg = eval(&web.metric[0][0],x,NULLID,NULL); if ( gg == 0.0 ) kb_error(1582,"Conformal metric evaluates to zero.\n",WARNING); else { if ( mode & CALC_FORCE ) for ( j = 0 ; j < SDIM ; j++ ) velocity[j] = force[j]/gg; if ( mode & CALC_VOLGRADS ) for ( vgptr=vgptr0; vgptr ; vgptr = vgptr->chain ) for ( j = 0 ; j < SDIM ; j++ ) vgptr->velocity[j] = vgptr->grad[j]/gg; } } else { /* if here, have general metric */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) metric[i][j] = eval(&web.metric[i][j],x,NULLID,NULL); mat_inv(metric,SDIM); if ( mode & CALC_FORCE ) matvec_mul(metric,force,velocity,SDIM,SDIM); if ( mode & CALC_VOLGRADS ) for ( vgptr=vgptr0; vgptr ; vgptr = vgptr->chain ) matvec_mul(metric,vgptr->grad,vgptr->velocity,SDIM,SDIM); } } else if ( web.area_norm_flag && !approx_curve_flag) switch ( web.modeltype ) { case LINEAR: { REAL area=((web.representation==STRING)?get_vertex_length_star(v_id): get_vertex_area_star(v_id))/star_fraction; REAL ff; if ( get_vattr(v_id) & FIXED ) continue; if ( effective_area_flag && (web.representation == STRING) ) { /* calculate effective area */ int valence = get_vertex_evalence(v_id); if ( (valence == 2) || (valence==1) ) { REAL dummy[MAXCOORD];; area = calc_vertex_normal(v_id,get_vertex_fe(v_id),dummy)/2; } else if ( valence == 0 ) area = 1.0; /* disconnected pt; no force anyway */ else /* triple point at least */ { edge_id e_id,start_e; REAL ss,fs,side[MAXCOORD]; area = 0.0; e_id = start_e = get_vertex_edge(v_id); do { double det; get_edge_side(e_id,side); ss = SDIM_dot(side,side); fs = SDIM_dot(force,side); ff = SDIM_dot(force,force); det = ff*ss - fs*fs; if ( det > 0.0 ) area += 0.5*sqrt(det)/sqrt(ff); e_id = get_next_tail_edge(e_id); } while ( !equal_id(e_id,start_e) ); } set_vertex_star(v_id,star_fraction*area); } else if ( effective_area_flag && (web.representation == SOAPFILM) ) { /* crude correction for triple edges and tetra points */ REAL dummy[MAXCOORD]; if ( get_vattr(v_id) & TRIPLE_PT ) { area /= sqrt(3.); } else if ( get_vattr(v_id) & TETRA_PT ) { area /= sqrt(6.); } else area = calc_vertex_normal(v_id,get_vertex_fe(v_id),dummy) /star_fraction; } if ( area == 0.0 ) { if ( mode & CALC_FORCE ) for ( i = 0 ; i < SDIM ; i++ ) velocity[i] = 0.0; if ( mode & CALC_VOLGRADS ) for ( vgptr=vgptr0; vgptr ; vgptr = vgptr->chain ) for ( i = 0 ; i < SDIM ; i++ ) vgptr->velocity[i] = 0.0; sprintf(errmsg,"Zero area star around vertex %s.\n", ELNAME(v_id)); kb_error(1775,errmsg,WARNING); } else { if ( mode & CALC_FORCE ) for ( i = 0 ; i < SDIM ; i++ ) velocity[i] = force[i]/area; if ( mode & CALC_VOLGRADS ) for ( vgptr=vgptr0; vgptr ; vgptr = vgptr->chain ) for ( i = 0 ; i < SDIM ; i++ ) vgptr->velocity[i] = vgptr->grad[i]/area; } } break; case QUADRATIC: /* area weights carefully chosen to make volume grad vector constant for flat surface */ { edge_id e_id, start_e; if ( get_vattr(v_id) & Q_MIDPOINT ) { REAL area = (web.representation==STRING) ? get_vertex_length_star(v_id)*2/3.: get_vertex_area_star(v_id)*2/3.; if ( mode & CALC_FORCE ) for ( i = 0 ; i < SDIM ; i++ ) velocity[i] = force[i]/area; if ( mode & CALC_VOLGRADS ) for ( vgptr=vgptr0; vgptr ; vgptr = vgptr->chain ) for ( i = 0 ; i < SDIM ; i++ ) vgptr->velocity[i] = vgptr->grad[i]/area; } else /* corner vertex */ { REAL area; /* first, self term */ area = (web.representation==STRING) ? get_vertex_length_star(v_id)/6: get_vertex_area_star(v_id)/5.; if ( mode & CALC_FORCE ) for ( i = 0 ; i < SDIM ; i++ ) velocity[i] = force[i]/area; if ( mode & CALC_VOLGRADS ) for ( vgptr=vgptr0; vgptr ; vgptr = vgptr->chain ) for ( i = 0 ; i < SDIM ; i++ ) vgptr->velocity[i] = vgptr->grad[i]/area; /* next, neighboring midpoints */ if ( web.representation == SOAPFILM ) { start_e = e_id = get_vertex_edge(v_id); area = get_vertex_area_star(v_id)*4/3; do { vertex_id vv_id = get_edge_midv(e_id); REAL *ff = get_force(vv_id); volgrad *vgptri; volgrad *vgptri0 = get_vertex_vgrad(vv_id); if ( mode & CALC_FORCE ) for ( i = 0 ; i < SDIM ; i++ ) velocity[i] += ff[i]/area; if ( mode & CALC_VOLGRADS ) for ( vgptri=vgptri0; vgptri ; vgptri = vgptri->chain ) for ( vgptr=vgptr0; vgptr ; vgptr = vgptr->chain ) { if ( vgptr->fixnum == vgptri->fixnum ) for ( i = 0 ; i < SDIM ; i++ ) vgptr->velocity[i] += vgptri->grad[i]/area; } e_id = get_next_tail_edge(e_id); } while ( !equal_id(start_e,e_id) ); } } } break; case LAGRANGE: /* did earlier */ break; } /* end area_normalization */ else /* not any special mobility */ { if ( mode & CALC_FORCE ) for ( i = 0 ; i < SDIM ; i++ ) velocity[i] = force[i]; /* identity metric */ if ( mode & CALC_VOLGRADS ) for ( vgptr=vgptr0; vgptr ; vgptr = vgptr->chain ) for ( i = 0 ; i < SDIM ; i++ ) vgptr->velocity[i] = vgptr->grad[i]; } /* apply mobility */ if ( mobility_flag ) { if ( mobility_tensor_flag ) { REAL *x = get_coord(v_id); REAL newv[MAXCOORD]; REAL mob[MAXCOORD][MAXCOORD]; for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) mob[i][j] = eval(&mobility_tensor[i][j],x,v_id,NULL); if ( mode & CALC_FORCE ) { for ( i = 0 ; i < SDIM ; i++ ) for ( newv[i] = 0.0, j = 0; j < SDIM ; j++ ) newv[i] += mob[i][j]*velocity[j]; for ( i = 0 ; i < SDIM ; i++ ) velocity[i] = newv[i]; } if ( mode & CALC_VOLGRADS ) for ( vgptr=vgptr0; vgptr ; vgptr = vgptr->chain ) { for ( i = 0 ; i < SDIM ; i++ ) for ( newv[i] = 0.0, j = 0; j < SDIM ; j++ ) newv[i] += mob[i][j]*vgptr->velocity[j]; for ( i = 0 ; i < SDIM ; i++ ) vgptr->velocity[i] = newv[i]; } } /* end mobility_tensor */ else { REAL mobility = eval(&mobility_formula,get_coord(v_id),v_id,NULL); if ( mode & CALC_FORCE ) for ( j = 0 ; j < SDIM ; j++ ) velocity[j] *= mobility; if ( mode & CALC_VOLGRADS ) for ( vgptr=vgptr0; vgptr ; vgptr = vgptr->chain ) for ( i = 0 ; i < SDIM ; i++ ) vgptr->velocity[i] *= mobility; } } /* end mobility */ /* project vector to pointwise constraints */ if ( normal_motion_flag && !(attr & BOUNDARY) ) { /* project to normal */ REAL d; REAL *normal = vertex_normals[loc_ordinal(v_id)]; REAL *h; if ( mode & CALC_FORCE ) { d = SDIM_dot(velocity,normal); for ( j = 0 ; j < SDIM ; j++ ) velocity[j] = d*normal[j]; if ( cg_hvector ) /* conjugate gradient history vector also */ { h = cg_hvector[loc_ordinal(v_id)]; d = SDIM_dot(h,normal); for ( j = 0 ; j < SDIM ; j++ ) h[j] = d*normal[j]; } } if ( mode & CALC_VOLGRADS ) for ( vgptr=vgptr0; vgptr ; vgptr = vgptr->chain ) { d = SDIM_dot(vgptr->velocity,normal); for ( i = 0 ; i < SDIM ; i++ ) vgptr->velocity[i] = d*normal[i]; } } /* end normal motion */ if ( (attr & CONSTRAINT) && (!check_pinning_flag || (attr & PINNED_V)) ) { conmap_t * conmap = get_v_constraint_map(v_id); int oncount = 0; struct constraint *con[MAXCONPER]; int conlist[MAXCONPER]; REAL perp[MAXCOORD]; int one_sided_flag = 0; for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { if ( conmap[j] & CON_HIT_BIT ) { conlist[oncount] = conmap[j] & CONMASK; con[oncount] = get_constraint(conmap[j]); if ( con[oncount]->attr & (NONPOSITIVE|NONNEGATIVE) ) one_sided_flag = 1; oncount++; } } if ( oncount > SDIM ) { sprintf(errmsg, "Vertex %s is on more constraints than the dimension of space.\n", ELNAME(v_id)); kb_error(2086,errmsg,WARNING); oncount = SDIM; } if ( one_sided_flag ) { REAL *raw_velocity = VREAL(v_id,raw_velocity_attr); for ( j = 0 ; j < SDIM ; j++ ) raw_velocity[j] = velocity[j]; for ( vgptr=vgptr0; vgptr ; vgptr = vgptr->chain ) for ( j = 0 ; j < SDIM ; j++ ) vgptr->raw_velocity[j] = vgptr->velocity[j]; } if ( oncount ) { if ( mode & CALC_FORCE ) { constr_proj(TANGPROJ,oncount,con,get_coord(v_id), velocity,perp,conlist,DETECT,v_id); for ( j = 0 ; j < SDIM ; j++ ) velocity[j] -= perp[j]; if ( web.area_norm_flag && (web.representation==STRING) ) { /* correction factor for moving vertex along constraint is to divide projected force by sin^2 of contact angle, here crudely estimated. */ REAL sinsq = SDIM_dot(perp,perp); if ( (sinsq != 0.0) && (sinsq < 1.0) ) for ( j = 0 ; j < SDIM ; j++ ) velocity[j] /= sinsq; } } if ( mode & CALC_VOLGRADS ) for ( vgptr=vgptr0; vgptr ; vgptr = vgptr->chain ) { REAL perp[MAXCOORD]; constr_proj(TANGPROJ,oncount,con,get_coord(v_id), vgptr->velocity,perp,NULL,NO_DETECT,v_id); for ( j = 0 ; j < SDIM ; j++ ) vgptr->velocity[j] -= perp[j]; } } } } /* end for all vertices */ for ( k = 0 ; k < optparamcount ; k++ ) optparam[k].velocity = globals(optparam[k].pnum)->attr.varstuff.pscale*optparam[k].grad; if ( zener_drag_flag ) { FOR_ALL_VERTICES(v_id) { REAL *v = get_velocity(v_id); REAL mag = sqrt(SDIM_dot(v,v)); for ( k = 0 ; k < SDIM ; k++ ) if ( mag > zener_coeff ) v[k] = (mag - zener_coeff)*v[k]/mag; else v[k] = 0.0; } } if ( web.h_inverse_metric_flag ) apply_h_inverse_metric(); /* project to parameter space for boundary points */ if ( (mode & CALC_FORCE) && web.bdrymax ) FOR_ALL_VERTICES(v_id) { int pcount; REAL *v = get_velocity(v_id); REAL tmp[MAXCOORD]; struct boundary *bdry; int m; ATTR attr = get_vattr(v_id); if ( attr & FIXED ) continue; if ( !(attr & BOUNDARY) ) continue; bdry = get_boundary(v_id); pcount = bdry->pcount; b_proj(bdry,get_param(v_id),a,PARAMPROJ,v_id); matvec_mul(a,v,tmp,pcount,SDIM); for ( m = 0 ; m < pcount ; m++ ) v[m] = tmp[m]; for ( m = pcount ; m < SDIM ; m++ ) v[m] = 0.0; } /* for debugging, transfer volgrads to vertex attribute */ vgrad_attr = find_extra("__volgrad",&eltype); vvelocity_attr = find_extra("__volvelocity",&eltype); if ( (vgrad_attr >= 0) || (vvelocity_attr >= 0) ) { /* user should expand so each vertex has volgrad for each body */ struct extra *gradex = EXTRAS(VERTEX)+vgrad_attr; int maxgradbody = gradex->array_spec.sizes[0]; int maxgraddim = gradex->array_spec.sizes[1]; struct extra *velex = EXTRAS(VERTEX)+vvelocity_attr; int maxvelbody = velex->array_spec.sizes[0]; int maxveldim = gradex->array_spec.sizes[1]; if ( maxveldim > SDIM ) maxveldim = SDIM; if ( maxgraddim > SDIM ) maxgraddim = SDIM; FOR_ALL_VERTICES(v_id) { struct volgrad *vgptr; REAL *vg = (vgrad_attr >= 0 ) ? VREAL(v_id,vgrad_attr) : NULL; REAL *vv = (vvelocity_attr >= 0 ) ? VREAL(v_id,vvelocity_attr) : NULL; if ( vgrad_attr >= 0 ) memset((char*)vg,0,maxgradbody*maxgraddim*sizeof(REAL)); if ( vvelocity_attr >= 0 ) memset((char*)vv,0,maxvelbody*maxveldim*sizeof(REAL)); for ( vgptr=get_vertex_vgrad(v_id) ; vgptr ; vgptr = vgptr->chain ) { if ( valid_id(vgptr->bb_id) ) /* just doing bodies */ { int bnum = loc_ordinal(vgptr->bb_id); if ( (vgrad_attr >= 0) && (bnum < maxgradbody) ) { for ( i = 0 ; i < maxgraddim ; i++ ) vg[bnum*gradex->array_spec.sizes[1]+i] = vgptr->grad[i]; } if ( (vvelocity_attr >= 0) && (bnum < maxvelbody) ) { for ( i = 0 ; i < maxveldim ; i++ ) vv[bnum*velex->array_spec.sizes[1]+i] = vgptr->velocity[i]; } } } } } if ( weights ) free_matrix(weights); } /* end convert_forms_to_vectors() */ /************************************************************************* * * function: check_pinning() * * purpose: check for vertices that can't move because adjacent * vertices are not on same constraint when they could be */ void check_pinning() { edge_id e_id; vertex_id v_id; int i,j; FOR_ALL_VERTICES(v_id) /* clear pinning flag */ vptr(v_id)->attr &= ~PINNED_V; FOR_ALL_EDGES(e_id) { vertex_id headv = get_edge_headv(e_id); vertex_id tailv = get_edge_tailv(e_id); conmap_t * hstat = get_v_constraint_map(headv); conmap_t * tstat = get_v_constraint_map(tailv); for ( i=1, j=0 ; i <= (int)hstat[0] ; i++ ) if (hstat[i] & CON_HIT_BIT) j++; if ( j == 1 ) set_attr(tailv,PINNED_V); for ( i=1, j=0 ; i <= (int)tstat[0] ; i++ ) if (tstat[i] & CON_HIT_BIT) j++; if ( j == 1 ) set_attr(headv,PINNED_V); } FOR_ALL_VERTICES(v_id) /* see if 2 con vertex has 2 con nbrs */ { conmap_t * hit = get_v_constraint_map(v_id); for ( i = 1, j = 0 ; i <= (int)hit[0] ; i++ ) if ( hit[i] & CON_HIT_BIT ) j++; if ( j != 2 ) set_attr(v_id,PINNED_V); } } /* end check_pinning() */ evolver-2.30c.dfsg/src/veravg.c0000644000175300017530000006660611410765113016671 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ #include "include.h" int find_vertex_average ARGS(( vertex_id , REAL *, int)); void old_vertex_average ARGS((int)); /************************************************************** * * Function: vertex_average() * * Purpose: For soapfilm model, move unconstrained vertices * to average position of neighbors. * */ static struct averages { REAL x[MAXCOORD]; REAL normal[MAXCOORD]; REAL area; /* weighting */ int status; /* <0 for don't move */ int triples; /* number of triple lines into vertex */ edge_id trip_e[2]; /* id's of triple edges */ } *average; void vertex_average(mode) int mode; /* VOLKEEP to keep volumes on both sides same */ /* NOVOLKEEP to obey just constraints */ /* RAWEST to ignore all restrictions */ { REAL *x; vertex_id v_id; int i; #ifdef MPI_EVOLVER if ( this_task == 0 ) mpi_vertex_average(mode); #endif if ( web.skel[VERTEX].count == 0 ) return; average = (struct averages *)temp_calloc(web.skel[VERTEX].max_ord+1, sizeof(struct averages)); FOR_ALL_VERTICES(v_id) if ( get_vattr(v_id) & (FIXED|BOUNDARY) ) average[loc_ordinal(v_id)].status = -10; if ( web.representation == SIMPLEX ) { old_vertex_average(mode); return; } FOR_ALL_VERTICES(v_id) average[loc_ordinal(v_id)].status = find_vertex_average(v_id,average[loc_ordinal(v_id)].x,mode); FOR_ALL_VERTICES(v_id) { int attr = get_vattr(v_id); REAL *newx = average[loc_ordinal(v_id)].x; x = get_coord(v_id); if (attr & BOUNDARY ) { /* update boundary parameter to agree with coordinates */ b_extrapolate(get_boundary(v_id),x,newx,newx,get_param(v_id),get_param(v_id),v_id); } else { for ( i = 0 ; i < SDIM ; i++ ) x[i] = newx[i]; if (attr & CONSTRAINT ) project_v_constr(v_id,ACTUAL_MOVE,RESET_ONESIDEDNESS); } } temp_free((char*)average); } /*********************************************************************** * * Function: old_vertex_average() * * Purpose: Mass vertex averaging, invoked by V, rawv, rawestv. */ void old_vertex_average(mode) int mode; /* VOLKEEP to keep volumes on both sides same */ /* NOVOLKEEP to obey just constraints */ /* RAWEST to ignore all restrictions */ { facet_id f_id; edge_id e_id; vertex_id v_id; REAL *x; struct averages *ave; int i; REAL xbar[MAXCOORD]; REAL lambda; if ( web.modeltype == LAGRANGE ) kb_error(1379,"No vertex averaging in Lagrange model yet.\n",RECOVERABLE ); if ( web.skel[VERTEX].count == 0 ) return; average = (struct averages *)temp_calloc(web.skel[VERTEX].max_ord+1, sizeof(struct averages)); FOR_ALL_VERTICES(v_id) if ( get_vattr(v_id) & (FIXED|BOUNDARY) ) average[loc_ordinal(v_id)].status = -10; /* accumulate data on neighbors and weights */ if ( web.representation == SIMPLEX ) { if ( mode == VOLKEEP ) kb_error(1380, "Vertex averaging mode V not implemented for simplex model. Use rawv.\n", RECOVERABLE); FOR_ALL_FACETS(f_id) simplex_facet_average(f_id,mode); } else if ( web.representation == SOAPFILM ) { FOR_ALL_FACETS(f_id) facet_average(f_id,mode); } else if ( web.representation == STRING ) { FOR_ALL_EDGES(e_id) edge_average(e_id,mode); } /* move vertices */ if ( web.representation != STRING ) { FOR_ALL_VERTICES(v_id) { if ( get_vattr(v_id) & (FIXED|BOUNDARY) ) continue; ave = average + loc_ordinal(v_id); if (ave->status <= 0 ) continue; if (ave->area == 0.0 ) continue; if (ave->triples == 1 ) continue; x = get_coord(v_id); for ( i = 0 ; i < SDIM ; i++ ) xbar[i] = ave->x[i]/ave->area; if ( mode == VOLKEEP ) { REAL numer; /* volume preserving projection */ numer = SDIM_dot(ave->normal,xbar) - SDIM_dot(ave->normal,x); if ( numer != 0.0 ) /* in case ave->normal == 0 */ lambda = numer/SDIM_dot(ave->normal,ave->normal); else lambda = 0.0; if ( ave->triples > 0 ) /* along edge */ for ( i = 0 ; i < SDIM ; i++ ) xbar[i] = x[i] + lambda*ave->normal[i]; else /* subtract normal component */ for ( i = 0 ; i < SDIM ; i++ ) xbar[i] -= lambda*ave->normal[i]; } /* tends to round off sharp vertices, I hope */ if ( web.modeltype == QUADRATIC ) { REAL *xm; edge_id next_e; { next_e = e_id = get_vertex_edge(v_id); do { xm = get_coord(get_edge_midv(next_e)); for ( i = 0 ; i < SDIM ; i++ ) xm[i] += (xbar[i]-x[i])/2; next_e = get_next_tail_edge(next_e); } while ( !equal_element(next_e,e_id) ); } } for ( i = 0 ; i < SDIM ; i++ ) x[i] = xbar[i]; if ( get_vattr(v_id) & CONSTRAINT ) project_v_constr(v_id,ACTUAL_MOVE,RESET_ONESIDEDNESS); } } else /* string */ { FOR_ALL_VERTICES(v_id) { ave = average + loc_ordinal(v_id); if (ave->status != 2 ) continue; /* too many edges or something */ x = get_coord(v_id); if ( mode == VOLKEEP ) { /* ave->normal really tangent */ /* area preserving projection */ lambda = - SDIM_dot(ave->normal,ave->x)/3 /* prevent overshoot */ /SDIM_dot(ave->normal,ave->normal); for ( i = 0 ; i < SDIM ; i++ ) x[i] -= lambda*ave->normal[i]; } else { for ( i = 0 ; i < SDIM ; i++ ) x[i] += ave->x[i]/ave->status/2; /* avoid overshoot */ } } } if ( web.modeltype == QUADRATIC ) { FOR_ALL_EDGES(e_id) new_vertex_average(get_edge_midv(e_id),VOLKEEP); } temp_free((char *)average); /* recalculation responsibility of caller */ } /* end old_vertex_average() */ /*********************************************************************** * * function: facet_average() * * purpose: find one facet's contributions to averaging of its vertices. * */ void facet_average(f_id,mode) facet_id f_id; int mode; /* averaging mode */ { int i,j,k,ii; REAL side[FACET_EDGES][2][MAXCOORD]; REAL normal[MAXCOORD]; REAL area; facetedge_id fe_id; int sign; REAL *x[3]; /* get side vectors */ fe_id = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { get_fe_side(fe_id,side[i][0]); get_fe_side(inverse_id(fe_id),side[i][1]); /* in case of wraps */ x[i] = get_coord(get_fe_tailv(fe_id)); fe_id = get_next_edge(fe_id); } /* calculate normal */ cross_prod(side[0][0],side[1][0],normal); for ( i = 3 ; i < SDIM ; i++ ) normal[i] = 0.0 ; /* kludge */ area = get_facet_area(f_id); /* in case of metric or wraps */ k = -1; fe_id = get_facet_fe(f_id); for ( ii = 0 ; ii < FACET_EDGES ; ii++, fe_id = get_next_edge(fe_id) ) { edge_id e_id = get_fe_edge(fe_id); vertex_id headv = get_fe_headv(fe_id); vertex_id tailv = get_fe_tailv(fe_id); struct averages *head,*tail; k++; /* instead of maybe being skipped by continue at end of loop */ head = average + loc_ordinal(headv); tail = average + loc_ordinal(tailv); if ( mode != RAWEST ) { /* check constraint compatibility */ int attr = get_vattr(tailv); int found,kk,bad; if ( (attr & CONSTRAINT) ) { conmap_t * vconmap = get_v_constraint_map(tailv); conmap_t * econmap = get_e_constraint_map(e_id); bad = 0; for ( j = 1 ; j <= (int)vconmap[0] ; j++ ) { for ( kk = 1, found = 0 ; kk <= (int)econmap[0] ; kk++ ) if ( econmap[kk] == (vconmap[j] & ~CON_HIT_BIT) ) { found = 1; break; } if ( !found ) {bad = 1; break;} } if ( bad ) continue; /* with next edge */ } else if ( attr & BOUNDARY ) { if ( get_edge_boundary(e_id) != get_boundary(tailv) ) continue; } } if ( mode != RAWEST ) if ( !equal_id(get_prev_facet(fe_id),get_next_facet(fe_id)) ) { /* triple edge, just move by edge */ if ( head->status < 0 ) {} else if ( head->triples == 0 ) { /* first detection */ for ( i = 0 ; i < SDIM ; i++ ) { head->x[i] = x[(k+1)%3][i] - side[k][0][i]; head->normal[i] = -side[k][0][i]; } head->status = 1; head->area = 1.0; head->triples++; head->trip_e[0] = e_id; } else if ( head->triples == 1 ) { /* second detection */ if ( !equal_element(e_id,head->trip_e[0]) ) { for ( i = 0 ; i < SDIM ; i++ ) { head->x[i] += x[(k+1)%3][i] - side[k][0][i]; head->normal[i] += side[k][0][i]; } head->status = 2; head->area = 2.0; head->triples++; head->trip_e[1] = e_id; } } else if ( !equal_element(e_id,head->trip_e[0]) && !equal_element(e_id,head->trip_e[1]) ) { head->status = -10; /* don't move */ head->triples = 3; } if ( tail->status < 0 ) {} else if ( tail->triples == 0 ) { /* first detection */ for ( i = 0 ; i < SDIM ; i++ ) { tail->x[i] = x[k][i] + side[k][0][i]; tail->normal[i] = side[k][0][i]; } tail->status = 1; tail->area = 1.0; tail->triples++; tail->trip_e[0] = e_id; } else if ( tail->triples == 1 ) { /* second detection */ if ( !equal_element(e_id,tail->trip_e[0]) ) { for ( i = 0 ; i < SDIM ; i++ ) { tail->x[i] += x[k][i] + side[k][0][i]; tail->normal[i] -= side[k][0][i]; } tail->status = 2; tail->area = 2.0; tail->triples++; tail->trip_e[1] = e_id; } } else if ( !equal_element(e_id,tail->trip_e[0]) && !equal_element(e_id,tail->trip_e[1]) ) { tail->status = -10; /* don't move */ tail->triples = 3; } } if ( tail->triples > 0 ) continue; if ( (tail->status >= 0) || (mode == RAWEST) ) { /* make sure signs consistent around vertex */ facetedge_id vfe = get_vertex_fe(tailv); /* reference orientation */ facetedge_id thisfe = fe_id; for(;;) { if ( equal_id(vfe,thisfe) ) { sign = 1; break; } thisfe = get_next_facet(thisfe); if ( equal_id(vfe,thisfe) ) { sign = -1; break; } thisfe = inverse_id(get_prev_edge(thisfe)); if ( equal_id(fe_id,thisfe) ) { sign = 0; goto afterlost; /* lost */ } } tail->area += area; for ( i = 0 ; i < SDIM ; i++ ) { tail->normal[i] += sign*normal[i]; tail->x[i] += (x[k][i]+(side[k][0][i]+side[(k+2)%3][1][i])/3)*area; } tail->status++; } afterlost: ; } } /*********************************************************************** * * function: edge_average() * * purpose: find one edge's contributions to averaging of its vertices. * */ void edge_average(e_id,mode) edge_id e_id; int mode; /* averaging mode */ { int i,j,k; struct averages *head,*tail; vertex_id headv = get_edge_headv(e_id); vertex_id tailv = get_edge_tailv(e_id); REAL side[MAXCOORD]; int attr; get_edge_side(e_id,side); /* in case of wraps */ /* head */ head = average + loc_ordinal(headv); attr = get_vattr(headv); if ( attr & FIXED ) goto dotail; if ( mode != RAWEST ) { if ( attr & BOUNDARY ) { if ( !get_eattr(e_id) & BOUNDARY ) goto dotail; if ( get_boundary(headv) != get_edge_boundary(e_id) ) goto dotail; } /* check constraint compatibility */ if ( (attr & CONSTRAINT) ) { conmap_t * vconmap = get_v_constraint_map(headv); conmap_t * econmap = get_e_constraint_map(e_id); int found; for ( j = 1 ; j <= (int)vconmap[0] ; j++ ) { for ( k = 1, found = 0 ; k <= (int)econmap[0] ; k++ ) if ( econmap[k] == (vconmap[j] & ~CON_HIT_BIT) ) { found = 1; break; } if ( !found ) goto dotail; } } } for ( i = 0 ; i < SDIM ; i++ ) head->x[i] -= side[i]; if ( head->status ) for ( i = 0 ; i < SDIM ; i++ ) head->normal[i] += side[i]; else for ( i = 0 ; i < SDIM ; i++ ) head->normal[i] -= side[i]; head->status++; /* mark one more edge done */ dotail: tail = average + loc_ordinal(tailv); attr = get_vattr(tailv); if ( attr & FIXED ) return; if ( mode != RAWEST ) { if ( attr & BOUNDARY ) { if ( !get_eattr(e_id) & BOUNDARY ) return; if ( get_boundary(tailv) != get_edge_boundary(e_id) ) return; } /* check constraint compatibility */ if ( (attr & CONSTRAINT) ) { conmap_t * vconmap = get_v_constraint_map(tailv); conmap_t * econmap = get_e_constraint_map(e_id); int found; for ( j = 1 ; j <= (int)vconmap[0] ; j++ ) { for ( k = 1, found = 0 ; k <= (int)econmap[0] ; k++ ) if ( econmap[k] == (vconmap[j] & ~CON_HIT_BIT) ) { found = 1; break; } if ( !found ) return; } } } for ( i = 0 ; i < SDIM ; i++ ) tail->x[i] += side[i]; if ( tail->status ) for ( i = 0 ; i < SDIM ; i++ ) tail->normal[i] -= side[i]; else for ( i = 0 ; i < SDIM ; i++ ) tail->normal[i] += side[i]; tail->status++; /* mark one more edge done */ } /*********************************************************************** * * function: simplex_facet_average() * * purpose: add neighboring vertex coordinates for one facet * */ void simplex_facet_average(f_id,mode) facet_id f_id; int mode; { struct averages *head; vertex_id *v = get_facet_vertices(f_id); int i,j,n; unsigned int k; for ( i = 0 ; i <= web.dimension ; i++ ) { REAL *x = get_coord(v[i]); for ( j = 0 ; j <= web.dimension ; j++ ) { head = average + loc_ordinal(v[j]); if ( mode != RAWEST ) { conmap_t *conmapj = get_v_constraint_map(v[j]); for ( k = 1 ; k <= conmapj[0] ; k++ ) if ( !v_on_constraint(v[i],conmapj[k]) ) break; if ( k <= conmapj[0] ) continue; } for ( n = 0 ; n < SDIM ; n++ ) head->x[n] += x[n]; head->area += 1.0; head->status++; } } } /*********************************************************************** * * function: new_vertex_average() * * purpose: move vertex to average position of its neighbors * Called via the vertex_average command. * * return: 0 if vertex not moved, * 1 if vertex moved. */ int new_vertex_average(v_id,mode) vertex_id v_id; int mode; /* VOLKEEP to keep volumes on both sides same */ /* NOVOLKEEP to obey just constraints */ /* RAWEST to ignore all restrictions */ { REAL newx[MAXCOORD]; REAL *x; int i; int attr = get_vattr(v_id); int retval; retval = find_vertex_average(v_id,newx,mode); if ( retval == 0 ) return 0; x = get_coord(v_id); if (attr & BOUNDARY ) { /* update boundary parameter to agree with coordinates */ b_extrapolate(get_boundary(v_id),x,newx,newx,get_param(v_id), get_param(v_id),v_id); } else { for ( i = 0 ; i < SDIM ; i++ ) x[i] = newx[i]; if (attr & CONSTRAINT ) project_v_constr(v_id,ACTUAL_MOVE,RESET_ONESIDEDNESS); } return 1; } /*********************************************************************** * * function: find_vertex_average() * * purpose: find average position for vertex, but do not move it. * * return: 0 if vertex not moved, position is old position. * 1 if vertex moved, position is new position. */ int find_vertex_average(v_id,vx,mode) vertex_id v_id; REAL *vx; int mode; /* VOLKEEP to keep volumes on both sides same */ /* NOVOLKEEP to obey just constraints */ /* RAWEST to ignore all restrictions */ { int attr = get_vattr(v_id); REAL xsum[MAXCOORD]; REAL *oldx; REAL x[MAXCOORD]; edge_id e_id,start_e; int i,j,k,n; REAL weight,total_weight; MAT2D(norm,MAXCOORD,MAXCOORD); int dm; int triple_flag = 0; /* whether any triple (or more) edges found */ int single_flag = 0; /* whether any single edges found */ oldx = get_coord(v_id); for ( i = 0 ; i < SDIM ; i++ ) vx[i] = oldx[i]; if ( web.modeltype == LAGRANGE ) return lagrange_vertex_average(v_id,vx); if ( attr & (FIXED|AXIAL_POINT) ) return 0; for ( i = 0 ; i < SDIM ; i++ ) xsum[i] = 0.0; if ( attr & Q_MIDPOINT ) { /* quadratic model midpoint of edge */ facetedge_id fe; facet_id f_id; REAL *xt,side[MAXCOORD],coeff; e_id = get_vertex_edge(v_id); xt = get_coord(get_edge_tailv(e_id)); get_edge_side(e_id,side); fe = get_edge_fe(e_id); /* for soapfilm, keep height above midpoint */ if ( (web.representation == SOAPFILM) && valid_id(fe) ) { f_id = get_fe_facet(fe); if ( valid_id(f_id) ) { get_facet_normal(f_id,norm[0]); coeff = (SDIM_dot(vx,norm[0])-SDIM_dot(xt,norm[0]))/ SDIM_dot(norm[0],norm[0]); for ( i = 0 ; i < SDIM ; i++ ) vx[i] = xt[i] + 0.5*side[i] + coeff*norm[0][i]; goto new_avg_exit; } } /* if didn't work, just center midpoints a little */ coeff = 0.5 - (SDIM_dot(vx,side)-SDIM_dot(xt,side))/ SDIM_dot(side,side); for ( i = 0 ; i < SDIM ; i++ ) vx[i] += coeff*side[i]; } else if ( attr & Q_MIDFACET ) { /* Lagrange model middle of facet */ return lagrange_vertex_average(v_id,vx); } else if ( attr & Q_MIDEDGE ) { /* Lagrange model middle of edge */ return lagrange_vertex_average(v_id,vx); } else { /* corner vertex, average with neighbors */ e_id = start_e = get_vertex_edge(v_id); if ( !valid_id(e_id) ) return 0; k = 0; total_weight = 0.0; /* get weighted sum of neighbors */ do { facetedge_id fe,start_fe; if ( mode != RAWEST ) { /* check constraint compatibility */ int m; if ( (attr & CONSTRAINT) ) { conmap_t * vconmap = get_v_constraint_map(v_id); conmap_t * econmap = get_e_constraint_map(e_id); for ( j = 1 ; j <= (int)vconmap[0] ; j++ ) { int found; for ( m = 1, found = 0 ; m <= (int)econmap[0] ; m++ ) if ( econmap[m] == (vconmap[j] & ~CON_HIT_BIT) ) { found = 1; break; } if ( !found ) goto loopend; } } else if ( attr & BOUNDARY ) { if ( get_edge_boundary(e_id) != get_boundary(v_id) ) goto loopend; } } /* check valence */ if ( (mode != RAWEST) && (web.representation == SOAPFILM) ) { fe = get_edge_fe(e_id); if ( equal_id(fe,get_next_facet(fe)) ) { /* edge edge */ if ( !single_flag ) { /* first, so reset */ total_weight = 0.0; k = 0; for ( i = 0 ; i < SDIM ; i++ ) xsum[i] = 0.0; } single_flag++; } else if ( !equal_id(get_next_facet(fe),get_prev_facet(fe)) ) { /* triple, at least */ if ( !triple_flag ) { /* first, so reset */ total_weight = 0.0; k = 0; for ( i = 0 ; i < SDIM ; i++ ) xsum[i] = 0.0; } triple_flag++; if ( triple_flag >= 3 ) return 0; /* too many triple lines */ } else { if ( triple_flag ) goto loopend; } } if ( (web.representation == STRING) || (get_eattr(e_id) & BARE_NAKED) || single_flag || triple_flag ) weight = 1.0; else { fe = start_fe = get_edge_fe(e_id); weight = 0.0; if ( valid_id(fe) ) do { facet_id f_id = get_fe_facet(fe); REAL a = get_facet_area(f_id); if ( a == 0.0 ) { (*calc_facet_energy)(f_id,AREA_ONLY); a = get_facet_area(f_id); } weight += a; fe = get_next_facet(fe); } while ( !equal_id(fe,start_fe) ); } if ( weight == 0.0 ) goto loopend; get_edge_side(e_id,x); for ( i = 0 ; i < SDIM ; i++ ) xsum[i] += weight*weight*x[i]; total_weight += weight*weight; k++; loopend: e_id = get_next_tail_edge(e_id); } while ( !equal_id(e_id,start_e) ); if ( k <= 1 ) return 0; /* only found at most one edge */ if ( mode == VOLKEEP ) { /* project motion tangentially */ dm = new_calc_vertex_normal(v_id,norm); if ( dm < SDIM ) { dm = gram_schmidt(norm,dm,SDIM); for ( n = 0 ; n < dm ; n++ ) { REAL c = SDIM_dot(xsum,norm[n]); for ( i = 0 ; i < SDIM ; i++ ) xsum[i] -= c*norm[n][i]; } } else for ( i = 0 ; i < SDIM ; i++ ) xsum[i] = 0.0; /* triple pt fixed */ } if ( (attr & CONSTRAINT) ) { unsigned int jj; conmap_t * conmap = get_v_constraint_map(v_id); int oncount = 0; struct constraint *con[MAXCONPER]; int conlist[MAXCONPER]; REAL perp[MAXCOORD]; for ( jj = 1 ; jj <= conmap[0] ; jj++ ) { if ( conmap[jj] & CON_HIT_BIT ) { conlist[oncount] = conmap[jj] & CONMASK; con[oncount] = get_constraint(conmap[jj]); if ( !(con[oncount]->attr & (NONNEGATIVE|NONPOSITIVE) ) ) oncount++; /* will do one-sided later */ } } if ( oncount ) { constr_proj(TANGPROJ,oncount,con,vx, xsum,perp,conlist,NO_DETECT,v_id); for ( j = 0 ; j < SDIM ; j++ ) xsum[j] -= perp[j]; } } for ( i = 0 ; i < SDIM ; i++ ) vx[i] += 0.25*xsum[i]/total_weight; } new_avg_exit: return 1; } /****************************************************************************** * * function: lagrange_vertex_average() * * purpose: Do vertex averaging for a vertex in the Lagrange model. */ int lagrange_vertex_average(v_id,newx) vertex_id v_id; REAL *newx; { int attr = get_vattr(v_id); int i,j,k; if ( attr & Q_MIDEDGE ) { /* average with neighbors on edge */ edge_id e_id = get_vertex_edge(v_id); vertex_id *v = get_edge_vertices(e_id); int knx; /* index of vertex */ vertex_id vv[2]; /* the two neighbors to average with */ REAL perturb[MAXCOORD]; REAL dxdu[MAXCOORD],u,du,rhs,*x; struct gauss_lag *g = &gauss_lagrange[1][web.gauss1D_order]; int ctrl = web.lagrange_order+1; REAL aa; REAL *xx[MAXLAGRANGE+1]; /* find which vertex it is in the edge */ for ( knx = 0 ; knx <= web.lagrange_order ; knx++ ) if ( equal_id(v_id,v[knx]) ) goto edge_found_knx; kb_error(3789,"Internal error:lagrange_vertex_average() couldn't find vertex index in edge.\n", RECOVERABLE); edge_found_knx: /* the two neighbors */ vv[0] = v[knx-1] ; vv[1] = v[knx+1]; /* find perturbation in u,v so can calculate new position exactly on the surface */ /* first, the xyz perturbation */ memset((char*)newx,0,SDIM*sizeof(REAL)); for ( i = 0 ; i < 2 ; i++ ) { REAL *xx = get_coord(vv[i]); for ( j = 0 ; j < SDIM ; j++ ) newx[j] += xx[j]; } x = get_coord(v_id); for ( j = 0 ; j < SDIM ; j++ ) perturb[j] = newx[j]/2 - x[j]; /* get partials matrix */ for(j=0;jlpolypart[knx][0][k]*xx[k][j]; } } rhs = dot(perturb,dxdu,SDIM); aa = dot(dxdu,dxdu,SDIM); du = rhs/aa; u = (knx + du)/web.lagrange_order; lagrange_eval_1d(web.lagrange_order,SDIM,u,xx,newx); /* newx holds return value */ return 1; } else if ( attr & Q_MIDFACET ) { facet_id f_id = get_vertex_facet(v_id); vertex_id *v = get_facet_vertices(f_id); int inx=0,jnx,knx; /* col, row, index of vertex */ vertex_id vv[6]; /* the six neighbors to average with */ MAT2D(dxdu,MAXCOORD,2); REAL *xx[(MAXLAGRANGE+1)*(MAXLAGRANGE+2)/2]; MAT2D(aa,2,2); REAL perturb[MAXCOORD], *p=perturb; REAL u[2],du[2],rhs[2],*r=rhs,*x; struct gauss_lag *g = &gauss_lagrange[2][web.gauss2D_order]; int ctrl = (web.lagrange_order+1)*(web.lagrange_order+2)/2; /* find which vertex it is in the facet */ for ( jnx = 0, knx = 0 ; jnx <= web.lagrange_order ; jnx++ ) for ( inx = 0 ; inx+jnx <= web.lagrange_order ; inx++,knx++ ) if ( equal_id(v_id,v[knx]) ) goto found_knx; kb_error(3788,"Internal error:lagrange_vertex_average() couldn't find vertex index in facet.\n", RECOVERABLE); found_knx: /* the six neighbors */ vv[0] = v[knx - web.lagrange_order + jnx - 2]; vv[1] = v[knx - web.lagrange_order + jnx - 1]; vv[2] = v[knx - 1]; vv[3] = v[knx + 1]; vv[4] = v[knx + web.lagrange_order - jnx]; vv[5] = v[knx + web.lagrange_order - jnx + 1]; /* find perturbation in u,v so can calculate new position exactly on the surface */ /* first, the xyz perturbation */ memset((char*)newx,0,SDIM*sizeof(REAL)); for ( i = 0 ; i < 6 ; i++ ) { REAL *xx = get_coord(vv[i]); for ( j = 0 ; j < SDIM ; j++ ) newx[j] += xx[j]; } x = get_coord(v_id); for ( j = 0 ; j < SDIM ; j++ ) perturb[j] = newx[j]/6 - x[j]; /* get partials matrix */ for(j=0;jlpolypart[knx][0][k]*xx[k][j]; dxdu[j][1] += g->lpolypart[knx][1][k]*xx[k][j]; } } /* kludging due to way lpolypart defined in terms of barycentric */ for ( j = 0 ; j < SDIM ; j++ ) { dxdu[j][0] -= dxdu[j][1]; dxdu[j][1] += dxdu[j][0]; } /* Least squares solve for du */ mat_mult(&p,dxdu,&r,1,SDIM,2); tr_mat_mul(dxdu,dxdu,aa,SDIM,2,2); mat_inv(aa,2); matvec_mul(aa,rhs,du,2,2); u[0] = (inx + du[0])/web.lagrange_order; u[1] = (jnx + du[1])/web.lagrange_order; lagrange_eval_2d(web.lagrange_order,SDIM,u,xx,newx); /* newx holds return value */ return 1; } else /* corner */ { /* not doing anything yet */ return 0; } } evolver-2.30c.dfsg/src/eval_all.c0000644000175300017530000013675311410765113017157 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /***************************************************************** * * File: eval_all.c * * Purpose: To evaluate first derivatives of expressions. * */ #include "include.h" #include "ytab.h" /* for easy assignments */ #define FIRST for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] #define SECOND for ( i = 0 ; i < pcount ; i++ )\ for ( j = 0 ; j < pcount ; j++ ) stacktop->second[i][j] /***************************************************************** * * Function eval_all() * * Purpose: runtime tree_evaluation of expression and all of its * first partial derivatives. * */ void eval_all(ex,params,pcount,fval,partials,q_id) struct expnode *ex; /* expression tree */ REAL *params; /* vector of parameters */ int pcount; /* number of variables */ REAL *fval; /* function value */ REAL *partials; /* values of partials */ element_id q_id; /* reference element, if any */ { int i,n; REAL x,y; struct estack { REAL value, deriv[2*MAXCOORD]; } localstack[100]; struct estack *stacktop = localstack; struct treenode *node; element_id id; struct locallist_t *localbase = ex->locals; for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] = 0.0; stacktop->value = 0.0; /* for empty expression */ if ( ex == NULL ) return; if ( ex->start == NULL ) kb_error(1017,"Internal error: eval_all expression node null.\n", RECOVERABLE); for ( node = ex->start+1 ; ; node++ ) { switch ( node->type ) { case SETUP_FRAME_: break; case EXPRLIST_: break; /* leave expression on stack */ case FUNCTION_CALL_: { struct thread_data *td = GET_THREAD_DATA; REAL value; /* push arguments on eval() stack */ for ( i = 0 ; i < node->op2.argcount ; i++ ) *(++(td->stack_top)) = stacktop[i-node->op2.argcount+1].value; value = eval(&globals(node->op1.name_id)->value.proc, NULL,NULLID,NULL); td->stack_top -= node->op2.argcount; /* pop arguments */ (++stacktop)->value = value; FIRST = 0.0; break; } case FUNCTION_CALL_RETURN_: { /* nothing to do here since FUNCTION_CALL used eval() */ break; } case INDEXSET_: break; /* just accumulate index values */ case ARRAY_HEAD_: break; /* let indices accumulate */ case ARRAYEVAL: { struct array *a = globals(node->op2.name_id)->attr.arrayptr; REAL value; int i,offset; void *lvalue; for ( i = 0 ; i < a->dim ; i++ ) { int k = (int)stacktop[i+1-a->dim].value; if ( k < 1 ) { sprintf(errmsg,"Array index %d of array %s is %d. Indexes start at 1.", i+1,globals(node->op2.name_id)->name,k); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2533,errmsg,RECOVERABLE); } if ( k > a->sizes[i] ) { sprintf(errmsg,"Array index %d of array %s is %d; exceeds bound of %d.", i+1,globals(node->op2.name_id)->name,k,a->sizes[i]); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2536,errmsg,RECOVERABLE); } } for ( i = 1, offset = (int)stacktop[1-a->dim].value-1 ; i < a->dim ; i++ ) { offset *= a->sizes[i]; offset += (int)stacktop[i+1-a->dim].value-1; /* 1-based indexing */ } stacktop -= a->dim; lvalue = ((char *)a) + a->datastart + offset*a->itemsize; switch ( a->datatype ) { case REAL_TYPE: value = *(REAL*)(lvalue); break; case INTEGER_TYPE: value = *(int*)(lvalue); break; case UINT_TYPE: value = *(unsigned int*)(lvalue); break; case CHAR_TYPE: value = *(char*)(lvalue); break; case UCHAR_TYPE: value = *(unsigned char*)(lvalue); break; case SHORT_TYPE: value = *(short int*)(lvalue); break; case USHORT_TYPE: value = *(unsigned short int*)(lvalue); break; case LONG_TYPE: value = *(long*)(lvalue); break; case ULONG_TYPE: value = *(unsigned long*)(lvalue); break; case PTR_TYPE: value = (unsigned long)*(char**)(lvalue); break; case VERTEX_TYPE: case EDGE_TYPE: case FACET_TYPE: case BODY_TYPE: case FACETEDGE_TYPE: case ELEMENTID_TYPE: value = (int)*(element_id*)(lvalue); break; default: value = *(int*)(lvalue); break; } (++stacktop)->value = value; FIRST=0.0; } break; case BACKQUOTE_START_: { struct expnode bqnode = *ex; bqnode.start = node; bqnode.root = node+node->op1.skipsize; bqnode.locals = localbase; eval(&bqnode,params,q_id,NULL); node += node->op1.skipsize-1; } break; case BACKQUOTE_END_ : break; /* just a placeholder */ case ACOMMANDEXPR_: /* backquoted command at start of expression */ break; case SET_GLOBAL_: { struct global *g = globals(node->op1.name_id); g->value.real = stacktop->value; if ( g->attr.varstuff.gradhess == NULL ) g->attr.varstuff.gradhess = (REAL*)mycalloc(MAXCOORD*(MAXCOORD+1), sizeof(REAL)); for ( i = 0 ; i < pcount ; i++ ) g->attr.varstuff.gradhess[i] = stacktop->deriv[i]; stacktop--; break; } case GET_VOLCONST_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; (++stacktop)->value = get_body_volconst(id); FIRST = 0.0; break; case GET_TARGET_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; (++stacktop)->value = get_body_fixvol(id); FIRST = 0.0; break; case GET_MPI_TASK_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; (++stacktop)->value = id_task(id); FIRST = 0.0; break; case REPLACECONST: for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] = 0.0; stacktop->value = node->op1.real; break; case COND_TEST_: if ( (stacktop--)->value == 0. ) { /* jump */ node += node->op1.skipsize; } break; case COND_EXPR_: /* did first command, so skip second */ node += node->op1.skipsize; break; case COND_ELSE_: break; case MAXIMUM_: stacktop--; if (stacktop[0].value < stacktop[1].value) stacktop[0] = stacktop[1]; break; case MINIMUM_: stacktop--; if (stacktop[0].value > stacktop[1].value) stacktop[0] = stacktop[1]; break; case TOGGLEVALUE: (++stacktop)->value = (REAL)get_toggle_value(node->op1.toggle_state); FIRST = 0.0; break; case SIZEOF_: (++stacktop)->value = (REAL) EXTRAS(node->op2.eltype)[node->op1.extranum].array_spec.datacount; FIRST = 0.0; break; case PUSHCONST: case PUSHPI: case PUSHE: (++stacktop)->value = node->op1.real; for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] = 0.0; break; case PUSHG: (++stacktop)->value = web.gravflag ? web.grav_const : 0.; for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] = 0.0; break; case PUSHGLOBAL_: case PUSHADJUSTABLE: { struct global *g = globals(node->op1.name_id); if ( g->flags & FILE_VALUES ) (++stacktop)->value = g->value.file.values[int_val]; else if ( g->flags & STRINGVAL ) (++stacktop)->value = 0.0; else (++stacktop)->value = g->value.real; if ( g->attr.varstuff.gradhess ) for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] = g->attr.varstuff.gradhess[i]; else FIRST = 0.0; } break; case PUSHPARAM: (++stacktop)->value = params[node->op1.coordnum]; for ( i = 0 ; i < pcount ; i++ ) if ( i == node->op1.coordnum ) stacktop->deriv[i] = 1.0; else stacktop->deriv[i] = 0.0; break; case PARAM_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; (++stacktop)->value = get_param(id)[node->op2.coordnum]; for ( i = 0 ; i < SDIM ; i++ ) if ( i == node->op2.coordnum ) stacktop->deriv[i] = 1.0; else stacktop->deriv[i] = 0.0; break; case PUSHQPRESSURE_: (++stacktop)->value = GEN_QUANT(node->op1.quant_id)->pressure; FIRST = 0.0; break; case PUSHQTARGET_: (++stacktop)->value = GEN_QUANT(node->op1.quant_id)->target; FIRST = 0.0; break; case PUSHQMODULUS_: (++stacktop)->value = GEN_QUANT(node->op1.quant_id)->modulus; FIRST = 0.0; break; case PUSHQTOLERANCE_: (++stacktop)->value = GEN_QUANT(node->op1.quant_id)->tolerance; FIRST = 0.0; break; case PUSHMMODULUS_: (++stacktop)->value = METH_INSTANCE(node->op1.quant_id)->modulus; FIRST = 0.0; break; case PUSHQVALUE_: { struct gen_quant *q = GEN_QUANT(node->op1.quant_id); (++stacktop)->value = q->value; FIRST = 0.0; } break; case PUSHMVALUE_: { struct method_instance *mi = METH_INSTANCE(node->op1.meth_id); (++stacktop)->value = mi->value; if ( (mi->flags & Q_COMPOUND) && (mi->stamp == comp_quant_stamp) ) { for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] = mi->grad[comp_quant_vertex][i]; } else FIRST = 0.0; } break; case PUSHQVOLCONST_: (++stacktop)->value = GEN_QUANT(node->op1.quant_id)->volconst; FIRST = 0.0; break; case PUSH_NAMED_QUANTITY: case PUSH_METHOD_INSTANCE_: (++stacktop)->value = (REAL)node->op1.quant_id; FIRST = 0.0; break; case GET_INTERNAL_: (++stacktop)->value = get_internal_variable(node->op1.name_id); FIRST = 0.0; break; case DYNAMIC_LOAD_FUNC_: (*node->op1.funcptr)(FUNC_DERIV,params,(struct dstack*)(++stacktop)); break; case USERFUNC: stacktop++; stacktop->value = (*userfunc_deriv[node->op1.userfunc])(params,stacktop->deriv); break; case INDEXED_ELEMENT_: { int ord = (int)((stacktop--)->value); element_id id = get_ordinal_id(node->op1.eltype,abs(ord)-1); if ( !valid_id(id) ) { sprintf(errmsg,"%s index %d is not valid.\n", typenames[node->op1.eltype],ord); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1289,errmsg,RECOVERABLE); } if ( ord < 0 ) invert(id); *(element_id*)get_localp(node->op2.localnum) = id; break; } case QUALIFIED_ATTRIBUTE: break; /* just a no-op in execution */ /* need logical expressions for conditional expressions */ case GT_: stacktop--; stacktop[0].value = (REAL)(stacktop[0].value > stacktop[1].value); FIRST = 0.0; break; case LT_: stacktop--; stacktop[0].value = (REAL)(stacktop[0].value < stacktop[1].value); FIRST = 0.0; break; case LE_: stacktop--; stacktop[0].value = (REAL)(stacktop[0].value <= stacktop[1].value); FIRST = 0.0; break; case GE_: stacktop--; stacktop[0].value = (REAL)(stacktop[0].value >= stacktop[1].value); FIRST = 0.0; break; case NE_: stacktop--; stacktop[0].value = (REAL)(stacktop[0].value != stacktop[1].value); FIRST = 0.0; break; case EQ_: stacktop--; stacktop[0].value = (REAL)(stacktop[0].value == stacktop[1].value); FIRST = 0.0; break; case AND_: /* short-circuit */ if ( stacktop->value == 0.0 ) { (++stacktop)->value = 0.0; FIRST = 0.0; node += node->op1.skipsize; } break; case CONJUNCTION_END: /* short-circuiting results in second arg being answer */ stacktop--; *stacktop = stacktop[1]; if ( stacktop->value != 0.0 ) stacktop->value = 1.0; break; case OR_: if ( stacktop->value != 0.0 ) { (++stacktop)->value = 1.0; FIRST = 0.0; node += node->op1.skipsize; } break; case NOT_: stacktop[0].value = (REAL)(!stacktop[0].value); FIRST = 0.0; break; case PLUS: stacktop--; stacktop[0].value += stacktop[1].value; for ( i = 0 ; i < pcount ; i++ ) stacktop[0].deriv[i] += stacktop[1].deriv[i]; break; case MINUS: case EQUATE: stacktop--; stacktop[0].value -= stacktop[1].value; for ( i = 0 ; i < pcount ; i++ ) stacktop[0].deriv[i] -= stacktop[1].deriv[i]; break; case TIMES: stacktop--; for ( i = 0 ; i < pcount ; i++ ) stacktop[0].deriv[i] = stacktop[1].value*stacktop[0].deriv[i] + stacktop[0].value*stacktop[1].deriv[i]; stacktop[0].value *= stacktop[1].value; break; case DIVIDE: stacktop--; if ( stacktop[1].value == 0.0 ) { sprintf(errmsg,"Divide by zero.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1019,errmsg,RECOVERABLE); } for ( i = 0 ; i < pcount ; i++ ) stacktop[0].deriv[i] = (stacktop[1].value*stacktop[0].deriv[i] - stacktop[0].value*stacktop[1].deriv[i]) /stacktop[1].value/stacktop[1].value; stacktop[0].value /= stacktop[1].value; break; case REALMOD: stacktop--; if ( stacktop[1].value == 0.0 ) { sprintf(errmsg,"Modulus base zero.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1020,errmsg,RECOVERABLE); } for ( i = 0 ; i < pcount ; i++ ) stacktop[0].deriv[i] = stacktop[0].deriv[i] - floor(stacktop[0].value/stacktop[1].value) *stacktop[1].deriv[i]; stacktop[0].value -= floor(stacktop[0].value/stacktop[1].value) *stacktop[1].value; break; case IMOD_: stacktop--; if ( stacktop[1].value == 0.0 ) kb_error(1021,"Modulus base zero.\n",RECOVERABLE); stacktop[0].value = floor(stacktop[0].value) - floor(floor(stacktop[0].value)/floor(stacktop[1].value)) *floor(stacktop[1].value); for ( i = 0 ; i < pcount ; i++ ) stacktop[0].deriv[i] = 0.0; break; case IDIV_: stacktop--; if ( (int)stacktop[1].value == 0 ) kb_error(1022,"Division by zero.\n",RECOVERABLE); stacktop[0].value = (REAL)((int)(stacktop[0].value)/(int)(stacktop[1].value)); for ( i = 0 ; i < pcount ; i++ ) stacktop[0].deriv[i] = 0.0; break; case INTPOW: if ( node->op1.intpow == 0 ) { stacktop->value = 1.0; for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] = 0.0; } else if ( node->op1.intpow == 1 ) { /* no action necessary */ } else { x = stacktop->value; if ( node->op1.intpow > 1 ) /* get n-1 power first */ for ( n = 1 ; n < node->op1.intpow-1 ; n++ ) stacktop->value *= x; else if ( node->op1.intpow < 0 ) { if ( x == 0.0 ) { sprintf(errmsg,"Negative power (%d) of zero.\n",node->op1.intpow); kb_error(1023,errmsg,RECOVERABLE); } stacktop->value = 1/x; for ( n = 0 ; n < -node->op1.intpow ; n++ ) stacktop->value *= 1/x; } for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] = node->op1.intpow*stacktop->value*stacktop->deriv[i]; stacktop->value *= x; } break; case POW: stacktop--; x = stacktop[0].value; y = stacktop[1].value; if ( x == 0.0 ) { stacktop->value = 0.0; if ( y == 1.0 ) for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] = stacktop[0].deriv[i]; else if ( y > 1.0 ) for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] = 0.; else { sprintf(errmsg,"Negative power (%f) of zero in derivative.\n", (DOUBLE)(y-1)); kb_error(1024,errmsg,RECOVERABLE); } } else if ( (x < 0) && ( (REAL)(int)y != y ) ) { sprintf(errmsg,"Nonintegral power (%f) of negative number.\n",(double)y); kb_error(2003,errmsg,RECOVERABLE); } else { stacktop->value = pow(x,y); for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] = (log(fabs(x))*stacktop[1].deriv[i] + y/x*stacktop[0].deriv[i]) *stacktop->value; } break; case ATAN2_: stacktop--; y = stacktop[0].value; x = stacktop[1].value; stacktop->value = atan2(y,x); for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] = (x*stacktop[0].deriv[i] - y*stacktop[1].deriv[i])/(x*x + y*y); break; case INCOMPLETE_ELLIPTICF: stacktop--; y = stacktop[0].value; x = stacktop[1].value; stacktop->value = incompleteEllipticF(x,y); for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] = incompleteEllipticFdphi(x,y)*stacktop[0].deriv[i] + incompleteEllipticFdm(x,y)*stacktop[1].deriv[i]; break; case INCOMPLETE_ELLIPTICE: stacktop--; y = stacktop[0].value; x = stacktop[1].value; stacktop->value = incompleteEllipticE(x,y); for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] = incompleteEllipticEdphi(x,y)*stacktop[0].deriv[i] + incompleteEllipticEdm(x,y)*stacktop[1].deriv[i]; break; case ELLIPTICK: x = stacktop[0].value; stacktop->value = ellipticK(x); for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] = ellipticKdm(x)*stacktop[0].deriv[i]; break; case ELLIPTICE: x = stacktop[0].value; stacktop->value = ellipticE(x); for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] = ellipticEdm(x)*stacktop[0].deriv[i]; break; case SQR: for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] *= 2*stacktop->value; stacktop->value *= stacktop->value; break; case SQRT: if ( stacktop->value < 0.0 ) kb_error(2496,"Square root of negative number.\n",RECOVERABLE); stacktop->value = sqrt(stacktop->value); if ( stacktop->value == 0.0 ) for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] = 0.0; else for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] /= 2*stacktop->value; break; case CEIL_: stacktop->value = ceil(stacktop->value); FIRST = 0; break; case FLOOR_: stacktop->value = floor(stacktop->value); FIRST = 0; break; case ABS: if ( stacktop->value < 0.0 ) { for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] = -stacktop->deriv[i]; stacktop->value = -stacktop->value; } break; case SIN: x = cos(stacktop->value); for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] *= x; stacktop->value = sin(stacktop->value); break; case COS: x = -sin(stacktop->value); for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] *= x; stacktop->value = cos(stacktop->value); break; case ATAN: x = (1 + stacktop->value*stacktop->value); for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] /= x; stacktop->value = atan(stacktop->value); break; case EXP: stacktop->value = exp(stacktop->value); for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] *= stacktop->value; break; case SINH: y = exp(stacktop->value); x = (y+1/y)/2; for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] *= x; stacktop->value = (y-1/y)/2; break; case COSH: y = exp(stacktop->value); x = (y-1/y)/2; for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] *= x; stacktop->value = (y+1/y)/2; break; case TANH: y = exp(stacktop->value); x = (y-1/y)/2; y = (y+1/y)/2; FIRST /= y*y ; stacktop->value = x/y; break; case ASINH: y = 1/sqrt(1+stacktop->value*stacktop->value); FIRST *= y; stacktop->value = log(stacktop->value + sqrt(stacktop->value*stacktop->value + 1)); break; case ACOSH: if ( stacktop->value <= 1.0 ) kb_error(2490,"Acosh argument less than 1.\n",RECOVERABLE); y = 1/sqrt(stacktop->value*stacktop->value - 1); FIRST *= y; stacktop->value = 2*log(sqrt(stacktop->value + 1) + sqrt(stacktop->value - 1)) - log(2.0); break; case ATANH: if ( stacktop->value >= 1.0 ) kb_error(2491,"Acosh argument less than 1.\n",RECOVERABLE); y = 1/(1 - stacktop->value*stacktop->value); FIRST *= y; stacktop->value = log(stacktop->value + 1)/2 - log(1 - stacktop->value)/2; break; case LOG: if ( stacktop->value <= 0.0 ) { sprintf(errmsg,"Log argument is %18.15f; must be positive.\n", stacktop->value); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2577,errmsg, RECOVERABLE ); } for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] /= stacktop->value; stacktop->value = log(stacktop->value); break; case ASIN: if ( fabs(stacktop->value) > 1.0 ) { sprintf(errmsg,"Asin argument is %18.15f, magnitude greater than 1.\n", stacktop->value); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2579,errmsg, RECOVERABLE ); } x = sqrt(1 - stacktop->value*stacktop->value); for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] /= x; stacktop->value = asin(stacktop->value); break; case ACOS: if ( fabs(stacktop->value) >= 1.0 ) { sprintf(errmsg,"Acos argument is %18.15f, magnitude greater than 1.\n", stacktop->value); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2583,errmsg, RECOVERABLE ); } x = -sqrt(1 - stacktop->value*stacktop->value); for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] /= x; stacktop->value = acos(stacktop->value); break; case TAN: stacktop->value = tan(stacktop->value); x = 1+stacktop->value*stacktop->value; for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] *= x; break; case CHS: for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] = -stacktop->deriv[i]; stacktop->value = -stacktop->value; break; case INV: if ( stacktop->value == 0.0 ) kb_error(2495,"Division by zero.\n",RECOVERABLE); for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] /= stacktop->value*stacktop->value; stacktop->value = 1/stacktop->value; break; /* attribute values */ case GET_SQ_MEAN_CURV_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; stacktop->value = vertex_sq_mean_curvature(id); FIRST = 0.0; break; case GET_MEANCURV_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; stacktop->value = vertex_mean_curvature(id); FIRST = 0.0; break; case LENGTH_: case GET_LENGTH_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; calc_edge(id); ++stacktop; stacktop->value = get_edge_length(id); FIRST = 0.0; break; case GET_DIHEDRAL_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; if ( id_type(id) == EDGE ) stacktop->value = dihedral(id); else if ( id_type(id) == VERTEX ) stacktop->value = vertex_angle(id); else stacktop->value = 0.0; FIRST = 0.0; break; case VALENCE_: case GET_VALENCE_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch ( id_type(id) ) { case VERTEX: stacktop->value = (REAL)get_vertex_evalence(id); break; case EDGE: stacktop->value = (REAL)get_edge_valence(id); break; case FACET: stacktop->value = (REAL)get_facet_valence(id); break; case BODY: stacktop->value = (REAL)get_body_valence(id); break; } FIRST = 0.0; break; case GET_EDGE_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = (REAL)(ordinal(get_fe_edge(id))+1); FIRST = 0.0; break; case GET_FACET_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = (REAL)(ordinal(get_fe_facet(id))+1); FIRST = 0.0; break; case AREA_: case GET_AREA_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = get_facet_area(id); FIRST = 0.0; break; case GET_WRAP_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = (REAL)get_edge_wrap(id); FIRST = 0.0; break; case GET_PRESSURE_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch ( id_type(id) ) { case BODY: stacktop->value = get_body_pressure(id); break; default: kb_error(1025,"Pressure only for bodies.\n",RECOVERABLE); } FIRST = 0.0; break; case GET_USERATTR_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = user_attribute(id); FIRST = 0.0; break; case GET_QUANTITY_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = quantity_attribute(id,node->op2.quant_id); FIRST = 0.0; break; case GET_INSTANCE_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = instance_attribute(id,node->op2.meth_id); FIRST = 0.0; break; case GET_PHASE_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch ( id_type(id) ) { case FACET: stacktop->value = (REAL)get_f_phase(id); break; case BODY: stacktop->value = (REAL)get_b_phase(id); break; default: kb_error(1026,"Phase of wrong type element.\n",RECOVERABLE); } FIRST = 0.0; break; case DENSITY_: case GET_DENSITY_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch ( id_type(id) ) { case EDGE: stacktop->value = get_edge_density(id); break; case FACET: stacktop->value = get_facet_density(id); break; case BODY: stacktop->value = get_body_density(id); break; default: kb_error(1027,"Density of wrong type element.\n",RECOVERABLE); } FIRST = 0.0; break; case GET_STAR_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch ( id_type(id) ) { case VERTEX: stacktop->value = get_vertex_star(id); break; case EDGE: stacktop->value = get_edge_star(id); break; default: stacktop->value = 0.0; break; } FIRST = 0.0; break; case VOLUME_: case GET_VOLUME_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = get_body_volume(id); FIRST = 0.0; break; case ID_: case GET_ID_: case GET_OID_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; if ( (node->type == GET_OID_) && inverted(id) ) stacktop->value = -(REAL)(ordinal(id)+1); else stacktop->value = (REAL)(ordinal(id)+1); FIRST = 0.0; break; case ORIGINAL_: case GET_ORIGINAL_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = valid_id(id) ? (REAL)ordinal(get_original(id))+1:0; FIRST = 0.0; break; case GET_FRONTCOLOR_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = (REAL)get_facet_frontcolor(id); FIRST = 0.0; break; case GET_COLOR_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch ( id_type(id) ) { case EDGE: stacktop->value = (REAL)get_edge_color(id); break; case FACET: stacktop->value = (REAL)get_facet_color(id); break; default: stacktop->value = 0.0; } FIRST = 0.0; break; case GET_BACKCOLOR_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch ( id_type(id) ) { case FACET: stacktop->value = (REAL)get_facet_backcolor(id); break; default: stacktop->value = 0.0; } FIRST = 0.0; break; case GET_FRONTBODY_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch ( id_type(id) ) { case BODY: stacktop->value = (REAL)ordinal(get_facet_body(id))+1; break; default: stacktop->value = 0.0; } FIRST = 0.0; break; case GET_BACKBODY_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch ( id_type(id) ) { case BODY: stacktop->value = (REAL)ordinal(get_facet_body(inverse_id(id)))+ 1; break; default: stacktop->value = 0.0; } FIRST = 0.0; break; case TAG_: case GET_TAG_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = (REAL)get_tag(id); FIRST = 0.0; break; case BARE_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = (get_attr(id) & BARE_NAKED) ? 1.0 : 0.0; FIRST = 0.0; break; case GET_MIDV_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = ordinal(get_edge_midv(id)) + 1.; FIRST = 0.0; break; case FIXED_: case GET_FIXED_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = (get_attr(id) & FIXED) ? 1.0 : 0.0; FIRST = 0.0; break; case GET_EXTRA_ATTR_: { struct extra *ext; int spot,k; n = node->op3.extranum; if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ext = EXTRAS(node->op2.eltype) + n; /* get index */ spot = 0; for ( k = 0 ; k < ext->array_spec.dim ; k++ ) { int j = (int)(stacktop[-ext->array_spec.dim+k+1].value); spot *= ext->array_spec.sizes[k]; if ( (j < 1) || (j > ext->array_spec.sizes[k]) ) { sprintf(errmsg, "Attribute %s index %d is %d; maximum is %d (in %s).\n", ext->name,k+1,j,ext->array_spec.sizes[k],ex->name); kb_error(2525,errmsg,RECOVERABLE); } spot += (int)(stacktop[-ext->array_spec.dim+k+1].value) - 1; } stacktop -= ext->array_spec.dim; if ( id_type(id) != node->op2.eltype ) { if ( (id_type(id)==EDGE) && (node->op2.eltype==VERTEX) && params ) { ext = EXTRAS(VERTEX) + n; (++stacktop)->value = interp_edge_attribute(id,ext,spot,(int)params[2*SDIM]); FIRST = 0.0; break; } else if ( (id_type(id)==FACET) && (node->op2.eltype==VERTEX) && params ) { ext = EXTRAS(VERTEX) + n; (++stacktop)->value = interp_facet_attribute(id,ext,spot,(int)params[2*SDIM]); FIRST = 0.0; break; } else { sprintf(errmsg, "Attribute %s is %s attribute, not %s attribute (in %s).\n", EXTRAS(node->op2.eltype)[n].name, typenames[node->op2.eltype], typenames[id_type(id)],ex->name); kb_error(2537,errmsg,RECOVERABLE); } } if ( ext->code.start ) { int oldflag = autorecalc_flag; autorecalc_flag = 0; eval(&ext->code,NULL,id,NULL); /* side-effect fills in values */ autorecalc_flag = oldflag; } switch ( ext->type ) { case REAL_TYPE: (++stacktop)->value = ((REAL*)get_extra(id,n))[spot]; break; case INTEGER_TYPE: case CONSTRAINT_TYPE: case BOUNDARY_TYPE: case QUANTITY_TYPE: case INSTANCE_TYPE: case PROCEDURE_TYPE: (++stacktop)->value = (REAL)((int*)get_extra(id,n))[spot]; break; case UINT_TYPE: (++stacktop)->value = (REAL)((unsigned int*)get_extra(id,n))[spot]; break; case USHORT_TYPE: (++stacktop)->value = (REAL)((unsigned short*)get_extra(id,n))[spot]; break; case SHORT_TYPE: (++stacktop)->value = (REAL)((short*)get_extra(id,n))[spot]; break; case ULONG_TYPE: (++stacktop)->value = (REAL)((unsigned long*)get_extra(id,n))[spot]; break; case LONG_TYPE: (++stacktop)->value = (REAL)((long*)get_extra(id,n))[spot]; break; case UCHAR_TYPE: (++stacktop)->value = (REAL)((unsigned char*)get_extra(id,n))[spot]; break; case CHAR_TYPE: (++stacktop)->value = (REAL)((char*)get_extra(id,n))[spot]; break; case PTR_TYPE: (++stacktop)->value = (REAL)(unsigned long int)((char**)get_extra(id,n))[spot]; break; case ELEMENTID_TYPE: case VERTEX_TYPE: case EDGE_TYPE: case FACET_TYPE: case BODY_TYPE: case FACETEDGE_TYPE: (++stacktop)->value = (REAL)((element_id*)get_extra(id,n))[spot]; break; } } FIRST = 0.0; break; case ON_CONSTRAINT_: { int testcon = (int)(stacktop--)->value; if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch(id_type(id)) { case VERTEX: stacktop->value = (REAL)v_on_constraint(id,testcon); break; case EDGE : stacktop->value = (REAL)e_on_constraint(id,testcon); break; case FACET : stacktop->value = (REAL)f_on_constraint(id,testcon); break; default: kb_error(1030,"Can't do constraints on this type element.\n", RECOVERABLE); } } FIRST = 0.0; break; case HIT_CONSTRAINT_: { int testcon = (int)(stacktop--)->value; if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch(id_type(id)) { case VERTEX: stacktop->value = (REAL)get_v_constraint_status(id,testcon); break; default: kb_error(1031,"Can do hit_constraints only on vertices.\n", RECOVERABLE); } } FIRST = 0.0; break; case ON_BOUNDARY_: { struct boundary *b=NULL; int testb = (int)(stacktop--)->value; if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch(id_type(id)) { case VERTEX: b = get_boundary(id); break; case EDGE : b = get_edge_boundary(id); break; case FACET : b = get_facet_boundary(id); break; default: kb_error(1032,"Can't do boundary on this type element.\n", RECOVERABLE); } stacktop->value = (b == web.boundaries+testb) ? 1.0 : 0.0; } FIRST = 0.0; break; /* whole-array syntax */ case ARRAYIDENT_: /* push datastart for array */ { struct global *glvalue = globals(node->op2.name_id); struct array *alvalue = glvalue->attr.arrayptr; if ( glvalue->flags & FIXED_SIZE_ARRAY ) *(struct estack**)(++stacktop) = localstack + glvalue->attr.arrayptr->datastart; else *(char**)(++stacktop) = (char*)alvalue + alvalue->datastart; break; } case ATTRIB_LVALUE_: /* push datastart for attribute array */ { element_id id; n = node->op2.name_id & GLOBMASK; /* attribute number */ if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *(char**)(++stacktop) = (char*)get_extra(id,n); } break; case ARRAY_VERTEX_NORMAL_: case ARRAY_EDGE_VECTOR_: case ARRAY_FACET_NORMAL_: { element_id id; REAL *datastart = (REAL*)get_localp(node->op3.localnum); *(REAL**)(++stacktop) = datastart; if ( node->flags & IS_RVALUE ) { if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; switch ( node->type ) { case ARRAY_VERTEX_NORMAL_: { MAT2D(normal,MAXCOORD,MAXCOORD); REAL mag; int normcount; normcount = new_calc_vertex_normal(id,normal); project_vertex_normals(id,normal,normcount); mag = sqrt(SDIM_dot(normal[0],normal[0])); if ( mag == 0.0 ) mag = 1; for ( i = 0 ; i < SDIM ; i++ ) datastart[i] = normal[0][i]/mag; break; } case ARRAY_EDGE_VECTOR_: get_edge_side(id,datastart); break; case ARRAY_FACET_NORMAL_: get_facet_normal(id,datastart); break; } } } break; case ARRAY_LVALUE_INDEXED_: break; case ARRAY_RVALUE_ : break; case DOT_: /* dot product */ { struct array *a,*b; int name1 = node->op2.name_id; int name2 = node->op3.name_id; REAL *datastart1,*datastart2; REAL sum; int count; a = get_name_arrayptr(name1,NULL,localbase); b = get_name_arrayptr(name2,NULL,localbase); count = (a->datacount < b->datacount) ? a->datacount : b->datacount; datastart1 = *(REAL**)(stacktop--); datastart2 = *(REAL**)(stacktop--); for ( sum = 0.0, i = 0 ; i < count ; i++ ) sum += datastart1[i]*datastart2[i]; (++stacktop)->value = sum; FIRST = 0.0; break; } case ARRAY_EVAL_: /* rexpr: arraylvalue indexset */ { /* use info on stack to push value of array element. stack: datastart index-values -> rexpr */ struct array *a; REAL value=0.0; int i,offset; void *lvalue; char *datastart; a = get_name_arrayptr(node->op2.name_id,NULL,NULL); for ( i = 0 ; i < a->dim ; i++ ) { int k = (int)stacktop[i+1-a->dim].value; if ( k < 1 ) { sprintf(errmsg, "Array index %d of array %s is %d. Indexes start at 1.\n", i+1,get_name_name(node->op2.name_id,localbase),k); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3007,errmsg,RECOVERABLE); } if ( k > a->sizes[i] ) { sprintf(errmsg,"Array index %d of array %s is %d; exceeds bound of %d.\n", i+1,get_name_name(node->op2.name_id,localbase),k,a->sizes[i]); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3008,errmsg,RECOVERABLE); } } for ( i = 1, offset = (int)stacktop[1-a->dim].value-1 ; i < a->dim ; i++ ) { offset *= a->sizes[i]; offset += (int)stacktop[i+1-a->dim].value-1; /* 1-based indexing */ } stacktop -= a->dim; datastart = *(char**)(stacktop--); lvalue = datastart + offset*a->itemsize; switch ( a->datatype ) { case REAL_TYPE: value = *(REAL*)(lvalue); break; case INTEGER_TYPE: value = *(int*)(lvalue); break; case UINT_TYPE: value = *(unsigned int*)(lvalue); break; case SHORT_TYPE: value = *(short int*)(lvalue); break; case USHORT_TYPE: value = *(unsigned short int*)(lvalue); break; case LONG_TYPE: value = *(long int*)(lvalue); break; case ULONG_TYPE: value = *(unsigned long int*)(lvalue); break; case CHAR_TYPE: value = *(char*)(lvalue); break; case UCHAR_TYPE: value = *(unsigned char*)(lvalue); break; case PTR_TYPE: value = (unsigned long int)*(char**)(lvalue); break; case VERTEX_TYPE: case EDGE_TYPE: case FACET_TYPE: case BODY_TYPE: case FACETEDGE_TYPE: case ELEMENTID_TYPE: break; default: value = *(int*)(lvalue); break; } (++stacktop)->value = value; FIRST = 0.0; break; } case FINISHED: *fval = stacktop->value; for ( i = 0 ; i < pcount ; i++ ) partials[i] = stacktop->deriv[i]; return; default: sprintf(errmsg,"Bad expression eval_all() node type %d: %s.", node->type,tokname(node->type)); kb_error(1033,errmsg,RECOVERABLE); break; } if ( node == ex->root ) break; } *fval = stacktop->value; for ( i = 0 ; i < pcount ; i++ ) partials[i] = stacktop->deriv[i]; return; } /* end eval_all() */ evolver-2.30c.dfsg/src/glutgraph.c0000644000175300017530000031317611410765113017371 0ustar hazelscthazelsct/******************************************************************** * * File: glutgraph.c * * Contents: OpenGL/GLUT display for Surface Evolver * For OpenGL Version 1.1 * (modified from oglgraph.c, which used aux) */ /* This runs in a separate thread from the main program, so it avoids calling kb_error, since that would longjump to the wrong place. Except for WARNINGS, which are OK since kb_error() just returns. */ /* Some timings in various OpenGL drawing modes: (done with cat.fe and gtime.cmd)(list, arrays exclude setup time) (done in 'u' mode for 10 drawings; time for one reported here) (flat shading for no arrays; doesn't matter for others) facets edges no arrays display list arrays indexed arrays strips 6144 9132 0.2224 0.1500 0.1021 0.0691 0.0420 24576 37056 0.8552 0.5740 0.3846 0.2544 0.1292 98304 147840 3.4059 1.4932 1.0154 0.4888 98304 0 1.6513 0.6630 0.5208 0.5348 0.2223 393216 0 7.8273 3.2527 2.1140 2.1827 0.8720 To get a sense of the set-up times, here are recalc times, flat shading except for strips. facets edges no arrays display list arrays indexed arrays strips 6144 9132 0.32 1.2319 0.3796 0.5558 0.9143 24576 37056 0.9164 11.1961 1.0064 1.6804 2.9993 98304 147840 3.6503 4.2781 6.9260 12.5481 On transforms, using srol.8.fe Transforms facets edges arrays indexed arrays strips 24 196608 297216 3.3058 2.2122 1.2307 A little slower than one would expect, maybe due to normal flipping?? Or memory caching? But at least set-up is fast. */ #undef DOUBLE /* so can use glutPostWindowRedisplay() */ #define GLUT_API_VERSION 4 #if defined(WIN32) || defined(_WIN32) #undef FIXED #include #include #else #define __cdecl #if defined(MAC_OS_X) #include #else #include #endif #endif #include "include.h" #ifdef _DEBUG #define GL_ERROR_CHECK \ { int err = glGetError(); \ if ( err ) \ fprintf(stderr,\ "OpenGL error %08X window %d at line %d\n",err,glutGetWindow(),\ __LINE__);\ } #else #define GL_ERROR_CHECK #endif struct stripstruct { int mode; /* GL_TRIANGLE_STRIP or GL_LINE_STRIP */ int start; /* starting offset in indexarray */ int count; /* number of vertices in strip */ }; /* projmode values */ #define P_ORTHO 0 #define P_PERSP 1 /* stereomode values */ #define NO_STEREO 0 #define CROSS_STEREO 1 #define NOLIST 0 #define NORMALLIST 1 #define RESETLIST 2 /* Multiple graphing windows stuff */ int graph_thread_running; /* whether graph thread is running */ int draw_pid; /* draw process id, for signalling from main */ int main_pid; /* to see if same as main thread */ #ifdef PTHREADS sigset_t newset; /* mask SIGKICK from main thread */ #define SIGKICK SIGALRM #endif int dup_window; /* window for new window to duplicate */ #define GET_DATA (gthread_data + glutGetWindow()) #define MAXGRAPHWINDOWS 10 #define WINTITLESIZE 120 struct thread_data glutgraph_thread_data; /* for eval() */ struct graph_thread_data { int in_use; int new_title_flag; char wintitle[WINTITLESIZE]; int newarraysflag; /* to cause rebuild of arrays */ int arrays_timestamp; /* last arrays computed in this thread */ int declared_timestamp; /* last arrays declared in this thread */ long win_id; /* GLUT graphics window id */ #ifdef WIN32 HWND draw_hwnd; /* handle to graphics window */ #endif int olddim; double scrx[4],scry[4]; /* screen corners */ double aspect; /* aspect ratio */ REAL window_aspect; /* set by user */ double xscale,yscale; /* scaling factors */ GLsizei xsize,ysize; /* window size */ int oldx,oldy,newx,newy; /* for tracking motion */ int basex,basey; /* translation to do on object */ REAL *view[MAXCOORD+1]; /* this window's view matrix */ REAL viewspace[MAXCOORD+1][MAXCOORD+1]; int view_initialized; vertex_id focus_vertex_id; /* rotate and zoom focus */ REAL *to_focus[MAXCOORD+1]; REAL to_focus_space[MAXCOORD+1][MAXCOORD+1]; REAL *from_focus[MAXCOORD+1]; REAL from_focus_space[MAXCOORD+1][MAXCOORD+1]; float kb_norm[3]; /* state of normal vector */ int projmode; /* kind of projection to do */ float projmat[16]; /* for saving projection matrix */ int stereomode; int facetshow_flag; /* whether to show facets */ int normflag; /* whether normals should be calculuated by graphgen() */ REAL linewidth; REAL edge_bias; /* amount edges drawn in front */ int mouse_mode; int mouse_left_state; /* state of left mouse button */ int idle_flag; /* Mac OS X kludge for eliminating excessive glutPostRedisplays, set by idle_func and cleared by draw_screen */ int resize_flag; /* whether redraw event caused by resize. To get around Mac OS X problem of not combining events. */ /* not used; didn't work as expected */ int aspect_flag; /* whether pending reshape due to aspect fixing */ int dlistflag; /* whether to use display list */ int arraysflag; /* whether to use OpenGL 1.1 arrays */ struct vercol *fullarray; int fullarray_original; /* if this thread allocated fullarray */ float *colorarray; int edgestart,edgecount,facetstart,facetcount; int vertexcount; struct vercol *edgearray,*facetarray; int edgemax; int facetmax; /* allocated */ int interleaved_flag; /* whether to do arrays as interleaved */ int indexing_flag; /* whether to use indexed arrays (smaller,but random access) */ int *indexarray; int strips_flag; /* whether to do GL strips */ int strip_color_flag; /* whether to color facets according to strip number */ struct stripstruct *striparray; int stripcount; /* size of striparray */ int estripcount; /* number of edge strips */ int fstripcount; /* number of facet strips */ int *stripdata; int doing_lazy; /* whether glutgraph should do transforms itself */ int q_flag; /* whether to print drawing stats */ int mpi_graph_task; /* for MPI Evolver */ } gthread_data[MAXGRAPHWINDOWS]; /* end multiple graphing windows stuff */ struct vercol { float c[4]; float n[3]; float x[3]; int inx; }; static int mainmenu, submenu; /* menu identifiers */ static int mpi_taskmenu; /* for task-picking menu */ static char opengl_version[20]; /* from glGetString */ int close_flag = 0; /* whether close_show has been done */ void Ogl_close ARGS((void)); void Ogl_close_show ARGS((void)); void idle_func ARGS((void)); void make_strips(void); void make_indexlists(void); void set_title ARGS((struct graph_thread_data*)); #ifdef PTHREADS void * draw_thread ARGS((void *)); #else void __cdecl draw_thread ARGS((void *)); #endif static int glutInit_called; /* so don't call again if close and reopen */ static REAL gleps = 1e-5; /* tolerance for identifying vertices */ static REAL imagescale = 1.0; /* scaling factor for image */ static float rgba[16][4]; void declare_arrays ARGS((void)); void draw_one_image ARGS((void)); void enlarge_edge_array ARGS((struct graph_thread_data*)); void enlarge_facet_array ARGS((struct graph_thread_data*)); void pick_func ARGS((int,int)); element_id name_to_id ARGS((GLuint)); void myMenuInit ARGS((void)); void my_glLoadName ARGS(( element_id )); void e_glColor ARGS(( struct graph_thread_data *,int )); void f_glColor ARGS(( struct graph_thread_data *,int )); void e_glVertex3dv ARGS(( struct graph_thread_data *,REAL * )); void f_glVertex3dv ARGS(( struct graph_thread_data *,REAL * )); int hashfunc ARGS (( struct vercol *)); int vercolcomp ARGS(( struct vercol *, struct vercol *)); int eecomp ARGS(( int *, int *)); int build_arrays ARGS((void)); void mpi_get_task_graphics ARGS((int)); static int initz_flag = 0; /* z buffer */ int no_graphthread_flag = 0; /* kludge for mpi task graphics */ /***************************************************************************** * * function glut_text_display() * * purpose: display text lines in graphics window. */ void glut_text_display() { char *c; int i; struct graph_thread_data *td = GET_DATA; /* set up window coords */ glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.0,1.0,0.0,1.0,0.0,1.0); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); for ( i = 0 ; i < MAXTEXTS ; i++ ) { REAL yspot; if ( text_chunks[i].text == NULL ) continue; yspot = text_chunks[i].start_y; glColor3f(0.0,0.0,0.0); /* black */ glRasterPos3d(text_chunks[i].start_x,text_chunks[i].start_y,0.0); for ( c = text_chunks[i].text ; *c ; c++ ) { if ( *c == '\n' ) { yspot -= 18.0/td->ysize; glRasterPos3d(text_chunks[i].start_x,yspot,0.0); } glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18,*c); } } glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); } /***************************************************************************** * * function: my_glLoadName() * * purpose: translate element id into GLuint name. * Needful if element_id is longer than GLuint. * Format: high 2 bits set for type of element. * then task id bits, for MPI * then element number. */ #define NAMETASKBITS 8 #define NAMEOFFSETBITS (8*sizeof(GLuint) - NAMETASKBITS - 2) #define NAMEOFFSETMASK (((GLuint)1 << NAMEOFFSETBITS) - 1 ) #define NAMETYPESHIFT (8*sizeof(GLuint)-2) #define NAMETYPE_MASK (3 << NAMETYPESHIFT) #define NAMETASKMASK (((1 << NAMETASKBITS) - 1) << NAMEOFFSETBITS) void my_glLoadName ARGS1((id), element_id id) { GLuint name; name = id_type(id) << NAMETYPESHIFT; #ifdef MPI_EVOLVER name |= id_task(id) << NAMEOFFSETBITS; #endif if ( valid_id(id) ) name |= id & OFFSETMASK & NAMEOFFSETMASK; else name |= NAMEOFFSETMASK; glLoadName(name); } /*************************************************************************** * * function: name_to_id() * * purpose: unravel picked name to element id. */ element_id name_to_id ARGS1((name), GLuint name) { element_id id; id = (element_id)((name & NAMETYPE_MASK) >> NAMETYPESHIFT) << TYPESHIFT; if ( (name & NAMEOFFSETMASK) != NAMEOFFSETMASK ) id |= VALIDMASK | (name & NAMEOFFSETMASK); #ifdef MPI_EVOLVER id |= (element_id)((name & NAMETASKMASK) >> NAMEOFFSETBITS) << TASK_ID_SHIFT; #endif return id; } /******************************************************************** * * function: enlarge_edge_array() * * purpose: Expand the space for the edge list */ void enlarge_edge_array(td) struct graph_thread_data *td; { int more = td->edgemax + 10; td->edgearray = (struct vercol*)realloc((char*)td->edgearray, (td->edgemax+more)*sizeof(struct vercol)); td->edgemax += more; } /******************************************************************** * * function: enlarge_facet_array() * * purpose: Expand the space for the facet list */ void enlarge_facet_array(td) struct graph_thread_data *td; { int more = 3*web.skel[FACET].count + 10; td->facetarray = (struct vercol*)realloc((char*)td->facetarray, (td->facetmax+more)*sizeof(struct vercol)); td->facetmax += more; } /*********************************************************************** * * function: kb_glNormal3fv() etc. * * purpose: Either save current normal in kb_norm[] for later list entry, * or pass it on to gl. */ void kb_glNormal3fv(struct graph_thread_data *td,float *v) /* save for edges and facets */ { if ( !td->arraysflag ) glNormal3fv(v); else { td->kb_norm[0] = v[0]; td->kb_norm[1] = v[1]; td->kb_norm[2] = v[2]; } } void kb_glNormal3dv(struct graph_thread_data *td,REAL *v) /* save for edges and facets */ { td->kb_norm[0] = (float)v[0]; td->kb_norm[1] = (float)v[1]; td->kb_norm[2] = (float)v[2]; if ( !td->arraysflag ) glNormal3fv(td->kb_norm); } void kb_glAntiNormal3dv(struct graph_thread_data *td,REAL *v) /* save for edges and facets */ { td->kb_norm[0] = -(float)v[0]; td->kb_norm[1] = -(float)v[1]; td->kb_norm[2] = -(float)v[2]; if ( !td->arraysflag ) glNormal3fv(td->kb_norm); } /*********************************************************************** * * function: e_glColor() * * purpose: Either save current edge color in er,eg,eb for later list entry, * or pass it on to gl. */ static float er,eg,eb,ea; /* current edge color */ void e_glColor ARGS2((td,c),struct graph_thread_data *td, int c) { if ( edge_rgb_color_attr > 0 ) { if ( !td->arraysflag ) glColor4ubv((const GLubyte*)&c); else { er=(float)(((c>>24)&0xFF)/255.0); eg=(float)(((c>>16)&0xFF)/255.0); eb=(float)(((c>>8)&0xFF)/255.0); ea= (float)(((c)&0xFF)/255.0); } } else { if ( !td->arraysflag ) glColor4fv(rgba[c]); else { er = rgba[c][0]; eg = rgba[c][1]; eb = rgba[c][2]; ea = rgba[c][3]; } } } /********************************************************************* * * function: e_glVertex3dv() * * purpose: Either save current edge data in edge list, or pass on to gl. */ void e_glVertex3dv ARGS2((td,x),struct graph_thread_data *td, REAL *x) { if ( !td->arraysflag ) { glVertex3d((GLdouble)x[0],(GLdouble)x[1],(GLdouble)x[2]); return; } if ( !td->edgearray ) td->edgemax = 0; if ( td->edgecount > td->edgemax-5 ) enlarge_edge_array(td); td->edgearray[td->edgecount].c[0] = er; td->edgearray[td->edgecount].c[1] = eg; td->edgearray[td->edgecount].c[2] = eb; td->edgearray[td->edgecount].c[3] = ea; td->edgearray[td->edgecount].x[0] = (fabs(x[0]) < 30000) ? (float)x[0] : 0.0; td->edgearray[td->edgecount].x[1] = (fabs(x[1]) < 30000) ? (float)x[1] : 0.0; td->edgearray[td->edgecount].x[2] = (fabs(x[2]) < 30000) ? (float)x[2] : 0.0; td->edgearray[td->edgecount].n[0] = td->kb_norm[0]; td->edgearray[td->edgecount].n[1] = td->kb_norm[1]; td->edgearray[td->edgecount].n[2] = td->kb_norm[2]; td->edgecount++; } /*********************************************************************** * * function: f_glColor() * * purpose: Either save current facet color in fr,fg,fb for later list entry, * or pass it on to gl. */ static float fr,fg,fb,fa; /* current facet color */ void f_glColor ARGS2((td,c),struct graph_thread_data *td, int c) { if ( facet_rgb_color_attr > 0 ) { if ( !td->arraysflag ) glColor4ubv((const GLubyte*)&c); else { fr=(float)(((c>>24)&0xFF)/255.); fg=(float)(((c>>16)&0xFF)/255.); fb=(float)(((c>>8)&0xFF)/255.); fa=(float)(((c)&0xFF)/255.); } } else { if ( !td->arraysflag ) glColor4fv(rgba[c]); else { fr = rgba[c][0]; fg = rgba[c][1]; fb = rgba[c][2]; fa = rgba[c][3]; } } } /********************************************************************* * * function: f_glVertex3dv() * * purpose: Either save current facet data in facet list, or pass on to gl. */ void f_glVertex3dv ARGS2((td,x),struct graph_thread_data *td, REAL *x) { if ( !td->arraysflag ) { glVertex3f((float)x[0],(float)x[1],(float)x[2]); return; } if ( !td->facetarray ) td->facetmax = 0; if ( td->facetcount > td->facetmax-5 ) enlarge_facet_array(td); td->facetarray[td->facetcount].c[0] = fr; td->facetarray[td->facetcount].c[1] = fg; td->facetarray[td->facetcount].c[2] = fb; td->facetarray[td->facetcount].c[3] = fa; td->facetarray[td->facetcount].x[0] = (fabs(x[0]) < 30000) ? (float)x[0] : 0; td->facetarray[td->facetcount].x[1] = (fabs(x[1]) < 30000) ? (float)x[1] : 0; td->facetarray[td->facetcount].x[2] = (fabs(x[2]) < 30000) ? (float)x[2] : 0; td->facetarray[td->facetcount].n[0] = td->kb_norm[0]; td->facetarray[td->facetcount].n[1] = td->kb_norm[1]; td->facetarray[td->facetcount].n[2] = td->kb_norm[2]; td->facetcount++; } /* gl matrices have vector on left! */ typedef double Matrix[4][4]; Matrix vt3 = /* gl transform matrix */ { {0.,0.,1.,0.}, {1.,0.,0.,0.}, {0.,-1.,0.,0.}, {0.,0.,0.,1.} }; Matrix vt3p = /* gl transform matrix, perspective version */ { {0.,0.,1.,0.}, {1.,0.,0.,0.}, {0.,1.,0.,0.}, {0.,0.,0.,1.} }; Matrix vt2 = /* gl transform matrix */ { {2.,0.,0.,0.}, /* can't figure why I need the 2's */ {0.,-2.,0.,0.}, {0.,0.,2.,0.}, {0.,0.,0.,1.} }; Matrix flip = /* gl transform matrix */ { {1.,0.,0.,0.}, {0.,1.,0.,0.}, {0.,0.,-1.,0.}, {0.,0.,0.,1.} }; Matrix flip2D = /* gl transform matrix */ { {1.,0.,0.,0.}, {0.,-1.,0.,0.}, {0.,0.,1.,0.}, {0.,0.,0.,1.} }; static long prev_timestamp; /* for remembering surface version */ #define MM_ROTATE 1 #define MM_TRANSLATE 2 #define MM_SCALE 3 #define MM_SPIN 4 #define MM_SLICE 5 #define MM_SLICE_SPIN 6 static int dindex = 1; /* display list object index */ void draw_screen ARGS((void)); void Ogl_init ARGS((void)) { } void Ogl_finish ARGS((void)) { } void graph_new_surface() { int k; struct graph_thread_data *td; ENTER_GRAPH_MUTEX; /* to account for global deallocation at start of new surface */ /* but we've given that up due to synchronization problems */ /* td->fullarray = NULL; td->colorarray = NULL; td->edgearray = NULL; td->facetarray = NULL; td->indexarray = NULL; td->striparray = NULL; td->stripdata = NULL; */ for ( k = 0, td = gthread_data ; k < MAXGRAPHWINDOWS ; k++,td++ ) { int win_id = td->win_id; td->interleaved_flag = 1; /* whether to do arrays as interleaved */ td->indexing_flag = 1; /* whether to use indexed arrays (smaller,but random access) */ td->kb_norm[3] = 1.0; td->win_id = win_id; td->view_initialized = 0; td->mouse_mode = MM_ROTATE; td->mpi_graph_task = 1; set_title(td); if ( td->to_focus[0] ) { matcopy(td->to_focus,identmat,HOMDIM,HOMDIM); matcopy(td->from_focus,identmat,HOMDIM,HOMDIM); } } LEAVE_GRAPH_MUTEX; } /*********************************************************************************** * * Function: set_title() * * Purpose: Set title of graphics window. */ void set_title(td) struct graph_thread_data *td; { int titlespot = (strlen(datafilename) > 60) ? (strlen(datafilename)-60):0; int k = td - gthread_data; #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) { if ( k == 1 ) sprintf(td->wintitle," %1.*s (task %d from task %d) ", WINTITLESIZE-30, datafilename+titlespot, this_task,td->mpi_graph_task ); else sprintf(td->wintitle," %1.*s (task %d from task %d) - Camera %d", WINTITLESIZE-45,datafilename+titlespot,this_task,td->mpi_graph_task,k); } else { if ( k == 1 ) sprintf(td->wintitle,"%1.*s (task %d) ",WINTITLESIZE-20, datafilename+titlespot,this_task); else sprintf(td->wintitle," %1.*s (task %d) - Camera %d", WINTITLESIZE-30, datafilename+titlespot,this_task,k); } #else if ( k == 1 ) sprintf(td->wintitle," %1.*s",WINTITLESIZE-10,datafilename+titlespot); else sprintf(td->wintitle," %1.*s - Camera %d",WINTITLESIZE-20, datafilename+titlespot,k); #endif td->new_title_flag = 1; } /* end set_title() */ void init_Oglz ARGS((void)); /************************************************************************ * * function: pick_func() * * purpose: To handle mouse picking of element. Invoked by right click. */ #define PICKBUFLENGTH 500 GLuint pickbuf[PICKBUFLENGTH]; int pick_flag; void pick_func ARGS2((x,y), int x, int y) { struct graph_thread_data *td = GET_DATA; GLint hits, viewport[4]; int i,n; unsigned int enearz = 0xFFFFFFFF; unsigned int fnearz = 0xFFFFFFFF; unsigned int vnearz = 0xFFFFFFFF; facet_id f_id = NULLID; edge_id e_id = NULLID; vertex_id v_id = NULLID; int count; glSelectBuffer(PICKBUFLENGTH,pickbuf); glGetIntegerv(GL_VIEWPORT,viewport); glMatrixMode(GL_PROJECTION); glPushMatrix(); glRenderMode(GL_SELECT); /* see what picked when drawn */ glLoadIdentity(); gluPickMatrix(x,y,4,4,viewport); glMultMatrixf(td->projmat); if ( SDIM >= 3 ) glMultMatrixd(flip[0]); /* don't know why, but need */ else glMultMatrixd(flip2D[0]); pick_flag = 1; if ( td->arraysflag ) /* have to turn arrays off during picking */ { td->arraysflag = 0; graph_timestamp = ++global_timestamp; draw_screen(); td->arraysflag = 1; graph_timestamp = ++global_timestamp; } else draw_screen(); pick_flag = 0; hits = glRenderMode(GL_RENDER); /* back to ordinary drawing */ if ( hits < 0 ) { hits = PICKBUFLENGTH/4; /* buffer overflow */ kb_error(2173, "Pick buffer overflow; selected element may not be foreground element.\n", WARNING); } for ( i=0, n=0 ; (i < hits) && (n < PICKBUFLENGTH-4) ; i++, n += count + 3 ) { element_id id = name_to_id(pickbuf[n+3]); count = pickbuf[n]; switch ( id_type(id) ) { case FACET: if ( pickbuf[n+1] < fnearz ) { f_id = id; fnearz = pickbuf[n+1]; } break; case EDGE: if ( pickbuf[n+1] < enearz ) if ( valid_id(id) || !valid_id(e_id) ) { e_id = id; enearz = pickbuf[n+1]; } break; case VERTEX: if ( pickbuf[n+1] < vnearz ) if ( valid_id(id) ) { v_id = id; vnearz = pickbuf[n+1]; } break; } } erroutstring("\n"); /* to get to next line after prompt */ if ( valid_id(v_id) ) { pickvnum = ordinal(v_id) + 1; #ifdef MPI_EVOLVER sprintf(msg,"Picked vertex %d@%d\n",pickvnum,id_task(v_id)); #else sprintf(msg,"Picked vertex %d\n",pickvnum); #endif erroutstring(msg); } if ( valid_id(e_id) ) { #ifdef OLDPICKVERTEX /* check for vertex in common to picked edges */ for ( i=0, n=0 ; (i < hits) && (n < PICKBUFLENGTH-4) ; i++, n += count+3 ) { element_id id,ee_id; int ii,nn,ccount; count = pickbuf[n]; id = name_to_id(pickbuf[n+3]); if ( (id_type(id) == EDGE) && valid_id(id) ) { vertex_id v1 = get_edge_headv(id); vertex_id v2 = get_edge_tailv(id); for ( ii = i+1, nn = n+count+3 ; (ii < hits) && (n < PICKBUFLENGTH-4) ; ii++, nn += ccount + 3 ) { ccount = pickbuf[nn]; ee_id = name_to_id(pickbuf[nn+3]); if ( (id_type(ee_id) == EDGE) && valid_id(ee_id) && !equal_element(id,ee_id) ) { if ( v1 == get_edge_tailv(ee_id) ) { v_id = v1; break; } if ( v1 == get_edge_headv(ee_id) ) { v_id = v1; break; } if ( v2 == get_edge_tailv(ee_id) ) { v_id = v2; break; } if ( v2 == get_edge_headv(ee_id) ) { v_id = v2; break; } } } } } if ( valid_id(v_id) ) { pickvnum = ordinal(v_id) + 1; #ifdef MPI_EVOLVER sprintf(msg,"Picked vertex %d@%d\n",pickvnum,id_task(v_id)); #else sprintf(msg,"Picked vertex %d\n",pickvnum); #endif erroutstring(msg); } #endif pickenum = ordinal(e_id) + 1; #ifdef MPI_EVOLVER sprintf(msg,"Picked edge %d@%d\n",pickenum,id_task(e_id)); #else sprintf(msg,"Picked edge %d\n",pickenum); #endif erroutstring(msg); } else if ( e_id == NULLEDGE ) erroutstring("Picked facet subdivision edge.\n"); if ( valid_id(f_id) ) { pickfnum = ordinal(f_id) + 1; #ifdef MPI_EVOLVER sprintf(msg,"Picked facet %d@%d\n",pickfnum,id_task(f_id)); #else sprintf(msg,"Picked facet %d\n",pickfnum); #endif erroutstring(msg); } erroutstring(current_prompt); glMatrixMode(GL_PROJECTION); glPopMatrix(); } /* end pick_func() */ /************************************************************************ * * function: my_own_pick_func() * * purpose: To handle mouse picking of element. Invoked by right click. * This version is entirely self-contained, and does not * use OpenGL picking, which is horribly slow in Vista. */ static REAL my_own_pick_radsq; int my_own_pick_flag; static vertex_id my_own_pick_vertex; static edge_id my_own_pick_edge; static facet_id my_own_pick_facet; static REAL my_own_pick_vertex_depth; static REAL my_own_pick_edge_depth; static REAL my_own_pick_facet_depth; static REAL mopt[4][4]; static REAL *my_own_pick_transmat[4] = {mopt[0],mopt[1],mopt[2],mopt[3]}; static REAL my_own_pick_x,my_own_pick_y; void my_own_pick_func ARGS2((x,y), int x, int y) /* pixel coordinates of pick spot */ { int i,j,k; float projmat[4][4]; float modelmat[4][4]; GLint viewport[4]; glGetIntegerv(GL_VIEWPORT,viewport); my_own_pick_x = 2*(REAL)x/viewport[2] - 1.0; my_own_pick_y = -(2*(REAL)y/viewport[3] - 1.0); my_own_pick_radsq = 10*2.0/viewport[2]*2.0/viewport[3]; /* Get current transformation matrices; note these are transposed from my convention */ glGetFloatv(GL_PROJECTION_MATRIX,projmat[0]); glGetFloatv(GL_MODELVIEW_MATRIX,modelmat[0]); /* get product */ for ( i = 0 ; i < 4 ; i++ ) for ( j = 0 ; j < 4 ; j++ ) { REAL sum = 0; for ( k = 0 ; k < 4 ; k++ ) sum += projmat[k][i]*modelmat[j][k]; my_own_pick_transmat[i][j] = sum; } my_own_pick_vertex = NULLID; my_own_pick_edge = NULLID; my_own_pick_facet = NULLID; my_own_pick_vertex_depth = 1e30; my_own_pick_edge_depth = 1e30; my_own_pick_facet_depth = 1e30; my_own_pick_flag = 1; graphgen(); my_own_pick_flag = 0; erroutstring("\n"); /* to get to next line after prompt */ if ( valid_id(my_own_pick_vertex) ) { pickvnum = ordinal(my_own_pick_vertex) + 1; sprintf(msg,"Picked vertex %s\n",ELNAME(my_own_pick_vertex)); erroutstring(msg); } if ( valid_id(my_own_pick_edge) ) { pickenum = ordinal(my_own_pick_edge) + 1; sprintf(msg,"Picked edge %s\n",ELNAME(my_own_pick_edge)); erroutstring(msg); } if ( valid_id(my_own_pick_facet) ) { pickfnum = ordinal(my_own_pick_facet) + 1; sprintf(msg,"Picked facet %s\n",ELNAME(my_own_pick_facet)); erroutstring(msg); } erroutstring(current_prompt); } /* end my_own_pick_func() */ /************************************************************************** * * function: my_own_edge_pick() * * purpose: See if an edge intersects pick window. */ void my_own_edge_pick(gtail,ghead,e_id) struct graphdata *gtail,*ghead; edge_id e_id; { REAL tailx[4],headx[4]; REAL px,py,qx,qy,ex,ey,lensq,dotprod,lambda,this_z,distsq; if ( ! valid_id(e_id) ) return; /* get endpoint pixel coordinates */ matvec_mul(my_own_pick_transmat,gtail->x,tailx,4,4); matvec_mul(my_own_pick_transmat,ghead->x,headx,4,4); px = my_own_pick_x - tailx[0]; py = my_own_pick_y - tailx[1]; qx = my_own_pick_x - headx[0]; qy = my_own_pick_y - headx[1]; /* see if endpoints in pick circle */ if ( px*px + py*py < my_own_pick_radsq ) { if ( valid_id(gtail->v_id) && (tailx[2] < my_own_pick_vertex_depth) ) { my_own_pick_vertex = gtail->v_id; my_own_pick_vertex_depth = tailx[2]; } } if ( qx*qx + qy*qy < my_own_pick_radsq ) { if ( valid_id(ghead->v_id) && (headx[2] < my_own_pick_vertex_depth) ) { my_own_pick_vertex = ghead->v_id; my_own_pick_vertex_depth = headx[2]; } } /* now find closest point on edge, via barycentric parameter lambda */ ex = headx[0] - tailx[0]; ey = headx[1] - tailx[1]; lensq = ex*ex + ey*ey; dotprod = px*ex + py*ey; lambda = dotprod/lensq; if ( lambda < 0.0 || lambda > 1.0 ) return; distsq = px*px + py*py - lambda*lambda*(ex*ex + ey*ey); if ( distsq > my_own_pick_radsq ) return; this_z = (1-lambda)*tailx[2] + lambda*tailx[2]; if ( this_z < my_own_pick_edge_depth ) { my_own_pick_edge = e_id; my_own_pick_edge_depth = this_z; } } /* end my_own_edge_pick() */ /************************************************************************** * * function: my_own_facet_pick() * * purpose: See if an edge intersects pick window. */ void my_own_facet_pick(g,f_id) struct graphdata *g; edge_id f_id; { REAL base[4],head1[4],head2[4]; REAL px,py,ax,ay,bx,by; REAL det,alpha,beta; REAL this_z; if ( ! valid_id(f_id) ) return; /* get vertex pixel coordinates */ matvec_mul(my_own_pick_transmat,g[0].x,base,4,4); matvec_mul(my_own_pick_transmat,g[1].x,head1,4,4); matvec_mul(my_own_pick_transmat,g[2].x,head2,4,4); px = my_own_pick_x - base[0]; py = my_own_pick_y - base[1]; ax = head1[0] - base[0]; ay = head1[1] - base[1]; bx = head2[0] - base[0]; by = head2[1] - base[1]; /* Calculate barycentric parameters of pick point */ det = ax*by - ay*bx; if ( det == 0 ) return; alpha = (by*px - bx*py)/det; beta = (ax*py - ay*px)/det; if ( alpha < 0.0 || beta < 0.0 || alpha+beta > 1.0 ) return; this_z = (1-alpha-beta)*base[2] + alpha*head1[2] + beta*head2[2]; if ( this_z < my_own_pick_facet_depth ) { my_own_pick_facet = f_id; my_own_pick_facet_depth = this_z; } } /* end my_own_facet_pick() */ /****************************************************************************/ /*********************************************************** * * function: mouse_func() * * purpose: Called on mouse button events, records position. */ void mouse_func ARGS((int,int,int,int)); void mouse_func ARGS4((button,state,x,y), int button, int state, int x, int y) { struct graph_thread_data *td = GET_DATA; switch ( button ) { case GLUT_LEFT_BUTTON: switch ( state ) { case GLUT_DOWN: /* start tracking */ td->oldx = x; td->oldy = y; td->mouse_left_state = GLUT_DOWN; glutIdleFunc(idle_func); break; case GLUT_UP: /* stop tracking */ td->basex = td->newx; td->basey = td->newy; td->mouse_left_state = GLUT_UP; glutPostRedisplay(); glutIdleFunc(idle_func); break; } break; case GLUT_RIGHT_BUTTON: switch ( state ) { case GLUT_DOWN: // pick_func(x,y); my_own_pick_func(x,y); glutPostRedisplay(); /* get image back */ break; } break; } } /******************************************************************* * * function: mouse_loc_func() * * purpose: Called as mouse moves with left button down, this * moves surface according to current mouse_mode. */ void mouse_loc_func ARGS((int,int)); void mouse_loc_func ARGS2((x,y), int x, int y) { struct graph_thread_data *td = GET_DATA; int i,j; td->newx = x; td->newy = y; if ( td->mouse_left_state == GLUT_DOWN ) { switch ( td->mouse_mode ) { case MM_SLICE: if ( slice_view_flag ) { slice_coeff[SDIM] += (td->newx-td->oldx)*td->xscale/view[0][0]; td->newarraysflag = 1; break; } else if ( clip_view_flag ) { clip_coeff[0][SDIM] += (td->newx-td->oldx)*td->xscale/view[0][0]; td->newarraysflag = 1; break; } else /* reset back to rotate by default and fall through */ td->mouse_mode = MM_ROTATE; case MM_ROTATE: mat_mult(td->to_focus,td->view,td->view,HOMDIM,HOMDIM,HOMDIM); fix_ctm(td->view,(REAL)( td->newx - td->oldx), -(REAL)(td->newy - td->oldy)); mat_mult(td->from_focus,td->view,td->view,HOMDIM,HOMDIM,HOMDIM); break; case MM_SCALE: mat_mult(td->to_focus,td->view,td->view,HOMDIM,HOMDIM,HOMDIM); for(i = 0 ; i < HOMDIM-1; i++ ) for ( j = 0 ; j < HOMDIM ; j++ ) td->view[i][j] *= 1.0 +0.002*(td->newx-td->oldx); mat_mult(td->from_focus,td->view,td->view,HOMDIM,HOMDIM,HOMDIM); break; case MM_TRANSLATE: if ( SDIM == 2 ) { td->view[0][2] += (td->newx-td->oldx)*td->xscale; td->view[1][2] -= (td->newy-td->oldy)*td->yscale; td->to_focus[0][2] -= (td->newx-td->oldx)*td->xscale; td->to_focus[1][2] += (td->newy-td->oldy)*td->yscale; td->from_focus[0][2] += (td->newx-td->oldx)*td->xscale; td->from_focus[1][2] -= (td->newy-td->oldy)*td->yscale; } else { td->view[1][HOMDIM-1] += (td->newx-td->oldx)*td->xscale; td->view[2][HOMDIM-1] -= (td->newy-td->oldy)*td->yscale; td->to_focus[1][HOMDIM-1] -= (td->newx-td->oldx)*td->xscale; td->to_focus[2][HOMDIM-1] += (td->newy-td->oldy)*td->yscale; td->from_focus[1][HOMDIM-1] += (td->newx-td->oldx)*td->xscale; td->from_focus[2][HOMDIM-1] -= (td->newy-td->oldy)*td->yscale; }; break; case MM_SPIN: /* about z axis */ { MAT2D(rot,MAXCOORD+1,MAXCOORD+1); REAL dth; REAL dang; /* fourth dimension */ for ( i = 0 ; i < HOMDIM ; i++ ) { for ( j = 0 ; j < HOMDIM ; j++ ) rot[i][j] = 0.0; rot[i][i] = 1.0; } dth = (td->newx - td->oldx)/300.0*M_PI; dang = (td->newy - td->oldy)/300.0*M_PI; if ( SDIM == 2 ) { rot[0][0] = rot[1][1] = cos(dth); rot[0][1] = -(rot[1][0] = sin(dth)); } else { rot[1][1] = rot[2][2] = cos(dth); rot[1][2] = -(rot[2][1] = sin(dth)); } mat_mult(td->to_focus,td->view,td->view,HOMDIM,HOMDIM,HOMDIM); mat_mult(rot,td->view,td->view,HOMDIM,HOMDIM,HOMDIM); mat_mult(td->from_focus,td->view,td->view,HOMDIM,HOMDIM,HOMDIM); } break; case MM_SLICE_SPIN: { REAL dth; REAL dang; REAL temp1[3]; REAL temp2[3]; dang = (td->newx - td->oldx)/300.0*M_PI; dth = (td->newy - td->oldy)/300.0*M_PI; matvec_mul(td->from_focus,clip_coeff[0],temp1,3,3); temp2[0] = cos(dth)*temp1[0] - sin(dth)*temp1[2]; temp2[1] = temp1[1]; temp2[2] = sin(dth)*temp1[0] + cos(dth)*temp1[2]; temp1[2] = temp2[2]; temp1[0] = cos(dang)*temp2[0] - sin(dang)*temp2[1]; temp1[1] = sin(dang)*temp2[0] + cos(dang)*temp2[1]; matvec_mul(td->to_focus,temp1,clip_coeff[0],3,3); td->newarraysflag = 1; break; } } if ( td->idle_flag ) glutPostRedisplay(); } td->oldx = td->newx; td->oldy = td->newy; } /************************************************************************ * * function: reshape_func() * * purpose: handle window resize messages. */ void reshape_func ARGS(( int, int )); void reshape_func ARGS2(( x, y ), int x, int y) { struct graph_thread_data *td = GET_DATA; if ( window_aspect_ratio != 0.0 ) { /* munge x,y */ /* see if moving just one side of frame */ if ( abs(td->xsize - x) < abs(td->ysize - y) ) { /* moving y */ /* if ( td->ysize == y ) return; */ /* not changing */ x = (int)(y/fabs(window_aspect_ratio)); } else { /* moving x */ y = (int)(x*fabs(window_aspect_ratio)); } td->window_aspect = window_aspect_ratio; td->aspect_flag = 0; glutReshapeWindow(x,y); } td->xsize = x; td->ysize = y; td->aspect = (double)y/x; glViewport(0,0,x,y); if ( td->aspect > 1 ) { td->xscale = 2.8/td->aspect/x; td->yscale = 2.8/y; imagescale = td->yscale*100; } else { td->xscale = 2.8/x; td->yscale = 2.8*td->aspect/y; imagescale = td->xscale*100; } if ( td == gthread_data+1 ) { /* first window corresponds to printing graphics state */ if ( x < y ) { minclipx = -1.5; maxclipx = 1.5; minclipy = -1.5*y/x; maxclipy = 1.5*y/x; } else { minclipx = -1.5*x/y; maxclipx = 1.5*x/y; minclipy = -1.5; maxclipy = 1.5; } } glMatrixMode(GL_PROJECTION); glLoadIdentity(); if ( (td->projmode == P_PERSP) || td->stereomode ) { gluPerspective((float)(y/1600.*180/3.14159),1/td->aspect,1.0,20.0); if ( SDIM == 2 ) glMultMatrixd(vt2[0]); else glMultMatrixd(vt3p[0]); /* rotate axes */ } else { if ( td->aspect >= 1.0 ) { glOrtho(td->scrx[0],td->scrx[2],td->aspect*td->scry[0], td->aspect*td->scry[2],-20.0,20.0); if ( td == gthread_data+1 ) { minclipx = td->scrx[0]; maxclipx = td->scrx[2]; minclipy = td->aspect*td->scry[2]; maxclipy = td->aspect*td->scry[0]; } } else { glOrtho(td->scrx[0]/td->aspect,td->scrx[2]/td->aspect,td->scry[0], td->scry[2],-20.0,20.0); if ( td == gthread_data+1 ) { minclipx = td->scrx[0]/td->aspect; maxclipx = td->scrx[2]/td->aspect; minclipy = td->scry[2]; maxclipy = td->scry[0]; } } if ( SDIM == 2 ) glMultMatrixd(vt2[0]); /* upside down */ else glMultMatrixd(vt3[0]); /* upside down and rotate axes */ } glGetFloatv(GL_PROJECTION_MATRIX,td->projmat); /* save */ GL_ERROR_CHECK #ifdef resizequick td->resize_flag = 1; /* So Mac OS X won't try too much redrawing */ glutIdleFunc(idle_func); #endif } /*********************************************************************** * * Function: specialkey_func() * * purpose: handle special keystrokes in graphics window. */ void specialkey_func ARGS((int,int,int)); void specialkey_func ARGS3((key,x,y), int key, int x, int y) { struct graph_thread_data *td = GET_DATA; switch ( key ) { case GLUT_KEY_RIGHT: /* right arrow */ td->view[SDIM>2?1:0][HOMDIM-1] += .25; break; case GLUT_KEY_LEFT: /* left arrow */ td->view[SDIM>2?1:0][HOMDIM-1] -= .25; break; case GLUT_KEY_UP: /* up arrow */ td->view[SDIM>2?2:1][HOMDIM-1] += .25; break; case GLUT_KEY_DOWN: /* down arrow */ td->view[SDIM>2?2:1][HOMDIM-1] -= .25; break; } glutPostRedisplay(); /* generate redraw message */ } /*********************************************************************** * * Function: key_func() * * purpose: handle ASCII keystrokes in graphics window. */ void key_func ARGS((unsigned char,int,int)); void key_func ARGS3((key,x,y), unsigned char key, int x, int y) { struct graph_thread_data *td = GET_DATA; int i,j; switch ( key ) { case 'G': /* new graphics window */ dup_window = glutGetWindow(); draw_thread(NULL); /* actually, just a new window */ break; case 'r': td->mouse_mode = MM_ROTATE; break; case 't': td->mouse_mode = MM_TRANSLATE; break; case 'z': td->mouse_mode = MM_SCALE; break; case 'c': td->mouse_mode = MM_SPIN; break; case 'k': if ( !slice_view_flag && !clip_view_flag ) td->newarraysflag = 1; if ( !slice_view_flag ) clip_view_flag = 1; /* turn clip_view on */ td->mouse_mode = MM_SLICE_SPIN; break; case 'l': if ( !slice_view_flag && !clip_view_flag ) td->newarraysflag = 1; if ( !slice_view_flag ) clip_view_flag = 1; /* turn clip_view on */ td->mouse_mode = MM_SLICE; break; case 'L': slice_view_flag = clip_view_flag = 0; td->newarraysflag = 1; td->mouse_mode = MM_ROTATE; break; case 'o': box_flag = !box_flag; graph_timestamp = ++global_timestamp; break; case 'b': td->edge_bias -= 0.001; sprintf(msg,"\nEdge front bias now %f\n",td->edge_bias); erroutstring(msg); erroutstring(current_prompt); break; case 'B': td->edge_bias += 0.001; sprintf(msg,"\nEdge front bias now %f\n",td->edge_bias); erroutstring(msg); erroutstring(current_prompt); break; case 'R': resize(); for ( i = 0 ; i < HOMDIM ; i++ ) for ( j = 0 ; j < HOMDIM ; j++ ) td->view[i][j] = view[i][j]; matcopy(td->to_focus,identmat,HOMDIM,HOMDIM); matcopy(td->from_focus,identmat,HOMDIM,HOMDIM); break; case '-': if ( td->linewidth > 0.6 ) { td->linewidth -= 0.5; glLineWidth(td->linewidth); } break; case '+': if ( td->linewidth < 9.9 ) { td->linewidth += 0.5; glLineWidth(td->linewidth); } break; case 'e': edgeshow_flag = !edgeshow_flag; graph_timestamp = ++global_timestamp; td->newarraysflag = 1; break; case 'f': td->facetshow_flag = !td->facetshow_flag; graph_timestamp = ++global_timestamp; td->newarraysflag = 1; break; case 'g': /* Gourard toggle */ td->normflag = !td->normflag; graph_timestamp = ++global_timestamp; td->newarraysflag = 1; break; case 'm': /* move to middle */ { do_gfile(0,NULL); /* get bounding box */ if ( SDIM == 2 ) { td->view[0][HOMDIM-1] -= (bbox_maxx+bbox_minx)/2; td->view[1][HOMDIM-1] -= (bbox_maxy+bbox_miny)/2; } else { td->view[1][HOMDIM-1] -= (bbox_maxx+bbox_minx)/2; td->view[2][HOMDIM-1] -= (bbox_maxy+bbox_miny)/2; } break; } case 'i': /* toggle interleaved elements in opengl arrays */ td->interleaved_flag = !td->interleaved_flag; erroutstring(td->interleaved_flag?"Interleaving ON.\n":"Interleaving OFF.\n"); erroutstring(current_prompt); td->newarraysflag = 1; break; case 'I': /* indexed arrays */ td->indexing_flag = !td->indexing_flag; erroutstring(td->indexing_flag?"Array indexing ON.\n":"Array indexing OFF.\n"); erroutstring(current_prompt); td->newarraysflag = 1; break; case 'S': /* toggle sending strips in arrays */ td->strips_flag = !td->strips_flag; erroutstring(td->strips_flag?"Element strips ON.\n":"Element strips OFF.\n"); erroutstring(current_prompt); td->normflag = 1; /* gourard shading */ td->indexing_flag = 1; td->newarraysflag = 1; break; case 'Y': /* colored strips */ { int *fcolors; int i; fcolors = (int*)temp_calloc(web.skel[FACET].maxcount,sizeof(int)); for ( i = 0 ; i < web.skel[FACET].maxcount ; i++ ) fcolors[i] = get_facet_color(i); if ( !td->strips_flag ) key_func('S',0,0); /* make sure strips on */ td->strip_color_flag = 1; td->newarraysflag = 1; draw_screen(); /* get facets colored */ td->newarraysflag = 1; draw_screen(); /* draw colored facets */ td->strip_color_flag = 0; for ( i = 0 ; i < web.skel[FACET].maxcount ; i++ ) set_facet_color(i,fcolors[i]); temp_free((char*)fcolors); } break; case 'F': /* use last pick to set rotation center */ if ( pickvnum > 0 ) { int i,m; REAL *x,xx[MAXCOORD]; REAL focus[MAXCOORD]; td->focus_vertex_id = get_ordinal_id(VERTEX,pickvnum-1); x = get_coord(td->focus_vertex_id); for ( m = 0 ; m < SDIM ; m++ ) xx[m] = x[m]; if ( torus_display_mode == TORUS_CLIPPED_MODE ) { for ( m = 0 ; m < SDIM ; m++ ) { int wrap = (int)floor(SDIM_dot(web.inverse_periods[m],x)); for ( i = 0 ; i < SDIM ; i++ ) xx[i] -= wrap*web.torus_period[m][i]; } } matvec_mul(td->view,xx,focus,HOMDIM-1,HOMDIM-1); for ( i = 0 ; i < SDIM ; i++ ) { td->to_focus[i][HOMDIM-1] = -focus[i] - td->view[i][HOMDIM-1]; td->from_focus[i][HOMDIM-1] = focus[i] + td->view[i][HOMDIM-1]; } } break; case 'D': /* toggle display list */ td->dlistflag = td->dlistflag ? NOLIST:RESETLIST; if ( td->dlistflag ) { erroutstring("\nOpenGL display list now ON.\n"); if ( td->arraysflag ) { glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); erroutstring("\nOpenGL arrays now OFF.\n"); } td->arraysflag = 0; } else erroutstring("\nOpenGL display list now OFF.\n"); erroutstring(current_prompt); graph_timestamp = ++global_timestamp; /* force recalculate arrays */ break; case 'a': /* toggle vertex arrays */ if ( strcmp(opengl_version,"1.1") < 0 ) { sprintf(errmsg, "Vertex arrays require OpenGL version at least 1.1. This is %s.\n", opengl_version); kb_error(2174,errmsg,WARNING); return; } td->arraysflag = !td->arraysflag; if ( td->arraysflag ) { /* Workaround really bizarre line-drawing bug */ if ( td->linewidth == 1.0 ) { td->linewidth = 0.5; glLineWidth(0.5);} erroutstring("\nOpenGL vertex arrays now ON.\n"); glInterleavedArrays(GL_C4F_N3F_V3F,0,(void*)td->fullarray); td->newarraysflag = 1; td->dlistflag = 0; } else { glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); erroutstring("\nOpenGL vertex_arrays now OFF.\n"); } erroutstring(current_prompt); break; case 's': /* toggle stereo */ td->stereomode = (td->stereomode==NO_STEREO) ? CROSS_STEREO:NO_STEREO; reshape_func(td->xsize,td->ysize); break; case 'p': /* perspective mode */ if ( td->stereomode ) erroutstring("\nOpenGL projection now CROSS-EYED STEREO.\n"); else if ( td->projmode==P_ORTHO ) { td->projmode=P_PERSP; erroutstring("\nOpenGL projection now PERSPECTIVE.\n"); } else { td->projmode=P_ORTHO; erroutstring("\nOpenGL projection now ORTHOGONAL.\n"); } erroutstring(current_prompt); reshape_func(td->xsize,td->ysize); break; case 'M': /* menu mode on right mouse button */ glutSetMenu(mainmenu); glutAttachMenu(GLUT_RIGHT_BUTTON); break; case 'P': /* pick mode on right mouse button */ glutSetMenu(mainmenu); glutDetachMenu(GLUT_RIGHT_BUTTON); break; case 'Q': /* toggle drawing stats printing */ td->q_flag = !td->q_flag; erroutstring(td->q_flag ? "\nPrinting drawing stats ON\n":"\nPrinting drawing stats OFF\n"); erroutstring(current_prompt); break; #ifdef MPI_EVOLVER case 'y': /* MPI version only */ mpi_show_corona_flag = ! mpi_show_corona_flag; td->newarraysflag = 1; erroutstring(mpi_show_corona_flag ? "\nShowing MPI corona ON\n":"\nShowing MPI corona OFF\n"); erroutstring(current_prompt); break; #endif case 'x': Ogl_close(); return; case 'h': case '?': erroutstring("\nGraphics window help:\n"); erroutstring("Left mouse: move Right mouse: pick\n"); erroutstring("Graphics window keys:\n"); erroutstring(td->mouse_mode==MM_ROTATE? "r Rotate mode for left mouse button, now ACTIVE\n": "r Rotate mode for left mouse button\n"); erroutstring(td->mouse_mode==MM_TRANSLATE? "t Translate mode for left mouse button, now ACTIVE\n": "t Translate mode for left mouse button\n"); erroutstring(td->mouse_mode==MM_SCALE? "z Zoom mode for left mouse button, now ACTIVE\n": "z Zoom mode for left mouse button\n"); erroutstring(td->mouse_mode==MM_SPIN? "c Clockwise/counterclockwise mode, now ACTIVE\n": "c Clockwise/counterclockwise mode\n"); erroutstring("W Widen edges\n"); erroutstring("w Narrow edges\n"); erroutstring("B Increase edge front bias by 0.001\n"); erroutstring("b Decrease edge front bias by 0.001\n"); erroutstring(normflag? "g Gourard shading (smooth shading) toggle, now ON\n": "g Gourard shading (smooth shading) toggle, now OFF\n" ); erroutstring("R Reset view\n"); erroutstring("m Center image\n"); erroutstring(edgeshow_flag?"e Toggle showing all edges, now ON\n": "e Toggle showing all edges, now OFF\n"); erroutstring(td->facetshow_flag?"f Toggle showing facets, now ON\n": "f Toggle showing facets, now OFF\n"); erroutstring(td->projmode==P_PERSP? "p Toggle orthogonal/perspective projection, now perspective\n": "p Toggle orthogonal/perspective projection, now orthogonal\n"); erroutstring("s Toggle cross-eyed stereo\n"); erroutstring(normflag? "g Gourard shading (smooth shading) toggle, now ON\n": "g Gourard shading (smooth shading) toggle, now OFF\n" ); erroutstring("F Set rotate/zoom focus to last picked vertex\n"); erroutstring("arrow keys translate image\n"); erroutstring("G Another graphics window\n"); erroutstring("H Guru-level help items\n"); erroutstring("x Close graphics\n"); erroutstring(current_prompt); break; case 'H': /* guru level help */ erroutstring("\nFollowing for fiddling with OpenGL drawing modes:\n"); erroutstring(td->dlistflag?"D Toggle using display list, now ON\n": "D Toggle using display list, now OFF\n"); erroutstring(td->arraysflag?"a Toggle using vertex and color arrays, now ON\n" :"a Toggle using vertex and color arrays, now OFF\n"); erroutstring(td->indexing_flag ? "I Indexed vertex arrays, now ON\n" : "I Indexed vertex arrays, now OFF\n"); erroutstring(td->interleaved_flag ? "i Interleaved vertex arrays, now ON\n" : "i Interleaved vertex arrays, now OFF\n"); erroutstring(td->strips_flag? "S Use element strips, now ON (indexed elements automatically turned on)\n" :"S Use element strips, now OFF\n"); erroutstring("Y One-time coloring of strips generated in S mode.\n"); erroutstring(" Caution: assumes display facets same as real, with no gaps.\n"); erroutstring("u Toggle window update every graphics command, now "); erroutstring("Q Toggle printing some statistics during drawing\n"); #ifdef MPI_EVOLVER erroutstring("y Toggle corona display\n"); #endif erroutstring(current_prompt); break; } glutPostRedisplay(); /* generate redraw message */ } /* end of key_func() */ void mainmenu_func ARGS((int)); void submenu_func ARGS((int)); void mpi_taskmenu_func ARGS((int)); void mainmenu_func ARGS1((choice), int choice) { key_func(choice,0,0); } void submenu_func ARGS1((choice), int choice) { key_func(choice,0,0); } #ifdef MPI_EVOLVER void mpi_taskmenu_func ( task ) int task; { struct graph_thread_data *td = GET_DATA; td->mpi_graph_task = task; set_title(td); glutSetWindowTitle(td->wintitle); graph_timestamp = ++global_timestamp; td->newarraysflag = 1; glutPostRedisplay(); } #endif void myMenuInit() { mainmenu = glutCreateMenu(mainmenu_func); glutSetMenu(mainmenu); glutAddMenuEntry("Left button modes:",' '); glutAddMenuEntry("Rotate mode (r)",'r'); glutAddMenuEntry("Translate mode (t)",'t'); glutAddMenuEntry("Spin mode (c)",'c'); glutAddMenuEntry("Scale mode (z)",'z'); glutAddMenuEntry("Slice mode (l)",'l'); glutAddMenuEntry("Right button modes:",' '); glutAddMenuEntry("Pick mode (P)",'P'); glutAddMenuEntry("Menu mode (M)",'M'); glutAddMenuEntry("Edges front (B)",'B'); glutAddMenuEntry("Edges back (b)",'b'); glutAddMenuEntry("Edges thicker (+)",'+'); glutAddMenuEntry("Edges thinner (-)",'-'); glutAddMenuEntry("Center object (m)",'m'); glutAddMenuEntry("Toggle edges (e)",'e'); glutAddMenuEntry("Toggle faces (f)",'f'); glutAddMenuEntry("Focus on picked vertex (F)",'F'); glutAddMenuEntry("Bounding box (o)",'o'); glutAddMenuEntry("Reset graphics (R)",'R'); glutAddMenuEntry("",' '); /* skip funny entry before submenu */ submenu = glutCreateMenu(submenu_func); glutSetMenu(submenu); glutAddMenuEntry("Another graphics window (G)",'G'); glutAddMenuEntry("Toggle Gourard shading (g)",'g'); glutAddMenuEntry("Toggle stereo view (s)",'s'); glutAddMenuEntry("Toggle orthogonal/perspective view (p)",'p'); glutAddMenuEntry("Toggle arrays (a)",'a'); glutAddMenuEntry("Toggle interleaved arrays (i)",'i'); glutAddMenuEntry("Toggle indexed arrays (I)",'I'); glutAddMenuEntry("Toggle strips (S)",'S'); glutAddMenuEntry("Toggle strip coloring (Y)",'Y'); glutAddMenuEntry("Toggle display lists (D)",'D'); glutAddMenuEntry("Print drawing stats (Q)",'Q'); #ifdef MPI_EVOLVER glutAddMenuEntry("Toggle corona display (y)",'y'); #endif glutSetMenu(mainmenu); glutAddSubMenu("Advanced",submenu); #ifdef MPI_EVOLVER { int task; mpi_taskmenu = glutCreateMenu(mpi_taskmenu_func); glutSetMenu(mpi_taskmenu); for ( task = 1 ; task < mpi_nprocs ; task++ ) { char number[10]; sprintf(number," %4d ",task); glutAddMenuEntry(number,task); } glutSetMenu(mainmenu); glutAddSubMenu("Pick MPI task",mpi_taskmenu); } #endif glutAddMenuEntry("Close graphics (x)",'x'); glutAttachMenu(GLUT_MIDDLE_BUTTON); } /* lighting info for surface */ static GLfloat mat_specular[] = {.5f,.5f,.5f,1.0f}; static GLfloat mat_shininess[] = {10.0f}; static GLfloat mat_diffuse[] = {1.0f,1.0f,1.0f,1.0f}; static GLfloat mat_white[] = {1.0f,1.0f,1.0f,1.0f}; static GLfloat mat_emission[] = {0.3f,0.3f,0.3f,1.0f}; #define INTENSITY1 0.5f static GLfloat light0_position[] = {1.0f,0.0f,1.0f,0.0f}; /* front */ static GLfloat light0_diffuse[] = {INTENSITY1,INTENSITY1,INTENSITY1,1.0f}; static GLfloat light0_ambient[] = {.3f,.3f,.3f,1.0f}; #define INTENSITY2 0.5f static GLfloat light1_position[] = {0.0f,0.0f,1.0f,0.0f}; /* above */ static GLfloat light1_diffuse[] = {INTENSITY2,INTENSITY2,INTENSITY2,1.0f}; static GLfloat light_none[] = {0.f,0.f,0.f,.0f}; #ifdef WIN32 /******************************************************************* * * function: handle_func() * * purpose: callback for EnumThreadWindows() */ static int handle_count; BOOL __stdcall handle_func(HWND hwnd, LPARAM lParam) { int i; for ( i = 1 ; i < MAXGRAPHWINDOWS ; i++ ) { if ( gthread_data[i].draw_hwnd == hwnd ) return TRUE; if ( !gthread_data[i].draw_hwnd ) { gthread_data[i].draw_hwnd = hwnd; break; } } return TRUE; } #endif #ifndef WIN32 /***************************************************************** * * function: glut_catcher() * * purpose: catch signals meant to wake up thread. */ void glut_catcher ARGS1((x), int x) { signal(SIGKICK,glut_catcher); } #endif /***************************************************************** * * function: draw_thread() * * purpose: Create OpenGL display thread and window. */ #ifdef PTHREADS void * draw_thread ARGS1((arglist), void *arglist) #else void __cdecl draw_thread ARGS1((arglist), void *arglist) #endif { int i,j; int argc = 1; /* to keep glutInit happy */ char *argv = "Evolver"; struct graph_thread_data *td; char wintitle[WINTITLESIZE]; static int win_id; int glut_id; int xpixels,ypixels; #ifdef WIN32 HWND foregroundhwnd = GetForegroundWindow(); draw_thread_id = GetCurrentThreadId(); /* set per-thread data */ TlsSetValue(thread_data_key,(void*)&glutgraph_thread_data); #else draw_thread_id = pthread_self(); /* get thread id */ draw_pid = getpid(); /* set per-thread data */ pthread_setspecific(thread_data_key,(void*)&glutgraph_thread_data); #endif for ( i = 0 ; i < 16 ; i++ ) for ( j = 0 ; j < 4 ; j++ ) rgba[i][j] = (float)rgb_colors[i][j]; if ( !glutInit_called ) glutInit(&argc,&argv); glutInit_called = 1; if ( !graph_thread_running ) win_id = 0; win_id++; if ( win_id >= MAXGRAPHWINDOWS ) { kb_error(2556,"No more graphics windows available.\n",WARNING); } glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH ); glutInitWindowPosition((20*win_id)%400,(20*win_id)%400); if ( window_aspect_ratio ) { xpixels = (int)(400/sqrt(fabs(window_aspect_ratio))); ypixels = (int)(400*sqrt(fabs(window_aspect_ratio))); } else { xpixels = 400; ypixels = 400; } glutInitWindowSize(xpixels,ypixels); #ifdef MAC_OS_X { char title[1000]; sprintf(title," %s (CTRL-click for right mouse button)",datafilename); glutCreateWindow(title); } #else glutCreateWindow(datafilename); #endif glutMouseFunc(mouse_func); glutMotionFunc(mouse_loc_func); glutKeyboardFunc(key_func); glutSpecialFunc(specialkey_func); glutReshapeFunc(reshape_func); glutIdleFunc(idle_func); myMenuInit(); glutShowWindow(); /* start on top */ glut_id = glutGetWindow(); /* window identifier */ if ( glut_id >= 10 ) kb_error(2596,"glut window id too high.\n",RECOVERABLE); else td = gthread_data + glut_id; #ifdef WIN32 /* get window handle, do this before setting win_id to prevent race with main thread display() */ handle_count = 1; EnumThreadWindows(draw_thread_id,handle_func,0); /* try to get on top */ SetForegroundWindow(gthread_data[glut_id].draw_hwnd); SetForegroundWindow(foregroundhwnd); /* get console back on top */ #endif td = GET_DATA; td->in_use = 1; td->win_id = glut_id; td->aspect = 1; td->xscale=2.8/xpixels; td->yscale=2.8/ypixels; background_color = LIGHTBLUE; td->xsize = xpixels; td->ysize = ypixels; td->projmode = P_ORTHO; /* kind of projection to do */ td->stereomode = NO_STEREO; td->facetshow_flag = 1; /* whether to show facets */ td->linewidth = 1.0; td->edge_bias = 0.005; /* amount edges drawn in front */ td->mouse_left_state = GLUT_UP; /* state of left mouse button */ td->mouse_mode = MM_ROTATE; if (glut_id > 1 ) { if ( strlen(datafilename) > 60 ) sprintf(wintitle," %1.*s - Camera %d",WINTITLESIZE-30, datafilename+strlen(datafilename)-60, glut_id); else sprintf(wintitle," %1.*s - Camera %d",WINTITLESIZE-30, datafilename,glut_id); #ifdef MPI_EVOLVER sprintf(wintitle+strlen(wintitle)," (task %d)",this_task); #endif glutSetWindowTitle(wintitle); } ENTER_GRAPH_MUTEX; /* due to view[][] */ if ( dup_window ) { struct graph_thread_data *tdd = gthread_data+dup_window; for ( i = 0 ; i < HOMDIM ; i++ ) { td->view[i] = td->viewspace[i]; td->to_focus[i] = td->to_focus_space[i]; td->from_focus[i] = td->from_focus_space[i]; for ( j = 0 ; j < HOMDIM ; j++ ) { td->view[i][j] = tdd->view[i][j]; td->to_focus[i][j] = tdd->to_focus[i][j]; td->from_focus[i][j] = tdd->from_focus[i][j]; } } dup_window = 0; } else { for ( i = 0 ; i < HOMDIM ; i++ ) { td->view[i] = td->viewspace[i]; td->to_focus[i] = td->to_focus_space[i]; td->from_focus[i] = td->from_focus_space[i]; for ( j = 0 ; j < HOMDIM ; j++ ) td->view[i][j] = view[i][j]; /* initialize to global view */ } matcopy(td->to_focus,identmat,HOMDIM,HOMDIM); matcopy(td->from_focus,identmat,HOMDIM,HOMDIM); } LEAVE_GRAPH_MUTEX; glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE); /*glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular); */ /*glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess); */ glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_white); /*glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, mat_emission); */ glEnable(GL_LIGHTING); glLightfv(GL_LIGHT0, GL_POSITION, light0_position); glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse); glLightfv(GL_LIGHT0, GL_AMBIENT, light0_ambient); glLightfv(GL_LIGHT1, GL_POSITION, light1_position); glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, light_none); glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE); /* screen corners */ td->scrx[0] = td->scrx[1] = -1.4; td->scrx[2] = td->scrx[3] = 1.4; td->scry[0] = td->scry[3] = 1.4; td->scry[1] = td->scry[2] = -1.4; go_display_flag = 1; /* default, since fast graphics */ /* version check */ strncpy(opengl_version,(char*)glGetString(GL_VERSION),sizeof(opengl_version)); if ( strcmp(opengl_version,"1.1") >= 0 ) td->arraysflag = 1; glutDisplayFunc(draw_screen); GL_ERROR_CHECK if ( !graph_thread_running ) { #ifndef WIN32 signal(SIGKICK,glut_catcher); #endif graph_thread_running = 1; glutMainLoop(); } #ifdef PTHREADS return NULL; #endif } /************************************************************************ * * function: init_Oglz() * * purpose: start up graphics thread */ void init_Oglz() { if ( !initz_flag ) { initz_flag = 1; if ( !no_graphthread_flag ) { #ifdef WIN32 _beginthread(draw_thread,0,0); #elif defined(PTHREADS) { pthread_t th; /* { sigemptyset(&newset); sigaddset(&newset,SIGKICK); pthread_sigmask(SIG_BLOCK,&newset,NULL); } */ main_pid = getpid(); #ifdef MAC_OS_X /* GLUT doesn't work as secondary thread on 10.0.2, so main draws */ curdir = getcwd(NULL,0); /* so command thread gets proper directory */ pthread_create(&th,NULL,(void*(*)(void*))mac_exec_commands,NULL); draw_thread(NULL); #else pthread_create(&th,NULL,draw_thread,NULL); #endif } #else kb_error(2447,"Internal error: Evolver compiled with GLUT graphics without threading.\n",RECOVERABLE); #endif } } } /**************************************************************** * * function: Oglz_start() * * purpose: To be called at the start of each Evolver-initiated redraw. */ void Oglz_start ARGS((void)); void Oglz_start(void) { if ( initz_flag == 0 ) init_Oglz(); } /****************************************************************** * * function: Oglz_edge() * * purpose: graph one edge. */ void Oglz_edge ARGS((struct graphdata *,edge_id)); void Oglz_edge ARGS2((g,e_id), struct graphdata *g, edge_id e_id) { struct graph_thread_data *td = GET_DATA; int k; int e_color; e_color = g[0].ecolor; if ( e_color == CLEAR ) return; if ( my_own_pick_flag ) { my_own_edge_pick(g,g+1,e_id); return; } if ( (e_color < 0) || (e_color >= IRIS_COLOR_MAX) ) e_color = DEFAULT_EDGE_COLOR; if ( pick_flag ) my_glLoadName(e_id); /* for picking */ /* display */ e_glColor(td,e_color); if ( !td->arraysflag ) glBegin(GL_LINES); for ( k = 0 ; k < 2 ; k++ ) e_glVertex3dv(td,g[k].x); if ( !td->arraysflag ) glEnd(); /* pickable vertices */ if ( pick_flag ) { for ( k = 0 ; k < 2 ; k++ ) { my_glLoadName(g[k].v_id); /* for picking */ glBegin(GL_POINTS); glVertex3dv(g[k].x); glEnd(); } } } /****************************************************************** * * function: Oglz_facet() * * purpose: graph one facet. */ void Oglz_facet ARGS((struct graphdata *,facet_id)); void Oglz_facet ARGS2((g,f_id), struct graphdata *g, facet_id f_id) { int i,k; REAL len; facetedge_id fe; struct graph_thread_data *td = GET_DATA; float norm[3]; float backnorm[3]; /* need normal for lighting */ for ( i = 0 ; i < 3 ; i++ ) { int ii = (i+1)%3; int iii = (i+2)%3; norm[i] = (float)((g[1].x[ii]-g[0].x[ii])*(g[2].x[iii]-g[0].x[iii]) - (g[1].x[iii]-g[0].x[iii])*(g[2].x[ii]-g[0].x[ii])); } len = sqrt(dotf(norm,norm,3)); if ( len <= 0.0 ) return; for ( i = 0 ; i < 3 ; i++ ) norm[i]= (float)(norm[i]/len); if ( web.hide_flag && (g[0].color != UNSHOWN) && td->facetshow_flag ) { if ( pick_flag ) my_glLoadName(f_id); /* for picking */ if ( g[0].color != CLEAR ) { if ( my_own_pick_flag ) my_own_facet_pick(g,f_id); else { if ( color_flag ) f_glColor(td,g->color); else f_glColor(td,INDEX_TO_RGBA(WHITE)); kb_glNormal3fv(td,norm); if ( !td->arraysflag ) glBegin(GL_TRIANGLES); for ( k = 0 ; k < 3 ; k++ ) { if ( td->normflag ) kb_glNormal3dv(td,g[k].norm); f_glVertex3dv(td,g[k].x); } if ( !td->arraysflag ) glEnd(); } } if ( my_own_pick_flag && g->backcolor != g->color && g->backcolor != CLEAR ) my_own_facet_pick(g,f_id); else if ( (g->color != g->backcolor) && (g->backcolor != CLEAR) ) { REAL x[MAXCOORD]; f_glColor(td,g->backcolor); for ( i = 0 ; i < 3 ; i++ ) backnorm[i] = -norm[i]; kb_glNormal3fv(td,backnorm); if ( !td->arraysflag ) glBegin(GL_TRIANGLES); for ( k = 2 ; k >= 0 ; k-- ) { for ( i = 0 ; i < 3 ; i++ ) x[i] = g[k].x[i] + thickness*backnorm[i]; if ( td->normflag ) kb_glAntiNormal3dv(td,g[k].norm); f_glVertex3dv(td,x); } if ( !td->arraysflag ) glEnd(); } } fe = valid_id(f_id) ? get_facet_fe(f_id) : NULLID; for ( k = 0 ; k < 3 ; k++, fe = valid_id(fe)?get_next_edge(fe):NULLID ) { if ( g[k].ecolor == CLEAR ) continue; if ( !edgeshow_flag || (g[0].color == UNSHOWN) ) { if ( (g[k].etype & EBITS) == INVISIBLE_EDGE ) continue; } if ( my_own_pick_flag ) { my_own_edge_pick(g+k,g+((k+1)%3),g[k].id); continue; } if ( pick_flag ) my_glLoadName(g[k].id); /* for picking */ e_glColor(td,g[k].ecolor); kb_glNormal3fv(td,norm); if ( !td->arraysflag ) glBegin(GL_LINES); if ( td->normflag ) kb_glNormal3dv(td,g[k].norm); e_glVertex3dv(td,g[k].x); if ( td->normflag ) kb_glNormal3dv(td,g[(k+1)%3].norm); e_glVertex3dv(td,g[(k+1)%3].x); if ( !td->arraysflag ) glEnd(); if ( pick_flag ) { my_glLoadName(g[k].v_id); glBegin(GL_POINTS); glVertex3dv(g[k].x); glEnd(); my_glLoadName(g[(k+1)%3].v_id); glBegin(GL_POINTS); glVertex3dv(g[(k+1)%3].x); glEnd(); } } } /* end Oglz_facet() */ /********************************************************************** * * function: Oglz_end() * * purpose: to be called at end of presenting data. */ void Oglz_end ARGS((void)) { prev_timestamp = graph_timestamp; } void Ogl_close ARGS((void)) { int i; struct graph_thread_data *td = GET_DATA; glutDestroyWindow(td->win_id); memset((char*)td,0,sizeof(struct graph_thread_data)); #ifndef MAC_OS_X /* Mac objects to closing graphics thread */ for ( i = 0 ; i < MAXGRAPHWINDOWS ; i++ ) { if ( gthread_data[i].in_use != 0 ) break; } if ( i == MAXGRAPHWINDOWS ) { /* all graph windows closed */ glDeleteLists(dindex,1); go_display_flag = 0; init_flag = 0; initz_flag = 0; td->arrays_timestamp = 0; graph_thread_running = 0; #ifdef WIN32 ExitThread(0); #elif defined(PTHREADS) pthread_exit(NULL); #endif } #endif } /*********************************************************************** * * function: Ogl_close_show() * * purpose: to be called by main thread when wanting to close graphics. * sets close_flag to be read by threads' idle_func(). */ void Ogl_close_show(void) { close_flag = 1; } /********************************************************************** * * function: vercolcomp() * * purpose: comparison function for sorting vertex data in case of indexed arrays. * */ int vercolcomp ARGS2((a,b), struct vercol *a, struct vercol *b) { int i; for ( i = 0 ; i < 10 ; i++ ) { REAL diff = ((float*)a)[i] - ((float*)b)[i]; if ( diff < -gleps ) return -1; if ( diff > gleps ) return 1; } return 0; } /*********************************************************************** * * function: eecomp() * * purpose: comparison function for sorting indexed edges */ int eecomp ARGS2((a,b), int *a, int *b) { if ( *a < *b ) return -1; if ( *a > *b ) return 1; if ( a[1] > b[1] ) return 1; if ( a[1] < b[1] ) return -1; return 0; } /****************************************************************************** * * function: declare_arrays() * * purpose: Declare element arrays to current OpenGL context. Each thread * should check before drawing if arrays have changed since last * declaration. */ void declare_arrays() { struct graph_thread_data *td = GET_DATA; GL_ERROR_CHECK if ( td->arraysflag ) { /* declare arrays to OpenGL */ if ( td->interleaved_flag ) { glInterleavedArrays(GL_C4F_N3F_V3F,sizeof(struct vercol),(void*)td->fullarray); } else /* indexed */ { glColorPointer(4,GL_FLOAT,0,td->colorarray); glNormalPointer(GL_FLOAT,sizeof(struct vercol),td->fullarray->n); glVertexPointer(3,GL_FLOAT,sizeof(struct vercol),td->fullarray->x); } } td->declared_timestamp = td->arrays_timestamp; GL_ERROR_CHECK } /************************************************************************** * * function: draw_one_image() * * purpose: draw one image of surface. separated out so can be * called twice in stereo mode. */ void draw_one_image() { struct graph_thread_data *td = GET_DATA; if ( td->dlistflag ) glCallList(dindex); else if ( td->arraysflag ) { int i,j,m; GL_ERROR_CHECK for ( m = 0 ; (m < transform_count) || (m < 1) ; m++ ) { float tmat[4][4]; /* transform matrix in proper form */ if ( transforms_flag && td->doing_lazy && view_transform_det && (view_transform_det[m] == -1.0) ) { /* have to flip normals */ for ( i = 0 ; i < td->edgecount+td->facetcount ; i++ ) for ( j = 0 ; j < 3 ; j++ ) td->fullarray[i].n[j] *= -1.0; } if ( transforms_flag && td->doing_lazy && view_transforms ) { int hi = (SDIM <= 3) ? SDIM : 3; for ( i = 0 ; i < hi; i++ ) { for ( j = 0 ; j < hi; j++ ) tmat[i][j] = (float)view_transforms[m][j][i]; for ( ; j < 3 ; j++ ) tmat[i][j] = 0.0; tmat[i][3] = (float)view_transforms[m][SDIM][i]; } for ( ; i < 3 ; i++ ) { for ( j = 0 ; j < 4 ; j++ ) tmat[i][j] = 0.0; tmat[i][i] = 1.0; } for ( j = 0 ; j < hi ; j++ ) tmat[3][j] = (float)view_transforms[m][j][SDIM]; for ( ; j < 3 ; j++ ) tmat[3][j] = 0.0; tmat[3][3] = (float)view_transforms[m][SDIM][SDIM]; glMatrixMode(GL_MODELVIEW); glPushMatrix(); glMultMatrixf((float*)tmat); } GL_ERROR_CHECK if ( td->strips_flag ) { int i; /* do facets first, then edges in front */ for ( i = td->estripcount ; i < td->stripcount ; i++ ) glDrawElements(td->striparray[i].mode,td->striparray[i].count, GL_UNSIGNED_INT,td->stripdata+td->striparray[i].start); glMatrixMode(GL_PROJECTION); glTranslated(td->edge_bias*imagescale,0.0,0.0); for ( i = 0 ; i < td->estripcount ; i++ ) glDrawElements(td->striparray[i].mode,td->striparray[i].count, GL_UNSIGNED_INT,td->stripdata+td->striparray[i].start); glTranslated(-td->edge_bias*imagescale,0.0,0.0); } else if ( td->indexing_flag ) { glDrawElements(GL_TRIANGLES,td->facetcount,GL_UNSIGNED_INT, td->indexarray+td->facetstart); glMatrixMode(GL_PROJECTION); glTranslated(td->edge_bias*imagescale,0.0,0.0); glDisable(GL_LIGHTING); glDrawElements(GL_LINES,td->edgecount,GL_UNSIGNED_INT,td->indexarray+td->edgestart); glEnable(GL_LIGHTING); glTranslated(-td->edge_bias*imagescale,0.0,0.0); } else { glDrawArrays(GL_TRIANGLES,td->facetstart,td->facetcount); glMatrixMode(GL_PROJECTION); glTranslated(td->edge_bias*imagescale,0.0,0.0); glDrawArrays(GL_LINES,td->edgestart,td->edgecount); glTranslated(-td->edge_bias*imagescale,0.0,0.0); } if ( transforms_flag && td->doing_lazy && view_transforms ) { glMatrixMode(GL_MODELVIEW); glPopMatrix(); } if ( transforms_flag && td->doing_lazy && view_transform_det && (view_transform_det[m] == -1.0) ) { /* have to flip normals back */ for ( i = 0 ; i < td->edgecount+td->facetcount ; i++ ) for ( j = 0 ; j < 3 ; j++ ) td->fullarray[i].n[j] *= -1.0; } if ( !td->doing_lazy || !transforms_flag || !view_transforms ) break; } /* end transform loop */ } /* end arraysflag */ else /* not using display list or arrays */ { ENTER_GRAPH_MUTEX normflag = td->normflag; graphgen(); LEAVE_GRAPH_MUTEX } GL_ERROR_CHECK } /* end draw_one_image() */ /********************************************************************** * * function: draw_screen() * * purpose: Handle redraw messages from operating system. */ void draw_screen() { struct graph_thread_data *td = GET_DATA; Matrix viewf; int i,j; if ( td->aspect_flag ) reshape_func(td->xsize,td->ysize); #ifdef quickresize if ( td->resize_flag && !td->idle_flag ) /* Mac OS X resize recombine */ { glutIdleFunc(idle_func); return; } td->resize_flag = 0; #endif td->idle_flag = 0; if ( td->new_title_flag ) glutSetWindowTitle(td->wintitle); td->new_title_flag = 0; #ifdef __cplusplus try { #else /* Set up longjmp to return here in case of error */ if ( setjmp(graphjumpbuf) ) { return; } #endif GL_ERROR_CHECK /* New view loading point to try to eliminate problem with first load of very small surfaces. Works. It needs to have view matrix loaded before setting arrays so it knows the proper rounding scale. */ /* make sure global view matrix is same as first window */ if ( td->win_id == 1 ) { for ( i = 0 ; i < HOMDIM ; i++ ) for ( j = 0 ; j < HOMDIM ; j++ ) view[i][j] = td->view[i][j]; } glMatrixMode(GL_MODELVIEW); for ( i = 0 ; i < 4 ; i++ ) for ( j = 0 ; j < 4 ; j++ ) viewf[i][j] = td->view[(j<3)?j:(SDIM)][(i<3)?i:(SDIM)]; if ( SDIM == 2 ) { for ( i = 0 ; i < 3 ; i++ ) viewf[i][2] = viewf[2][i] = 0.0; viewf[2][2] = 1.0; } /* transpose, picking first 3 coordinates */ if ( (td->projmode == P_PERSP) || td->stereomode ) { if ( SDIM == 2 ) viewf[3][2] -= 16; else viewf[3][0] -= 16.0; } GL_ERROR_CHECK glLoadMatrixd(viewf[0]); /* end new view load */ /* build arrays if needed */ if ( td->arraysflag && ( ((graph_timestamp != td->arrays_timestamp) && go_display_flag ) || td->newarraysflag || (td->dlistflag == RESETLIST)) ) { /* if long time since last build, block and wait */ if ( build_arrays() ) { declare_arrays(); /* doing this here since facet_alpha_flag set in graphgen */ if ( facet_alpha_flag ) glEnable(GL_BLEND); else glDisable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); if ( td->indexing_flag ) { make_indexlists(); if ( td->q_flag ) { sprintf(msg,"After indexing: %d unique vertices, %d unique edges\n", td->vertexcount,td->edgecount/2); outstring(msg); } if ( td->strips_flag ) make_strips(); } /* Workaround really bizarre line-drawing bug */ if ( td->linewidth == 1.0 ) { td->linewidth = (float)1.1; glLineWidth(td->linewidth); } GL_ERROR_CHECK if ( !td->interleaved_flag ) { /* kludge for broken nVidia Detonater 2.08 driver */ if ( td->colorarray ) free((char*)td->colorarray); td->colorarray = (float*)calloc(td->edgecount+td->facetcount,4*sizeof(float)); for ( i = 0 ; i < td->edgecount+td->facetcount ; i++ ) for ( j = 0 ; j < 4 ; j++ ) td->colorarray[4*i+j] = td->fullarray[i].c[j]; declare_arrays(); } GL_ERROR_CHECK td->newarraysflag = 0; if ( td->dlistflag ) { declare_arrays(); glNewList(dindex,GL_COMPILE); if ( td->indexing_flag ) { glDrawElements(GL_TRIANGLES,td->facetcount,GL_UNSIGNED_INT,td->indexarray+td->facetstart); glMatrixMode(GL_PROJECTION); glTranslated(td->edge_bias*imagescale,0.0,0.0); /* edges in front */ glDrawElements(GL_LINES,td->edgecount,GL_UNSIGNED_INT,td->indexarray+td->edgestart); glTranslated(-td->edge_bias*imagescale,0.0,0.0); glMatrixMode(GL_MODELVIEW); } else { glDrawArrays(GL_TRIANGLES,td->facetstart,td->facetcount); glMatrixMode(GL_PROJECTION); glTranslated(td->edge_bias*imagescale,0.0,0.0); /* edges in front */ glDrawArrays(GL_LINES,td->edgestart,td->edgecount); glTranslated(-td->edge_bias*imagescale,0.0,0.0); glMatrixMode(GL_MODELVIEW); } glEndList(); td->dlistflag = NORMALLIST; } if ( td->q_flag ) outstring(current_prompt); } else glutPostRedisplay(); } else if ( ((td->dlistflag != NORMALLIST) || ((graph_timestamp != prev_timestamp) && go_display_flag )) ) { /* if long time since last build, block and wait */ static REAL last_mutex_time; REAL now = get_internal_variable(V_CLOCK); int timeout = (now-last_mutex_time > .5) ? LONG_TIMEOUT : IMMEDIATE_TIMEOUT; if ( TRY_GRAPH_MUTEX(timeout) ) { /* regenerate display list */ graph_start = Oglz_start; graph_facet = Oglz_facet; graph_edge = Oglz_edge; graph_end = Oglz_end; init_graphics = Ogl_init; finish_graphics = Ogl_finish; close_graphics = Ogl_close_show; last_mutex_time = now; if ( td->dlistflag != NOLIST ) { glNewList(dindex,GL_COMPILE); td->facetcount = td->edgecount = 0; normflag = td->normflag; graphgen(); glEndList(); td->dlistflag = NORMALLIST; } END_TRY_GRAPH_MUTEX } else glutPostRedisplay(); } if ( SDIM != td->olddim ) { reshape_func(td->xsize,td->ysize); /* in case dimension changes */ td->olddim = SDIM; } if ( web.sdim > 2 ) { glEnable(GL_LIGHT0); glEnable(GL_LIGHTING); } else glDisable(GL_LIGHTING); /*glEnable(GL_LIGHT1); */ GL_ERROR_CHECK /* clear screen and zbuffer */ glClearColor(rgba[background_color][0], rgba[background_color][1], rgba[background_color][2], rgba[background_color][3] ); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); if ( backcull_flag ) { glEnable(GL_CULL_FACE); } else { glDisable(GL_CULL_FACE); } glDepthFunc(GL_LEQUAL); /* so later things get drawn */ #ifdef GGG original loading of view matrix /* make sure global view matrix is same as first window */ if ( td->win_id == 1 ) { for ( i = 0 ; i < HOMDIM ; i++ ) for ( j = 0 ; j < HOMDIM ; j++ ) view[i][j] = td->view[i][j]; } glMatrixMode(GL_MODELVIEW); for ( i = 0 ; i < 4 ; i++ ) for ( j = 0 ; j < 4 ; j++ ) viewf[i][j] = td->view[(j<3)?j:(SDIM)][(i<3)?i:(SDIM)]; if ( SDIM == 2 ) { for ( i = 0 ; i < 3 ; i++ ) viewf[i][2] = viewf[2][i] = 0.0; viewf[2][2] = 1.0; } /* transpose, picking first 3 coordinates */ if ( (td->projmode == P_PERSP) || td->stereomode ) { if ( SDIM == 2 ) viewf[3][2] -= 16; else viewf[3][0] -= 16.0; } GL_ERROR_CHECK glLoadMatrixd(viewf[0]); #endif /* for picking */ if ( !td->arraysflag ) { glInitNames(); glPushName(0); /*{int depth; glGetIntegerv(GL_NAME_STACK_DEPTH,&depth); printf("OpenGL name stack depth: %d\n",depth); } */ } if ( td->arraysflag ) if ( td->declared_timestamp < td->arrays_timestamp ) declare_arrays(); /* Now the actual drawing */ if ( td->stereomode ) { int w = (SDIM==2) ? 0 : 1; /* Stereo mode always perspective */ glMatrixMode(GL_MODELVIEW); viewf[3][w] -= 1.5; glLoadMatrixd(viewf[0]); draw_one_image(); glMatrixMode(GL_MODELVIEW); viewf[3][w] += 3.0; glLoadMatrixd(viewf[0]); draw_one_image(); } else draw_one_image(); if ( display_text_count ) glut_text_display(); /* draw text lines */ glFlush(); glutSwapBuffers(); temp_free_all(); /* should free just graphics thread temps */ #ifdef __cplusplus } catch(...) { } #endif glutIdleFunc(idle_func); } /* end draw_screen() */ /************************************************************************ * * function: idle_func() * * purpose: Mac OS X kludge to prevent excessive redisplays on * mouse movement. */ void idle_func() { struct graph_thread_data *td = GET_DATA; if ( close_flag ) Ogl_close(); td->idle_flag = 1; #ifdef quickresize if ( td->resize_flag ) glutPostRedisplay(); td->resize_flag = 0; #endif if ( draw_pid != main_pid ) /* can use signals */ glutIdleFunc(NULL); /* else relying on idle_func to pick up redraw messages */ else { #if (defined(SUN) || defined(LINUX)) && !defined(__STRICT_ANSI__) /* let the thread could sleep .02 sec; need link with -lrt */ struct timespec delay; delay.tv_sec = 0; delay.tv_nsec = 20000000; #ifdef SUN __nanosleep(&delay,NULL); #else nanosleep(&delay,NULL); #endif #endif #ifdef WIN32 /* sleep until some event */ { HANDLE pHandles[MAXIMUM_WAIT_OBJECTS]; MsgWaitForMultipleObjects(0,pHandles,FALSE,100,QS_ALLINPUT); } #endif } } /*********************************************************************** * * function: display() * * purpose: called by Evolver to display on screen. */ void display() { struct graph_thread_data *td; int i,j; close_flag = 0; Oglz_start(); /* set first screen view to anything modified by main thread */ for ( i = 0 ; i < HOMDIM ; i++ ) { gthread_data[1].view[i] = gthread_data[1].viewspace[i]; for ( j = 0 ; j < HOMDIM ; j++ ) gthread_data[1].view[i][j] = view[i][j]; } for ( i = 0, td = gthread_data ; i < MAXGRAPHWINDOWS ; i++, td++ ) if ( td->win_id ) { if ( window_aspect_ratio != td->window_aspect ) td->aspect_flag = 1; #ifdef MAC_OS_X /* glutPostRedisplay() generates ugly NSEvent leak messages */ /* glutSetWindow(td->win_id); */ /* draw_screen(); Conflict with graphics thread! */ glutPostWindowRedisplay(td->win_id); /* generate redraw message */ #else /* WARNING: glutSetWindow() in non-drawing thread causes problems. */ /* glutSetWindow(td->win_id); */ /* glutPostRedisplay(); */ /* generate redraw message */ glutPostWindowRedisplay(td->win_id); /* generate redraw message */ #ifdef WIN32 InvalidateRect(td->draw_hwnd,NULL,FALSE); /* give draw thread a kick */ #else if ( draw_pid != main_pid ) { kill(draw_pid,SIGKICK); /* unix version of kick */ } #endif #endif } } /**************************************************************************** * * function: make_strips() * * purpose: Converts indexed arrays of edges and triangles to strips * for more efficient plotting. * Input: global array indexarray * global variables edgecount,facetcount * Output: striparray */ int ecomp ARGS2((a,b), char *a, int *b) { struct graph_thread_data *td = GET_DATA; int ai = td->indexarray[td->edgestart+*(int*)a]; int bi = td->indexarray[td->edgestart+*(int*)b]; if ( ai < bi ) return -1; if ( bi < ai ) return 1; return 0; } struct festruct { int v[3]; /* head and tail and opposite vertices */ int f; /* facet on left */ }; int fecomp ARGS2(( aa,bb), void *aa, void*bb) { struct festruct *a = (struct festruct *)aa; struct festruct *b = (struct festruct *)bb; if ( a->v[0] < b->v[0] ) return -1; if ( a->v[0] > b->v[0] ) return 1; if ( a->v[1] < b->v[1] ) return -1; if ( a->v[1] > b->v[1] ) return 1; return 0; } void make_strips() { int *edgeinx; /* oriented edge numbers, sorted by first vertex */ int *evlist; /* vertex indexed list of offsets into edgeinx */ int v; int i,k,kk; int *estripno; /* number of current strip */ int *fstripno; /* number of current strip */ int stripnum; int dataspot; struct festruct *felist; int striplength[3]; /* for testing 3 ways from each starting facet */ int *trialstrip; /* for unerasing markings */ int bestlength; int *bestverts; int *bestfacets; int way; int bestway; struct graph_thread_data *td = GET_DATA; /* strip arrays */ if ( td->stripdata ) free((char*)td->stripdata); if ( td->striparray ) free((char*)td->striparray); td->stripdata = (int*)calloc(td->edgecount+td->facetcount+5,sizeof(int)); td->striparray = (struct stripstruct *)calloc(td->edgecount/2+td->facetcount/3 + 10, sizeof(struct stripstruct)); dataspot = 0; stripnum = 0; /* edges */ /* make list indexed by vertex */ /* in einx, entry is 2*edgeindex+orientationbit */ edgeinx = (int *)temp_calloc(td->edgecount,sizeof(int)); for ( i = 0 ; i < td->edgecount ; i++ ) { edgeinx[i] = i; } /* using bit for orientation */ qsort((void*)edgeinx,td->edgecount,sizeof(int),FCAST ecomp); /* find where individual vertex segments start */ evlist = (int *)temp_calloc(td->edgecount+10,sizeof(int)); for ( i = 0, v = 0 ; i < td->edgecount ; i++ ) { while ( td->indexarray[td->edgestart+edgeinx[i]] > v ) evlist[++v] = i; } evlist[++v] = i; /* last sentinel */ /* now make strips. start with some edge and just keep going. */ estripno = (int*)temp_calloc(td->edgecount+5,sizeof(int)); for ( i = 0 ; i < td->edgecount/2 ; i++ ) { int nexte,headv; if ( estripno[i] ) continue; /* new strip */ td->striparray[stripnum].mode = GL_LINE_STRIP; td->striparray[stripnum].start = dataspot; nexte = 2*i; for (;;) { estripno[nexte>>1] = stripnum+1; headv = td->indexarray[td->edgestart+(nexte^1)]; td->stripdata[dataspot++] = headv; for ( k = evlist[headv] ; k < evlist[headv+1] ; k++ ) { if ( estripno[edgeinx[k]>>1] == 0 ) { nexte = edgeinx[k]; break; } } if ( k == evlist[headv+1] ) break; /* end of strip */ } /* flip list around */ for ( k = td->striparray[stripnum].start, kk = dataspot-1 ; k < kk ; k++,kk-- ) { int temp = td->stripdata[k]; td->stripdata[k] = td->stripdata[kk]; td->stripdata[kk] = temp; } /* now backwards */ nexte = 2*i+1; for (;;) { estripno[nexte>>1] = stripnum+1; headv = td->indexarray[td->edgestart+(nexte^1)]; td->stripdata[dataspot++] = headv; for ( k = evlist[headv] ; k < evlist[headv+1] ; k++ ) { if ( estripno[edgeinx[k]>>1] == 0 ) { nexte = edgeinx[k]; break; } } if ( k == evlist[headv+1] ) break; /* end of strip */ } td->striparray[stripnum].count = dataspot - td->striparray[stripnum].start; stripnum++; } td->estripcount = stripnum; temp_free((char*)evlist); temp_free((char*)estripno); temp_free((char*)edgeinx); /* facets */ /* make list of edges with left-hand facets */ felist = (struct festruct *)temp_calloc(td->facetcount,sizeof(struct festruct)); for ( i = 0, k = 0 ; i < td->facetcount ; i += 3, k++ ) { felist[i].v[0] = td->indexarray[td->facetstart+i]; felist[i].v[1] = td->indexarray[td->facetstart+i+1]; felist[i].v[2] = td->indexarray[td->facetstart+i+2]; felist[i].f = k; felist[i+1].v[0] = td->indexarray[td->facetstart+i+1]; felist[i+1].v[1] = td->indexarray[td->facetstart+i+2]; felist[i+1].v[2] = td->indexarray[td->facetstart+i]; felist[i+1].f = k; felist[i+2].v[0] = td->indexarray[td->facetstart+i+2]; felist[i+2].v[1] = td->indexarray[td->facetstart+i]; felist[i+2].v[2] = td->indexarray[td->facetstart+i+1]; felist[i+2].f = k; } qsort((void*)felist,td->facetcount,sizeof(struct festruct),FCAST fecomp); fstripno = (int *)temp_calloc(td->facetcount+5,sizeof(int)); trialstrip = (int *)temp_calloc(td->facetcount+5,sizeof(int)); bestverts = (int *)temp_calloc(td->facetcount+5,sizeof(int)); bestfacets = (int *)temp_calloc(td->facetcount+5,sizeof(int)); /* now make strips. start with some facet and just keep going. */ for ( i = 0 ; i < td->facetcount/3 ; i++ ) { int nextf,va,vb,vc,whichway; int firstcount,secondcount; /* for checking orientation at start */ if ( fstripno[i] ) continue; /* new strip */ td->striparray[stripnum].mode = GL_TRIANGLE_STRIP; td->striparray[stripnum].start = dataspot; bestlength = 0; for ( way = 0 ; way < 3 ; way++) { int m = 0; /* trialstrip index */ dataspot = td->striparray[stripnum].start; nextf = 3*i; td->stripdata[dataspot++] = va = td->indexarray[td->facetstart+nextf+((1+way)%3)]; td->stripdata[dataspot++] = vb = td->indexarray[td->facetstart+nextf+way]; whichway = 1; for (;;) { struct festruct *fe,key; /* find in felist */ if ( whichway ) { key.v[0] = va; key.v[1] = vb; } else { key.v[1] = va; key.v[0] = vb; } fe = (struct festruct *)bsearch(&key,(void*)felist,td->facetcount, sizeof(struct festruct), FCAST fecomp); if ( fe==NULL ) break; /* done; maybe hit edge of surface */ /*see if facet done yet */ if ( fstripno[fe->f] != 0 ) break; /* done in this direction */ /*add opposite vertex */ vc = fe->v[2]; td->stripdata[dataspot++] = vc; fstripno[fe->f] = stripnum+1; trialstrip[m++] = fe->f; /* ready for next time around */ va = vb; vb = vc; whichway = !whichway; } firstcount = dataspot - td->striparray[stripnum].start; /* flip list around */ for ( k = td->striparray[stripnum].start, kk = dataspot-1 ; k < kk ; k++,kk-- ) { int temp = td->stripdata[k]; td->stripdata[k] = td->stripdata[kk]; td->stripdata[kk] = temp; } /* now backwards */ va = td->indexarray[td->facetstart+nextf+way]; vb = td->indexarray[td->facetstart+nextf+((way+1)%3)]; whichway = 1; for (;;) { struct festruct *fe,key; /*find in felist */ if ( whichway ) { key.v[0] = va; key.v[1] = vb; } else { key.v[1] = va; key.v[0] = vb; } fe = (struct festruct*)bsearch(&key,(void*)felist,td->facetcount, sizeof(struct festruct), FCAST fecomp); if ( fe==NULL ) break; /* done; maybe hit edge of surface */ /*see if facet done yet */ if ( fstripno[fe->f] != 0 ) break; /* done in this direction */ /*add opposite vertex */ vc = fe->v[2]; td->stripdata[dataspot++] = vc; fstripno[fe->f] = stripnum+1; trialstrip[m++] = fe->f; /* ready for next time around */ va = vb; vb = vc; whichway = !whichway; } striplength[way] = dataspot - td->striparray[stripnum].start; secondcount = striplength[way] - firstcount; /* check orientation at start */ if ( firstcount & 1 ) { if ( secondcount & 1 ) { striplength[way]--; /* omit last, if necessary */ if ( i == trialstrip[m-1] ) i--; /* so loop doesn't skip omitted facet */ } /* flip order */ for ( k = td->striparray[stripnum].start, kk = td->striparray[stripnum].start+striplength[way]-1 ; k < kk ; k++,kk-- ) { int temp = td->stripdata[k]; td->stripdata[k] = td->stripdata[kk]; td->stripdata[kk] = temp; } } if ( striplength[way] > bestlength ) { bestlength = striplength[way]; bestway = way; memcpy(bestverts,td->stripdata+td->striparray[stripnum].start,bestlength*sizeof(int)); memcpy(bestfacets,trialstrip,(bestlength-2)*sizeof(int)); } for ( k = 0 ; k < m ; k++ ) /* unmark */ fstripno[trialstrip[k]] = 0; } /* end ways */ memcpy(td->stripdata+td->striparray[stripnum].start,bestverts,bestlength*sizeof(int)); for ( k = 0 ; k < bestlength-2 ; k++ ) /* remark */ { fstripno[bestfacets[k]] = stripnum+1; if ( td->strip_color_flag && (bestfacets[k] < web.skel[FACET].maxcount) ) set_facet_color(bestfacets[k],(stripnum % 14) + 1); } td->striparray[stripnum].count = bestlength; dataspot = td->striparray[stripnum].start + bestlength; stripnum++; } temp_free((char*)fstripno); temp_free((char*)felist); temp_free((char*)trialstrip); temp_free((char*)bestverts); temp_free((char*)bestfacets); td->stripcount = stripnum; td->fstripcount = td->stripcount - td->estripcount; /* cut down arrays to needed size */ td->stripdata = (int*)realloc((char*)td->stripdata,dataspot*sizeof(int)); td->striparray = (struct stripstruct *)realloc((char*)td->striparray, td->stripcount*sizeof(struct stripstruct)); if ( td->q_flag ) { sprintf(msg,"After stripping: %d edgestrips, %d facetstrips\n", td->estripcount,td->fstripcount); outstring(msg); } } /*************************************************************************** * * function: hashfunc() * * purpose: Compute hash value for vertex. */ int hashsize; /* size of hashtable */ int hashfunc ARGS1((a), struct vercol *a) { int h; int scale = 100000; double eps = 3.e-6; /* to prevent coincidences */ h = 15187*(int)(scale*a->x[0]+eps); h += 4021*(int)(scale*a->x[1]+eps); h += 2437*(int)(scale*a->x[2]+eps); h += 7043*(int)(scale*a->n[0]+eps); h += 5119*(int)(scale*a->n[1]+eps); h += 8597*(int)(scale*a->n[2]+eps); h += 1741*(int)(scale*a->c[0]+eps); h += 4937*(int)(scale*a->c[1]+eps); h += 1223*(int)(scale*a->c[2]+eps); h = h % hashsize; if ( h < 0 ) h += hashsize; return h; } /*************************************************************************** * * function: make_indexlists() * * purpose: Uniquify vertex and edge lists, and create index array for * OpenGL. Also needed for strips. */ void make_indexlists() { struct graph_thread_data *td = GET_DATA; int i,j; int rawcount = td->edgecount+td->facetcount; /* number of unsorted vertices */ struct vercol **hashlist; float mat[4][4]; /* get reasonable epsilon for identifying vertices */ glGetFloatv(GL_MODELVIEW_MATRIX,mat[0]); gleps = 1e-5/sqrt(mat[0][0]*mat[0][0]+mat[0][1]*mat[0][1] +mat[0][2]*mat[0][2]); /* Uniquify using hash table */ /* qsort here is a time hog */ if ( td->indexarray ) free((char*)td->indexarray); td->indexarray = (int*)calloc(rawcount+10,sizeof(int)); if ( !td->fullarray ) return; hashsize = 2*rawcount + 10; hashlist = (struct vercol**)calloc(hashsize,sizeof(struct vercol *)); hashlist[hashfunc(td->fullarray)] = td->fullarray; td->indexarray[0] = 0; for ( i = 1, j = 1 ; i < rawcount ; i++ ) { int h = hashfunc(td->fullarray+i); while ( hashlist[h] && vercolcomp(hashlist[h],td->fullarray+i) ) { h++; if ( h == hashsize ) h = 0; } if ( hashlist[h] == NULL ) /* new one */ { td->fullarray[j] = td->fullarray[i]; hashlist[h] = td->fullarray+j; j++; } td->indexarray[i] = hashlist[h]-td->fullarray; } free((char*)hashlist); td->vertexcount = j; /* Uniquify edges */ for ( i = td->edgestart ; i < td->edgestart+td->edgecount ; i += 2 ) { if ( td->indexarray[i] > td->indexarray[i+1] ) { int temp = td->indexarray[i]; td->indexarray[i] = td->indexarray[i+1]; td->indexarray[i+1] = temp; } } /* qsort here relatively minor in time */ qsort((void*)(td->indexarray+td->edgestart),td->edgecount/2,2*sizeof(int), FCAST eecomp); for ( i = 2, j = 0 ; i < td->edgecount ; i += 2 ) { if ( eecomp(td->indexarray+td->edgestart+i,td->indexarray+td->edgestart+j) != 0 ) { j += 2; if ( i > j ) { td->indexarray[td->edgestart+j] = td->indexarray[td->edgestart+i]; td->indexarray[td->edgestart+j+1] = td->indexarray[td->edgestart+i+1]; } } } if ( td->edgecount ) td-> edgecount = j+2; } /* end make_indexlists() */ /****************************************************************************** * * Function: build_arrays() * * Purpose: Construct arrays of graphics data. * * Return: 1 if success, 0 if failure */ int build_arrays() { static REAL last_mutex_time; struct graph_thread_data *td = GET_DATA; REAL now = get_internal_variable(V_CLOCK); int timeout = (now-last_mutex_time > .5) ? LONG_TIMEOUT : IMMEDIATE_TIMEOUT; #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) { mpi_get_task_graphics(td->mpi_graph_task); return 1; } else #endif if ( TRY_GRAPH_MUTEX(timeout) ) { int oldflag; /* see if already have current arrays in some other thread */ /* int i; for ( i = 0 ; i < MAXGRAPHWINDOWS ; i++ ) if ( td != gthread_data + i #ifdef MPI_EVOLVER && td->mpi_graph_task == gthread_data[i].mpi_graph_task #endif && td->arrays_timestamp < gthread_data[i].arrays_timestamp ) { td->fullarray = gthread_data[i].fullarray; td->edgestart = gthread_data[i].edgestart; td->edgecount = gthread_data[i].edgecount; td->facetstart = gthread_data[i].facetstart; td->facetcount = gthread_data[i].facetcount; td->arrays_timestamp = gthread_data[i].arrays_timestamp; td->fullarray_original = 0; return 1; } */ last_mutex_time = now; if ( td->fullarray && td->fullarray_original ) { free((char*)td->fullarray); td->fullarray = NULL; } td->edgecount = 0; td->edgemax = (web.representation==SIMPLEX) ? SDIM*web.skel[FACET].count + 100 : 4*web.skel[EDGE].count+10; /* 2 vertices per edge, each edge twice */ td->edgearray = (struct vercol *)calloc(td->edgemax,sizeof(struct vercol)); td->facetcount = 0; td->facetmax = 3*web.skel[FACET].count+10; /* 3 vertices per facet */ td->facetarray = (struct vercol *)calloc(td->facetmax,sizeof(struct vercol)); if ( !td->edgearray || !td->facetarray ) { erroutstring("Cannot allocate memory for graphics.\n"); return 0; } graph_start = Oglz_start; graph_facet = Oglz_facet; graph_edge = Oglz_edge; graph_end = Oglz_end; init_graphics = Ogl_init; finish_graphics = Ogl_finish; close_graphics = Ogl_close_show; /*glDisableClientState(GL_COLOR_ARRAY); */ /*glDisableClientState(GL_NORMAL_ARRAY); */ /*glDisableClientState(GL_VERTEX_ARRAY); */ oldflag = markedgedrawflag; markedgedrawflag = 1; if ( (td->mouse_mode != MM_SLICE) && !transform_colors_flag && (SDIM <= 3)) lazy_transforms_flag = 1; /* for graphgen use */ td->doing_lazy = lazy_transforms_flag; /* for glutgraph use */ td->arrays_timestamp = graph_timestamp = ++global_timestamp; /* prevent stale data */ normflag = td->normflag; graphgen(); /* fill in arrays */ td->doing_lazy = lazy_transforms_flag; /* for glutgraph use, in case graphgen() changed */ END_TRY_GRAPH_MUTEX lazy_transforms_flag = 0; markedgedrawflag = oldflag; if ( td->q_flag ) { sprintf(msg,"\n%d edges, %d facets\n",td->edgecount/2,td->facetcount/3); outstring(msg); } /* unify lists */ td->fullarray = (struct vercol *)realloc((char*)td->edgearray, (td->edgecount+td->facetcount)*sizeof(struct vercol)); td->fullarray_original = 1; memcpy((char*)(td->fullarray+td->edgecount),(char*)td->facetarray, td->facetcount*sizeof(struct vercol)); free((char*)td->facetarray); td->facetarray = NULL; td->edgearray = NULL; td->edgestart = 0; td->facetstart = td->edgecount; return 1; /* success */ } else return 0; } /* end build_arrays() */ #ifdef MPI_EVOLVER /*********************************************************************** * * Function: mpi_get_task_graphics() * * Purpose: Fetch graphics data from task in array format suitable for OpenGL. * Called by master task graphics module. * */ void mpi_get_task_graphics(task) int task; /* which task to get from */ { struct mpi_command message; MPI_Status status; struct graph_metadata metadata; struct graph_thread_data *td = GET_DATA; int i,j; ENTER_MPI_MUTEX; message.cmd = mpi_GET_GRAPHICS; message.task = task; /* MPI_Send(&message,sizeof(struct mpi_command),MPI_BYTE,task,GRAPHICS_TAG, mpi_comm_graphics); */ MPI_Bcast(&message,sizeof(struct mpi_command),MPI_BYTE,MASTER_TASK, MPI_COMM_WORLD); if ( mpi_debug ) printf("Task %d requested graphics from task %d\n",this_task,task); MPI_Recv(&metadata,sizeof(struct graph_metadata),MPI_BYTE,task,GRAPHICS_TAG, MPI_COMM_WORLD, &status); if ( mpi_debug ) printf("Got metadata\n"); td->edgestart = metadata.edgestart; td->edgecount = metadata.edgecount; td->facetstart = metadata.facetstart; td->facetcount = metadata.facetcount; if ( !td->view_initialized ) for ( i = 0 ; i < 4 ; i++ ) for ( j = 0 ; j < 4 ; j++ ) td->view[i][j] = metadata.view[i][j]; td->view_initialized = 1; /* allocate room for graphics data */ td->fullarray = realloc(td->fullarray,(td->facetstart+td->facetcount)*sizeof(struct vercol)); MPI_Recv(td->fullarray,(td->facetstart+td->facetcount)*sizeof(struct vercol), MPI_BYTE,task,GRAPHICS_TAG,MPI_COMM_WORLD,&status); if ( mpi_debug ) printf("Got graphics data, edgecount %d facetcount %d\n",td->edgecount, td->facetcount); td->arrays_timestamp = graph_timestamp; MPI_Barrier(MPI_COMM_WORLD); LEAVE_MPI_MUTEX; } /********************************************************************** * * Function: mpi_task_send_graphics() * * Purpose: Task handler for mpi_get_task_graphics(). Sends graphics * data array back to master task for display. */ void mpi_task_send_graphics() { struct graph_metadata metadata; int i,j; struct graph_thread_data *td = GET_DATA; memset(&metadata,0,sizeof(metadata)); if ( mpi_debug ) printf("Task %d got request for graphics.\n",this_task); gthread_data[0].facetshow_flag = 1; /* kludge */ td->arraysflag = 1; color_flag = 1; for ( i = 0 ; i < 16 ; i++ ) for ( j = 0 ; j < 4 ; j++ ) rgba[i][j] = (float)rgb_colors[i][j]; no_graphthread_flag = 1; build_arrays(); no_graphthread_flag = 0; metadata.edgecount = td->edgecount; metadata.edgestart = td->edgestart; metadata.facetcount = td->facetcount; metadata.facetstart = td->facetstart; for ( i = 0 ; i < 4 ; i++ ) for ( j = 0 ; j < 4 ; j++ ) metadata.view[i][j] = view[i][j]; if ( mpi_debug ) printf("Sending metadata\n"); MPI_Send(&metadata,sizeof(metadata),MPI_BYTE,MASTER_TASK,GRAPHICS_TAG, MPI_COMM_WORLD); if ( mpi_debug ) printf("Sending graph data\n"); MPI_Send(td->fullarray,(td->facetstart+td->facetcount)*sizeof(struct vercol), MPI_BYTE,MASTER_TASK,GRAPHICS_TAG,MPI_COMM_WORLD); } /* end mpi_task_send_graphics() */ #endif evolver-2.30c.dfsg/src/command.c0000644000175300017530000014470111410765113017006 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************* * * file: command.c * * Purpose: Interactive user interface command interpreter. */ #include "include.h" /************************************************************* * * function: recalc() * * purpose: get everything recalculated and redisplayed * after user changes surface. */ void recalc() { global_timestamp++; /* so INFO quantities will recalc later */ if ( need_fe_reorder_flag ) { /* straighten out facet order around edges */ if ( web.representation == SOAPFILM ) { edge_id e_id; FOR_ALL_EDGES(e_id) fe_reorder(e_id); } need_fe_reorder_flag = 0; } reset_rot_order(); if ( web.torus_flag ) calc_periods(ADJUST_VOLUMES); else if ( web.torus_period ) calc_periods(NO_ADJUST_VOLUMES); if ( transform_expr[0] ) { calc_view_transform_gens(); transform_gen_expr(transform_expr); } recalc_verts(); update_display(); if ( phase_flag ) { if ( web.representation == STRING ) { edge_id e_id; FOR_ALL_EDGES(e_id) set_e_phase_density(e_id); } else { facet_id f_id; FOR_ALL_FACETS(f_id) set_f_phase_density(f_id); } } #ifdef MPI_EVOLVER if ( this_task == 0 ) #endif { calc_content(Q_FIXED); /* if ( web.torus_flag ) fix_volconst(); */ calc_pressure(); calc_energy(); change_flag = 0; } reset_conj_grad(); if ( normal_motion_flag ) begin_normal_motion(); } /* end recalc() */ /****************************************************************** * * function: reset_conj_grad() * * purpose: re-initialize conjugate gradient. * */ void reset_conj_grad() { int i; vertex_id v_id; int size = SDIM; #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) { struct mpi_command message; message.cmd = mpi_RESET_CONJ_GRAD; message.mode = ribiere_flag; MPI_Bcast(&message,sizeof(struct mpi_command),MPI_BYTE,MASTER_TASK, MPI_COMM_WORLD); } #endif /* reset conjugate gradient */ if ( cg_hvector ) myfree((char *)cg_hvector); cg_hvector = NULL; cg_oldsum = 0.0; if ( conj_grad_flag && ( ribiere_flag ) ) { int r_attr = find_attribute(VERTEX,RIBIERE_ATTR_NAME); if ( r_attr == -1 ) { add_attribute(VERTEX,RIBIERE_ATTR_NAME,REAL_TYPE,1,&size,0,NULL); r_attr = find_attribute(VERTEX,RIBIERE_ATTR_NAME); } FOR_ALL_VERTICES(v_id) { REAL *g = (REAL*)get_extra(v_id,r_attr); for ( i = 0 ; i < SDIM ; i++ ) g[i] = 0.0; } } } /* end reset_conj_grad() */ /************************************************************* * * function: old_menu() * * purpose: Handle one command. Useful for event loopers. */ int old_menu ARGS1((text), char *text) { int retval = 0; if ( text[0] == '!' ) retval = old_history(text); /* history list */ else retval = command(text,ADD_TO_HISTORY); if ( change_flag ) recalc(); return retval; } /************************************************************* * * function: letter_commands() * * purpose: handle the single-letter commands. * */ void report_times ARGS((void)); void letter_command ARGS1((c), int c) { char response[140]; body_id b_id; REAL val; /* for scanf */ int i,n,k; int znum; int old; switch ( c ) { /* Reporting */ case 'C': run_checks(); outstring("Checks completed.\n"); break; case 'c': /* report count of elements and status */ memory_report(); break; case 'E': dump_force(); break; case 'e' : extrapolate(); break; case 'i': /* i for information */ information(); break; case 'v' : /* show volumes and quantities */ show_volumes(); break; case 'z' : /* curvature test */ if ( web.representation == SIMPLEX ) outstring("Not implemented for simplex representation.\n"); curtest(); break; case 'X' : /* extra attributes */ { outstring( " Element Attribute Type Offset Bytes Dimensions\n"); for ( i = 0 ; i < NUMELEMENTS; i++ ) { for ( k = 0 ; k < web.skel[i].extra_count ; k++ ) { int j; sprintf(msg,"%9s %32s %10s %5d %5d ",typenames[i], EXTRAS(i)[k].name, datatype_name[EXTRAS(i)[k].type], EXTRAS(i)[k].offset, datatype_size[EXTRAS(i)[k].type]*EXTRAS(i)[k].array_spec.datacount); for ( j = 0 ; j < EXTRAS(i)[k].array_spec.dim ; j++ ) sprintf(msg+strlen(msg),"[%d]",EXTRAS(i)[k].array_spec.sizes[j]); if ( EXTRAS(i)[k].array_spec.dim == 0 ) strcat(msg," scalar "); outstring(msg); outstring("\n"); } } } break; /* Controlling model characteristics */ case 'A' : /* set adjustable parameters */ if ( set_parameters() ) recalc(); break; case 'a' : /* toggle area normalization of force */ old = web.area_norm_flag; web.area_norm_flag = !web.area_norm_flag; web.norm_check_flag = 0; /* default OFF */ if ( web.area_norm_flag ) { outstring("Area normalization ON."); if ( old ) outstring(" (was on)\n"); else outstring(" (was off)\n"); prompt("If you want to check normal change, enter ratio: ",response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); if ( const_expr(response,&val) > 0 ) if ( val > 0.0000001 ) { web.norm_check_flag = 1; web.norm_check_max = val; } calc_energy(); /* to make sure vertex areas set */ } else { outstring("Area normalization OFF."); if ( old ) outstring(" (was on)\n"); else outstring(" (was off)\n"); } break; case 'b' : /* set body volumes and pressures */ if ( web.skel[BODY].count == 0 ) { outstring("No bodies.\n"); break; } { REAL pp; FOR_ALL_BODIES(b_id) { if ( get_battr(b_id) & PRESSURE ) { pp = get_body_pressure(b_id); sprintf(msg,"Body %s. Current pressure %f. Enter new: ", ELNAME(b_id),(DOUBLE)pp); prompt(msg,response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); if ( const_expr(response,&pp) > 0 ) { set_body_pressure(b_id,pp); reset_conj_grad(); if ( everything_quantities_flag ) { struct gen_quant *q = GEN_QUANT(bptr(b_id)->volquant); if ( pp == 0.0 ) { q->modulus = 1.0; q->flags &= ~(Q_FIXED|Q_ENERGY|Q_CONSERVED); q->flags |= Q_INFO; } else { q->modulus = -pp; q->flags &= ~(Q_FIXED|Q_INFO|Q_CONSERVED); q->flags |= Q_ENERGY; } } } } /* end PRESSURE */ else /* edit volumes */ { pp = get_body_fixvol(b_id); sprintf(msg,"Body %s. Current target volume %g. Enter new: ", ELNAME(b_id),(DOUBLE)pp); prompt(msg,response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); cmdptr = response; if ( const_expr(response,&pp) > 0 ) { set_body_fixvol(b_id,pp); reset_conj_grad(); if ( pp == 0.0 ) { if ( get_attr(b_id) & FIXEDVOL) { unset_attr(b_id,FIXEDVOL); if ( everything_quantities_flag ) { struct gen_quant *q = GEN_QUANT(bptr(b_id)->volquant); q->target = 0.0; q->flags &= ~(Q_FIXED|Q_ENERGY|Q_CONSERVED); q->flags |= Q_INFO; } } } else { set_attr(b_id,FIXEDVOL); if ( everything_quantities_flag ) { struct gen_quant *q = GEN_QUANT(bptr(b_id)->volquant); q->target = pp; q->flags &= ~(Q_INFO|Q_ENERGY|Q_CONSERVED); q->flags |= Q_FIXED; } } } } } /* end BODIES */ } /* end block */ recalc(); break; case 'f' : /* Set diffusion */ sprintf(msg,"Diffusion constant is %f. Enter new: ", (DOUBLE)web.diffusion_const); prompt(msg,response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); if ( const_expr(response,&val) > 0 ) web.diffusion_const = val; if ( web.diffusion_const == 0.0 ) web.diffusion_flag = 0; else web.diffusion_flag = 1; break; case 'G' : /* control gravity */ if ( web.representation == SIMPLEX ) { outstring( "Gravity not implemented for simplex representation.\n"); break; } if ( web.gravflag ) sprintf(msg,"Gravity is now ON with gravitational constant %f.\n", (DOUBLE)web.grav_const); else sprintf(msg,"Gravity is now OFF.\n"); outstring(msg); prompt("Enter new constant (0 for OFF): ",response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); if ( const_expr(response,&val) > 0 ) { web.grav_const = val; if ( web.grav_const != 0.0 ) web.gravflag = 1; else web.gravflag = 0; } if (gravity_quantity_num >= 0 ) GEN_QUANT(gravity_quantity_num)->modulus = web.gravflag ? web.grav_const : 0.0; recalc(); break; case 'J' : /* toggle jiggling on every move */ web.jiggle_flag = !web.jiggle_flag; if ( web.jiggle_flag ) { outstring("Now jiggling on every move.\n"); sprintf(msg, "Enter temperature for jiggling (default %f): ", (DOUBLE)web.temperature); prompt(msg,response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); if ( const_expr(response,&val) > 0 ) web.temperature = val; } else outstring("Jiggling on every move disabled.\n"); break; case 'k' : /* Magnitude of force opposing boundary short-circuiting */ sprintf(msg,"Gap constant is %f. Enter new: ", (DOUBLE)web.spring_constant); prompt(msg,response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); if ( const_expr(response,&val) > 0 ) web.spring_constant = val; if ( everything_quantities_flag ) GEN_QUANT(gap_quantity_num)->modulus = val; if ( web.spring_constant == 0.0 ) web.convex_flag = 0; else web.convex_flag = 1; recalc(); break; case 'M' : /* change model type */ if ( web.representation == SIMPLEX ) { outstring( "Higher-order models not implemented for simplex representation.\n"); break; } change_model(); recalc(); break; case 'm' : /* Setting motion scale factor */ web.motion_flag = !web.motion_flag; if ( web.motion_flag ) { sprintf(msg,"Enter scale factor (%g): ",(DOUBLE)web.scale); prompt(msg,response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); if ( const_expr(response,&val) > 0 ) web.scale = val; } else { energy_init = 0; sprintf(msg,"Scale optimizing. Enter scale limit (%g): ",(DOUBLE)web.maxscale); prompt(msg,response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); if ( const_expr(response,&val) > 0 ) web.maxscale = val; if ( web.scale > web.maxscale ) web.scale = web.maxscale; } break; case 'p' : /* Ambient pressure for compressible volumes */ if ( web.bodycount == 0 ) { outstring("Can't do pressure without bodies.\n"); break; } sprintf(msg,"Pressure now %f\n",(DOUBLE)web.pressure); outstring(msg); prompt("Enter pressure (0 for rigid volumes): ",response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); if ( const_expr(response,&val) > 0 ) web.pressure = val; if ( web.pressure > 0.00000001 ) { if ( !web.full_flag && !valid_id(web.outside_body) ) add_outside(); web.projection_flag = 0; web.pressure_flag = 1; if ( everything_quantities_flag ) FOR_ALL_BODIES(b_id) create_pressure_quant(b_id); } else { web.projection_flag = 1; web.pressure_flag = 0; } recalc(); break; case 'Q': /* quantities */ report_quantities(); break; case 'T': /* profiling times */ report_times(); break; case 'W': /* homothety toggle */ web.homothety = !web.homothety; sprintf(msg,"Homothety adjustment is %s.\n", web.homothety ? "ON" : "OFF"); outstring(msg); if ( web.homothety ) { sprintf(msg,"Enter target size (%g): ", (DOUBLE)homothety_target); prompt(msg,response,sizeof(response)); const_expr(response,&homothety_target); if ( logfd ) fprintf(logfd,"%f\n",(DOUBLE)homothety_target); } break; /* Surface modification */ case 'g' : /* one gradient descent iteration */ if ( breakflag ) break; iterate(); break; case 'j' : /* jiggling */ sprintf(msg,"Enter temperature for jiggling (default %f): ", (DOUBLE)web.temperature); prompt(msg,response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); if ( const_expr(response,&val) > 0 ) web.temperature = val; jiggle(); recalc(); break; case 'K' : /* skinny triangle long edge subdivide */ if ( web.representation == SIMPLEX ) { outstring( "Not implemented for simplex representation.\n"); break; } for (;;) { prompt("Enter minimum acute angle desired(h for histogram): ", response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); if ( tolower(response[0]) == 'h' ) skinny_histogram(); else break; } if ( const_expr(response,&val) > 0 ) { if ( web.counts_reported & facet_refine_count_bit ) web.facet_refine_count = 0; sprintf(msg,"Edges refined: %d\n", web.facet_refine_count += n = skinny(val)); web.counts_reported |= facet_refine_count_bit; outstring(msg); recalc(); } break; case 'l' : /* long edge subdivide */ for (;;) { prompt("Enter maximum edge length desired(h for histogram): ",response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); if ( tolower(response[0]) == 'h' ) edge_histogram(); else break; } if ( const_expr(response,&val) > 0 ) { web.max_len = val; if ( web.counts_reported & edge_refine_count_bit ) web.equi_count = 0; sprintf(msg,"Edges refined: %d\n", web.edge_refine_count += n = articulate(web.max_len)); outstring(msg); web.counts_reported |= edge_refine_count_bit; if ( n > 0 ) recalc(); } break; case 'N' : /* Normalize target volumes to current volumes */ FOR_ALL_BODIES(b_id) set_body_fixvol(b_id,get_body_volume(b_id)); outstring("Target volumes adjusted.\n"); break; case 'n' : /* notching ridges and valleys */ if ( web.representation == SIMPLEX ) { outstring( "Not implemented for simplex representation.\n"); break; } for (;;) { prompt("Enter maximum angle(radians) between normals(h for histogram): ",response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); if ( tolower(response[0]) == 'h' ) { if ( web.representation == STRING ) command("histogram(vertex,dihedral)",NO_HISTORY); else ridge_histogram(); } else break; } if ( const_expr(response,&val) > 0 ) { if ( web.representation == STRING ) { sprintf(msg, "refine edge ee where max(ee.vertex,dihedral) > %f", (DOUBLE)val); command(msg,NO_HISTORY); web.notch_count = web.where_count; } else { web.max_angle = val; if ( web.max_angle <= 0.0 ) break; if ( web.counts_reported & notch_count_bit ) web.equi_count = 0; sprintf(msg,"Number of edges notched: %d\n", web.notch_count += n = ridge_notcher(web.max_angle)); outstring(msg); web.counts_reported |= notch_count_bit; if ( n > 0 ) recalc(); } } break; case 'O' : /* pop nonminimal edges */ if ( web.representation == SIMPLEX ) { outstring( "Edge popping not implemented for simplex representation.\n"); break; } if ( web.representation == STRING ) { if ( web.counts_reported & vertex_pop_count_bit ) web.vertex_pop_count = 0; web.vertex_pop_count += n = verpop_str(); sprintf(msg,"Vertices popped: %d\n",web.vertex_pop_count); web.counts_reported |= vertex_pop_count_bit; } else { if ( web.counts_reported & edge_pop_count_bit ) web.edge_pop_count = 0; web.edge_pop_count = n = edgepop_film(); sprintf(msg,"Edges popped: %d\n",web.edge_pop_count); web.counts_reported |= edge_pop_count_bit; } if ( n > 0 ) recalc(); break; case 'o' : /* pop nonminimal edges and vertices */ if ( web.representation == SIMPLEX ) { outstring( "Vertex popping not implemented for simplex representation.\n"); break; } if ( web.representation == STRING ) web.vertex_pop_count = n = verpop_str(); else web.vertex_pop_count = n = popfilm(); if ( web.counts_reported & vertex_pop_count_bit ) web.edge_pop_count = 0; sprintf(msg,"Vertices popped: %d\n",web.vertex_pop_count); outstring(msg); web.counts_reported |= vertex_pop_count_bit; if ( n > 0 ) recalc(); break; case 'r' : refine(); energy_init = 0; memory_report(); web.min_area /= 4; web.max_len /= 2; web.min_length /= 2; recalc(); break; case 't' : /* tiny edge subdivide */ for (;;) { prompt("Enter minimum edge length desired(h for histogram): ",response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); if ( tolower(response[0]) == 'h' ) edge_histogram(); else break; } web.min_length = 0.0; if ( const_expr(response,&val) > 0 ) web.min_length = val; else break; if ( web.min_length <= 0.0 ) break; if ( web.counts_reported & edge_delete_count_bit ) web.edge_delete_count = 0; sprintf(msg,"Deleted edges: %d\n", web.edge_delete_count += n = edgeweed(web.min_length)); outstring(msg); web.counts_reported |= edge_delete_count_bit; if ( n > 0 ) recalc(); break; case 'U' : /* conjugate gradient */ old = conj_grad_flag; if ( conj_grad_flag ) { if ( cg_hvector ) myfree((char *)cg_hvector); cg_hvector = NULL; cg_oldsum = 0.0; conj_grad_flag = 0; outstring("Conjugate gradient now OFF."); if ( old ) outstring(" (was on)\n"); else outstring(" (was off)\n"); } else { conj_grad_flag = 1; outstring("Conjugate gradient now ON."); if ( old ) outstring(" (was on)\n"); else outstring(" (was off)\n"); reset_conj_grad(); if ( web.motion_flag ) kb_error(1639,"Fixed scale is ON! Probably not a good idea with conjugate gradient.\n",WARNING); } break; case 'u' : /* equiangulate */ if ( web.counts_reported & equi_count_bit ) web.equi_count = 0; sprintf(msg,"Edges switched in equiangulation: %d\n", web.equi_count += n = equiangulate() ); web.counts_reported |= equi_count_bit; outstring(msg); if ( n > 0 ) { recalc(); if ( web.torus_flag ) fix_volconst(); } break; case 'V' : /* move vertex to average of neighbors */ vertex_average(VOLKEEP); outstring("Vertex averaging done.\n"); recalc(); break; case 'w' : /* weed small triangles */ if ( web.representation == SIMPLEX ) { outstring( "Triangle weeding not implemented for simplex representation.\n"); break; } for (;;) { prompt("Enter minimum area desired(h for histogram): ", response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); if ( tolower(response[0]) == 'h' ) area_histogram(); else break; } web.min_area = 0.0; if ( const_expr(response,&val) > 0 ) web.min_area = val; else break; if ( web.min_area <= 0.0 ) break; if ( web.counts_reported & facet_delete_count_bit ) web.facet_delete_count = 0; sprintf(msg,"Skinny triangles weeded: %d\n", web.facet_delete_count += n = areaweed(web.min_area)); outstring(msg); web.counts_reported |= facet_delete_count_bit; if ( n > 0 ) recalc(); break; case 'y' : /* torus duplication */ if ( web.representation == SIMPLEX ) { outstring( "Torus duplication not implemented for simplex representation.\n"); break; } if ( ! web.torus_flag ) { outstring("Torus model not in effect.\n"); break; } if ( SDIM == 2 ) prompt("Duplicate which period(1,2)? ",response,sizeof(response)); else prompt("Duplicate which period(1,2,3)? ",response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); i = atoi(response); if ( i < 1 || i > SDIM ) outstring("Improper period number.\n"); else tordup(i-1); recalc(); break; case 'Z' : /* zooming in on vertex */ znum = loc_ordinal(web.zoom_v) + 1; sprintf(msg,"Enter zoom vertex number (%d): ",znum); prompt(msg,response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); sscanf(response,"%d",&znum); /* check vertex still exists */ { vertex_id v_id; int found = 0; FOR_ALL_VERTICES(v_id) { if ( znum == (loc_ordinal(v_id)+1) ) { web.zoom_v = v_id; found = 1; break; } } if ( !found ) { kb_error(1640,"Zoom vertex not found.\n",WARNING); break; } } sprintf(msg,"Enter cut-off radius (%f): ",(DOUBLE)web.zoom_radius); prompt(msg,response,sizeof(response)); if ( logfd ) fprintf(logfd,"%s\n",response); if ( const_expr(response,&val) > 0 ) web.zoom_radius = val; zoom_vertex(web.zoom_v,web.zoom_radius); recalc(); break; /* Graphical output */ case 'D' : old = go_display_flag; go_display_flag = !go_display_flag; if ( go_display_flag ) { outstring("Automatic display ON."); update_display(); } else outstring("Automatic display OFF."); if ( old ) outstring(" (was on)\n"); else outstring(" (was off)\n"); break; case 'd' : dump(); break; case 'P' : /* create graphics data file */ display_file(-1); break; case 's' : /* Show image */ if ( !go_display_flag && !dont_resize_flag ) resize(); go_display_flag = 1; do_show(); break; /* Process control */ case 'F' : /* log commands */ if ( logfd ) { fclose(logfd); logfd = NULL; outstring("Command logging OFF.\n"); break; } prompt("Name of log file: ",response,sizeof(response)); if ( response[0] == '\0' ) break; logfd = fopen(response,"w"); if ( logfd == NULL ) { perror(response); break; } break; case 'S' : /* save binary form */ outstring("Binary save not currently functional.\nUse 'd' Ascii dump.\n"); break; case 'R' : /* restore(NULL); */ outstring("R (restore) command discontinued.\n"); break; case 'x' : case 'q' : /* Exit */ if ( logfd ) { fclose(logfd); logfd = NULL; outstring("Command logging OFF.\n"); } if ( subshell_depth ) { exit_flag = 1; break; } #ifdef __cplusplus loadfilename[0] = 0; loadstub(); #else startup(NULL); #ifdef MPI_EVOLVER /* MPI_Barrier(MPI_COMM_WORLD); */ /* extra kludge needed */ #endif longjmp(jumpbuf[0],1); /* kludge, but current command list just got deallocated */ #endif break; case '?' : case 'H' : case 'h' : /* help screen */ main_help(); break; case ' ' : break; case '\t' : break; case '\b' : break; case '\r' : break; case '\n' : break; default : if ( c != '\0' ) { sprintf(msg,"Illegal command: %c. Type h for help.\n", c); outstring(msg); } break; } /* end switch */ } /* end letter_command() */ /******************************************************************** * * Function: extrapolate() * * Purpose: prints extrapolation of results to infinite resolution * */ void extrapolate() { int m; REAL d1,d2,ext; for ( m = 0 ; m <= reflevel ; m++ ) { #ifdef LONGDOUBLE sprintf(msg,"refinement: %1d energy: %*.*Lg ",m,DWIDTH,DPREC,extrap_val[m]); #else sprintf(msg,"refinement: %1d energy: %19.15f ",m,extrap_val[m]); #endif outstring(msg); if ( m > 1 ) /* can extrapolate */ { d1 = extrap_val[m-1] - extrap_val[m-2]; d2 = extrap_val[m] - extrap_val[m-1]; ext = extrap_val[m] - d2*d2/(d2 - d1); #ifdef LONGDOUBLE sprintf(msg,"extrapolation: %*.*Lg\n",DWIDTH,DPREC,ext); #else sprintf(msg,"extrapolation: %19.15f\n",ext); #endif outstring(msg); } else outstring("\n"); } } /***************************************************************** * * function: recalc_verts() * * purpose: recalculate vertex coordinates after boundaries or * constraints changed. * */ void recalc_verts() { vertex_id v_id; if ( threadflag ) thread_launch(TH_PROJECT_ALL_ACTUAL,VERTEX); else FOR_ALL_VERTICES(v_id) { if ( get_vattr(v_id) & BOUNDARY ) { struct boundary *boundary = get_boundary(v_id); REAL *param = get_param(v_id); REAL *x = get_coord(v_id); int j; for ( j = 0 ; j < SDIM ; j++ ) x[j] = eval(boundary->coordf[j],param,v_id,NULL); } if ( get_vattr(v_id) & CONSTRAINT ) project_v_constr(v_id,ACTUAL_MOVE,RESET_ONESIDEDNESS); } } /***************************************************************** * * function: information() * * purpose: prints information for 'i' command * */ void information() { REAL total_volume; body_id b_id; sprintf(msg,"Datafile: %s\n",datafilename); outstring(msg); if ( web.area_norm_flag ) { sprintf(msg,"Total time: %f\n",(DOUBLE)total_time); outstring(msg); } #ifdef LONGDOUBLE sprintf(msg,"Total energy: %*.*Lg\n",DWIDTH,DPREC,web.total_energy); #else sprintf(msg,"Total energy: %17.15g\n",web.total_energy); #endif outstring(msg); if ( web.spring_energy != 0.0 ) { #ifdef LONGDOUBLE sprintf(msg,"Gap energy: %*.*Lg\n",DWIDTH,DPREC,web.spring_energy); #else sprintf(msg,"Gap energy: %17.15g\n",web.spring_energy); #endif outstring(msg); } #ifdef LONGDOUBLE sprintf(msg,"Total %s: %*.*Lg\n",areaname,DWIDTH,DPREC,web.total_area); #else sprintf(msg,"Total %s: %17.15g\n",areaname,web.total_area); #endif outstring(msg); if ( web.conformal_flag ) { sprintf(msg,"Euclidean measure: %17.15f\n", (DOUBLE)euclidean_area); outstring(msg); } if ( web.modeltype == LAGRANGE ) { if ( bezier_flag ) sprintf(msg,"Lagrange order %d (Bezier basis polynomials)\n", web.lagrange_order); else sprintf(msg,"Lagrange order %d\n",web.lagrange_order); outstring(msg); } sprintf(msg,"Integral order 1D: %d 2D: %d\n",web.gauss1D_order, web.gauss2D_order); outstring(msg); memory_report(); if ( web.area_norm_flag ) { outstring("Area normalization ON."); if ( web.norm_check_flag ) { sprintf(msg," Max normal change %4.2f\n",(DOUBLE)web.norm_check_max); outstring(msg); } else outstring(" Normal change checking OFF.\n"); } if ( web.modeltype == LINEAR ) outstring("Representation: LINEAR\n"); else if ( web.modeltype == QUADRATIC ) outstring("Representation: QUADRATIC\n"); else if ( web.modeltype == LAGRANGE ) outstring("Representation: LAGRANGE\n"); if ( web.homothety ) { sprintf(msg,"Homothety ON, target size %g\n", (DOUBLE)homothety_target); outstring(msg); } if ( post_project_flag ) outstring("Post-projection ON.\n"); if ( conj_grad_flag ) outstring("Conjugate gradient ON.\n"); if ( web.motion_flag ) sprintf(msg,"Motion scale factor fixed at %g\n", (DOUBLE)web.scale); else sprintf(msg,"Motion scale factor optimizing at %g; upper bound %f\n", (DOUBLE)web.scale,(DOUBLE)web.maxscale); outstring(msg); if ( self_similar_flag ) { sprintf(msg,"Self similar motion is ON; coefficient %f\n,", (DOUBLE)globals(lookup_global(SELFSIM_NAME))->value.real); outstring(msg); } if ( web.diffusion_flag ) { sprintf(msg,"Diffusion is ON; diffusion constant: %f\n,", (DOUBLE)web.diffusion_const); outstring(msg); } if ( web.gravflag ) { sprintf(msg,"Gravity is ON; gravitational constant %f.\n", (DOUBLE)web.grav_const); outstring(msg); } if ( autochop_flag ) { sprintf(msg,"Autochopping is ON; cutoff length %g.\n", (DOUBLE)autochop_length); outstring(msg); } if ( autopop_flag ) outstring("Autopopping is ON.\n"); if ( normal_curvature_flag ) outstring("Normal_curvature is ON.\n"); if ( effective_area_flag ) outstring("Effective_area is ON.\n"); if ( mean_curv_int_flag ) { sprintf(msg,"Integral mean curvature is ON, modulus %g.\n", (DOUBLE)globals(square_curvature_param)->value.real); outstring(msg); } if ( square_curvature_flag ) { sprintf(msg,"Square curvature is ON, modulus %g.\n", (DOUBLE)globals(square_curvature_param)->value.real); outstring(msg); if ( kusner_flag ) outstring(" Kusner edge curvature mode.\n"); if ( conf_edge_curv_flag ) outstring(" Conformal edge curvature mode.\n"); } if ( boundary_curvature_flag ) outstring("Boundary curvature ON.\n"); if ( normal_motion_flag ) outstring("Normal motion ON.\n"); if ( web.jiggle_flag ) { sprintf(msg,"Jiggling is ON; temperature is %f.\n", (DOUBLE)web.temperature); outstring(msg); } if ( web.convex_flag ) { sprintf(msg,"Convexity gap constant is %f.\n", (DOUBLE)web.spring_constant); outstring(msg); } if ( web.pressure_flag ) { sprintf(msg,"Ambient pressure: %f\n",(DOUBLE)web.pressure); outstring(msg); } if ( klein_metric_flag ) outstring("Klein metric.\n"); total_volume = 0.0; FOR_ALL_BODIES(b_id) { if ( equal_id(b_id,web.outside_body) ) continue; total_volume += get_body_volume(b_id); } if ( web.meritfactor && (total_volume != 0.0) ) { sprintf(msg,"Area^3/volume^2 figure of merit: %17.15f\n\n", (DOUBLE)(web.meritfactor*pow(web.total_energy,3.0)/ total_volume/total_volume)); outstring(msg); } } /* end information() */ /***************************************************************** * * function: show_volumes() * * purpose: prints information for 'v' command * */ void show_volumes() { body_id b_id; int k; struct gen_quant *quan; int headerflag = 0; int recalc_flag = 0; if ( (web.bodycount == 0) && !gen_quant_count) { outstring("No bodies or quantities.\n"); return; } if ( (fixed_volume_timestamp < global_timestamp) || ( info_volume_timestamp < global_timestamp) ) recalc_flag = 1; for ( k=0 ; k < gen_quant_count; k++ ) { quan = GEN_QUANT(k); if ( (quan->flags & DEFAULT_QUANTITY) && !show_all_quantities ) continue; if (quan->flags & Q_DELETED ) continue; if ( !(quan->flags & (Q_FIXED|Q_INFO|Q_ENERGY)) ) continue; if ( (quan->timestamp < global_timestamp) || (quan->timestamp < global_timestamp) ) { #ifdef MPI_EVOLVER if ( this_task == MASTER_TASK ) #endif { outstring("recalculating...\n"); recalc_flag = 1; break; } } } if ( recalc_flag ) calc_content(Q_FIXED|Q_INFO|Q_ENERGY); if ( web.bodycount ) { outstring("Body target volume actual volume pressure\n"); FOR_ALL_BODIES(b_id) { if ( equal_id(b_id,web.outside_body) ) continue; #ifdef LONGDOUBLE if ( get_battr(b_id) & FIXEDVOL ) sprintf(msg,"%3s %*.*Lg %*.*Lg %*.*Lg\n", ELNAME(b_id),DWIDTH,DPREC,get_body_fixvol(b_id), DWIDTH,DPREC, get_body_volume(b_id),DWIDTH,DPREC,get_body_pressure(b_id)); else if ( get_battr(b_id) & PRESSURE ) sprintf(msg,"%3s ------------ %*.*Lg %*.*Lg\n", ELNAME(b_id), DWIDTH,DPREC,get_body_volume(b_id),DWIDTH,DPREC,get_body_pressure(b_id)); else /* not a constraint */ sprintf(msg,"%3s ------------ %*.*Lg \n", ELNAME(b_id), DWIDTH,DPREC,get_body_volume(b_id)); #else if ( get_battr(b_id) & FIXEDVOL ) sprintf(msg,"%3s %17.15g %17.15g %17.15g\n", ELNAME(b_id),get_body_fixvol(b_id), get_body_volume(b_id),get_body_pressure(b_id)); else if ( get_battr(b_id) & PRESSURE ) sprintf(msg,"%3s ------------ %17.15g %17.15g\n", ELNAME(b_id), get_body_volume(b_id),get_body_pressure(b_id)); else /* not a constraint */ sprintf(msg,"%3s ------------ %17.15g \n", ELNAME(b_id), get_body_volume(b_id)); #endif outstring(msg); } } if ( gen_quant_count ) { for ( k=0 ; k < gen_quant_count; k++ ) { quan = GEN_QUANT(k); if ( (quan->flags & DEFAULT_QUANTITY) && !show_all_quantities ) continue; if ( quan->flags & Q_DELETED ) continue; if ( !headerflag ) outstring( " Quantity target value actual value pressure\n"); headerflag = 1; #ifdef LONGDOUBLE if ( quan->flags & Q_CONSERVED ) sprintf(msg,"%20s (conserved) ----------------- %*.*Lg\n", quan->name, DWIDTH,DPREC,quan->pressure); else if ( quan->flags & Q_FIXED ) sprintf(msg,"%20s %*.*Lg %*.*Lg %*.*Lg\n", quan->name,DWIDTH,DPREC,quan->target,DWIDTH,DPREC, quan->value,DWIDTH,DPREC,quan->pressure); else sprintf(msg,"%20s --------- %*.*Lg\n", quan->name,DWIDTH,DPREC,quan->value); #else if ( quan->flags & Q_CONSERVED ) sprintf(msg,"%20s (conserved) ---------------- %17.15g\n", quan->name,quan->pressure); else if ( quan->flags & Q_FIXED ) sprintf(msg,"%20s %17.15g %17.15g %17.15g\n", quan->name,quan->target,quan->value,quan->pressure); else sprintf(msg,"%20s --------- %17.15g\n", quan->name,quan->value); #endif outstring(msg); } } } /* end show_volumes() */ /***************************************************************** * * function: set_parameters() * * purpose: lets user change parameters for 'A' command * * return: 1 if any changed, 0 if not. */ int set_parameters() { int n; REAL val; char response[100]; int pchange_flag = 0; int showcount = 0; outstring("Variables: \n"); for ( n = 0 ; n < web.global_count ; n++ ) { if ( globals(n)->flags & SUBROUTINE ) continue; if ( !globals(n)->flags ) continue; if ( globals(n)->flags & ORDINARY_PARAM ) { #ifdef LONGDOUBLE sprintf(msg,"%2d. %31.31s %-#*.*Lg\n", n+1,globals(n)->name,DWIDTH,DPREC,globals(n)->value.real); #else sprintf(msg,"%2d. %31.31s %-#21.15g\n", n+1,globals(n)->name,globals(n)->value.real); #endif if ( globals(n)->flags & OPTIMIZING_PARAMETER ) strcpy(msg+strlen(msg)-1," optimizing_parameter\n"); } else if ( globals(n)->flags & STRINGVAL ) sprintf(msg,"%2d. %31.31s %1.40s\n", n+1,globals(n)->name,globals(n)->value.string); else continue; outstring(msg); showcount++; } if ( web.perm_global_count ) { int titleflag = 0; for ( n = 0 ; n < web.perm_global_count ; n++ ) { if ( perm_globals(n)->flags & SUBROUTINE ) continue; if ( !perm_globals(n)->flags ) continue; if ( perm_globals(n)->flags & ORDINARY_PARAM ) { #ifdef LONGDOUBLE sprintf(msg,"%2d. %31.31s %-#*.*Lg\n", web.global_count + n+1,perm_globals(n)->name,DWIDTH,DPREC,perm_globals(n)->value.real); #else sprintf(msg,"%2d. %31.31s %-#21.15g\n", web.global_count + n+1,perm_globals(n)->name,perm_globals(n)->value.real); #endif if ( globals(n)->flags & OPTIMIZING_PARAMETER ) strcpy(msg+strlen(msg)-1," optimizing_parameter\n"); } else if ( perm_globals(n)->flags & STRINGVAL ) sprintf(msg,"%2d. %31.31s %1.40s\n", web.global_count + n+1,perm_globals(n)->name,perm_globals(n)->value.string); else continue; if ( !titleflag ) { outstring("Permanent variables: \n"); titleflag = 1; } outstring(msg); showcount++; } } if ( showcount == 0 ) outstring(" Nothing to report.\n"); else /* ask for new values */ for(;;) { int retval; char *c; prompt("Number and new value: ",response,sizeof(response)); retval = sscanf(response,"%d",&n); if ( retval <= 0 ) break; /* no more responses */ strtok(response," \t"); c = strtok(NULL,""); if ( const_expr(c,&val) > 0 ) { if ( (n > 0) && (n <= web.global_count) ) { if ( globals(n-1)->flags & ORDINARY_PARAM ) globals(n-1)->value.real = val; else outstring("Bad parameter number. \n"); if ( logfd ) #ifdef LONGDOUBLE fprintf(logfd,"%d %*.*Lg\n",n,DWIDTH,DPREC,val); #else fprintf(logfd,"%d %17.15g\n",n,val); #endif pchange_flag = 1; } else if ( (n >= web.global_count ) && (n <= web.global_count+web.perm_global_count) ) { n -= web.global_count; if ( globals(n-1)->flags & ORDINARY_PARAM ) globals(n-1)->value.real = val; else outstring("Bad parameter number. \n"); } else outstring("Bad parameter number."); } else outstring("Need number AND value.\n"); } if ( logfd ) fprintf(logfd,"\n"); return pchange_flag; } /* end set_parameters() */ /************************************************************************* * * function: report_quantities() * * purpose: Implements 'Q' command; lists quantities, their instances, etc. * Hidden quantities shown if show_all_quantities flag set. * */ void report_quantities() { struct gen_quant *q; int k,j; outstring("Quantities and instances:\n"); if ( everything_quantities_flag ) { if ( show_all_quantities ) outstring( "(showing internal quantities also; to suppress, do \"show_all_quantities off\")\n"); else outstring( "(not showing internal quantities; to show, do \"show_all_quantities\")\n"); } for ( k=0 ; k < gen_quant_count ; k++ ) { q = GEN_QUANT(k); if ( (q->flags & DEFAULT_QUANTITY) && !show_all_quantities ) continue; if ( q->flags & Q_DELETED ) continue; if ( !(q->flags & (Q_INFO|Q_ENERGY|Q_FIXED)) ) continue; if ( (q->timestamp < graph_timestamp) || (q->timestampname,DWIDTH,DPREC,q->value); #else sprintf(msg,"%2d. %-31.31s %#21.15g",k+1,q->name,q->value); #endif if ( q->flags & Q_FIXED ) strcat(msg," fixed quantity\n"); else if ( q->flags & Q_ENERGY ) strcat(msg," energy quantity\n"); else if ( q->flags & Q_CONSERVED ) strcat(msg," conserved quantity\n"); else strcat(msg," info_only quantity\n"); outstring(msg); if ( q->flags & Q_FIXED ) { #ifdef LONGDOUBLE sprintf(msg," %31.31s %#*.*Lg\n","target",DWIDTH,DPREC, q->target); #else sprintf(msg," %31.31s %#21.15g\n","target",q->target); #endif outstring(msg); } /* modulus */ #ifdef LONGDOUBLE sprintf(msg," %31.31s %#*.*Lg\n","modulus",DWIDTH, DPREC, q->modulus); #else sprintf(msg," %31.31s %#21.15g\n","modulus",q->modulus); #endif outstring(msg); /* volconst */ if ( q->volconst != 0.0 ) { #ifdef LONGDOUBLE sprintf(msg," %31.31s %#*.*Lg\n","volconst",DWIDTH, DPREC, q->modulus); #else sprintf(msg," %31.31s %#21.15g\n","volconst",q->volconst); #endif outstring(msg); } /* instances */ for ( j = 0 ; j < q->method_count ; j++ ) { struct method_instance *mi = METH_INSTANCE(q->meth_inst[j]); if ( !verbose_flag && (mi->flags & (IMPLICIT_INSTANCE|Q_DELETED)) ) continue; /* don't clutter up things */ /* name and value */ #ifdef LONGDOUBLE sprintf(msg," %-31.31s %#*.*Lg",mi->name,DWIDTH,DPREC,mi->value); #else sprintf(msg," %-31.31s %#21.15g",mi->name,mi->value); #endif strcat(msg," method instance\n"); outstring(msg); /* modulus */ #ifdef LONGDOUBLE sprintf(msg," %31.31s %#*.*Lg\n","modulus", DWIDTH, DPREC, mi->modulus); #else sprintf(msg," %31.31s %#21.15g\n","modulus",mi->modulus); #endif outstring(msg); #ifdef PROFILING_ENABLED /* timings */ if ( verbose_flag ) { sprintf(msg," %31.31s %#21.15f %10d calls\n", "value elapsed time, sec",mi->value_elapsed_time,mi->value_call_count); outstring(msg); sprintf(msg," %31.31s %#21.15f %10d calls\n", "gradient elapsed time, sec",mi->grad_elapsed_time,mi->grad_call_count); outstring(msg); sprintf(msg," %31.31s %#21.15f %10d calls\n", "hessian elapsed time, sec",mi->hess_elapsed_time,mi->hess_call_count); outstring(msg); } #endif } } } /************************************************************************** * * function: report_times() * * purpose: report profiling times on systems with that enabled. */ void report_times() { #ifdef PROFILING_ENABLED sprintf(msg,"Method instance element setup time: %15.10f\n", PROF_ELAPSED(element_setup)); outstring(msg); sprintf(msg,"Overall calc_quants time: %15.10f\n", PROF_ELAPSED(calc_quants)); outstring(msg); sprintf(msg,"Overall calc_quant_grads time: %15.10f\n", PROF_ELAPSED(calc_quant_grads)); outstring(msg); sprintf(msg,"Overall calc_quant_hess time: %15.10f\n", PROF_ELAPSED(calc_quant_hess)); outstring(msg); sprintf(msg,"Hessian solution time: %15.10f\n", PROF_ELAPSED(hessian_AIJ_setup) + PROF_ELAPSED(hessian_constraint_setup) + PROF_ELAPSED(hessian_project_setup) + PROF_ELAPSED(hessian_factor) + PROF_ELAPSED(hessian_CHinvC) + PROF_ELAPSED(hessian_solve) + PROF_ELAPSED(hessian_mul) ); outstring(msg); if ( verbose_flag ) { sprintf(msg," Hessian AIJ setup time: %15.10f\n", PROF_ELAPSED(hessian_AIJ_setup)); outstring(msg); sprintf(msg," Hessian constraint setup time: %15.10f\n", PROF_ELAPSED(hessian_constraint_setup)); outstring(msg); sprintf(msg," Hessian project setup time: %15.10f\n", PROF_ELAPSED(hessian_project_setup)); outstring(msg); sprintf(msg," Hessian factor time: %15.10f\n", PROF_ELAPSED(hessian_factor)); outstring(msg); sprintf(msg," Hessian CHinvC time: %15.10f\n", PROF_ELAPSED(hessian_CHinvC)); outstring(msg); sprintf(msg," Hessian solve time: %15.10f\n", PROF_ELAPSED(hessian_solve)); outstring(msg); sprintf(msg," Hessian multiplication time: %15.10f\n", PROF_ELAPSED(hessian_mul)); outstring(msg); } sprintf(msg,"exparse time: %15.10f\n", PROF_ELAPSED(exparse)); outstring(msg); sprintf(msg,"yyparse time: %15.10f\n", PROF_ELAPSED(yyparse)); outstring(msg); sprintf(msg,"yylex+kblex total time: %15.10f\n", PROF_ELAPSED(yylex)); outstring(msg); sprintf(msg,"kblex time: %15.10f\n", PROF_ELAPSED(kblex)); outstring(msg); sprintf(msg,"Using processor speed of %g Hz\n",cpu_speed); outstring(msg); #else outstring("Profiling not enabled on this system.\n"); #endif } evolver-2.30c.dfsg/src/fixvol.c0000644000175300017530000013657311410765113016707 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /****************************************************** * * File: fixvol.c * * Contents: Routines for projecting force on vertices * to perpendicular of all body volume gradients. * Does constrained quantities also. * */ #include "include.h" static int maxquants; /* total number of constraints */ REAL *vpressures = NULL; /* so global pressures not messed */ static int fixcount = 0; /* number of constrained quantities */ /* for numbering various types of constraints */ static int gen_quant_start; static int one_sided_start_fixcount; static struct linsys LS; /* for sparse constraints */ struct fixcount_list { vertex_id v_id; int connum; }; static struct fixcount_list *one_sided_fixcount_list; REAL **vgev = NULL; /* vector vol grads, for approx curvature */ REAL **vgef = NULL; /* form vol grads, for approx curvature */ /************************************************************************* * * Function: calc_volgrads() * * purpose: wrapper for local_calc_volgrads() * */ void calc_volgrads(mode) int mode; { if ( itdebug) outstring("Calculating volgrads.\n"); #ifdef MPI_EVOLVER mpi_calc_volgrads(mode); #else local_calc_volgrads(mode); #endif #ifdef _DEBUGXXX if ( itdebug ) /* dump vgrads */ { vertex_id v_id; FOR_ALL_VERTICES(v_id) { volgrad *vgptr; vgptr = get_vertex_vgrad(v_id); while ( vgptr ) { printf("v %3s b %3s grad %f %f %f\n",ELNAME(v_id), ELNAME1(vgptr->bb_id),vgptr->grad[0],vgptr->grad[1],vgptr->grad[2]); vgptr = vgptr->chain; } } } #endif } /************************************************************************* * * Function: local_calc_volgrads() * * purpose: calculate gradients of constrained volumes and quantities. * Leaves them in vgptr... array. * * Also does variable_parameter constraint gradients. */ void local_calc_volgrads(mode) int mode; /* DO_OPTS or NO_OPTS */ { body_id bi_id; /* identifier for body i */ vertex_id v_id; int i,k,n; struct boundary *bdry; int qfixed = 0; struct gen_quant *gq; MAT2D(a,MAXCOORD,MAXCOORD); /* for numbering various types of constraints */ gen_quant_start = web.skel[BODY].max_ord + 1; /* see if anything needs to be done */ fixed_constraint_flag = 0; if ( !web.pressure_flag && !everything_quantities_flag ) FOR_ALL_BODIES(bi_id) if ( get_battr(bi_id) & (FIXEDVOL|PRESSURE) ) fixed_constraint_flag = 1; for ( k = n = 0 ; n < gen_quant_count ; n++ ) { gq = GEN_QUANT(n); if ( gq->flags & (Q_FIXED|Q_CONSERVED) ) { if ( !valid_id(gq->b_id) || !web.pressure_flag ) { fixed_constraint_flag = 1; qfixed++; gq->vol_number = gen_quant_start + k++; } else if ( web.pressure_flag ) gq->vol_number = gen_quant_start + k++; } } maxquants = gen_quant_start + k; if ( !web.pressure_flag && !fixed_constraint_flag && !one_sided_present ) return; /* allocate space to hold vertex body volume gradients */ vgrad_init(qfixed); /* calculate body volume gradients at all control points due to free surfaces */ if ( !quantities_only_flag ) { if ( web.representation == SIMPLEX ) simplex_grad_l(); else if ( web.representation == STRING ) (*string_grad)(); else /* web.representation == SOAPFILM */ (*film_grad)(); } calc_quant_grads(Q_FIXED|Q_CONSERVED); // calc_one_sided_grads(); /* no, doing one-sided lagranges after others */ /* calculate optimizing_parameter gradients by finite differences */ if ( (mode == DO_OPTS) && (optparamcount > 0) ) { REAL **convalues; struct oldcoord osaved; if ( optparam_congrads ) free_matrix(optparam_congrads); optparam_congrads = dmatrix(0,optparamcount,0,maxquants); convalues = dmatrix(0,maxquants,0,2); osaved.coord = NULL; save_coords(&osaved,SAVE_SEPARATE); /* save values */ if ( !web.pressure_flag && !everything_quantities_flag ) FOR_ALL_BODIES(bi_id) if ( get_battr(bi_id) & FIXEDVOL ) convalues[loc_ordinal(bi_id)][0] = get_body_volume(bi_id); for ( k = n = 0 ; n < gen_quant_count ; n++ ) { gq = GEN_QUANT(n); if ( gq->flags & Q_FIXED ) if ( !valid_id(gq->b_id) || !web.pressure_flag ) convalues[gq->vol_number][0] = gq->value; } for ( i = 0 ; i < optparamcount ; i++ ) { REAL dp; dp = globals(optparam[i].pnum)->attr.varstuff.delta; /* right difference */ globals(optparam[i].pnum)->value.real += dp; project_all(0, TEST_MOVE); calc_content(Q_FIXED); /* save values */ if (!web.pressure_flag && !everything_quantities_flag ) FOR_ALL_BODIES(bi_id) if ( get_battr(bi_id) & FIXEDVOL ) convalues[loc_ordinal(bi_id)][1] = get_body_volume(bi_id); for ( k = n = 0 ; n < gen_quant_count ; n++ ) { gq = GEN_QUANT(n); if ( gq->flags & Q_FIXED ) if ( !valid_id(gq->b_id) || !web.pressure_flag ) convalues[gq->vol_number][1] = gq->value; } restore_coords(&osaved,SAVE_SEPARATE); /* also fixes opt params */ /* left difference */ globals(optparam[i].pnum)->value.real -= dp; project_all(0, TEST_MOVE); if ( fixed_constraint_flag || web.pressure_flag || web.pressflag ) calc_content(Q_FIXED); /* save values */ if (!web.pressure_flag && !everything_quantities_flag ) FOR_ALL_BODIES(bi_id) if ( get_battr(bi_id) & FIXEDVOL ) convalues[loc_ordinal(bi_id)][2] = get_body_volume(bi_id); for ( k = n = 0 ; n < gen_quant_count ; n++ ) { gq = GEN_QUANT(n); if ( gq->flags & Q_FIXED ) if ( !valid_id(gq->b_id) || !web.pressure_flag ) convalues[gq->vol_number][2] = gq->value; } restore_coords(&osaved,SAVE_SEPARATE); /* also fixes opt params */ /* calculate gradients */ if (!web.pressure_flag && !everything_quantities_flag ) FOR_ALL_BODIES(bi_id) if ( get_battr(bi_id) & FIXEDVOL ) optparam_congrads[i][loc_ordinal(bi_id)] = (convalues[loc_ordinal(bi_id)][1] - convalues[loc_ordinal(bi_id)][2])/2/dp; for ( k = n = 0 ; n < gen_quant_count ; n++ ) { gq = GEN_QUANT(n); if ( gq->flags & Q_FIXED ) if ( !valid_id(gq->b_id) || !web.pressure_flag ) optparam_congrads[i][gq->vol_number] = (convalues[gq->vol_number][1]-convalues[gq->vol_number][2])/2/dp; } } /* restore values */ if (!web.pressure_flag && !everything_quantities_flag ) FOR_ALL_BODIES(bi_id) if ( get_battr(bi_id) & FIXEDVOL ) set_body_volume(bi_id, convalues[loc_ordinal(bi_id)][0],SETSTAMP); for ( k = n = 0 ; n < gen_quant_count ; n++ ) { gq = GEN_QUANT(n); if ( gq->flags & Q_FIXED ) if ( !valid_id(gq->b_id) || !web.pressure_flag ) gq->value = convalues[gq->vol_number][2]; } free_matrix(convalues); unsave_coords(&osaved,SAVE_SEPARATE); } /* end optimizing parameters */ /* add on gradients due to constraint integrals */ if ( !quantities_only_flag ) { if ( web.representation == STRING ) { string_constr_grad(); film_constr_grad(); /* for quantities */ } else /* web.representation == SOAPFILM */ { film_constr_grad(); } } /* project to parameter space for boundary points */ FOR_ALL_VERTICES(v_id) { REAL dummy; REAL temp[MAXCOORD]; int pcount,j; volgrad *vgptri; if ( get_vattr(v_id) & FIXED ) continue; if ( !(get_vattr(v_id) & BOUNDARY) ) continue; bdry = get_boundary(v_id); pcount = bdry->pcount; for ( j = 0 ; j < SDIM ; j++ ) { eval_all(bdry->coordf[j],get_param(v_id),pcount,&dummy,temp,v_id); for ( i = 0 ; i < pcount ; i++ ) a[i][j] = temp[i]; } vgptri = get_vertex_vgrad(v_id); while ( vgptri ) { REAL tmp[MAXCOORD]; int m; matvec_mul(a,vgptri->grad,tmp,pcount,SDIM); for ( m = 0 ; m < pcount ; m++ ) vgptri->grad[m] = tmp[m]; for ( m = pcount ; m < SDIM ; m++ ) vgptri->grad[m] = 0.0; vgptri = vgptri->chain; } } } /* end local_calc_volgrads() */ /************************************************************************* * * function: calc_one_sided_grads() * * purpose: calculate one-sided constraint gradients for vertices * hitting constraints, so can calculate Lagrange multipliers * so can see if they want to leave constraint. */ void calc_one_sided_grads() { vertex_id v_id; int list_alloc = 2*web.skel[VERTEX].count; int list_spot = 0; if ( itdebug ) outstring("calc_one_sided_grads() - adding onesided constraints to constraint matrix\n"); one_sided_start_fixcount = fixcount; one_sided_fixcount_list = (struct fixcount_list *) temp_calloc(list_alloc,sizeof(struct fixcount_list)); FOR_ALL_VERTICES(v_id) { conmap_t *conmap; int j; if ( get_vattr(v_id) & FIXED ) continue; conmap = get_v_constraint_map(v_id); for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { struct constraint *thiscon; thiscon = get_constraint(conmap[j]); if ( (thiscon->attr & (NONNEGATIVE|NONPOSITIVE)) && (conmap[j] & CON_HIT_BIT) ) { /* need this one */ REAL fval; REAL *x = get_coord(v_id); volgrad *vgptr; if ( list_spot >= list_alloc ) { list_alloc *= 2; one_sided_fixcount_list = (struct fixcount_list *) realloc((char*)one_sided_fixcount_list, list_alloc*sizeof(struct fixcount_list)); } one_sided_fixcount_list[list_spot].v_id = v_id; one_sided_fixcount_list[list_spot].connum = conmap[j] & CONMASK; list_spot++; vgptr = get_bv_new_vgrad(fixcount,v_id); vgptr->qnum = conmap[j] & CONMASK; vgptr->bb_id = v_id; vgptr->fixnum = fixcount; eval_all(thiscon->formula,x,SDIM,&fval,vgptr->grad,v_id); fixcount++; } } /* end j loop */ } /* end all vertices */ /* free extra space */ one_sided_fixcount_list = (struct fixcount_list *) temp_realloc((char*)one_sided_fixcount_list, list_spot*sizeof(struct fixcount_list)); maxquants = fixcount; } /* end calc_one_sided_grads() */ /********************************************************************** * * function: pressure_forces() * * purpose: add forces due to dynamic pressure */ void pressure_forces() { vertex_id v_id; int k; body_id b_id; volgrad *vgptr; int to_do = 0; if ( everything_quantities_flag ) return; if ( itdebug ) outstring("pressure_forces(): adding forces due to prescribed pressure.\n"); if ( web.pressure_flag ) { /* add forces due to dynamic pressure */ FOR_ALL_VERTICES(v_id) { REAL *f; if ( get_vattr(v_id) & FIXED ) continue; f = get_force(v_id); for ( vgptr = get_vertex_vgrad(v_id) ; vgptr ; vgptr = vgptr->chain ) { if ( valid_id(vgptr->bb_id) ) if ( (get_battr(vgptr->bb_id) & FIXEDVOL) ) { REAL p = get_body_pressure(vgptr->bb_id); for ( k = 0 ; k < SDIM ; k++ ) f[k] += (p - web.pressure)*vgptr->grad[k]; } } } return; } /* add prescribed pressure forces */ /* first, see if there are any */ FOR_ALL_BODIES(b_id) if ( get_battr(b_id) & PRESSURE ) to_do = 1; if ( to_do == 0 ) return; FOR_ALL_VERTICES(v_id) { REAL *f; if ( get_vattr(v_id) & FIXED ) continue; f = get_force(v_id); for ( vgptr = get_vertex_vgrad(v_id) ; vgptr ; vgptr = vgptr->chain ) { if ( valid_id(vgptr->bb_id) ) /* check for real bodies */ if ( get_battr(vgptr->bb_id) & PRESSURE ) for ( k = 0 ; k < SDIM ; k++ ) f[k] += get_body_pressure(vgptr->bb_id)*vgptr->grad[k]; } } } /* end pressure_forces() */ /************************************************************************ * * function: one_sided_adjust() * * purpose: see which vertices need to have velocity and vgrads * restricted by one-sided constraints. Uses old * Lagrange multipliers to calculate force, then sees * which constraints violated, and projects tangent * to violated constraints and regular constraints. */ void one_sided_adjust(mode) int mode; /* CALC_FORCE and/or CALC_VOLGRADS */ { vertex_id v_id; int i,j,k; REAL vel[MAXCOORD]; int flag; /* see if we need to do anything */ if ( !fixed_constraint_flag ) return; for ( i = 0, flag = 0 ; i < web.maxcon ; i++ ) { struct constraint *con = get_constraint(i); if ( con->attr & (NONNEGATIVE|NONPOSITIVE) ) { flag = 1; break; } } if ( flag == 0 ) return; if ( itdebug ) outstring("one_sided_adjust(): see which grads violate 1-sided constraints \n"); if ( !pressure_set_flag ) calc_lagrange(); /* for first time only */ FOR_ALL_VERTICES(v_id) { volgrad *vgptr; int ord = loc_ordinal(v_id); REAL *f; conmap_t * conmap = get_v_constraint_map(v_id); int oncount = 0; struct constraint *con[MAXCONPER]; REAL *x; REAL perp[MAXCOORD]; REAL fval; REAL grad[MAXCOORD]; REAL fp; if ( get_vattr(v_id) & FIXED ) continue; if ( !(get_vattr(v_id) & CONSTRAINT) ) continue; x = get_coord(v_id); f = get_velocity(v_id); for ( i = 0 ; i < SDIM ; i++ ) vel[i] = f[i]; for ( vgptr = get_vertex_vgrad(v_id) ; vgptr ; vgptr = vgptr->chain ) { REAL p; /* Lagrange multiplier */ if ( valid_id(vgptr->bb_id) ) p = get_body_pressure(vgptr->bb_id); else /* for quantities */ p = GEN_QUANT(vgptr->qnum)->pressure; if ( approx_curve_flag ) for ( k = 0 ; k < SDIM ; k++ ) vel[k] += p*vgev[vgptr->fixnum][SDIM*ord + k]; else for ( k = 0 ; k < SDIM ; k++ ) vel[k] += p*vgptr->velocity[k]; /* Note: positive signs since pressure is negative of Lagrange multiplier */ } for ( j = 1,oncount = 0 ; j <= (int)conmap[0] ; j++ ) { if ( conmap[j] & CON_HIT_BIT ) { struct constraint *cc = get_constraint(conmap[j]); if ( cc->attr & (NONNEGATIVE | NONPOSITIVE) ) { /* check for violation */ eval_all(cc->formula,x,SDIM,&fval,grad,v_id); fp = SDIM_dot(vel,grad); if ( (cc->attr & NONNEGATIVE) && (fp < 0.0) ) con[oncount++] = cc; else if ( (cc->attr & NONPOSITIVE) && (fp > 0.0) ) con[oncount++] = cc; } } } if ( mode & CALC_VOLGRADS ) for ( vgptr = get_vertex_vgrad(v_id) ; vgptr ; vgptr = vgptr->chain ) { constr_proj(TANGPROJ,oncount,con,x,vgptr->velocity,perp,NULL,NO_DETECT,v_id); for ( j = 0 ; j < SDIM ; j++ ) vgptr->velocity[j] -= perp[j]; } if ( mode & CALC_FORCE ) { constr_proj(TANGPROJ,oncount,con,x,f,perp,NULL,NO_DETECT,v_id); for ( j = 0 ; j < SDIM ; j++ ) f[j] -= perp[j]; } } } /* one_sided_adjust() */ /*************************************************************************** * * function: find_fixed() * * purpose: find which bodies and quantities are fixed and assign them * their fixnum. Sets file static variable fixcount, and returns * fixcount. */ int find_fixed() { int i,k; struct gen_quant *q; int redundant_bi = -1; /* for torus_filled */ body_id b_id; if ( itdebug ) outstring("find_fixed(): count fixed bodies and constraints\n"); /* figure out which quantities are fixed */ fixcount = 0; if ( !everything_quantities_flag ) MFOR_ALL_BODIES(b_id) { if ( get_battr(b_id) & (FIXEDVOL/*|PRESSURE*/) ) { if ( web.full_flag && (redundant_bi < 0) ) { set_body_fixnum(b_id,-1); redundant_bi = 1; } else { set_body_fixnum(b_id,fixcount); fixcount++; } } else set_body_fixnum(b_id,-1); } for ( i = 0,k=0 ; i < gen_quant_count ; i++ ) { q = GEN_QUANT(i); if ( q->flags & (Q_FIXED|Q_CONSERVED) ) { if ( web.full_flag && valid_id(q->b_id) && redundant_bi < 0 ) { q->fixnum = -1; redundant_bi = 1; } else { q->fixnum = fixcount++; k++; } } else q->fixnum = -1; } gen_quant_start = web.skel[BODY].max_ord + 1; maxquants = gen_quant_start + k; fixed_constraint_flag = fixcount; return fixcount; } /************************************************************************** * * function: calc_leftside() * * purpose: set up DV^T DV */ static int degfree; /* for check there is sufficient degrees of freedom */ static int calc_leftside_hash_count; /* for estimating need */ void calc_leftside() { int i,j,k; int bi,bj; if ( itdebug ) outstring("calc_leftside(): for finding Lagrange multipliers\n"); degfree = 0; /* set up matrices for DV^T DV */ if ( sparse_constraints_flag ) { memset(&LS,0,sizeof(LS)); sp_hash_init(&LS,calc_leftside_hash_count); } else { rleftside = dmatrix(0,fixcount,0,fixcount); } #ifdef MPI_EVOLVER if ( sparse_constraints_flag ) kb_error(3381,"Cannot do sparse constraints yet in MPI.\n",RECOVERABLE); mpi_calc_leftside(fixcount); #else local_calc_leftside(); #endif if ( approx_curve_flag ) { int NV = SDIM*(1+web.skel[VERTEX].max_ord); /* dot products */ for ( bi = 0 ; bi <= maxquants ; bi++ ) { int fixi = GEN_QUANT(bi)->fixnum; if ( fixi < 0 ) continue; /* self product */ if ( sparse_constraints_flag ) { sp_hash_search(&LS,fixi,fixi,dot(vgev[bi],vgef[bi],NV)); } else rleftside[fixi][fixi] += dot(vgev[fixi],vgef[fixi],NV); for ( bj = bi+1 ; bj <= maxquants ; bj++ ) /* other products */ { int fixj = GEN_QUANT(bj)->fixnum; REAL tmp; if ( fixj < 0 ) continue; tmp = dot(vgev[bi],vgef[bj],NV); if ( sparse_constraints_flag ) { if ( fixi < fixj ) sp_hash_search(&LS,fixi,fixj,tmp); else sp_hash_search(&LS,fixj,fixi,tmp); } else { rleftside[fixi][fixj] += tmp; rleftside[fixj][fixi] += tmp; } } } degfree += NV; } /* end approx_curve_flag */ /* variable_parameter contributions */ if ( optparamcount ) for ( i = 0 ; i < gen_quant_count ; i++ ) { int fixi = GEN_QUANT(i)->fixnum; int voli = GEN_QUANT(i)->vol_number; if ( fixi < 0 ) continue; for ( j = 0 ; j < gen_quant_count ; j++ ) { int fixj = GEN_QUANT(j)->fixnum; int volj = GEN_QUANT(j)->vol_number; if ( fixj < 0 ) continue; for ( k = 0 ; k < optparamcount ; k++ ) { REAL tmp = optparam_congrads[k][voli] *globals(optparam[k].pnum)->attr.varstuff.pscale*optparam_congrads[k][volj]; if ( sparse_constraints_flag ) { if ( fixi < fixj ) sp_hash_search(&LS,fixi,fixj,tmp); else if ( fixi == fixj ) { sp_hash_search(&LS,fixi,fixi,tmp); } } else rleftside[fixi][fixj] += tmp; } } } degfree += optparamcount; #ifndef MPI_EVOLVER if ( degfree < fixcount ) { sprintf(errmsg, "Degrees of freedom, %d, is less than number of constraints, %d\n", degfree,fixcount); if ( degfree == 0 ) strcat(errmsg,"Perhaps constraint is not applied to any elements?\n"); kb_error(3004,errmsg,RECOVERABLE); } #endif /* solve for coefficients */ if ( sparse_constraints_flag ) { REAL old_hessian_epsilon = hessian_epsilon; calc_leftside_hash_count = sp_hash_end(&LS,fixcount,fixcount,A_OFF); LS.N = fixcount; hessian_epsilon = 0.0; ysmp_factor(&LS); /* since mindeg uses vertex info at the moment */ hessian_epsilon = old_hessian_epsilon; } else { for ( k = 0 ; k < fixcount ; k++ ) if ( rleftside[k][k] == 0.0 ) rleftside[k][k] = 1.0; /* for invertibility */ LD_factor(rleftside,fixcount); } } /* end calc_leftside() */ /************************************************************************* * * function: local_calc_leftside() * * purpose: Do local dot products of constraint gradients */ void local_calc_leftside() { vertex_id v_id; int bi,bj; /* generate DV^T DV */ if ( !approx_curve_flag ) FOR_ALL_VERTICES(v_id) { volgrad *vgptri,*vgptrj; ATTR attr = get_vattr(v_id); if ( attr & FIXED ) continue; for ( vgptri = get_vertex_vgrad(v_id); vgptri ; vgptri = vgptri->chain ) { REAL tmp; bi = vgptri->fixnum; if ( (bi < 0) || (bi >= fixcount) ) continue; degfree++; tmp = SDIM_dot(vgptri->velocity,vgptri->grad); /* if ( !(attr & HIT_WALL) ) */ { if ( sparse_constraints_flag ) { sp_hash_search(&LS,bi,bi,tmp); } else rleftside[bi][bi] += tmp; } for ( vgptrj = vgptri->chain ; vgptrj ; vgptrj = vgptrj->chain ) { tmp = SDIM_dot(vgptri->grad,vgptrj->velocity); bj = vgptrj->fixnum; if ( (bj < 0) || (bj >= fixcount) ) continue; /* if ( !(attr & HIT_WALL) ) */ { if ( sparse_constraints_flag ) { if ( bi < bj ) sp_hash_search(&LS,bi,bj,tmp); else sp_hash_search(&LS,bj,bi,tmp); } else { rleftside[bi][bj] += tmp; rleftside[bj][bi] += tmp; } } } } } } /************************************************************************ * * function: calc_lagrange() * * purpose: calculate Lagrange multipliers * */ void calc_lagrange() { int i,k; body_id b_id; struct gen_quant *gq; if ( fixcount == 0 ) return; if ( itdebug ) outstring("calc_lagrange(): find Lagrange multipliers\n"); calc_leftside(); /* set up rleftside in factored form */ rightside = (REAL*)temp_calloc(maxquants,sizeof(REAL)); vpressures = (REAL*)temp_calloc(maxquants,sizeof(REAL)); #ifdef MPI_EVOLVER mpi_calc_rightside(maxquants); #else local_calc_rightside(); #endif /* optimizing_parameter contributions */ if ( optparamcount ) for ( i = 0 ; i < gen_quant_count ; i++ ) { int fixi = GEN_QUANT(i)->fixnum; int voli = GEN_QUANT(i)->vol_number; if ( fixi < 0 ) continue; for ( k = 0 ; k < optparamcount ; k++ ) rightside[fixi] -= optparam_congrads[k][voli]*optparam[k].velocity; /* negative since using grad, not force */ } /* solve for coefficients */ if ( sparse_constraints_flag ) {ysmp_solve(&LS,rightside,vpressures); free_system(&LS); } else LD_solve(rleftside,rightside,vpressures,fixcount); /* check for singular matrix */ for ( i = 0 ; i < fixcount ; i++ ) { if ( !is_finite(vpressures[i]) ) { #ifdef used_calc_one_sided_grads if ( i >= one_sided_start_fixcount ) { int j = i-one_sided_start_fixcount; element_id v_id = one_sided_fixcount_list[j].v_id; int connum = one_sided_fixcount_list[j].connum; struct constraint *con = get_constraint(connum); sprintf(errmsg,"Singular one-sided constraint matrix, vertex %s, constraint %s.\n", ELNAME(v_id),con->name); } else #endif sprintf(errmsg,"Constraint adjustment matrix singular. \nMore constraints than degrees of freedom?\nConstraints with no elements?"); kb_error(3005,errmsg,RECOVERABLE); } } /* install pressures into body structures */ if ( !web.pressure_flag ) MFOR_ALL_BODIES(b_id) { if ( get_battr(b_id) & FIXEDVOL ) { if ( everything_quantities_flag ) { int fixi; gq = GEN_QUANT(get_body_volquant(b_id)); fixi = gq->fixnum; set_body_pressure(b_id,(fixi<0)?0.0 : -vpressures[fixi]); } else { int fixi = get_body_fixnum(b_id); set_body_pressure(b_id,(fixi<0)?0.0:-vpressures[fixi]); } } } for ( k = 0 ; k < gen_quant_count ; k++ ) { gq = GEN_QUANT(k); if ( gq->flags & (Q_FIXED|Q_CONSERVED) ) if ( !valid_id(gq->b_id) || !web.pressure_flag ) gq->pressure = -vpressures[gq->fixnum]; } #ifdef ZZZZZZZZ /* Check for loosening, and record */ /* one-sided constraint lagrange multipliers, if user wants */ { int one_sided_lagrange_attr = find_attribute(VERTEX,ONE_SIDED_LAGRANGE_ATTR_NAME); volgrad *vgptri; for ( k = one_sided_start_fixcount, j = 0 ; k < fixcount ; k++,j++ ) { element_id v_id = one_sided_fixcount_list[j].v_id; int connum = one_sided_fixcount_list[j].connum; struct constraint *con = get_constraint(connum); if ( ((con->attr & NONNEGATIVE) && (vpressures[k] > 0)) || ((con->attr & NONPOSITIVE) && (vpressures[k] < 0)) ) { unset_v_constraint_status(v_id,connum); /* also have to kill vgrad so velocity not chopped */ for ( vgptri = get_vertex_vgrad(v_id); vgptri ; vgptri = vgptri->chain ) if ( vgptri->fixnum == k ) vgptri->fixnum = -1; } if ( one_sided_lagrange_attr >= 0 ) { REAL *v = (REAL*)get_extra(v_id,one_sided_lagrange_attr); for ( i = 0 ; i < osl_size ; i++ ) if ( v[i] == 0.0 ) { v[i] = vpressures[k]; break; } } } } #endif } /* end calc_lagrange() */ /************************************************************************** * * function: local_calc_rightside() * * purpose: local part of calculation of Lagrange multiplier right side. */ void local_calc_rightside() { int bi; vertex_id v_id; if ( itdebug ) outstring("calc_rightside(): for finding Lagrange multipliers\n"); /* generate right side of matrix equation */ if ( !approx_curve_flag ) FOR_ALL_VERTICES(v_id) { volgrad *vgptri; ATTR attr = get_vattr(v_id); REAL *f; if ( attr & FIXED ) continue; f = get_velocity(v_id); for ( vgptri = get_vertex_vgrad(v_id); vgptri ; vgptri = vgptri->chain ) { bi = vgptri->fixnum; if ( (bi < 0) || (bi >= fixcount) ) continue; if (valid_id(vgptri->bb_id) && !web.pressure_flag && !everything_quantities_flag ) { if ( (id_type(vgptri->bb_id) == BODY) && !(get_battr(vgptri->bb_id)&FIXEDVOL) ) continue; } rightside[bi] += SDIM_dot(f,vgptri->grad); } } if ( approx_curve_flag ) { REAL *f; /* calculate right side */ FOR_ALL_VERTICES(v_id) { volgrad *vgptri; ATTR attr = get_vattr(v_id); if ( attr & FIXED ) continue; f = get_velocity(v_id); for ( vgptri=get_vertex_vgrad(v_id); vgptri ; vgptri = vgptri->chain ) { bi = vgptri->fixnum; if ( (bi < 0) || (bi >= fixcount) ) continue; if (valid_id(vgptri->bb_id) &&!web.pressure_flag && !everything_quantities_flag ) { if ( !(get_battr(vgptri->bb_id)&FIXEDVOL) ) continue; } rightside[bi] += SDIM_dot(f,&vgef[bi][SDIM*loc_ordinal(v_id)]); } } } /* end approx_curve_flag */ } /************************************************************************* * * function: lagrange_adjust() * * purpose: wrapper for local_lagrange_adjust() */ void lagrange_adjust() { int i,k; if ( !fixed_constraint_flag ) return; if ( itdebug ) outstring("lagrange_adjust(): subtract multiples of volume gradients from force\n"); #ifdef MPI_EVOLVER mpi_lagrange_adjust(maxquants); #else local_lagrange_adjust(); #endif /* optimizing parameter adjust */ if ( optparamcount ) for ( i = 0 ; i < optparamcount ; i++ ) for ( k = 0 ; k < gen_quant_count ; k++ ) { int fixi = GEN_QUANT(k)->fixnum; int volk = GEN_QUANT(k)->vol_number; if ( fixi >= 0 ) optparam[i].velocity += vpressures[fixi]* globals(optparam[i].pnum)->attr.varstuff.pscale* optparam_congrads[i][volk]; } } /* end lagrange_adjust() */ /********************************************************************* * * function: local_lagrange_adjust() * * purpose: adjust forces by Lagrange multipliers of constraint grads. * * return value: number of vertices loosened from one-sided constraints. * */ int local_lagrange_adjust() { vertex_id v_id; int k; int loosened = 0; one_sided_lagrange_attr = find_attribute(VERTEX,ONE_SIDED_LAGRANGE_ATTR_NAME); /* clear one_sided_lagrange */ if ( one_sided_lagrange_attr >= 0 ) { struct extra *ex = EXTRAS(VERTEX)+one_sided_lagrange_attr; vertex_id v_id; int i; int osl_size = ex->array_spec.datacount; FOR_ALL_VERTICES(v_id) { REAL *v = (REAL*)get_extra(v_id,one_sided_lagrange_attr); for ( i = 0 ; i < osl_size ; i++ ) v[i] = 0.0; } } /* subtract multiples of volume gradients from force */ FOR_ALL_VERTICES(v_id) { volgrad *vgptr; int ord = loc_ordinal(v_id); REAL *f; int bi; /* row number for constraint */ if ( get_vattr(v_id) & FIXED ) continue; f = get_velocity(v_id); for ( vgptr = get_vertex_vgrad(v_id) ; vgptr ; vgptr = vgptr->chain ) { bi = vgptr->fixnum; if ( (bi < 0) || (bi >= fixcount) ) continue; /* bi = -1 for killed one-sided constraints */ if ( approx_curve_flag ) for ( k = 0 ; k < SDIM ; k++ ) f[k] -= vpressures[bi]*vgev[bi][SDIM*ord +k]; else for ( k = 0 ; k < SDIM ; k++ ) f[k] -= vpressures[bi]*vgptr->velocity[k]; } if ( one_sided_present ) loosened += one_sided_lagrange_adjust(v_id); } return loosened; } /* end local_lagrange_adjust() */ /****************************************************************** * * function: one_sided_lagrange_adjust() * * purpose; Find Lagrange multipliers for one-sided constraints * for a vertex. Callde from local_lagrange_adjust(). * * return value; 1 if vertex loosened from a one-sided constraint. */ int one_sided_lagrange_adjust(v_id) vertex_id v_id; { REAL *raw_velocity = VREAL(v_id,raw_velocity_attr); volgrad *vgptr; int bi; /* row number for constraint */ conmap_t * conmap = get_v_constraint_map(v_id); int oncount = 0; int one_sided_count = 0; struct constraint *con[MAXCONPER]; int conlist[MAXCONPER]; MAT2D(grads,MAXCOORD,MAXCOORD); MAT2D(ss,MAXCOORD,MAXCOORD); REAL lagmul[MAXCOORD]; REAL *coord; int i,j,k,n; REAL value; int loosened = 0; for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { if ( conmap[j] & CON_HIT_BIT ) { conlist[oncount] = conmap[j] & CONMASK; con[oncount] = get_constraint(conmap[j]); if ( con[oncount]->attr & (NONNEGATIVE|NONPOSITIVE) ) one_sided_count++; oncount++; } } if ( one_sided_count == 0 ) return 0; /* nothing to do here */ /* adjust raw velocity for global constraints */ for ( vgptr = get_vertex_vgrad(v_id) ; vgptr ; vgptr = vgptr->chain ) { bi = vgptr->fixnum; if ( (bi < 0) || (bi >= fixcount) ) continue; /* bi = -1 for killed one-sided constraints */ for ( k = 0 ; k < SDIM ; k++ ) raw_velocity[k] -= vpressures[bi]*vgptr->raw_velocity[k]; } /* get local constraint gradients */ coord = get_coord(v_id); for ( i = 0 ; i < oncount ; i++ ) eval_all(con[i]->formula,coord,SDIM,&value,grads[i],v_id); matvec_mul(grads,raw_velocity,lagmul,oncount,SDIM); mat_mul_tr(grads,grads,ss,oncount,SDIM,oncount); mat_approx_solve(ss,oncount,lagmul); /* now inspect lagrange multipliers */ for ( n = 0 ; n < oncount ; ) { if ( one_sided_lagrange_attr >= 0 ) { REAL *v = (REAL*)get_extra(v_id,one_sided_lagrange_attr); struct extra *ex = EXTRAS(VERTEX)+one_sided_lagrange_attr; int osl_size = ex->array_spec.datacount; for ( i = 0 ; i < osl_size ; i++ ) if ( v[i] == 0.0 ) { v[i] = lagmul[n]; /* sign so acts like pressure */ break; } } if ( ((con[n]->attr & NONPOSITIVE) && ( lagmul[n] < 0.0 )) || ((con[n]->attr & NONNEGATIVE) && ( lagmul[n] > 0.0 )) ) { /* loosen */ unset_v_constraint_status(v_id,conlist[n]); oncount--; /* replace with last in list */ conlist[n] = conlist[oncount]; con[n] = con[oncount]; lagmul[n] = lagmul[oncount]; loosened++; continue; /* don't increment n */ } n++; } if ( loosened ) { /* recalculate projected velocity */ REAL perp[MAXCOORD]; REAL *velocity = get_velocity(v_id); constr_proj(TANGPROJ,oncount,con,coord,raw_velocity,perp,NULL,NO_DETECT,v_id); for ( j = 0 ; j < SDIM ; j++ ) velocity[j] = raw_velocity[j] - perp[j]; } return loosened; } /******************************************************************* * * function: volume_restore() * * purpose: adjust surface to global constraints. * Assumes quantities and gradients already calculated. * */ void volume_restore ARGS2((stepsize,mode), REAL stepsize, /* multiplier for motion; usually 1 */ int mode)/* TEST_MOVE or ACTUAL_MOVE */ { body_id bi_id; struct gen_quant *gq; int i,k; int fixi; if ( itdebug ) outstring("volume_restore()\n"); if ( !fixed_constraint_flag ) return; vol_deficit = (REAL*)temp_calloc(maxquants,sizeof(REAL)); vol_restore = (REAL*)temp_calloc(maxquants,sizeof(REAL)); /* gather differences from targets */ if ( !web.pressure_flag && !everything_quantities_flag ) FOR_ALL_BODIES(bi_id) { if ( !(get_battr(bi_id) & FIXEDVOL) ) continue; fixi = get_body_fixnum(bi_id); if ( fixi < 0 ) continue; vol_deficit[fixi] = get_body_fixvol(bi_id) - get_body_volume(bi_id); } for ( k = 0 ; k < gen_quant_count ; k++ ) { gq = GEN_QUANT(k); if ( !(gq->flags & Q_FIXED) ) continue; if ( valid_id(gq->b_id) && web.pressure_flag ) continue; fixi = gq->fixnum; if ( fixi < 0 ) continue; vol_deficit[fixi] = gq->target - gq->value; } /* solve for volume restoration coefficients */ if ( sparse_constraints_flag ) { if ( LS.A == NULL ) calc_leftside(); ysmp_solve(&LS,vol_deficit,vol_restore); free_system(&LS); } else { if ( rleftside == NULL ) calc_leftside(); LD_solve(rleftside,vol_deficit,vol_restore,fixcount); } /* subtract multiples of volume gradients from force */ /* and combine multiples of gradients for restoring motion */ /* set restoring motion */ /* optimizing parameter adjust */ if ( optparamcount ) for ( i = 0 ; i < optparamcount ; i++ ) for ( k = 0 ; k < gen_quant_count ; k++ ) { int fixk = GEN_QUANT(k)->fixnum; int volk = GEN_QUANT(k)->vol_number; if ( fixk >= 0 ) globals(optparam[i].pnum)->value.real += vol_restore[fixk] *globals(optparam[i].pnum)->attr.varstuff.pscale*optparam_congrads[i][volk]; } #ifdef MPI_EVOLVER mpi_volume_restore(maxquants,stepsize,mode); #else local_volume_restore(stepsize,mode); #endif partner_move(); /* in case doing partners */ if ( vol_deficit ) temp_free((char *)vol_deficit); vol_deficit = NULL; if ( vol_restore ) temp_free((char *)vol_restore); vol_restore = NULL; } /* end volume_restore() */ /*************************************************************************** * * function: local_volume_restore() * * purpose: apply volume restoration to local part of surface */ void local_volume_restore(stepsize,mode) REAL stepsize; int mode; { vertex_id v_id; int bi,i,k; /* vertices */ FOR_ALL_VERTICES(v_id) { REAL *x; volgrad *vgptr; int ord = loc_ordinal(v_id); int attr = get_vattr(v_id); if ( (attr & CONSTRAINT) && one_sided_present ) one_sided_volume_adjust(v_id); x = get_coord(v_id); if ( attr & BOUNDARY ) { REAL *p = get_param(v_id); struct boundary *boundary = get_boundary(v_id); if ( !(attr & FIXED) ) for ( vgptr = get_vertex_vgrad(v_id) ; vgptr ; vgptr = vgptr->chain ) { bi = vgptr->fixnum; if ( ( bi < 0 ) || (bi >= fixcount) ) continue; if ( approx_curve_flag ) for ( k = 0 ; k < SDIM ; k++ ) p[k] += stepsize*vol_restore[bi]*vgev[bi][SDIM*ord+k]; else for ( k = 0 ; k < boundary->pcount ; k++ ) p[k] += stepsize*vol_restore[bi]*vgptr->velocity[k]; } for ( i = 0 ; i < SDIM ; i++ ) x[i] = eval(boundary->coordf[i],p,v_id,NULL); } else { if ( !(attr & FIXED) ) for ( vgptr = get_vertex_vgrad(v_id) ; vgptr ; vgptr = vgptr->chain ) { bi = vgptr->fixnum; if ( ( bi < 0 ) || (bi >= fixcount) ) continue; if ( approx_curve_flag ) for ( k = 0 ; k < SDIM ; k++ ) x[k] += stepsize*vol_restore[bi]*vgev[bi][SDIM*ord+k]; else { for ( k = 0 ; k < SDIM ; k++ ) x[k] += stepsize*vol_restore[bi]*vgptr->velocity[k]; } } if ( attr & CONSTRAINT ) project_v_constr(v_id,mode,KEEP_ONESIDEDNESS); } } } /******************************************************************* * function: one_sided_volume_adjust() * * purpose: to see if volume correction velocity at a vertex * will detach it from one-sided constraints. If so, * raw volume adjust velocities are re-projected. */ void one_sided_volume_adjust ARGS1((v_id), vertex_id v_id) { REAL raw_velocity[MAXCOORD]; volgrad *vgptr; int bi; /* row number for constraint */ conmap_t * conmap = get_v_constraint_map(v_id); int oncount = 0; int one_sided_count = 0; struct constraint *con[MAXCONPER]; int conlist[MAXCONPER]; MAT2D(grads,MAXCOORD,MAXCOORD); MAT2D(ss,MAXCOORD,MAXCOORD); REAL lagmul[MAXCOORD]; REAL *coord; int i,j,k,n; REAL value; int loosened = 0; for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { if ( conmap[j] & CON_HIT_BIT ) { conlist[oncount] = conmap[j] & CONMASK; con[oncount] = get_constraint(conmap[j]); if ( con[oncount]->attr & (NONNEGATIVE|NONPOSITIVE) ) one_sided_count++; oncount++; } } if ( one_sided_count == 0 ) return; /* nothing to do here */ /* form volume adjust net velocity */ for ( i = 0 ; i < SDIM ; i++ ) raw_velocity[i] = 0.0; for ( vgptr = get_vertex_vgrad(v_id) ; vgptr ; vgptr = vgptr->chain ) { bi = vgptr->fixnum; if ( (bi < 0) || (bi >= fixcount) ) continue; /* bi = -1 for killed one-sided constraints */ for ( k = 0 ; k < SDIM ; k++ ) raw_velocity[k] += vol_restore[bi]*vgptr->raw_velocity[k]; } /* get local constraint gradients */ coord = get_coord(v_id); for ( i = 0 ; i < oncount ; i++ ) eval_all(con[i]->formula,coord,SDIM,&value,grads[i],v_id); matvec_mul(grads,raw_velocity,lagmul,oncount,SDIM); mat_mul_tr(grads,grads,ss,oncount,SDIM,oncount); mat_approx_solve(ss,oncount,lagmul); /* now inspect lagrange multipliers */ for ( n = 0 ; n < oncount ; ) { if ( ((con[n]->attr & NONPOSITIVE) && ( lagmul[n] < 0.0 )) || ((con[n]->attr & NONNEGATIVE) && ( lagmul[n] > 0.0 )) ) { /* loosen */ unset_v_constraint_status(v_id,conlist[n]); oncount--; /* replace with last in list */ conlist[n] = conlist[oncount]; con[n] = con[oncount]; lagmul[n] = lagmul[oncount]; loosened++; continue; /* don't increment n */ } n++; } if ( loosened ) { /* recalculate projected velocity */ REAL perp[MAXCOORD]; for ( vgptr = get_vertex_vgrad(v_id) ; vgptr ; vgptr = vgptr->chain ) { constr_proj(TANGPROJ,oncount,con,coord,vgptr->raw_velocity,perp,NULL,NO_DETECT,v_id); for ( j = 0 ; j < SDIM ; j++ ) vgptr->velocity[j] = vgptr->raw_velocity[j] - perp[j]; } } } /******************************************************************* * * function: vgrad_init() * * purpose: allocates storage for vertex volume gradients * */ char *vgrad_attr_name = "__vgrad_head"; int vgrad_attr; void vgrad_init(qfixed) int qfixed; /* how many fixed quantities (aside from body volumes)*/ { int one = 1; vertex_id v_id; int i; int stride; vgrad_end(); /* take care of any leftovers */ /* allocate chain start for each vertex */ vgrad_attr = find_attribute(VERTEX,vgrad_attr_name); if ( vgrad_attr < 0 ) vgrad_attr = add_attribute(VERTEX,vgrad_attr_name,PTR_TYPE,0,&one,0,NULL); MFOR_ALL_VERTICES(v_id) VPTR(v_id,vgrad_attr)[0] = NULL; /* allocate initial block of structures, using info from last time around, plus a little margin for growth. */ vgradbase = (struct vgradblock *)temp_calloc(1,sizeof(struct vgradblock)); vgradbase->max = vgradlastused ? (vgradlastused + vgradlastused/10) : web.skel[VERTEX].count; vgradbase->base = (volgrad*)temp_calloc(vgradbase->max,sizeof(volgrad)); stride = one_sided_present?3:2; vgradbase->values = (REAL*)temp_calloc(vgradbase->max,stride*SDIM*sizeof(REAL)); for ( i = 0 ; i < vgradbase->max ; i++ ) { vgradbase->base[i].grad = vgradbase->values + stride*i*SDIM; vgradbase->base[i].velocity = vgradbase->values + (stride*i+1)*SDIM; if ( one_sided_present ) vgradbase->base[i].raw_velocity = vgradbase->values + (stride*i+2)*SDIM; } vgradbase->top = 0; vgradbase->next = NULL; vgradtop = 0; vgradmax = 0; } /* end vgrad_init() */ /********************************************************************* * * function: vgrad_end() * * purpose: Deallocate constraint gradients structures. * */ void vgrad_end() { struct vgradblock *block,*next; vgradlastused = vgradtop; for ( block = vgradbase ; block ; block = next ) { temp_free((char *)block->base); temp_free((char *)block->values); next = block->next; temp_free((char *)block); } vgradbase = NULL; vgradmax = 0; if ( sparse_constraints_flag ) { free_system(&LS); memset(&LS,0,sizeof(LS)); } else if ( rleftside ) free_matrix(rleftside); rleftside = NULL; if ( rightside ) temp_free((char *)rightside); rightside = NULL; if ( vpressures ) temp_free((char *)vpressures); vpressures = NULL; if ( optparam_congrads ) free_matrix(optparam_congrads); optparam_congrads = NULL; #ifdef MPI_EVOLVER if ( this_task == 0 ) mpi_vgrad_end(); #endif } /* end vgrad_end() */ /******************************************************************** * * function: get_vertex_vgrad() * * purpose: return pointer to first vgrad structure in vertex's chain */ volgrad *get_vertex_vgrad(v_id) vertex_id v_id; { if ( !vgradbase ) return NULL; return (volgrad*)VPTR(v_id,vgrad_attr)[0]; } /******************************************************************** * * function: set_vertex_vgrad() * * purpose: set pointer to first vgrad structure in vertex's chain */ void set_vertex_vgrad(v_id,vgptr) vertex_id v_id; volgrad *vgptr; { VPTR(v_id,vgrad_attr)[0] = (char*)vgptr; } /********************************************************************** * * function: new_vgrad() * * purpose: Allocate a new vgrad structure from pool. Returns index. * Allocates more space if necessary; possibility of reallocation * is the reason for using an index instead of a pointer. * */ struct volgrad * new_vgrad() { struct volgrad * vg; struct vgradblock *newblock; LOCK_WEB; if ( vgradbase->top >= vgradbase->max ) { int i,stride; /* need new block */ newblock = (struct vgradblock *)temp_calloc(1,sizeof(struct vgradblock)); newblock->max = vgradtop/3+100; newblock->base = (volgrad*)temp_calloc(newblock->max,sizeof(volgrad)); stride = one_sided_present ? 3 : 2; newblock->values = (REAL*)temp_calloc(newblock->max,stride*SDIM*sizeof(REAL)); for ( i = 0 ; i < newblock->max ; i++ ) { newblock->base[i].grad = newblock->values + stride*i*SDIM; newblock->base[i].velocity = newblock->values + (stride*i+1)*SDIM; if ( one_sided_present ) newblock->base[i].raw_velocity = newblock->values + (stride*i+2)*SDIM; } newblock->top = 0; newblock->next = vgradbase; vgradbase = newblock; } vg = vgradbase->base + vgradbase->top++; vgradtop++; UNLOCK_WEB; return vg; } /****************************************************************** * * function: get_bv_vgrad() * * purpose: return pointer to vgrad structure of given body at * given vertex. NULL if none. */ volgrad *get_bv_vgrad(fixnum,v_id) int fixnum; /* which constrained quantity */ vertex_id v_id; { volgrad *vgptr; vgptr = get_vertex_vgrad(v_id); while ( vgptr ) if ( vgptr->fixnum == fixnum ) break; else vgptr = vgptr->chain; return vgptr; /* null if not found */ } /****************************************************************** * * function: get_bv_new_vgrad() * * purpose: return pointer to vgrad structure of given body at * given vertex. Allocates if none. */ volgrad *get_bv_new_vgrad(fixnum,v_id) int fixnum; vertex_id v_id; { volgrad **ptrptr; /* pointer to pointer so can update if need be */ ptrptr = (volgrad**)VPTR(v_id,vgrad_attr); while ( *ptrptr ) if ( (*ptrptr)->fixnum == fixnum ) return *ptrptr; else ptrptr = &(*ptrptr)->chain; /* need to get new structure */ *ptrptr = new_vgrad(); (*ptrptr)->fixnum = fixnum; return *ptrptr; } /********************************************************************** * * function: approx_curv_calc() * * purpose: converts energy gradients to approximate curvature * */ void approx_curv_calc(mode) int mode; /* bits for CALC_FORCE and CALC_VOLGRADS */ { int j; REAL *B; vertex_id v_id; if ( mode & CALC_FORCE ) { B = (REAL *)temp_calloc(SDIM*(web.skel[VERTEX].max_ord+1), sizeof(REAL)); /* each coordinate gives a right side */ FOR_ALL_VERTICES(v_id) { int vnum = SDIM*loc_ordinal(v_id); for ( j = 0 ; j < SDIM ; j++ ) B[vnum+j] = get_force(v_id)[j]; } mobility_mult(B); FOR_ALL_VERTICES(v_id) { REAL *vel = get_velocity(v_id); int vnum = SDIM*loc_ordinal(v_id); for ( j = 0 ; j < SDIM ; j++ ) vel[j] = B[vnum+j]; } temp_free((char *)B); } if ( mode & CALC_VOLGRADS ) /* constraint gradients */ { int NV = SDIM*(1+web.skel[VERTEX].max_ord); int bi; vgef = dmatrix(0,maxquants+1,0,NV); vgev = dmatrix(0,maxquants+1,0,NV); /* load volume gradients */ FOR_ALL_VERTICES(v_id) { volgrad *vgptri; ATTR attr = get_vattr(v_id); int ord = loc_ordinal(v_id); if ( attr & FIXED ) continue; for ( vgptri=get_vertex_vgrad(v_id); vgptri ; vgptri = vgptri->chain ) { bi = vgptri->fixnum; if (valid_id(vgptri->bb_id) && !web.pressure_flag && !everything_quantities_flag ) { if ( !(get_battr(vgptri->bb_id)&FIXEDVOL) ) continue; } for ( j = 0 ; j < SDIM ; j++ ) vgef[bi][SDIM*ord+j] = vgptri->grad[j]; } } /* convert gradients to vectors */ for ( bi = 0 ; bi <= maxquants ; bi++ ) { memcpy((char*)vgev[bi],(char*)vgef[bi],NV*sizeof(REAL)); mobility_mult(vgev[bi]); /* to vector */ } } } /* end approx_curv_calc() */ evolver-2.30c.dfsg/src/yexparse.c0000644000175300017530000047574111410765113017243 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /***************************************************************** * * File: yexparse.c * * Purpose: To read and parse user commands and functions in algebraic form * for evolver constraints. Expressions are given * in algebraic form and parsed with yacc (see command.yac * and y.tab.c). * */ #include "include.h" #include "lex.h" #include "ytab.h" /***************************************************************** * * Function exparse() * * Purpose: reading, parsing, checking of algebraic function definition. * Takes tokens from yylex() until non-expression token. * Function value is size in bytes of tree. * */ #define LISTMAX 200 struct treenode *list; /* tree */ struct treenode *permlist; /* to use if user doesn't want own copy */ NTYPE listtop; /* first spot reserved for root */ NTYPE maxp; int listmax; /* allocated */ /* for BREAK and CONTINUE */ #define LOOPMAX 30 int loopdepth; int loopbase[LOOPMAX]; int using_param_flag; /* so can detect if using boundary parameters */ int exparse ARGS3((maxparam,enode,flag), int maxparam, /* maximum number of parameters allowed */ struct expnode *enode, /* pointer to storage pointer */ int flag) /* whether to make copy for user */ { int retval; struct treenode *old_list = list; /* for nested parsing */ int old_listtop = listtop; int old_listmax = listmax; int old_brace_depth = brace_depth; PROF_START(exparse); listmax = LISTMAX; if ( permlist == NULL ) permlist = (struct treenode *)mycalloc(listmax,sizeof(struct treenode)); maxp = (NTYPE)maxparam; if ( flag == USERCOPY ) { enode->start = list = (struct treenode *)mycalloc(listmax,sizeof(struct treenode)); } else enode->start = list = permlist; list[1].type = SETUP_FRAME_; listtop = 2; parse_error_flag = 0; brace_depth = parens = in_quote = 0; /* unput expression start token for yacc */ tok = EXPRESSION_START_; unput_tok(); using_param_flag = 0; if ( !const_expr_flag || backquote_flag ) { /*local_nest_depth = 0;*/ init_local_scope(0); begin_local_scope(); } PROF_FINISH(exparse); PROF_START(yyparse); retval = yyparse(); /* 0 for accept, 1 for error */ PROF_FINISH(yyparse); PROF_START(exparse); if ( !const_expr_flag || backquote_flag ) { end_local_scope(); enode->locals = localbase; if ( localbase ) localbase->flags |= LL_IN_USE; exit_local_scope(); } if ( (tok != 0) && (tok != ',') && (tok != LEXERROR) ) { /* push back last token */ unput_tok(); } if ( (retval == 1) || parse_error_flag || (listtop == 1) ) { if ( flag == USERCOPY ) { myfree((char *)enode->start); if ( enode->start == list ) list = NULL; if ( enode->start == permlist ) permlist = NULL; } enode->start = NULL; retval = -1; goto exparse_exit; } /* free excess list */ if ( flag == USERCOPY ) enode->start = list = (struct treenode *)kb_realloc((char *)list, (listtop+2)*sizeof(struct treenode)); enode->root = list + listtop - 1; list[0] = list[listtop-1]; /* root also in first spot */ list[0].left += listtop - 1; list[0].right += listtop - 1; /* put DONE marker after root */ list[listtop++].type = FINISHED; /* figure stack usage */ stack_usage(enode); enode->flag = flag; if ( using_param_flag ) enode->flag |= USING_PARAM_FLAG; retval = listtop*sizeof(struct treenode); list = old_list; /* for nested parsing */ listtop = old_listtop; listmax = old_listmax; brace_depth = old_brace_depth; exparse_exit: PROF_FINISH(exparse); return retval; } /********************************************************************* * * Function: free_expr() * * Purpose: Deallocate lists of expression. * */ void free_expr ARGS1((ex), struct expnode *ex) { if ( ex == NULL ) return; if ( ex->flag == NOUSERCOPY ) return; if ( ex->start ) { struct treenode *node; for ( node = ex->start ; node != ex->root ; node ++ ) { #ifdef XXXXXXX /* free local symbol tables ?? */ if ( node->flags & HAS_LOCALLIST ) { struct locallist_t *locals = node->op5.locals; if ( locals->list ) { if ( locals->flags & LL_PERMANENT ) free((char*)locals->list); else myfree((char*)locals->list); } if ( locals->flags & LL_PERMANENT ) free((char*)locals); else myfree((char*)locals); } #endif if ( node->flags & HAS_STRING ) myfree(node->op1.string); if ( node->flags & HAS_STRING_5 ) myfree(node->op5.string); } myfree((char *)ex->start); } ex->root = NULL; ex->start = NULL; if ( ex->locals ) { myfree((char*)(ex->locals->list)); myfree((char*)(ex->locals)); } ex->locals = NULL; } /********************************************************************* * * Function: perm_free_expr() * * Purpose: Deallocate lists of permanent expression. * */ void perm_free_expr ARGS1((ex), struct expnode *ex) { struct treenode *node; if ( ex == NULL ) return; if ( ex->flag == NOUSERCOPY ) return; if ( ex->start ) { for ( node = ex->start ; node != ex->root ; node ++ ) if ( node->flags & HAS_STRING ) free(node->op1.string); if ( node->flags & HAS_STRING_5 ) free(node->op5.string); free((char *)ex->start); } ex->root = NULL; ex->start = NULL; } /********************************************************************* * * Function: makenode() * * Purpose: Add nodes to parse tree when called by yyparse. * Linear order of node list is such that sequential * execution is postorder. If left > right, then * subtrees physically swapped, but they must be adjacent. * * Return: Index of created node. */ void more_makenode ARGS((NTYPE,NTYPE,NTYPE)); int makenode ARGS3((ntype,left,right), NTYPE ntype, /* type of node */ NTYPE left, /* left son, if any */ NTYPE right) /* right son, if any */ { short type = (short)ntype; int i,n; struct treenode *nnode; struct global *g; int etype = -1; /* element type */ if ( listtop > listmax - 10 ) { list = (struct treenode *)kb_realloc((char*)list, (listmax+LISTMAX)*sizeof(struct treenode)); listmax += LISTMAX; } else memset((char*)(list+listtop),0,2*sizeof(struct treenode)); /* clear nodes */ switch ( type ) { case NULLBLOCK_: list[listtop].type = NULLBLOCK_; break; case NULLCMD_: list[listtop].type = NULLCMD_; break; case NOP_: list[listtop].type = NOP_; break; case SUPPRESS_WARNING_: case UNSUPPRESS_WARNING_: list[listtop].type = type; list[listtop].op1.name_id = left; list[listtop].stack_delta = -1; break; case WHEREAMI_COMMAND_: list[listtop].type = WHEREAMI_COMMAND_; break; case SET_BREAKPOINT_: list[listtop].type = type; list[listtop].op1.name_id = left; list[listtop].left = right - listtop; list[listtop].flags |= EPHEMERAL; break; case UNSET_BREAKPOINT_: list[listtop].type = type; list[listtop].op1.name_id = left; if ( right ) list[listtop].left = right - listtop; list[listtop].flags |= EPHEMERAL; break; case BACKQUOTE_START_: list[listtop].type = type; break; case BACKQUOTE_END_: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].right = right - listtop; list[left].op1.skipsize = right-left+1; /* so START can jump over */ break; /* command ',' expr */ case ACOMMANDEXPR_: list[listtop].type = ACOMMANDEXPR_; list[listtop].left = left - listtop; list[listtop].right = right - listtop; list[listtop].datatype = REAL_TYPE; break; /* IF THEN ELSE parse tree ELSE_ / \ IF_ command2 / \ IFTEST_ command1 / testexpr sequence: testexpr IFTEST_ command1 IF_ command2 ELSE_ */ case IFTEST_: list[listtop].type = IFTEST_; list[listtop].left = left - listtop; /* test expression */ /* op1 will be offset for jump if condition false */ list[listtop].stack_delta = -1; break; case IF_: list[listtop].type = IF_; list[listtop].left = left - listtop; /* IFTEST_ */ list[listtop].right = right - listtop; /* THEN command */ list[left].op1.skipsize = listtop - left; /* in IFTEST_ node */ /* op1 will be offset to skip ELSE part */ break; case ELSE_: /* really the continue node at root of IF tree */ list[listtop].type = ELSE_; list[listtop].left = left - listtop; /* IF_ node */ if ( right ) list[listtop].right = right - listtop; /* command2 */ list[left].op1.skipsize = listtop - left; /* in IF_ node */ break; case DECLARE_LOCAL_: list[listtop].type = DECLARE_LOCAL_; list[listtop].op1.name_id = left; /* identifier */ globals(left)->flags |= GLOB_LOCALVAR; /* globals(left)->flags |= ORDINARY_PARAM; */ break; case LOCAL_LIST_START_: list[listtop].type = LOCAL_LIST_START_; list[listtop].left = left-listtop; break; case INDEXSET_: list[listtop].type = INDEXSET_; /* "left" is previous indexset */ /* "right" is next index expression */ /* note we have to use left pointer if any used at all for later nodes trying to find start of subtree */ if ( left ) { /* then left is previous indexlist */ list[listtop].left = left-listtop; list[listtop].right = right-listtop; list[listtop].op1.indexcount = list[left].op1.indexcount+1; } else { list[listtop].op1.indexcount = 1; list[listtop].left = right-listtop; } /* Sanity check on index */ if ( list[right].type == PUSHCONST ) { if ( (int)(list[right].op1.real) < 1 ) { sprintf(errmsg,"Index %d must be positive.\n", list[listtop].op1.indexcount); kb_error(2238,errmsg, COMMAND_ERROR); } } break; case DIMENSIONSET_: /* "left" is previous indexset, except in leaf it is index expr */ /* "right" is next index expression, except 0 in leaf */ list[listtop].type = DIMENSIONSET_; if ( left ) /* have previous dimensionset */ { list[listtop].left = left-listtop; list[listtop].op1.indexcount = list[left].op1.indexcount+1; list[listtop].right = right-listtop; } else { list[listtop].op1.indexcount = 1; list[listtop].left = right-listtop; } /* Sanity check on index */ if ( list[right].type == PUSHCONST ) { if ( (int)(list[right].op1.real) < 0 ) /* can be 0 in define */ { sprintf(errmsg,"Index %d must be nonnegative.\n", list[listtop].op1.indexcount); kb_error(2522,errmsg, COMMAND_ERROR); } } break; case SINGLE_ASSIGN_: /* tree: SINGLE_ASSIGN / \ SINGLE_ELEMENT_ SET_ATTRIBUTE_A / / \ single expr index expr */ list[listtop].type = type; list[listtop].op1.assigntype = assigntype; list[listtop].left = left-listtop; /* single element */ list[listtop].right = right-listtop; /* attribute and expression */ list[listtop].stack_delta = -3; break; case DEFINE_EXTRA_: /* new element attribute */ list[listtop].type = type; if ( left ) list[listtop].left = left-listtop; /* dimension expression */ list[listtop].op2.eltype = right; /* element type */ break; case DEFINE_EXTRA_INDEX_: /* index added to extra attribute */ { struct extra *ex; list[listtop].type = type; list[listtop].left = left-listtop; /* definition header */ list[listtop].right = right-listtop; /* indexset expression */ list[listtop].op1.extranum = list[left].op1.extranum; list[listtop].op2.eltype = list[left].op2.eltype; ex = EXTRAS(list[left].op2.eltype) + list[left].op1.extranum; if ( (ex->array_spec.datacount > 0) && (ex->array_spec.dim != list[right].op1.indexcount) ) { sprintf(errmsg,"Cannot change number of dimensions of %s\n", ex->name); kb_error(4562,errmsg,COMMAND_ERROR); } ex->array_spec.dim = list[right].op1.indexcount; ex->flags |= DIMENSIONED_ATTR; list[listtop].stack_delta = -ex->array_spec.dim; } break; case ATTR_FUNCTION_: /* attribute function */ list[listtop].type = type; list[listtop].left = left-listtop; /* main define */ break; case ATTR_FUNCTION_END_: /* attribute function */ list[listtop].type = type; list[listtop].left = left-listtop; /* ATTR_FUNCTION node */ list[listtop].right = right-listtop; /* function */ break; case RETURN_: /* end command */ list[listtop].type = type; if ( left ) list[listtop].left = left - listtop; /* return expr */ break; case RESET_COUNTS_: /* set counts back to 0 */ list[listtop].type = type; break; case FLUSH_COUNTS_: /* print pending counts */ list[listtop].type = type; break; case PAUSE_: /* wait for user */ list[listtop].type = type; break; case PRINT_PROFILING_: case RESET_PROFILING_: list[listtop].type = type; break; case BREAK_: case CONTINUE_: list[listtop].type = type; if ( loopdepth < 1 ) kb_error(1388,"Cannot BREAK or CONTINUE unless inside a loop.\n", COMMAND_ERROR); if ( loopdepth < left ) kb_error(1389,"BREAK or CONTINUE out of too many levels.\n", COMMAND_ERROR); list[listtop].op1.skipsize = loopbase[loopdepth-left]-listtop; list[listtop].op2.breakdepth = left; break; /* WHILE DO tree: WHILE_END_ / \ WHILE_TOP_ command / testexpr execution sequence: testexpr WHILE_TOP_ command WHILE_END_ */ case WHILE_TOP_: list[listtop].type = WHILE_TOP_; list[listtop].left = left - listtop; /* op1 will be offset to after WHILE_END_ */ loopbase[loopdepth++] = listtop; /* find start of WHILE expression */ for ( n = left ; list[n].left ; n += list[n].left ) ; list[listtop].op4.contjump = n - listtop - 1; /* for continue */ i = add_local_var(NULL,1); list[listtop].stackpos = get_local(i).offset; list[listtop].stack_delta = -1; break; case WHILE_END_: list[listtop].type = WHILE_END_; list[listtop].left = left - listtop; /* test */ list[listtop].right = right - listtop; /* command */ list[left].op1.skipsize = listtop - left; /* jump if test fails */ list[left].op3.breakjump = listtop - left; /* for break */ /* find start of WHILE test expression and set jump back */ for ( n = left ; list[n].left ; n += list[n].left ) ; list[listtop].op1.skipsize = n - listtop - 1; /* allow increment */ loopdepth--; if ( loopdepth < 0 ) kb_error(1390,"Internal error: loopdepth negative.\n",COMMAND_ERROR); break; /* DO WHILE tree: DO_END_ / \ DO_TOP_ test expr / / DO_ENTRY_ command execution sequence: DO_ENTRY command DO_TOP_ test expr DO_END_ */ case DO_ENTRY_: list[listtop].type = type; loopbase[loopdepth++] = listtop; /* op3 will hold offsets for break and continue */ i = add_local_var(NULL,1); list[listtop].stackpos = get_local(i).offset; break; case DO_TOP_: list[listtop].type = type; list[listtop].left = left - listtop; /* DO_ENTRY */ list[listtop].right = right - listtop; /* command */ i = add_local_var(NULL,1); list[listtop].stackpos = get_local(i).offset; break; case DO_END_: { int entry; list[listtop].type = type; list[listtop].left = left - listtop; /* DO_TOP_ */ list[listtop].right = right - listtop; /* command */ /* find start of DO loop */ for ( n = left ; list[n].left ; n += list[n].left ) ; list[listtop].op1.skipsize = n - listtop - 1; /* allow increment */ /* find start of test loop */ for ( n = right ; list[n].left ; n += list[n].left ) ; entry = left + list[left].left; /* DO_ENTRY_ */ list[entry].op3.breakjump = listtop-entry; /* break */ list[entry].op4.contjump = n - entry - 1; /* continue */ loopdepth--; if ( loopdepth < 0 ) kb_error(1391,"Internal error: loopdepth negative.\n",COMMAND_ERROR); list[listtop].stack_delta = -1; break; } /* FOR syntax: FOR ( command1 ; expr ; command2 ) command3 FOR tree: FOR_END_ / \ FOR_TOP_ command3 / \ FOR_HEAD_ command2 / \ FOR_ENTRY expr | command1 FOR execution sequence: command1 expr FOR_HEAD_ command2 FOR_TOP_ command3 FOR_END_ FOR_ENTRY.op3.breakjump holds break jump FOR_ENTRY.op4.contjump holds continue jump FOR_HEAD_.op1.skipsize is jump to command3 FOR_HEAD_.op2.jumpsize is jump out of loop FOR_TOP_.op1.skipsize is jump back to expr FOR_END_.op1.skipsize is jump back to command2 */ case FOR_ENTRY_: list[listtop].type = type; list[listtop].left = left - listtop; /* command1 */ loopbase[loopdepth++] = listtop; /* op3 will hold offsets for break and continue */ i = add_local_var(NULL,1); list[listtop].stackpos = get_local(i).offset; break; case FOR_HEAD_: list[listtop].type = type; list[listtop].left = left - listtop; /* FOR_ENTRY */ list[listtop].right = right - listtop; /* expr */ list[left].op4.contjump = listtop-left; /* expr follows immediately */ list[listtop].stack_delta = -1; break; case FOR_TOP_: list[listtop].type = type; list[listtop].left = left - listtop; /* FOR_HEAD_ */ list[listtop].right = right - listtop; /* command2 */ list[listtop].op1.skipsize = left + list[left].left - listtop; list[left].op1.skipsize = listtop - left; break; case FOR_END_: list[listtop].type = type; list[listtop].left = left - listtop; /* FOR_TOP_ */ if ( right ) list[listtop].right = right - listtop; /* command3 */ list[left+list[left].left].op2.jumpsize = listtop - left - list[left].left; list[listtop].op1.skipsize = (left + list[left].left) - listtop; n = left+list[left].left; /* n = FOR_HEAD */ n += list[n].left; /* n = FOR_ENTRY */ list[n].op3.breakjump = listtop-n; /* break */ loopdepth--; if ( loopdepth < 0 ) kb_error(2515,"Internal error: loopdepth negative.\n",COMMAND_ERROR); break; /* >> redirection tree: REDIRECT_END_ / \ REDIRECT_ command / stringexpr execution sequence: stringexpr REDIRECT_ command REDIRECT_END_ */ case REDIRECT_: /* >> */ case REDIRECTOVER_: /* >>> */ list[listtop].type = type; list[listtop].left = left - listtop; /* stringexpr */ break; case REDIRECT_END_: list[listtop].type = type; list[listtop].left = left - listtop; if ( right ) list[listtop].right = right - listtop; list[listtop].stack_delta = -1; break; /* piping tree: PIPE_END_ / \ PIPE_ command / stringexpr execution sequence: stringexpr PIPE_ command PIPE_END_ */ case PIPE_: list[listtop].type = PIPE_; list[listtop].left = left - listtop; /* command */ break; case PIPE_END_: list[listtop].type = type; list[listtop].left = left - listtop; if ( right ) list[listtop].right = right - listtop; list[listtop].stack_delta = -1; break; case TRANSFORM_DEPTH_: list[listtop].type = type; list[listtop].left = left - listtop; /* number of transforms */ list[listtop].stack_delta = -1; break; case VIEW_TRANSFORM_PARITY_: list[listtop].type = type; list[listtop].left = left - listtop; /* index of transform */ list[listtop].datatype = REAL_TYPE; break; case VIEW_TRANSFORM_SWAP_COLORS_: list[listtop].type = type; list[listtop].left = left - listtop; /* index of transform */ list[listtop].datatype = REAL_TYPE; break; case CREATE_VERTEX_: list[listtop].type = type; list[listtop].left = left - listtop; /* expression list for coords */ if ( list[left].op1.indexcount != SDIM ) { sprintf(msg, "Need exactly %d coordinates in NEW_VERTEX (...).\n",SDIM); kb_error(2217,msg,COMMAND_ERROR); } list[listtop].stack_delta = 1 - SDIM; list[listtop].datatype = REAL_TYPE; break; case CREATE_EDGE_: list[listtop].type = type; list[listtop].left = left - listtop; /* id of tail vertex */ list[listtop].right = right - listtop; /* id of head vertex */ list[listtop].stack_delta = -1; list[listtop].datatype = REAL_TYPE; break; case CREATE_FACET_: list[listtop].type = type; list[listtop].left = left - listtop; /* expression list for edges */ if ( (web.representation == SIMPLEX) && (list[left].op1.argcount != web.dimension+1) ) { sprintf(msg,"Need exactly %d vertices in NEW_FACET (...).\n", web.dimension+1); kb_error(2218,msg,COMMAND_ERROR); } list[listtop].stack_delta = 1 - list[left].op1.argcount; list[listtop].datatype = REAL_TYPE; break; case CREATE_BODY_: list[listtop].type = type; list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case ELINDEX_: list[listtop].type = type; list[listtop].left = left-listtop; if ( right ) { list[listtop].right = right - listtop; list[listtop].stack_delta = -1; } break; case PUSH_ELEMENT_ID_: list[listtop].type = PUSH_ELEMENT_ID_; if ( left == 0 ) kb_error(6343,"Element id 0 is illegal.\n",RECOVERABLE); list[listtop].op1.id = (element_id)abs(left) - 1; if ( left < 0 ) invert(list[listtop].op1.id); #ifdef MPI_EVOLVER list[listtop].op1.id |= ((element_id)right << TASK_ID_SHIFT); #endif list[listtop].stack_delta = 1; break; case VALID_ELEMENT_: list[listtop].type = type; list[listtop].op1.eltype = left; list[listtop].left = right - listtop; /* index expression */ list[listtop].datatype = REAL_TYPE; break; case VALID_CONSTRAINT_: list[listtop].type = type; list[listtop].left = left - listtop; /* index expression */ list[listtop].datatype = REAL_TYPE; break; case VALID_BOUNDARY_: list[listtop].type = type; list[listtop].left = left - listtop; /* index expression */ list[listtop].datatype = REAL_TYPE; break; case MATRIX_MULTIPLY_: { struct array *a = get_name_arrayptr(list[left].op2.name_id,NULL,localbase); struct array *b = get_name_arrayptr(list[right].op2.name_id,NULL,localbase); struct array *c = get_name_arrayptr(list[int_val].op2.name_id,NULL,localbase); list[listtop].type = type; list[listtop].op1.name_id = list[left].op2.name_id; /* first multiplicand */ list[listtop].op2.name_id = list[right].op2.name_id; /* second multiplicand */ list[listtop].op3.name_id = list[int_val].op2.name_id; /* result */ list[listtop].stack_delta = -3; if ( a->datatype != REAL_TYPE) kb_error(3863,"matrix_multiply: first matrix is not of type REAL.\n", RECOVERABLE); if ( b->datatype != REAL_TYPE ) kb_error(3864,"matrix_multiply: second matrix is not of type REAL.\n", RECOVERABLE); if ( c->datatype != REAL_TYPE ) kb_error(3865,"matrix_multiply: third matrix is not of type REAL.\n", RECOVERABLE); if ( a == c ) kb_error(3866, "matrix_multiply: first and third matrices must not be the same matrix.\n", RECOVERABLE); if ( b == c ) kb_error(3867, "matrix_multiply: second and third matrices must not be the same matrix.\n", RECOVERABLE); } break; case MATRIX_INVERSE_: { struct array *a = get_name_arrayptr(list[left].op2.name_id,NULL,localbase); struct array *b = get_name_arrayptr(list[right].op2.name_id,NULL,localbase); list[listtop].type = type; list[listtop].op1.name_id = list[left].op2.name_id; /* original */ list[listtop].op2.name_id = list[right].op2.name_id; /* inverse */ list[listtop].stack_delta = -1; list[listtop].datatype = REAL_TYPE; if ( a->datatype != REAL_TYPE ) kb_error(3874,"matrix_inverse: first matrix is not of type REAL.\n", RECOVERABLE); if ( b->datatype != REAL_TYPE ) kb_error(3875,"matrix_inverse: second matrix is not of type REAL.\n", RECOVERABLE); if ( a->dim != 2 ) kb_error(3870,"matrix_inverse first array is not two-dimensional.\n", RECOVERABLE); if ( b->dim != 2 ) kb_error(3871,"matrix_inverse second array is not two-dimensional.\n", RECOVERABLE); } break; case MATRIX_DETERMINANT_: { struct array *a = get_name_arrayptr(list[left].op2.name_id,NULL,localbase); list[listtop].type = type; list[listtop].op1.name_id = list[left].op2.name_id; /* original */ list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; if ( a->dim != 2 ) kb_error(3219,"matrix_determinant array is not two-dimensional.\n", RECOVERABLE); } break; case MERGE_VERTEX_: list[listtop].type = type; list[listtop].left = left - listtop; /* id of first vertex */ list[listtop].right = right - listtop; /* id of second vertex */ list[listtop].stack_delta = -2; break; case MERGE_EDGE_: list[listtop].type = type; list[listtop].left = left - listtop; /* oid of first edge */ list[listtop].right = right - listtop; /* oid of second edge */ list[listtop].stack_delta = -2; break; case MERGE_FACET_: list[listtop].type = type; list[listtop].left = left - listtop; /* oid of first facet */ list[listtop].right = right - listtop; /* oid of second facet */ list[listtop].stack_delta = -2; break; /* Aggregate loops. (not all nodes need be present) Tree: AGGREGATE_END_ / \ AGGREGATE_INIT_ AGGREGATE_ or SET_INIT_ / \ WHERE_ SET_ATTRIBUTE_L / \ / \ element_gen expr value expr index expr sequence: INIT_ element_gen expr WHERE_ value index SET_AT_L AGGR_ AGGR_END_ prep cleanup If present, SET_ATTRIBUTE_L will do setting and AGGREGATE_ does looping. Else AGGREGATE_ does both. Actually, AGGREGATE_ type is collection of node types. */ case SET_INIT_: list[listtop].type = type; loopbase[loopdepth++] = listtop; i = add_local_var(NULL,1); list[listtop].stackpos = get_local(i).offset; break; case AGGREGATE_INIT_: list[listtop].type = type; list[listtop].op1.aggrtype = aggrtype; loopbase[loopdepth-1] = listtop; i = add_local_var(NULL,1); list[listtop].stackpos = get_local(i).offset; switch ( aggrtype ) { case MAX_: case MIN_: case SUM_: case COUNT_: list[listtop].stack_delta = 1; break; case AVG_: list[listtop].stack_delta = 2; break; case HISTOGRAM_: case LOGHISTOGRAM_: list[listtop].stack_delta = 3 + HISTBINS + 1; break; } /* op1 has aggregate type; op2 will have element type; */ /* op3 will have break and continue jump offsets */ break; case WHERE_: list[listtop].type = type; list[listtop].left = left - listtop; /* generator */ list[listtop].right = right - listtop; /* condition expr */ list[listtop].stack_delta = -1; list[listtop].op2.localnum = list[left].op2.localnum; /* op1 will hold runtime where count */ break; case AGGREGATE_: list[listtop].type = aggrtype; /* op1 holds offset back to start of loop */ if ( list[left].type == SINGLE_ELEMENT_ ) { list[listtop].op1.skipsize = 1; /* don't go back */ nnode = list+left; nnode->op1.skipsize = listtop - left; /* in case of WHERE_ */ } else if ( list[left].type == WHERE_ ) { list[listtop].op1.skipsize = list[left].left + left - listtop; nnode = list+left+list[left].left; if ( nnode->type == SINGLE_ELEMENT_ ) nnode->op1.skipsize = listtop - (left+list[left].left); } else { list[listtop].op1.skipsize = left - listtop; /* no WHERE_ */ nnode = list+left; } list[listtop].left = left - listtop; /* generator */ if ( right ) list[listtop].right = right - listtop; /* value */ /* also handy to have element type */ etype = (nnode+nnode->left)->op1.eltype; /* from INIT */ /* some type error checking and stuff */ switch ( aggrtype ) { case SUM_: case AVG_: case COUNT_: case HISTOGRAM_: case LOGHISTOGRAM_: case MAX_: case MIN_: list[listtop].stack_delta = -1; break; case FIX_: if ( etype == BODY ) kb_error(2230, "Cannot FIX bodies. To fix volume, do \"set body target expr\".\n", COMMAND_ERROR); break; case UNFIX_: if ( etype == BODY ) kb_error(2881, "Cannot UNFIX bodies. To unfix volume, \"unset body target\".\n", COMMAND_ERROR); break; case VERTEX_AVERAGE_: case RAW_VERTEX_AVERAGE_: case RAWEST_VERTEX_AVERAGE_: if ( web.representation == SIMPLEX ) kb_error(2219,"No vertex_average in simplex model yet.\n", COMMAND_ERROR); if ( (etype != VERTEX) ) kb_error(1238,"Can vertex_average only vertices.\n",COMMAND_ERROR); break; case DELETE_: if ( (etype != EDGE) && (etype != FACET) ) kb_error(1392,"Can delete only edges or facets.\n",COMMAND_ERROR); break; case REVERSE_ORIENTATION_: if ( (etype != EDGE) && (etype != FACET) ) kb_error(4392,"Can reverse_orientation only edges or facets.\n",COMMAND_ERROR); break; case POP_: if ( web.representation == SIMPLEX ) kb_error(2432,"Can pop only in SOAPFILM or STRING model.\n", COMMAND_ERROR); if ( (etype != EDGE) && (etype != VERTEX) ) kb_error(2433,"Can pop only edges or vertices.\n", COMMAND_ERROR); if ( (etype == EDGE) && (web.representation==STRING) ) kb_error(2434,"Can pop edges only in soapfilm model.\n", COMMAND_ERROR); break; case POP_TRI_TO_EDGE_: if ( web.representation != SOAPFILM ) kb_error(2803,"Can pop_tri_to_edge only in SOAPFILM model.\n", COMMAND_ERROR); if ( etype != FACET ) kb_error(2804,"Can pop_tri_to_edge only facets.\n", COMMAND_ERROR); break; case POP_EDGE_TO_TRI_: if ( web.representation != SOAPFILM ) kb_error(2806,"Can pop_edge_to_tro only in SOAPFILM model.\n", COMMAND_ERROR); if ( etype != EDGE ) kb_error(2807,"Can pop_edge_to_tri only edges.\n", COMMAND_ERROR); break; case POP_QUAD_TO_QUAD_: if ( web.representation != SOAPFILM ) kb_error(2808,"Can pop_quad_to_quad only in SOAPFILM model.\n", COMMAND_ERROR); if ( etype != FACET ) kb_error(2809,"Can pop_quad_to_quad only facets.\n", COMMAND_ERROR); break; case EDGESWAP_: if ( web.representation != SOAPFILM ) kb_error(1393,"Can edgeswap only in the SOAPFILM model. Did you mean t1_edgeswap?\n", COMMAND_ERROR); if ( (etype != EDGE) ) kb_error(1394,"Can edgeswap only edges.\n",COMMAND_ERROR); break; case T1_EDGESWAP_: if ( web.representation != STRING ) kb_error(3910,"Can t1_edgeswap only in the STRING model.\n", COMMAND_ERROR); if ( (etype != EDGE) ) kb_error(3657,"Can t1_edgeswap only edges.\n",COMMAND_ERROR); break; case EQUIANGULATE_: if ( web.representation != SOAPFILM ) kb_error(2500,"Can equiangulate only in the SOAPFILM model.\n", COMMAND_ERROR); if ( (etype != EDGE) ) kb_error(2501,"Can edgeswap only edges.\n",COMMAND_ERROR); break; case REFINE_: if ( (etype != EDGE) && (etype != FACET) ) kb_error(1241,"Can refine only edges or facets.\n",COMMAND_ERROR); break; case SET_NO_DISPLAY_: if ( etype != FACET ) kb_error(1265,"No_display only applies to facets.\n", COMMAND_ERROR); break; case SET_NONCONTENT_: if ( (etype != EDGE) && (etype != FACET) ) kb_error(2902,"Noncontent only applies to edges or facets.\n", COMMAND_ERROR); break; case SET_HIT_PARTNER_: if ( etype != VERTEX ) kb_error(3001,"Hit_partner only applies to vertices.\n", COMMAND_ERROR); break; case SET_NO_REFINE_: if ( (etype != EDGE) && (etype != FACET) ) kb_error(1395,"No_refine only applies to edges or facets.\n", COMMAND_ERROR); break; case SET_CONSTRAINT_: case UNSET_CONSTRAINT_: case SET_BOUNDARY_: case UNSET_BOUNDARY_: list[listtop].stack_delta = -1; break; case SET_EXTRA_ATTR_: { struct extra *ex; ex = EXTRAS(etype); for ( n = 0 ; n < web.skel[etype].extra_count ; n++,ex++ ) if ( stricmp(ex->name,set_extra_name) == 0 ) break; if ( n == web.skel[etype].extra_count ) kb_error(1396,"Internal error: Invalid extra attribute number.\n",COMMAND_ERROR); n += etype << ESHIFT; /* handy to have type here */ list[listtop].op3.extra_info = n; list[listtop].flags |= EPHEMERAL; list[listtop].stack_delta = -ex->array_spec.dim-1; } break; } if ( (aggrtype >= SET_COORD_) && (aggrtype <= SET_COORD_ + MAXCOORD)) { list[listtop].type = SET_COORD_; n = aggrtype - SET_COORD_ - 1; if ( n >= SDIM ) kb_error(2220,"Coordinate dimension exceeds space dimension.\n", COMMAND_ERROR); if ( etype != VERTEX ) kb_error(1398,"Can set coordinates only for vertices.\n", COMMAND_ERROR); list[listtop].op2.coordnum = n; } else if ((aggrtype>=SET_PARAM_) && (aggrtype <= SET_PARAM_+MAXPARAM)) { list[listtop].type = SET_PARAM_; list[listtop].op2.coordnum = aggrtype - SET_PARAM_ - 1; if ( etype != VERTEX ) kb_error(1399,"Can set parameters only for vertices.\n", COMMAND_ERROR); } else list[listtop].op2.eltype = etype; break; case AGGREGATE_END_: { struct treenode *wnode; list[listtop].type = type; list[listtop].op1.aggrtype = aggrtype; list[listtop].left = left - listtop; /* aggr init */ list[listtop].right = right - listtop; /* aggr op */ list[listtop].stack_delta = -list[left].stack_delta - 3; list[listtop].datatype = REAL_TYPE; if ( (aggrtype == SUM_) || (aggrtype == COUNT_) || (aggrtype == AVG_) || (aggrtype == MAX_ ) || (aggrtype == MIN_ ) ) list[listtop].stack_delta += 1; wnode = list+right + list[right].left; list[listtop].flags |= IN_ELEMENT_LOOP; if ( wnode[wnode->left].type != SINGLE_ELEMENT_ ) { if ( wnode->type == WHERE_ ) { nnode = wnode + wnode->left; nnode->op1.skipsize = (int)((list + listtop) - nnode); } else wnode->op1.skipsize = listtop - (right + list[right].left); } } /* element type in aggr_init node */ list[left].op2.eltype = list[right].op2.eltype; list[left].op3.breakjump = listtop-left-1; /* break to here */ list[left].op4.contjump = right-left+list[right].op1.skipsize-1; list[loopbase[loopdepth-1]].op3.breakjump = listtop-loopbase[loopdepth-1]-1; /* break to here */ loopdepth--; if ( loopdepth < 0 ) kb_error(1400,"Internal error: loopdepth negative.\n",COMMAND_ERROR); break; /* Element generator. Tree: WHERE_ / \ element_gen expr SINGLE_ELEMENT_ / INDEXED_SUBTYPE_ / \ single expr INDEXED_ELEMENT_ / expr SYMBOL_ELEMENT_ NEXT_ELEMENT_ / INIT_ELEMENT_ */ case INIT_ELEMENT_: if ( (list[listtop-1].type != AGGREGATE_INIT_) && (list[listtop-1].type != SET_INIT_) ) { int k; /* for break and continue */ int n; i = add_local_var(NULL,1); n = get_local(i).offset; list[listtop].stackpos = n; for ( k = listtop-1; k >= 0 ; k-- ) if ( list[k].type==AGGREGATE_INIT_ || list[k].type==SET_INIT_ ) { list[k].stackpos = n; break; } } list[listtop].op1.eltype = left; /* save type */ list[listtop].op2.localnum = use_given_id ? 0 : add_local_var(NULL,1); /* op5.string will have name, if any */ switch ( left ) { case VERTEX: etype = INIT_VERTEX_; break; case EDGE : etype = INIT_EDGE_ ; break; case FACET : etype = INIT_FACET_ ; break; case BODY : etype = INIT_BODY_ ; break; case FACETEDGE : etype = INIT_FACETEDGE_ ; break; default : kb_error(1401,"Internal error: Bad INIT_ELEMENT_ type.\n", COMMAND_ERROR); } list[listtop].type = etype; list[listtop].stack_delta = 3; break; case INIT_SUBELEMENT_: /* subelement of named element */ etype = right; list[listtop].op1.eltype = etype; /* save type */ list[listtop].left = left - listtop; /* single element */ list[listtop].op2.localnum = list[left].op2.localnum; { int k; /* for break and continue */ int n; i = add_local_var(NULL,1); n = get_local(i).offset; list[listtop].stackpos = 0; for ( k = listtop-1; k >= 0 ; k-- ) if ( list[k].type==AGGREGATE_INIT_ || list[k].type==SET_INIT_ ) { list[k].stackpos = n; break; } } switch ( list[left].op1.eltype ) /* parent type */ { case VERTEX: switch ( etype ) { case EDGE : etype = INIT_VERTEX_EDGE_ ; if ( web.representation == SIMPLEX ) kb_error(1402,"Vertex edge iterator not valid in simplex model.\n", COMMAND_ERROR); break; case FACET : etype = INIT_VERTEX_FACET_ ; break; case BODY : etype = INIT_VERTEX_BODY_ ; break; default : kb_error(1403,"Cannot do vertices of vertex.\n",COMMAND_ERROR); } break; case EDGE: switch ( etype ) { case VERTEX: etype = INIT_EDGE_VERTEX_; break; case EDGE : kb_error(1406,"Cannot do edges of edge.\n", COMMAND_ERROR); case FACET : etype = INIT_EDGE_FACET_ ; if ( web.representation == SIMPLEX ) kb_error(1404,"Edge facet iterator not valid in simplex model.\n", COMMAND_ERROR); break; case BODY : etype = INIT_EDGE_BODY_ ; if ( web.representation == SIMPLEX ) kb_error(1405,"Edge body iterator not valid in simplex model.\n", COMMAND_ERROR); break; case FACETEDGE: etype = INIT_EDGE_FACETEDGE_ ; break; default : kb_error(2830,"Cannot do facetedges of edge.\n", COMMAND_ERROR); } break; case FACET: switch ( etype ) { case VERTEX: etype = INIT_FACET_VERTEX_; break; case EDGE : etype = INIT_FACET_EDGE_ ; if ( web.representation == SIMPLEX ) kb_error(1407,"Facet edge iterator not valid in simplex model.\n", COMMAND_ERROR); break; case BODY : etype = INIT_FACET_BODY_ ; if ( web.representation == SIMPLEX ) kb_error(1408,"Facet body iterator not valid in simplex model.\n", COMMAND_ERROR); break; case FACETEDGE : kb_error(3755,"Cannot do facetedges of facet.\n",COMMAND_ERROR); default : kb_error(1409,"Cannot do facets of facet.\n",COMMAND_ERROR); } break; case BODY: switch ( etype ) { case VERTEX: etype = INIT_BODY_VERTEX_; break; case EDGE : etype = INIT_BODY_EDGE_ ; break; case FACET : etype = INIT_BODY_FACET_ ; break; default : kb_error(1410,"Internal error: Bad INIT_ELEMENT_ type.\n",COMMAND_ERROR); } break; case FACETEDGE: switch ( etype ) { case EDGE : etype = INIT_FACETEDGE_EDGE_ ; break; case FACET : etype = INIT_FACETEDGE_FACET_ ; break; default : kb_error(3914,"Facetedge can only have edge or facet subelement.\n",COMMAND_ERROR); } break; default: kb_error(1411,"Cannot do bodies of body.\n",COMMAND_ERROR); } list[listtop].type = etype; list[listtop].stack_delta = 3; break; case NEXT_ELEMENT_: list[listtop].left = left - listtop; /* element initializer */ /* op5.string will have name, if any */ /* op2.localnum will point to iteration local variable */ /* op1 reserved for jump to end of loop */ /* left->op2.localnum will point to parent */ list[listtop].op2.localnum = use_given_id ? 0 : add_local_var(NULL,1); if ( loopdepth > 0 ) list[loopbase[loopdepth-1]].op4.contjump = listtop-loopbase[loopdepth-1]-1; /* CONTINUE to here */ switch ( list[left].type ) /* parent type */ { case INIT_VERTEX_EDGE_ : type = NEXT_VERTEX_EDGE_ ; break; case INIT_VERTEX_FACET_ : type = NEXT_VERTEX_FACET_ ; break; case INIT_VERTEX_BODY_ : type = NEXT_VERTEX_BODY_ ; break; case INIT_EDGE_VERTEX_: type = NEXT_EDGE_VERTEX_; break; case INIT_EDGE_FACET_ : type = NEXT_EDGE_FACET_ ; break; case INIT_EDGE_FACETEDGE_ : type = NEXT_EDGE_FACETEDGE_ ; break; case INIT_EDGE_BODY_ : type = NEXT_EDGE_BODY_ ; break; case INIT_FACET_VERTEX_: type = NEXT_FACET_VERTEX_; break; case INIT_FACET_EDGE_ : type = NEXT_FACET_EDGE_ ; break; case INIT_FACET_BODY_ : type = NEXT_FACET_BODY_ ; break; case INIT_BODY_VERTEX_: type = NEXT_BODY_VERTEX_; break; case INIT_BODY_EDGE_ : type = NEXT_BODY_EDGE_ ; break; case INIT_BODY_FACET_ : type = NEXT_BODY_FACET_ ; break; case INIT_VERTEX_: type = NEXT_VERTEX_; break; case INIT_EDGE_ : type = NEXT_EDGE_ ; break; case INIT_FACET_: type = NEXT_FACET_ ; break; case INIT_BODY_ : type = NEXT_BODY_ ; break; case INIT_FACETEDGE_ : type = NEXT_FACETEDGE_; break; case INIT_FACETEDGE_EDGE_ : type = NEXT_FACETEDGE_EDGE_; break; case INIT_FACETEDGE_FACET_ : type = NEXT_FACETEDGE_FACET_; break; default : kb_error(1412,"Internal error: Bad NEXT_ELEMENT_ type.\n",COMMAND_ERROR); } list[listtop].type = type; break; case RITZ_: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].right = right - listtop; list[listtop].stack_delta = -2; break; case WRAP_VERTEX_: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].right = right - listtop; list[listtop].stack_delta = -2; break; case LANCZOS_: case EIGENPROBE_: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].stack_delta = -1; if ( right ) { list[listtop].right = right - listtop; list[listtop].stack_delta = -2; } break; case HESSIAN_SADDLE_: case HESSIAN_SEEK_: list[listtop].type = type; if ( left ) { list[listtop].left = left - listtop; list[listtop].stack_delta = -1; } break; case MOVE_: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].stack_delta = -1; break; case AREAWEED_: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].stack_delta = -1; break; case METIS_: case METIS_READJUST_: case KMETIS_: case BODY_METIS_: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].stack_delta = -1; break; case OMETIS_: list[listtop].type = type; if ( left ) { list[listtop].left = left - listtop; list[listtop].stack_delta = -1; } break; case EDGEWEED_: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].stack_delta = -1; break; case EDGEDIVIDE_: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].stack_delta = -1; break; case HISTORY_: list[listtop].type = type; break; case LAGRANGE_: list[listtop].type = type; list[listtop].left = left-listtop; /* degree approximation */ list[listtop].stack_delta = -1; break; case BURCHARD_: list[listtop].type = type; list[listtop].op1.maxsteps = left; /* number of steps */ break; case ZOOM_: list[listtop].type = type; if ( left ) { list[listtop].left = left - listtop; /* vertex number */ list[listtop].stack_delta = -1; } if ( right ) { list[listtop].left = right - listtop; /* radius expression */ list[listtop].stack_delta += -1; } break; case SET_GRAVITY_: list[listtop].type = type; list[listtop].left = left - listtop; /* value expression */ list[listtop].op1.assigntype = assigntype; list[listtop].stack_delta = -1; break; case SET_AMBIENT_PRESSURE_: case SET_GAP_CONSTANT_: case NOTCH_: case SET_AUTOCHOP_: case SET_FIXED_AREA_: case SET_OPTIMIZE_: case SET_SCALE_: case JIGGLE_: case SET_BACKGROUND_: case PRINT_: case STRPRINT_: case INVOKE_P_MENU_: case SET_MODEL_: case SKINNY_: case TORDUP_: list[listtop].type = type; list[listtop].left = left - listtop; /* value expression */ list[listtop].stack_delta = -1; break; case SET_INTERNAL_: list[listtop].type = type; list[listtop].left = right - listtop; /* value expression */ list[listtop].op1.name_id = left; /* variable */ list[listtop].op2.assigntype = assigntype; list[listtop].stack_delta = (assigntype == ASSIGN_) ? -1 : 0; switch(left) { /* break on settable variables */ case V_AMBIENT_PRESSURE: case V_HESSIAN_SLANT_CUTOFF: case GRAV_CONST_: case V_BREAKFLAG_: case V_VISIBILITY_DEBUG_: case V_TOLERANCE: case V_HESS_EPSILON: case V_SCALE_SCALE: case V_TIME: case V_SCALE: case V_JIG_TEMP: case V_SCALE_LIMIT: case V_GAP_CONSTANT: case V_THICKNESS: case V_TARGET_TOLERANCE: case V_SCROLLBUFFERSIZE_: case V_PICKVNUM: case V_PICKENUM: case V_PICKFNUM: case V_LINEAR_METRIC_MIX: case V_QUADRATIC_METRIC_MIX: case V_RANDOM_SEED: break; case V_BRIGHTNESS: case V_BACKGROUND: break; case V_LAST_ERROR: case V_DIFFUSION_: case V_INTEGRAL_ORDER_1D: case V_INTEGRAL_ORDER_2D: case V_DIFFUSION: case V_PS_STRINGWIDTH_: case V_PS_FIXEDEDGEWIDTH_: case V_PS_TRIPLEEDGEWIDTH_: case V_PS_GRIDEDGEWIDTH_: case V_PS_CONEDGEWIDTH_: case V_PS_BAREEDGEWIDTH_: case V_PS_LABELSIZE_: case V_MINDEG_MARGIN: case V_MINDEG_DEBUG_LEVEL: case V_MINDEG_MIN_REGION_SIZE: case V_WINDOW_ASPECT_RATIO: case V_CORONA_STATE: case V_STRING_CURVE_TOLERANCE: break; case V_INTEGRAL_ORDER: kb_error(1413, "Please use integral_order_1d or integral_order_2d.\n",WARNING); break; default: sprintf(msg,"Cannot set internal variable '%s'.\n",keywordname(left)); kb_error(1414,msg,EXPRESSION_ERROR); } list[listtop].stack_delta = -1; break; case FIX_QUANTITY_: case UNFIX_QUANTITY_: case SET_Q_FIXED_: case SET_Q_ENERGY_: case SET_Q_INFO_: case SET_Q_CONSERVED_: list[listtop].type = type; list[listtop].op1.quant_id = left; /* named quantity var number */ list[listtop].flags |= EPHEMERAL; break; case FIX_PARAMETER_: case UNFIX_PARAMETER_: list[listtop].type = type; list[listtop].op1.name_id = left; /* variable number */ if ( !(globals(left)->flags & ORDINARY_PARAM ) ) { sprintf(errmsg,"'%s' cannot be made an optimizing parameter.\n", globals(left)->name); kb_error(2223,errmsg,COMMAND_ERROR); } list[listtop].flags |= EPHEMERAL; break; case PRINT_ARRAY_LVALUE_: list[listtop].type = type; list[listtop].left = left-listtop; /* arraylvalue */ list[listtop].op2.name_id = list[left].op2.name_id; list[listtop].stack_delta = -1; break; case PRINT_ARRAYPART_: list[listtop].type = type; list[listtop].left = left-listtop; /* arrayhead */ list[listtop].op1.indexcount = list[left].op2.indexcount; list[listtop].flags |= EPHEMERAL; list[listtop].stack_delta = -list[listtop].op1.indexcount; break; case PRINT_ARRAY_: case PRINT_PROCEDURE_: case EXPRINT_PROCEDURE_: case PRINT_LETTER_: list[listtop].type = type; list[listtop].op1.name_id = left; list[listtop].flags |= EPHEMERAL; break; case PRINT_PERM_PROCEDURE_: list[listtop].type = type; list[listtop].op1.name_id = left; break; case UNREDEFINE_SINGLE_: list[listtop].type = type; list[listtop].op1.letter = right; if ( right > 127 ) kb_error(1415,"Illegal unredefine.\n",COMMAND_ERROR); break; case REDEFINE_SINGLE_: list[listtop].type = type; list[listtop].op1.letter = right; /* letter */ list[listtop].op5.locals = localbase; if ( localbase ) localbase->flags |= LL_IN_USE; if ( right > 127 ) kb_error(1416,"Illegal redefine.\n",COMMAND_ERROR); right = listtop; subtree_swap(&left,&right); list[right].op2.jumpsize = left - right; /* proc root */ list[listtop].line_no = line_no; list[listtop].file_no = file_no; listtop++; list[listtop].type = SET_PROC_END_; list[listtop].left = right - listtop; /* action node */ list[listtop].right = left - listtop; /* procedure */ break; case SET_PROCEDURE_: { struct global *g = globals(right); list[listtop].type = type; list[listtop].op1.name_id = right; /* variable number */ if ( g->flags & ORDINARY_PARAM ) { sprintf(errmsg,"Cannot redefine variable '%s' as a command.\n", g->name); kb_error(3357,errmsg,COMMAND_ERROR); } g->flags |= SUBROUTINE; list[listtop].op5.locals = localbase; if ( localbase ) { list[listtop].flags |= HAS_LOCALLIST; localbase->flags |= LL_IN_USE; } right = listtop; subtree_swap(&left,&right); list[right].op2.jumpsize = left - right; /* proc root */ list[listtop].line_no = line_no; list[listtop].file_no = file_no; listtop++; list[listtop].type = SET_PROC_END_; list[listtop].left = right - listtop; /* action node */ list[listtop].right = left - listtop; /* procedure */ list[listtop].flags |= EPHEMERAL; break; } case SET_PERM_PROCEDURE_: { struct global *g = globals(right); list[listtop].type = type; list[listtop].op1.name_id = right; /* variable number */ if ( g->flags & ORDINARY_PARAM ) { sprintf(errmsg,"Cannot redefine variable '%s' as a command.\n", g->name); kb_error(2516,errmsg,COMMAND_ERROR); } g->flags |= SUBROUTINE; right = listtop; subtree_swap(&left,&right); /* see if any ephemeral stuff, and mark as part of permanent cmd */ for ( n = right ; n < listtop ; n++ ) { if ( list[n].flags & EPHEMERAL ) { sprintf(errmsg,"Non-permanent items in definition of %s.\n", g->name); kb_error(2517,errmsg,COMMAND_ERROR); } list[n].flags |= PERMNODE; } list[right].op2.jumpsize = left - right; /* proc root */ list[listtop].line_no = line_no; list[listtop].file_no = file_no; list[listtop].op5.locals = localbase; if ( localbase ) { list[listtop].flags |= HAS_LOCALLIST; localbase->flags |= LL_IN_USE; } listtop++; list[listtop].type = SET_PERM_PROC_END_; list[listtop].left = right - listtop; /* action node */ list[listtop].right = left - listtop; /* procedure */ break; } case ARGLIST_: list[listtop].type = type; list[listtop].left = left ? left - listtop : 0; /* prev args */ if ( right ) { list[listtop].op1.name_id = right; /* name id */ globals(right)->flags |= ORDINARY_PARAM; } list[listtop].op2.argcount = /* count of arguments */ (left ? list[left].op2.argcount : 0) + (right?1:0); list[listtop].op3.argtype = int_val; /* argument type */ break; case FUNCTION_DEF_START_: case FUNCTION_PROTO_START_: { struct global *g = globals(left); list[listtop].type = type; list[listtop].op1.name_id = left; /* name id */ if ( g->flags & ORDINARY_PARAM ) { sprintf(errmsg,"Cannot redefine '%s' as a function.\n",g->name); kb_error(3358,errmsg,COMMAND_ERROR); } g->flags |= FUNCTION_NAME; /* op2.jumpsize for skip ahead, set later */ /* op3.argcount for argument count */ list[listtop].op4.ret_type = right; /* type of return value */ break; } case FUNCTION_HEAD_: list[listtop].type = type; list[listtop].left = left - listtop; /* function def start */ list[listtop].right = right - listtop; /* function arglist */ list[listtop].op1.name_id = list[left].op1.name_id; /* name id */ list[left].op3.argcount = list[right].op2.argcount; /* arg count */ globals(list[left].op1.name_id)->attr.procstuff.argcount = list[right].op2.argcount; list[listtop].op4.ret_type = list[left].op4.ret_type; break; case SET_FUNCTION_: list[listtop].type = type; list[listtop].left = left - listtop; /* function head */ list[listtop].right = right - listtop; /* function body */ list[listtop].op1.name_id = list[left].op1.name_id; /* name id */ n = left + list[left].left; list[n].op2.jumpsize = listtop - n; /* for FUNCTION_START_ jump */ list[listtop].flags |= EPHEMERAL; list[listtop].op4.ret_type = list[left].op4.ret_type; if ( topflag ) globals(list[left].op1.name_id)->flags |= IN_DATAFILE_TOP; break; case FUNCTION_PROTO_: list[listtop].type = type; list[listtop].left = left - listtop; /* function head */ list[listtop].op1.name_id = list[left].op1.name_id; /* name id */ n = left + list[left].left; list[n].op2.jumpsize = listtop - n; /* for FUNCTION_PROTO_START_ jump */ list[n].type = FUNCTION_PROTO_START_; list[listtop].flags |= EPHEMERAL; list[listtop].op4.ret_type = list[left].op4.ret_type; /* type */ break; case FUNCTION_CALL_: { struct global *g = globals(left); list[listtop].type = type; if ( right ) list[listtop].left = right - listtop; /* argument expressions */ list[listtop].op1.name_id = left; /* function name id */ if ( list[right].op1.argcount != g->attr.procstuff.argcount ) { sprintf(errmsg,"Function \"%s\" needs %d arguments; call has %d,\n", g->name,g->attr.procstuff.argcount,list[right].op1.argcount); kb_error(2620,errmsg,COMMAND_ERROR); } list[listtop].op2.argcount = g->attr.procstuff.argcount; list[listtop].stack_delta = 1 - g->attr.procstuff.argcount; list[listtop].datatype = g->type <= MAX_NUMERIC_TYPE ? REAL_TYPE : g->type; break; } case FUNCTION_CALL_RETURN_: list[listtop].type = type; list[listtop].left = left - listtop; /* to FUNCTION_CALL */ list[listtop].op2.argcount = list[left].op2.argcount; break; case PROCEDURE_DEF_START_: case PROCEDURE_PROTO_START_: { struct global *g = globals(left); list[listtop].type = type; list[listtop].op1.name_id = left; /* name id */ if ( g->flags & ORDINARY_PARAM ) { sprintf(errmsg,"Cannot redefine '%s' as a function.\n",g->name); kb_error(1417,errmsg,COMMAND_ERROR); } g->flags |= PROCEDURE_NAME; /* op2.jumpsize for skip ahead, set later */ /* op3.argcount for argument count */ break; } case PROCEDURE_HEAD_: { struct global *g; list[listtop].type = type; list[listtop].left = left - listtop; /* procedure def start */ list[listtop].right = right - listtop; /* procedure arglist */ list[listtop].op1.name_id = list[left].op1.name_id; /* name id */ list[left].op3.argcount = list[right].op2.argcount; /* arg count */ g = globals(list[left].op1.name_id); g->attr.procstuff.argcount = list[right].op2.argcount; break; } case SET_ARGSPROC_: list[listtop].type = type; list[listtop].left = left - listtop; /* function head */ list[listtop].right = right - listtop; /* function body */ list[listtop].op1.name_id = list[left].op1.name_id; /* name id */ n = left + list[left].left; list[n].op2.jumpsize = listtop - n; /* for PROCEDURE_START_ jump */ list[listtop].flags |= EPHEMERAL; if ( topflag ) globals(list[left].op1.name_id)->flags |= IN_DATAFILE_TOP; break; case PROCEDURE_PROTO_: list[listtop].type = type; list[listtop].left = left - listtop; /* function head */ list[listtop].op1.name_id = list[left].op1.name_id; /* name id */ n = left + list[left].left; list[n].op2.jumpsize = listtop - n; /* for PROCEDURE_PROTO_START_ jump */ list[n].type = PROCEDURE_PROTO_START_; list[listtop].flags |= EPHEMERAL; break; case PROCEDURE_CALL_: { struct global *g = globals(left); list[listtop].type = type; if ( right ) list[listtop].left = right - listtop; /* argument expressions */ list[listtop].op1.name_id = left; /* procedure name id */ if ( list[right].op1.argcount != g->attr.procstuff.argcount ) { sprintf(errmsg,"Procedure \"%s\" needs %d arguments; call has %d.\n", g->name,g->attr.procstuff.argcount,list[right].op1.argcount); kb_error(2623,errmsg,COMMAND_ERROR); } list[listtop].op2.argcount = g->attr.procstuff.argcount; list[listtop].stack_delta = -g->attr.procstuff.argcount; break; } case PROCEDURE_CALL_RETURN_: list[listtop].type = type; list[listtop].left = left - listtop; /* to PROCEDURE_CALL */ list[listtop].op2.argcount = list[left].op2.argcount; break; case DEFINE_IDENT_: { struct global *g = globals(left); list[listtop].type = type; list[listtop].op1.name_id = left; /* variable number */ list[listtop].op2.valtype = right; /* type */ g->type = right; if ( right == STRING_TYPE ) g->flags |= STRINGVAL; else if ( !(g->flags & ANY_TYPE) ) { g->flags |= ORDINARY_PARAM; list[listtop].flags |= EPHEMERAL; g->attr.varstuff.delta = OPTPARAM_DELTA; g->attr.varstuff.pscale = 1.0; } break; } case DEFINE_ARRAY_: { struct global *g = globals(left); int datatype = int_val; /* kludge due to shortage of arguments */ int dim = list[right].op1.indexcount; int datastart; if ( g->flags & ORDINARY_PARAM ) { sprintf(errmsg, "Illegal to convert ordinary variable \"%s\" into array.\n", g->name); kb_error(3289,errmsg,Q_ERROR); } if ( g->flags & FIXED_SIZE_ARRAY ) { sprintf(errmsg,"Cannot re-declare a local fixed-size array %s.\n", g->name); kb_error(3176,errmsg,COMMAND_ERROR); } if ( dim > MAXARRAYDIMS ) { sprintf(errmsg, "Maximum number of array dimensions is %d; %s has %d.\n", MAXARRAYDIMS,g->name,dim); kb_error(3177,errmsg,COMMAND_ERROR); } if ( !g->attr.arrayptr ) g->attr.arrayptr = (struct array*)mycalloc(1,sizeof(struct array)); g->attr.arrayptr->datatype = g->type = datatype; g->attr.arrayptr->dim = dim; g->flags |= ARRAY_PARAM; g->flags &= ~ORDINARY_PARAM; /* see if fixed-zized local array, so we can allocate stack space */ if ( g->flags & GLOB_LOCALVAR && !(g->flags & UNFIXED_SIZE_ARRAY) ) { /* test previous nodes for fixed sizes */ int cantflag = 0; /* set if not fixed sizes */ int indexset_node = right; int rexpr_node=0; int i; for ( i = dim-1 ; i >= 0 ; i-- ) { rexpr_node = indexset_node + (i ? list[indexset_node].right : list[indexset_node].left); if ( list[rexpr_node].type != PUSHCONST ) { cantflag = 1; break; } indexset_node += list[indexset_node].left; } if ( cantflag == 0 ) { int datacount = 1; int pointercount = 1; g->flags |= FIXED_SIZE_ARRAY; g->attr.arrayptr = (struct array*)mycalloc(1,sizeof(struct array)); g->attr.arrayptr->datatype = g->type = datatype; g->attr.arrayptr->dim = dim; /* gather sizes */ indexset_node = right; for ( i = dim-1 ; i >= 0 ; i-- ) { int size; rexpr_node = indexset_node + (i ? list[indexset_node].right : list[indexset_node].left); size = (int)list[rexpr_node].op1.real; g->attr.arrayptr->sizes[i] = size; datacount *= size; if ( i < dim-1 ) pointercount *= size; indexset_node += list[indexset_node].left; } /* pop index nodes off execution list */ listtop = rexpr_node; memset(list+listtop,0,sizeof(struct treenode)); g->attr.arrayptr->itemsize = datatype_size[datatype]; g->attr.arrayptr->datacount = datacount; i = add_local_var(NULL,datacount+pointercount); datastart = get_local(i).offset; g = globals(left); /* kludge since add_local_var can move things */ g->attr.arrayptr->datastart = datastart; list[listtop].type = DEFINE_FIXED_LOCAL_ARRAY_; list[listtop].op1.name_id = left; /* global variable number */ list[listtop].op2.valtype = datatype; /* type */ break; } else g->flags |= UNFIXED_SIZE_ARRAY; } list[listtop].type = type; list[listtop].left = right-listtop; /* index expression list */ list[listtop].op1.name_id = left; /* global variable number */ list[listtop].op2.valtype = datatype; g->flags |= ARRAY_PARAM; g->flags &= ~ORDINARY_PARAM; if ( !(g->flags & GLOB_LOCALVAR) && g->attr.arrayptr && g->type != datatype ) { sprintf(errmsg, "Array %s type declaration conflicts with previous type.\n", g->name); kb_error(2224,errmsg,COMMAND_ERROR); } g->type = datatype; if ( (g->attr.arrayptr->dim > 0) && (g->attr.arrayptr->dim != list[right].op1.indexcount) ) { sprintf(errmsg,"Cannot change the number of dimensions of array %s.\n", g->name); kb_error(2225,errmsg,COMMAND_ERROR); } else g->attr.arrayptr->dim = list[right].op1.indexcount; list[listtop].flags |= EPHEMERAL; list[listtop].stack_delta = -list[right].op1.indexcount; break; } case ARRAY_HEAD_: list[listtop].type = type; list[listtop].op1.name_id = right; /* global variable number */ list[listtop].left = left-listtop; /* indexset */ list[listtop].op2.indexcount = list[left].op1.indexcount; /* # indices */ /* if ( list[left].op1.indexcount != globals(right)->dim ) { sprintf(errmsg, "Array %s has wrong number of dimensions; should be %d\n", globals(int_val)->name,globals(int_val)->dim); kb_error(3360,errmsg,COMMAND_ERROR); } */ list[listtop].flags |= EPHEMERAL; break; /* whole-array syntax */ case ARRAYIDENT_: /* push datastart for an array */ list[listtop].type = type; list[listtop].op2.name_id = left; list[listtop].stack_delta = 1; break; case ATTRIB_LVALUE_ : list[listtop].type = type; if ( left ) list[listtop].left = left - listtop; list[listtop].stack_delta = 1; break; case ARRAY_LVALUE_INDEXED_: /* nop at execution */ { struct array *a = get_name_arrayptr(list[left].op2.name_id,NULL,localbase); if ( a->dim != list[right].op1.indexcount ) { sprintf(errmsg,"Array %s needs %d indexes, has %d.\n", get_name_name(list[left].op2.name_id,localbase),a->dim,list[right].op1.indexcount); kb_error(3267,errmsg,Q_ERROR); } check_readonly_attr(list[left].op2.name_id); list[listtop].type = type; list[listtop].left = left-listtop; list[listtop].right = right-listtop; list[listtop].op1.indexcount = list[right].op1.indexcount; list[listtop].op2.name_id = list[left].op2.name_id; break; } case ARRAY_EVAL_: /* rexpr: arraylvalue indexset */ { struct array *a = get_name_arrayptr(list[left].op2.name_id,NULL,localbase); if ( a->dim != list[right].op1.indexcount ) { sprintf(errmsg,"Array %s needs %d indexes, has only %d.\n", get_name_name(list[left].op2.name_id,localbase),a->dim,list[right].op1.indexcount); kb_error(3268,errmsg,Q_ERROR); } list[listtop].type = type; list[listtop].left = left-listtop; list[listtop].right = right-listtop; list[listtop].op2.name_id = list[left].op2.name_id; list[listtop].datatype = REAL_TYPE; if ( (list[left].type == ARRAY_VERTEX_NORMAL_) || (list[left].type == ARRAY_EDGE_VECTOR_) || (list[left].type == ARRAY_FACET_NORMAL_) ) list[listtop].flags |= IS_VIRTUAL_ATTR; list[listtop].stack_delta = list[right].op1.indexcount; break; } case ARRAY_ASSIGNOP_SINGLE_: check_readonly_attr(list[left].op2.name_id); list[listtop].type = type; list[listtop].left = left-listtop; list[listtop].right = right-listtop; if ( check_recalc_attr(list[left].op2.name_id) ) list[listtop].flags |= RECALC_NODE; /* list[listtop].stack_delta done in command.yac */ break; case DOT_: /* dot product */ { int name1 = list[left].op2.name_id; int name2 = list[right].op2.name_id; int adim = get_name_dim(name1,localbase); int bdim = get_name_dim(name2,localbase); if ( (adim != 1) || (bdim != 1) ) kb_error(3329,"Dot product operands must be one dimensional.\n", COMMAND_ERROR); if ( (get_name_datatype(name1,localbase) != REAL_TYPE) || (get_name_datatype(name2,localbase) != REAL_TYPE) ) kb_error(3330,"Dot product operands must be of type real.\n", COMMAND_ERROR); list[listtop].type = type; list[listtop].left = left-listtop; list[listtop].right = right-listtop; list[listtop].datatype = REAL_TYPE; list[listtop].op2.name_id = name1; list[listtop].op3.name_id = name2; list[listtop].stack_delta = 1; } break; case ARRAY_ASSIGNOP_ARRAY_: /* full array syntax */ /* see if we have special attributes on right side */ { int name_id = list[right].op2.name_id; check_readonly_attr(list[left].op2.name_id); if ( name_id == set_name_eltype(V_NORMAL_ATTR,VERTEX) ) list[listtop].type = ARRAY_ASSIGNOP_VERTEX_NORMAL_; else if ( name_id == set_name_eltype(E_VECTOR_ATTR,EDGE) ) list[listtop].type = ARRAY_ASSIGNOP_EDGE_VECTOR_; else if ( name_id == set_name_eltype(F_NORMAL_ATTR,FACET) ) list[listtop].type = ARRAY_ASSIGNOP_FACET_NORMAL_; else list[listtop].type = type; list[listtop].left = left-listtop; /* lvalue */ list[listtop].right = right-listtop; /* rvalue */ list[listtop].op2.name_id = list[left].op2.name_id; list[listtop].op3.name_id = list[right].op2.name_id; if ( check_recalc_attr(list[left].op2.name_id) ) list[listtop].flags |= RECALC_NODE; /* op1.assigntype will be set back in command.yac */ /* Can do dimension check now */ if ( check_array_dims_same(list[left].op2.name_id, list[right].op2.name_id) == 0 ) kb_error(4378,"Arrays don't have same number of dimensions or types different.\n", COMMAND_ERROR); list[listtop].stack_delta = -2; } break; case ARRAY_ASSIGNOP_SCALAR_: /* full array syntax */ check_readonly_attr(list[left].op2.name_id); list[listtop].type = type; list[listtop].op2.name_id = list[left].op2.name_id; list[listtop].left = left-listtop; /* lvalue array */ list[listtop].right = right-listtop; /* rexpr */ if ( check_recalc_attr(list[left].op2.name_id) ) list[listtop].flags |= RECALC_NODE; list[listtop].stack_delta = -2; /* op1.assigntype will be set back in command.yac */ break; case ARRAY_RVALUE_: /* nop needed for tree construction */ list[listtop].type = type; list[listtop].left = left-listtop; /* lvalue array */ list[listtop].right = right-listtop; /* rexpr */ break; case ARRAY_ASSIGNOP_S_X_A_: /* full array syntax, scalar times array */ check_readonly_attr(list[left].op2.name_id); list[listtop].type = type; list[listtop].op2.name_id = list[left].op2.name_id; list[listtop].left = left-listtop; /* lvalue array */ list[listtop].right = right-listtop; /* rvalue */ if ( check_recalc_attr(list[left].op2.name_id) ) list[listtop].flags |= RECALC_NODE; /* op1.assigntype will be set back in command.yac */ list[listtop].stack_delta = -3; break; case ARRAY_ASSIGNOP_A_P_A_: /* full array syntax, array plus array */ check_readonly_attr(list[left].op2.name_id); list[listtop].type = type; list[listtop].left = left-listtop; /* lvalue array */ list[listtop].right = right-listtop; /* rvalue */ list[listtop].op2.name_id = list[left].op2.name_id; if ( check_recalc_attr(list[left].op2.name_id) ) list[listtop].flags |= RECALC_NODE; /* op1.assigntype will be set back in command.yac */ list[listtop].stack_delta = -3; break; case ARRAY_ASSIGNOP_A_S_A_: /* full array syntax, array subtract array */ check_readonly_attr(list[left].op2.name_id); list[listtop].type = type; list[listtop].left = left-listtop; /* lvalue array */ list[listtop].right = right-listtop; /* rvalue */ list[listtop].op2.name_id = list[left].op2.name_id; if ( check_recalc_attr(list[left].op2.name_id) ) list[listtop].flags |= RECALC_NODE; /* op1.assigntype will be set back in command.yac */ list[listtop].stack_delta = -3; break; /* end whole-array syntax */ case ARRAYASSIGN: list[listtop].type = type; list[listtop].left = left-listtop; /* arrayhead */ list[listtop].right = right-listtop; /* rhs */ list[listtop].op1.assigntype = assigntype; list[listtop].op2.name_id = list[left].op1.name_id; /* glob var number */ g = globals(list[left].op1.name_id); if ( list[left].op2.indexcount != g->attr.arrayptr->dim ) { sprintf(errmsg, "Array %s has wrong number of dimensions; should be %d\n", g->name,g->attr.arrayptr->dim); kb_error(2226,errmsg,COMMAND_ERROR); } if ( g->flags & READONLY ) { sprintf(errmsg, "Array %s is read-only.\n",g->name); kb_error(2846,errmsg,COMMAND_ERROR); } list[listtop].stack_delta = -list[left].op2.indexcount-1; if ( !(g->flags & PERMANENT) ) list[listtop].flags |= EPHEMERAL; break; case ARRAYEVAL: { g = globals(list[left].op1.name_id); list[listtop].type = type; list[listtop].left = left-listtop; list[listtop].op2.name_id = list[left].op1.name_id; list[listtop].flags |= EPHEMERAL; if ( list[left].op2.indexcount != g->attr.arrayptr->dim ) { sprintf(errmsg, "Array %s has wrong number of dimensions; should be %d\n", g->name, g->attr.arrayptr->dim ); kb_error(2608,errmsg,COMMAND_ERROR); } list[listtop].stack_delta = -list[left].op2.indexcount+1; list[listtop].datatype = (g->type <= MAX_NUMERIC_TYPE) ? REAL_TYPE: g->type; break; } case PDELTA_LVALUE_: case PSCALE_LVALUE_: list[listtop].type = type; list[listtop].op1.name_id = left; /* variable number */ break; case SET_DELTA_: case SET_PARAM_SCALE: list[listtop].type = type; list[listtop].op1.name_id = left; /* variable number */ list[listtop].left = right - listtop; /* value expression */ list[listtop].op2.assigntype = assigntype; if ( !(globals(left)->flags & ANY_TYPE) ) globals(left)->flags |= ORDINARY_PARAM; if ( perm_flag ) globals(left)->flags |= PERMANENT; list[listtop].flags |= EPHEMERAL; list[listtop].stack_delta = -1; break; case SET_ELEMENT_GLOBAL_: { struct global *g = globals(left); int eltype = -1; /* of right side */ list[listtop].type = type; list[listtop].op1.name_id = left; /* variable number */ list[listtop].left = right - listtop; /* value expression */ if ( !(g->flags & ANY_TYPE) ) g->flags |= ORDINARY_PARAM; if ( (list[listtop].datatype < VERTEX_TYPE) || (list[listtop].datatype > FACETEDGE_TYPE) ) kb_error(2884,"Right side expression must be an element.\n", COMMAND_ERROR); switch ( list[right].op1.eltype ) { case VERTEX: eltype = VERTEX_TYPE; break; case EDGE: eltype = EDGE_TYPE; break; case FACET: eltype = FACET_TYPE; break; case BODY: eltype = BODY_TYPE; break; case FACETEDGE: eltype = FACETEDGE_TYPE; break; default: kb_error(3684,"Illegal element type.\n",Q_ERROR); } if ( g->type && (g->type != eltype) ) { sprintf(errmsg, "Cannot assign expression of type %s to variable %s of type %s.\n", datatype_name[eltype],globals(left)->name,datatype_name[g->type]); kb_error(3686,errmsg,Q_ERROR); } else g->type = eltype; if ( perm_flag ) g->flags |= PERMANENT; list[listtop].flags |= EPHEMERAL; list[listtop].stack_delta = -1; break; } case SET_GLOBAL_: case PLUSASSIGN_: case SUBASSIGN_: case MULTASSIGN_: case DIVASSIGN_: { struct global *g = globals(left); list[listtop].type = type; list[listtop].op1.name_id = left; /* variable number */ list[listtop].left = right - listtop; /* value expression */ if ( !(g->flags & ANY_TYPE) ) g->flags |= ORDINARY_PARAM; if ( !(g->flags & (ORDINARY_PARAM|GLOB_LOCALVAR)) ) { sprintf(errmsg, "Variable %s is not proper type for left side of assighmant.\n",g->name); kb_error(3021,errmsg,Q_ERROR); } if ( perm_flag ) g->flags |= PERMANENT; list[listtop].flags |= EPHEMERAL; list[listtop].stack_delta = -1; g->type = list[right].datatype; break; } case SET_PERM_GLOBAL_: list[listtop].type = type; list[listtop].op1.name_id = left; /* variable number */ list[listtop].left = right - listtop; /* value expression */ perm_globals(left)->flags |= ORDINARY_PARAM; perm_globals(left)->attr.varstuff.delta = OPTPARAM_DELTA; perm_globals(left)->attr.varstuff.pscale = 1.0; list[listtop].stack_delta = -1; break; case SET_SGLOBAL_: case SET_PERM_SGLOBAL_: list[listtop].type = type; list[listtop].op1.name_id = left; /* variable number */ list[listtop].left = right - listtop; /* value expression */ globals(left)->flags |= STRINGVAL; globals(left)->flags &= ~ORDINARY_PARAM; if ( type == SET_SGLOBAL_ ) list[listtop].flags |= EPHEMERAL; if ( perm_flag ) globals(left)->flags |= PERMANENT; list[listtop].stack_delta = -1; break; case SHOW_: case SHOW_EXPR_: list[listtop].type = type; /* put node in front of expression */ right = listtop; subtree_swap(&left,&right); list[right].op1.skipsize = left - right; list[listtop].line_no = line_no; list[listtop].file_no = file_no; listtop++; list[listtop].type = SHOW_END_; list[listtop].left = right - listtop; /* action node */ list[listtop].right = left - listtop; /* procedure */ break; case SELF_ELEMENT_: list[listtop].type = type; list[listtop].op1.eltype = left; list[listtop].op2.localnum = elsym->localnum; break; case SINGLE_ELEMENT_EXPR_: list[listtop].type = type; list[listtop].left = left - listtop; /* action node */ list[listtop].op1.eltype = right; list[listtop].stack_delta = 1; list[listtop].datatype = VERTEX_TYPE + right; break; case ELEMENT_IDENT_: list[listtop].type = type; list[listtop].op3.name_id = left; list[listtop].op2.localnum = add_local_var(NULL,1); break; case SYMBOL_ELEMENT_: list[listtop].type = type; list[listtop].op1.eltype = symtable[left].type; list[listtop].op2.localnum = symtable[left].localnum; list[listtop].op5.string = (char*)mycalloc(strlen(symtable[left].name)+1,1); list[listtop].flags |= HAS_STRING_5; strcpy(list[listtop].op5.string,symtable[left].name); break; case SINGLE_ELEMENT_: /* generator */ list[listtop].type = SINGLE_ELEMENT_INIT_; /* for dummy loop */ list[listtop].line_no = line_no; list[listtop].file_no = file_no; list[listtop].stack_delta = 3; listtop++; list[listtop].type = type; list[listtop].left = left-listtop; /* index value */ list[listtop].right = -1; /* to INIT */ list[listtop].op2.localnum = add_local_var(NULL,1); /* local will hold id at runtime */ /* op1 will hold jump to end of loop */ break; case INDEXED_ELEMENT_: list[listtop].type = type; list[listtop].left = right-listtop; /* index value */ list[listtop].op1.eltype = left; /* element type */ list[listtop].stack_delta = -1; list[listtop].op2.localnum = add_local_var(NULL,1); /* local will hold id at runtime */ /* Sanity check on index */ if ( list[right].type == PUSHCONST ) { if ( list[right].op1.real == 0.0 ) kb_error(2228,"Element index must be nonzero.\n",COMMAND_ERROR); } break; case INDEXED_SUBTYPE_: list[listtop].type = type; list[listtop].left = left-listtop; /* single element */ list[listtop].right = right-listtop; /* index value */ list[listtop].op1.eltype = subtype; /* element type */ list[listtop].stack_delta = -1; list[listtop].op2.localnum = add_local_var(NULL,1); /* local will hold id at runtime */ /* Sanity check on index */ if ( list[right].type == PUSHCONST ) { if ( list[right].op1.real <= 0.0 ) kb_error(2229,"Element index must be positive.\n",COMMAND_ERROR); } /* some type checking */ etype = list[left].op1.eltype; if ( etype == subtype ) kb_error(1418,"Cannot do same subtype as type of element.\n", COMMAND_ERROR); switch ( etype ) { case VERTEX: switch ( subtype /* subtype */ ) { case BODY: case FACETEDGE: kb_error(1419,"Unimplemented subtype of vertex.\n",COMMAND_ERROR); } break; case EDGE: switch ( subtype /* subtype */ ) { case BODY: kb_error(1420,"Unimplemented subtype of edge.\n",COMMAND_ERROR); } break; case FACET: switch ( subtype /* subtype */ ) { case FACETEDGE: kb_error(1421,"Unimplemented subtype of facet.\n",COMMAND_ERROR); } break; case BODY: switch ( subtype /* subtype */ ) { case FACETEDGE: case VERTEX: case EDGE: kb_error(1422,"Unimplemented subtype of body.\n",COMMAND_ERROR); } break; case FACETEDGE: switch ( subtype /* subtype */ ) { case FACETEDGE: case VERTEX: case BODY: kb_error(1423,"Unimplemented subtype of facet.\n",COMMAND_ERROR); } break; } break; case SHOW_VOL_: case CHECK_: case LONG_JIGGLE_: case RAW_VERAVG_: case STABILITY_TEST_: case UTEST_: case GO_: case SHELL_: case ALICE_: case TOPINFO_: case RECALC_: case COUNTS_: case EXTRAPOLATE_: case HESSIAN_: case SOBOLEV_: case SHOWQ_:case RAWEST_VERAVG_: case DIRICHLET_: case BOTTOMINFO_: case CLOSE_SHOW_: case REBODY_: case LIST_PROCS_: case HESSIAN_MENU_: case DIRICHLET_SEEK_: case SOBOLEV_SEEK_: case CONVERT_TO_QUANTS_: case LIST_ATTRIBUTES_: case REORDER_STORAGE_: case RENUMBER_ALL_: case DUMP_MEMLIST_: case FREE_DISCARDS_: case REPARTITION_: case SUBCOMMAND_: case ABORT_: case SIMPLEX_TO_FE_: list[listtop].type = type; break; case LIST_BOUNDARY_: case LIST_CONSTRAINT_: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].stack_delta = -1; break; case LIST_QUANTITY_: list[listtop].type = type; list[listtop].op1.quant_id = left; break; case LIST_METHOD_INSTANCE_: list[listtop].type = type; list[listtop].op1.meth_id = left; break; /* toggles */ case LINEAR_: case QUADRATIC_: case QUIETGO_: case KUSNER_: case ESTIMATE_: case DETURCK_: case HOMOTHETY_: case SQGAUSS_: case AUTOPOP_: case AUTOCHOP_: case QUIET_: case OLD_AREA_: case APPROX_CURV_: case RUNGE_KUTTA_: case CHECK_INCREASE_: case DEBUG_: case MEAN_CURV_: case RIBIERE_CG_: case DIFFUSION_: case GRAVITY_: case CONJ_GRAD_: case TRANSFORMS_: case CONF_EDGE_SQCURV_: case EFFECTIVE_AREA_: case AREA_FIXED_: case RAW_CELLS_: case CONNECTED_CELLS_: case CLIPPED_CELLS_: case THICKEN_: case SHOW_INNER_: case SHOW_OUTER_: case COLORMAP_: case HESSIAN_DIFF_: case POST_PROJECT_: case MEAN_CURV_INT_: case OPTIMIZE_: case NORMAL_CURVATURE_: case DIV_NORMAL_CURVATURE_: case SHADING_: case FACET_COLORS_: case BOUNDARY_CURVATURE_: case NORMAL_MOTION_: case PINNING_: case VIEW_4D_: case MEMDEBUG_: case METRIC_CONVERSION_: case AUTORECALC_: case GV_BINARY_: case SELF_SIMILAR_: case AUTODISPLAY_: case FORCE_POS_DEF_: case ASSUME_ORIENTED_: case HESSIAN_QUIET_: case JIGGLE_TOGGLE_: case HESSIAN_NORMAL_: case YSMP_: case BUNCH_KAUFMAN_: case QUANTITIES_ONLY_: case LINEAR_METRIC_: case GEOMVIEW_TOGGLE_: case GEOMPIPE_TOGGLE_: case SQUARED_GRADIENT_: case H_INVERSE_METRIC_: case HESSIAN_DOUBLE_NORMAL_: case INTERP_BDRY_PARAM_: case LOGFILE_TOGGLE_: case HESSIAN_NORMAL_ONE_: case PSCOLORFLAG_: case GRIDFLAG_: case CROSSINGFLAG_: case LABELFLAG_: case SHOW_ALL_QUANTITIES_: case HESSIAN_NORMAL_PERP_: case HESSIAN_SPECIAL_NORMAL_: case ITDEBUG_: case METIS_FACTOR_: case VOLGRADS_EVERY_: case ZENER_DRAG_: case BACKCULL_: case INTERP_NORMALS_: case TORUS_FILLED_: case VERBOSE_: case AMBIENT_PRESSURE_: case DIRICHLET_MODE_: case SOBOLEV_MODE_: case KRAYNIKPOPVERTEX_FLAG_: case KEYLOGFILE_TOGGLE_: case KRAYNIKPOPEDGE_FLAG_: case VISIBILITY_TEST_: case SPARSE_CONSTRAINTS_: case BLAS_FLAG_: case AUGMENTED_HESSIAN_: case BREAK_AFTER_WARNING_: case RGB_COLORS_FLAG_: case CIRCULAR_ARC_DRAW_: case BEZIER_BASIS_: case SMOOTH_GRAPH_: case MPI_DEBUG_: case POP_DISJOIN_: case POP_TO_EDGE_: case POP_TO_FACE_: case POP_ENJOIN_: case FULL_BOUNDING_BOX_: case BIG_ENDIAN_: case LITTLE_ENDIAN_: case QUIETLOAD_: case SLICE_VIEW_: case FUNCTION_QUANTITY_SPARSE_: case CLIP_VIEW_: case STAR_FINAGLING_: case FORCE_DELETION_: case AUTOPOP_QUARTIC_: case IMMEDIATE_AUTOPOP_: list[listtop].type = type; list[listtop].op1.toggle_state = left; /* toggle state */ break; case SYSTEM_: case READ_: case EXEC_: case PARALLEL_EXEC_: case CHDIR_: case SHOW_TRANS_: case SET_COLORMAP_: case TRANSFORM_EXPR_: case KEYLOGFILE_: case OOGLFILE_: case BINARY_OFF_FILE_: case GEOMVIEW_: case GEOMPIPE_: case POSTSCRIPT_: case LOGFILE_: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].stack_delta = -1; break; case TASK_EXEC_: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].right = right - listtop; list[listtop].stack_delta = -2; break; case TEXT_SPOT_: /* just accumulate two arguments */ list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].right = right - listtop; break; case DISPLAY_TEXT_: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].right = right - listtop; list[listtop].stack_delta = -2; /* pops 3, leaves string id */ break; case DELETE_TEXT_: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].stack_delta = -1; break; case PRINTFHEAD_: case BINARY_PRINTFHEAD_: case ERRPRINTFHEAD_: case SPRINTFHEAD_: list[listtop].type = type; /* copy and convert escape sequences */ if ( left ) { list[listtop].left = left - listtop; list[listtop].stack_delta = 0; /* since pushes result string */ } else { char *c, *s; list[listtop].op1.string = (char*)mycalloc(strlen(yytext)+1,sizeof(char)); list[listtop].flags |= HAS_STRING; s = yytext; /* source */ c = list[listtop].op1.string; /* destination */ while ( *s ) if ( *s == '\\' ) { switch ( s[1] ) { case 'n' : *(c++) = '\n'; s += 2; break; case 't' : *(c++) = '\t'; s += 2; break; case 'b' : *(c++) = '\b'; s += 2; break; case 'r' : *(c++) = '\r'; s += 2; break; default: *(c++) = s[1]; s += 2; break; } } else *(c++) = *(s++); *c = '\0'; } break; case SPRINTF_: /* with expressions */ list[listtop].type = type; list[listtop].left = left - listtop; /* has string */ list[listtop].right = right - listtop; /* expressions */ list[left].type = PRESPRINTF_; list[left].stack_delta = 0; list[listtop].stack_delta = -list[right].op1.argcount; list[listtop].datatype = STRING_TYPE; list[listtop].flags |= DEALLOCATE_POINTER; break; case PRINTF_: /* with expressions */ case BINARY_PRINTF_: /* with expressions */ case ERRPRINTF_: list[listtop].type = type; list[listtop].left = left - listtop; /* has string */ list[listtop].right = right - listtop; /* expressions */ list[left].type = PREPRINTF_; list[left].stack_delta = 0; list[listtop].stack_delta = -list[right].op1.argcount-1; break; case EXPRLIST_: list[listtop].type = type; list[listtop].left = left - listtop; /* expr */ if ( right ) { list[listtop].right = right - listtop; /* exprlist */ list[listtop].op1.argcount = list[right].op1.argcount+1; } else list[listtop].op1.argcount = 1; /* arg count */ break; case DATAFILENAME_: case WARNING_MESSAGES_: case GET_TRANSFORM_EXPR_: list[listtop].type = type; list[listtop].stack_delta = 1; list[listtop].datatype = STRING_TYPE; break; case HELP_KEYWORD: list[listtop].type = type; list[listtop].op1.string = (char*)mycalloc(strlen(yytext)+1,sizeof(char)); list[listtop].flags |= HAS_STRING; strcpy(list[listtop].op1.string,yytext); list[listtop].datatype = STRING_TYPE; break; default: more_makenode(type,left,right); break; } if ( listtop > listmax-2 ) kb_error(1336,"Expression too long",COMMAND_ERROR); list[listtop].line_no = line_no; list[listtop].file_no = file_no; return listtop++; } /********************************************************************* * * Function: subtree_swap() * * Purpose: Swap two adjacent subtrees at the end of the list * so they are in proper order for sequential execution. * * Return: Adjust index values in arguments. */ void subtree_swap ARGS2((left,right), int *left, /* index of left subtree */ int *right) /* index of right subtree */ { int n; struct treenode *temp; if ( *left && *right && (*left < *right ) ) { int leftstart = *left, rightstart = *right; int leftsize,rightsize; for (;;) { if ( list[leftstart].left && list[leftstart].right ) { if ( list[leftstart].left < list[leftstart].right ) leftstart += list[leftstart].left; else leftstart += list[leftstart].right; } else if ( list[leftstart].left ) leftstart += list[leftstart].left; else if ( list[leftstart].right ) leftstart += list[leftstart].right; else break; } for (;;) { if ( list[rightstart].left && list[rightstart].right ) { if ( list[rightstart].left < list[rightstart].right ) rightstart += list[rightstart].left; else rightstart += list[rightstart].right; } else if ( list[rightstart].left ) rightstart += list[rightstart].left; else if ( list[rightstart].right ) rightstart += list[rightstart].right; else break; } if ( rightstart != *left + 1 ) { sprintf(errmsg, "Internal error: Left: %d-%d right: %d-%d subtrees not adjacent.\n", leftstart,*left,rightstart,*right); kb_error(1387,errmsg,COMMAND_ERROR); } /* swap subtrees */ leftsize = *left - leftstart + 1; rightsize = *right - rightstart +1; temp = (struct treenode*)temp_calloc(leftsize,sizeof(struct treenode)); memcpy((char*)(temp),(char*)(list+leftstart), leftsize*sizeof(struct treenode)); kb_memmove((char*)(list+leftstart),(char*)(list+rightstart), rightsize*sizeof(struct treenode)); memcpy((char*)(list+leftstart+rightsize),(char*)(temp), leftsize*sizeof(struct treenode)); temp_free((char*)temp); /* adjust subtree pointers */ *left += rightsize; *right -= leftsize; /* kludge to adjust pointers to local variables */ for ( n = leftstart ; n < leftstart+rightsize ; n++ ) { if ( ((list[n].type == BREAK_) || (list[n].type == CONTINUE_)) && (n+leftsize+list[n].op1.skipsize < leftstart) ) list[n].op1.skipsize += leftsize; } for ( n = leftstart+rightsize ; n < leftstart+rightsize+leftsize ; n++ ) { if ( ((list[n].type == BREAK_) || (list[n].type == CONTINUE_)) && (n-rightsize+list[n].op1.skipsize < leftstart) ) list[n].op1.skipsize -= rightsize; } } } /********************************************************************** * * Function: more_makenode() * * Purpose: Overflow from makenode(), to keep code under 32K for Mac */ void more_makenode ARGS3((type,left,right), NTYPE type, NTYPE left, NTYPE right) { int n,etype; switch (type) { case QUOTATION_: list[listtop].type = type; list[listtop].op1.string = (char*)mycalloc(strlen(yytext)+1,sizeof(char)); list[listtop].flags |= HAS_STRING; strcpy(list[listtop].op1.string,yytext); list[listtop].stack_delta = 1; list[listtop].datatype = STRING_TYPE; break; case DUMP_: case LOAD_: case PERMLOAD_: case ADDLOAD_: list[listtop].type = type; if ( left ) { list[listtop].left = left - listtop; list[listtop].stack_delta = -1; } break; case SINGLE_LETTER_: list[listtop].type = SINGLE_LETTER_; list[listtop].op1.letter = left; break; case SINGLE_REDEFD_: list[listtop].type = SINGLE_REDEFD_; list[listtop].op1.letter = left; break; case CMDLIST_: list[listtop].type = type; list[listtop].left = left - listtop; if ( right ) list[listtop].right = right - listtop; break; case COMMAND_BLOCK_: list[listtop].type = COMMAND_BLOCK_; list[listtop].left = left - listtop; break; case REPEAT_INIT_: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].stack_delta = +1; break; case REPEAT_: list[listtop].type = REPEAT_; list[listtop].left = left - listtop; list[listtop].right = right - listtop; list[left].op1.skipsize = right+1-left; /* for skipping body */ list[listtop].stack_delta = -2; break; case EPRINT_: list[listtop].type = type; break; case COND_TEST_: list[listtop].type = COND_TEST_; list[listtop].left = left - listtop; /* inx will be target for false jmp */ list[listtop].stack_delta = -1; break; case COND_EXPR_: list[listtop].type = COND_EXPR_; list[listtop].left = left - listtop; list[listtop].right = right - listtop; list[left].op1.skipsize = listtop - left; /* to skip TRUE part */ list[listtop].stack_delta = -1; /* since one expression skipped */ list[listtop].datatype = list[right].datatype; break; case COND_ELSE_: /* really the continue node at root of IF */ list[listtop].type = COND_ELSE_; list[listtop].left = left - listtop; list[listtop].right = right - listtop; list[left].op1.skipsize = listtop - left; /* to skip ELSE part */ if ( list[left].datatype != list[right].datatype ) kb_error(3710,"Datatypes disagree in conditional expression.\n", COMMAND_ERROR); list[listtop].datatype = list[right].datatype; break; case SIZEOF_ATTR_: /* current attribute dimension */ list[listtop].type = SIZEOF_ATTR_; list[listtop].op1.extranum = left; /* attr num */ list[listtop].op2.eltype = right; /* etype */ list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case SIZEOF_ARRAY_: /* current array total size */ list[listtop].type = SIZEOF_ARRAY_; list[listtop].op1.name_id = left; /* array id */ list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case SIZEOF_STRING_: /* string length */ list[listtop].type = SIZEOF_STRING_; list[listtop].left = left-listtop; /* string expr */ list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case DEFINE_CONSTRAINT_: { int old_verb_flag; list[listtop].type = type; /* Parse-time evaluation and construction of quantity */ datafile_flag = PRETEND_DATAFILE; old_verb_flag = verb_flag; verb_flag = 0; list[listtop].op1.con_id = read_constraint(); datafile_flag = NOT_DATAFILE; verb_flag = old_verb_flag; if ( tok == ';' ) /* in case reentrant parser ate ';' */ kb_unput(';'); } break; case DEFINE_BOUNDARY_: { int old_verb_flag; list[listtop].type = type; /* Parse-time evaluation and construction of quantity */ datafile_flag = PRETEND_DATAFILE; old_verb_flag = verb_flag; verb_flag = 0; list[listtop].op1.bdry_id = read_boundary(); datafile_flag = NOT_DATAFILE; verb_flag = old_verb_flag; if ( tok == ';' ) /* in case reentrant parser ate ';' */ kb_unput(';'); } break; case DEFINE_QUANTITY_: { int old_verb_flag; list[listtop].type = type; /* Parse-time evaluation and construction of quantity */ datafile_flag = 1; old_verb_flag = verb_flag; verb_flag = 0; list[listtop].op1.quant_id = read_quantity(); datafile_flag = 0; verb_flag = old_verb_flag; if ( tok == ';' ) /* in case reentrant parser ate ';' */ kb_unput(';'); } break; case DEFINE_METHOD_INSTANCE_: { int old_verb_flag; list[listtop].type = type; /* Parse-time evaluation and construction of quantity */ datafile_flag = 1; old_verb_flag = verb_flag; verb_flag = 0; list[listtop].op1.quant_id = read_method_instance(); datafile_flag = 0; verb_flag = old_verb_flag; if ( tok == ';' ) /* in case reentrant parser ate ';' */ kb_unput(';'); } break; case IS_DEFINED_: /* Parse-time evaluation. */ list[listtop].type = type; list[listtop].left = left-listtop; list[listtop].stack_delta = 0; list[listtop].datatype = REAL_TYPE; break; case PUSHCONST: list[listtop].type = PUSHCONST; list[listtop].op1.real = real_val; list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case PUSH_NAMED_QUANTITY: list[listtop].type = type; list[listtop].op1.quant_id = left; list[listtop].flags |= EPHEMERAL; list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case PUSH_METHOD_INSTANCE_: list[listtop].type = type; list[listtop].op1.meth_id = left; list[listtop].flags |= EPHEMERAL; list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case PUSHPI: list[listtop].type = type; list[listtop].op1.real = M_PI; list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case PUSHE: list[listtop].type = type; list[listtop].op1.real = M_E; list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case PUSHG: list[listtop].type = type; list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case DATE_AND_TIME_: list[listtop].type = type; list[listtop].stack_delta = 1; list[listtop].datatype = STRING_TYPE; break; case TOGGLEVALUE: list[listtop].type = type; list[listtop].op1.toggle_id = left; /* which toggle */ list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case GET_TORUS_PERIODS_: case GET_INVERSE_PERIODS_: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].right = right - listtop; list[listtop].stack_delta = -1; list[listtop].datatype = REAL_TYPE; break; case EXP: if ( is_constant(left) ) { /* fold constants */ listtop--; list[listtop].type = PUSHCONST; list[listtop].op1.real = exp(list[listtop].op1.real); break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case LOG: if ( is_constant(left) ) { /* fold constants */ listtop--; list[listtop].type = PUSHCONST; if ( list[listtop].op1.real < 0.0 ) kb_error(1425,"log of negative number.\n", COMMAND_ERROR ); list[listtop].op1.real = log(list[listtop].op1.real); break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case ABS: if ( is_constant(left) ) { /* fold constants */ listtop--; list[listtop].type = PUSHCONST; list[listtop].op1.real = fabs(list[listtop].op1.real); break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case SIN: if ( is_constant(left) ) { /* fold constants */ listtop--; list[listtop].type = PUSHCONST; list[listtop].op1.real = sin(list[listtop].op1.real); break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case SINH: if ( is_constant(left) ) { /* fold constants */ REAL x; listtop--; x = exp(list[listtop].op1.real); list[listtop].type = PUSHCONST; list[listtop].op1.real = (x-1/x)/2; break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case COSH: if ( is_constant(left) ) { /* fold constants */ REAL x; listtop--; x = exp(list[listtop].op1.real); list[listtop].type = PUSHCONST; list[listtop].op1.real = (x+1/x)/2; break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case TANH: if ( is_constant(left) ) { /* fold constants */ REAL x; listtop--; x = exp(list[listtop].op1.real); list[listtop].type = PUSHCONST; list[listtop].op1.real = (x-1/x)/(x+1/x); break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case ATANH: if ( is_constant(left) ) { /* fold constants */ REAL x; listtop--; x = list[listtop].op1.real; list[listtop].type = PUSHCONST; list[listtop].op1.real = (log(x+1)-log(1-x))/2; break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case ASINH: if ( is_constant(left) ) { /* fold constants */ REAL x; listtop--; x = list[listtop].op1.real; list[listtop].type = PUSHCONST; list[listtop].op1.real = log(x + sqrt(x*x+1)); break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case ACOSH: if ( is_constant(left) ) { /* fold constants */ REAL x; listtop--; x = list[listtop].op1.real; list[listtop].type = PUSHCONST; list[listtop].op1.real = 2*log(sqrt(x+1) + sqrt(x-1))-log(2.0); break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case ASIN: if ( is_constant(left) ) { /* fold constants */ listtop--; list[listtop].type = PUSHCONST; if ( fabs(list[listtop].op1.real) > 1.0 ) kb_error(1426,"asin argument out of bounds.\n", COMMAND_ERROR ); list[listtop].op1.real = asin(list[listtop].op1.real); break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case COS: if ( is_constant(left) ) { /* fold constants */ listtop--; list[listtop].type = PUSHCONST; list[listtop].op1.real = cos(list[listtop].op1.real); break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case ACOS: if ( is_constant(left) ) { /* fold constants */ listtop--; list[listtop].type = PUSHCONST; if ( fabs(list[listtop].op1.real) > 1.0 ) kb_error(1427,"acos argument out of bounds.\n", COMMAND_ERROR ); list[listtop].op1.real = acos(list[listtop].op1.real); break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case TAN: if ( is_constant(left) ) { /* fold constants */ listtop--; list[listtop].type = PUSHCONST; list[listtop].op1.real = tan(list[listtop].op1.real); break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case ATAN: if ( is_constant(left) ) { /* fold constants */ listtop--; list[listtop].type = PUSHCONST; list[listtop].op1.real = atan(list[listtop].op1.real); break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case SQR: if ( is_constant(left) ) { /* fold constants */ listtop--; list[listtop].type = PUSHCONST; list[listtop].op1.real = list[listtop].op1.real*list[listtop].op1.real; break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case SQRT: if ( is_constant(left) ) { /* fold constants */ listtop--; list[listtop].type = PUSHCONST; if ( list[listtop].op1.real < 0.0 ) kb_error(1428,"sqrt of negative number.\n", COMMAND_ERROR ); list[listtop].op1.real = sqrt(list[listtop].op1.real); break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case ELLIPTICK: if ( is_constant(left) ) { /* fold constants */ listtop--; list[listtop].type = PUSHCONST; list[listtop].op1.real = ellipticK(list[listtop].op1.real); break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case ELLIPTICE: if ( is_constant(left) ) { /* fold constants */ listtop--; list[listtop].type = PUSHCONST; list[listtop].op1.real = ellipticE(list[listtop].op1.real); break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case CEIL_: if ( is_constant(left) ) { /* fold constants */ listtop--; list[listtop].type = PUSHCONST; list[listtop].op1.real = ceil(list[listtop].op1.real); break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case FLOOR_: if ( is_constant(left) ) { /* fold constants */ listtop--; list[listtop].type = PUSHCONST; list[listtop].op1.real = floor(list[listtop].op1.real); break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case CHS: if ( is_constant(left) ) { /* fold constants */ listtop--; list[listtop].type = PUSHCONST; list[listtop].op1.real = -list[listtop].op1.real; break; } list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case MAXIMUM_: if ( is_constant(right) && is_constant(left) ) { /* fold constants */ listtop -= 2; if ( (left != listtop) || (right != listtop+1) ) kb_error(1429,"Constant folding not working.",COMMAND_ERROR); list[listtop].op1.real = (list[left].op1.real > list[right].op1.real) ? list[left].op1.real : list[right].op1.real; list[listtop].type = PUSHCONST; list[listtop].stack_delta = 1; break; } list[listtop].right = right - listtop; list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].stack_delta = -1; list[listtop].datatype = REAL_TYPE; break; case MINIMUM_: if ( is_constant(right) && is_constant(left) ) { /* fold constants */ listtop -= 2; if ( (left != listtop) || (right != listtop+1) ) kb_error(1430,"Internal error: Constant folding not working.", COMMAND_ERROR); list[listtop].op1.real = (list[left].op1.real < list[right].op1.real) ? list[left].op1.real : list[right].op1.real; list[listtop].type = PUSHCONST; list[listtop].stack_delta = 1; break; } list[listtop].right = right - listtop; list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].stack_delta = -1; list[listtop].datatype = list[right].datatype; break; case ATAN2_: case INCOMPLETE_ELLIPTICF: case INCOMPLETE_ELLIPTICE: if ( is_constant(right) && is_constant(left) ) { /* fold constants */ listtop -= 2; if ( (left != listtop) || (right != listtop+1) ) kb_error(1431,"Internal error: Constant folding not working.", COMMAND_ERROR); switch(type) { case ATAN2_: list[listtop].op1.real = atan2(list[left].op1.real,list[right].op1.real); break; case INCOMPLETE_ELLIPTICF: list[listtop].op1.real = incompleteEllipticF(list[left].op1.real,list[right].op1.real); break; case INCOMPLETE_ELLIPTICE: list[listtop].op1.real = incompleteEllipticE(list[left].op1.real,list[right].op1.real); break; } list[listtop].type = PUSHCONST; list[listtop].stack_delta = 1; break; } list[listtop].right = right - listtop; list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].stack_delta = -1; list[listtop].datatype = REAL_TYPE; break; case WRAP_COMPOSE_: list[listtop].right = right - listtop; list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].stack_delta = -1; list[listtop].datatype = REAL_TYPE; break; case WRAP_INVERSE_: list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; case POW: case '^': if ( is_constant(right) ) { REAL p = list[right].op1.real; if ( floor(p) == p ) { /* integer power */ n = (int)p; if ( n == 0 ) { kb_error(1432,"Exponent 0. Did you mean this?\n",WARNING); listtop--; list[listtop].type = REPLACECONST; list[listtop].op1.real = 1.0; break; } else if ( n == 1 ) { /* leave alone */ kb_error(1433,"Exponent 1. Did you mean this?\n",WARNING); listtop-=2; break; } else if ( n == 2 ) { listtop--; if ( is_constant(left) ) { listtop--; list[listtop].type = PUSHCONST; list[listtop].op1.real *= list[listtop].op1.real; list[listtop].stack_delta = 1; } else { list[listtop].type = SQR; list[listtop].left = left - listtop; list[listtop].stack_delta = 0; } break; } else if ( n == -1 ) { listtop--; if ( is_constant(left) ) { listtop--; list[listtop].op1.real = 1./list[listtop].op1.real; } else { list[listtop].type = INV; list[listtop].left = left - listtop; list[listtop].stack_delta = 0; } break; } /* general */ if ( is_constant(left) ) { /* fold constants */ int k; REAL x; listtop -= 2; list[listtop].type = PUSHCONST; list[listtop].stack_delta = 1; if ( left != listtop ) kb_error(1434,"Internal error: Constant folding not working.", COMMAND_ERROR); x = list[left].op1.real; for ( k = 1 ; k < abs(n) ; k++ ) list[left].op1.real *= x; if ( n < 0 ) list[left].op1.real = 1/list[left].op1.real; break; } listtop--; /* pop constant power */ list[listtop].left = left - listtop; list[listtop].op1.intpow = n; list[listtop].type = INTPOW; break; } } if ( is_constant(right) && is_constant(left) ) { /* fold constants */ listtop -= 2; if ( (left != listtop) || (right != listtop+1) ) kb_error(1435,"Internal error: Constant folding not working.", COMMAND_ERROR); list[listtop].op1.real = pow(list[left].op1.real,list[right].op1.real); list[listtop].type = PUSHCONST; list[listtop].stack_delta = 1; break; } list[listtop].right = right - listtop; list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].stack_delta = -1; list[listtop].datatype = REAL_TYPE; break; case '+': if ( is_constant(right) && is_constant(left) ) { /* fold constants */ listtop -= 2; if ( (left != listtop) || (right != listtop+1) ) kb_error(1436,"Internal error: Constant folding not working.", COMMAND_ERROR); list[listtop].op1.real = list[left].op1.real + list[right].op1.real; list[listtop].type = PUSHCONST; list[listtop].stack_delta = 1; } else if ( is_constant(right) && (list[right].op1.real==0.0) ) listtop -= 2; /* just leave left summand */ else if ( is_constant(left) && (list[left].op1.real==0.0) ) { subtree_swap(&left,&right); listtop -= 2; } else { list[listtop].right = right - listtop; list[listtop].left = left - listtop; list[listtop].type = PLUS; list[listtop].stack_delta = -1; } list[listtop].datatype = REAL_TYPE; break; case '-': if ( is_constant(right) && is_constant(left) ) { /* fold constants */ listtop -= 2; if ( (left != listtop) || (right != listtop+1) ) kb_error(1437,"Internal error: Constant folding not working.", COMMAND_ERROR); list[listtop].op1.real = list[left].op1.real - list[right].op1.real; list[listtop].type = PUSHCONST; list[listtop].stack_delta = 1; } else if ( is_constant(right) && (list[right].op1.real==0.0) ) listtop -= 2; /* just leave left summand */ else if ( is_constant(left) && (list[left].op1.real==0.0) ) { subtree_swap(&left,&right); listtop -= 1; list[listtop].left = right - listtop; list[listtop].type = CHS; list[listtop].stack_delta = 0; } else { list[listtop].right = right - listtop; list[listtop].left = left - listtop; list[listtop].type = MINUS; list[listtop].stack_delta = -1; } list[listtop].datatype = REAL_TYPE; break; case '=': /* equality as low priority minus */ if ( is_constant(right) && is_constant(left) ) { /* fold constants */ listtop -= 2; if ( (left != listtop) || (right != listtop+1) ) kb_error(1438,"Internal error: Constant folding not working.", COMMAND_ERROR); list[listtop].op1.real = list[left].op1.real - list[right].op1.real; list[listtop].type = PUSHCONST; list[listtop].stack_delta = 1; } else if ( is_constant(right) && (list[right].op1.real==0.0) ) listtop -= 2; /* just leave left summand */ else { list[listtop].right = right - listtop; list[listtop].left = left - listtop; list[listtop].type = EQUATE; list[listtop].stack_delta = -1; } list[listtop].datatype = REAL_TYPE; break; case '*': if ( is_constant(right) && is_constant(left) ) { /* fold constants */ listtop -= 2; if ( (left != listtop) || (right != listtop+1) ) kb_error(1439,"Internal error: Constant folding not working.", COMMAND_ERROR); list[listtop].op1.real = list[left].op1.real * list[right].op1.real; list[listtop].type = PUSHCONST; list[listtop].stack_delta = 1; } else if ( is_constant(left) && (list[left].op1.real==0.0) ) { listtop = left; /* just leave 0 */ } else if ( ( is_constant(right) && (list[right].op1.real==0.0) ) ) { subtree_swap(&left,&right); listtop = right; /* just leave 0 */ } else if ( is_constant(right) && (list[right].op1.real==1.0) ) listtop -= 2; /* just leave left multiplicand */ else if ( is_constant(left) && (list[left].op1.real==1.0) ) { subtree_swap(&left,&right); listtop -= 2; } else { list[listtop].right = right - listtop; list[listtop].left = left - listtop; list[listtop].type = TIMES; list[listtop].stack_delta = -1; } list[listtop].datatype = REAL_TYPE; break; case '/': if ( is_constant(right) && is_constant(left) ) { /* fold constants */ listtop -= 2; if ( (left != listtop) || (right != listtop+1) ) kb_error(1440,"Internal error: Constant folding not working.", COMMAND_ERROR); if ( list[right].op1.real == 0.0 ) { kb_error(1441,"Divide by zero.",COMMAND_ERROR); } list[listtop].op1.real = list[left].op1.real / list[right].op1.real; list[listtop].type = PUSHCONST; } else if ( is_constant(left) && (list[left].op1.real==0.0) ) { listtop = left; /* just leave 0 */ list[listtop].op1.real = 0.0; list[listtop].type = PUSHCONST; list[listtop].stack_delta = 1; } else if ( is_constant(right) && (list[right].op1.real==0.0) ) kb_error(1442,"Divide by zero.",COMMAND_ERROR); else if ( is_constant(right) && (list[right].op1.real==1.0) ) listtop -= 2; /* just leave numerator */ else { list[listtop].right = right - listtop; list[listtop].left = left - listtop; list[listtop].type = DIVIDE; list[listtop].stack_delta = -1; } list[listtop].datatype = REAL_TYPE; break; case '%': if ( is_constant(right) && is_constant(left) ) { /* fold constants */ listtop -= 2; if ( (left != listtop) || (right != listtop+1) ) kb_error(1443,"Internal error: Constant folding not working.", COMMAND_ERROR); list[listtop].op1.real = list[left].op1.real - floor(list[left].op1.real / list[right].op1.real) *list[right].op1.real; list[listtop].type = PUSHCONST; list[listtop].stack_delta = 1; } else { list[listtop].right = right - listtop; list[listtop].left = left - listtop; list[listtop].type = REALMOD; list[listtop].stack_delta = -1; } list[listtop].datatype = REAL_TYPE; break; case IMOD_: if ( is_constant(right) && is_constant(left) ) { /* fold constants */ listtop -= 2; if ( (left != listtop) || (right != listtop+1) ) kb_error(1444,"Internal error: Constant folding not working.", COMMAND_ERROR); list[listtop].op1.real = floor(list[left].op1.real) - floor(floor(list[left].op1.real)/floor(list[right].op1.real)) *floor(list[right].op1.real); list[listtop].type = PUSHCONST; list[listtop].stack_delta = 1; } else { list[listtop].right = right - listtop; list[listtop].left = left - listtop; list[listtop].type = IMOD_; list[listtop].stack_delta = -1; } list[listtop].datatype = REAL_TYPE; break; case IDIV_: if ( is_constant(right) && is_constant(left) ) { /* fold constants */ listtop -= 2; if ( (left != listtop) || (right != listtop+1) ) kb_error(1445,"Internal error: Constant folding not working.", COMMAND_ERROR); list[listtop].op1.real = (int)(list[left].op1.real)/(int)(list[right].op1.real); list[listtop].type = PUSHCONST; list[listtop].stack_delta = 1; } else { list[listtop].right = right - listtop; list[listtop].left = left - listtop; list[listtop].type = IDIV_; list[listtop].stack_delta = -1; } list[listtop].datatype = REAL_TYPE; break; case PUSHPARAM: if ( maxp == 0 ) kb_error(1446,"Constant expression required.\n",EXPRESSION_ERROR); list[listtop].type = PUSHPARAM; list[listtop].op1.coordnum = n = coord_num-1; if ( (n < 0) || (n >= maxp) ) { sprintf(errmsg,"Coordinate number too high: %d\n",n+1); kb_error(1447,errmsg,EXPRESSION_ERROR); } list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case USERFUNC: list[listtop].type = USERFUNC; list[listtop].op1.userfunc = (NTYPE)int_val-1; if ( int_val < 1 || (userfunc[int_val-1] == NULL) ) { sprintf(errmsg,"Invalid user function number: %d\n",int_val); kb_error(1448,errmsg,EXPRESSION_ERROR); } list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case VIEW_MATRIX_: list[listtop].right = right - listtop; list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].stack_delta = -1; list[listtop].datatype = REAL_TYPE; break; case GET_INTERNAL_: list[listtop].type = GET_INTERNAL_; list[listtop].op1.name_id = left; list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case PUSHQPRESSURE_: case PUSHQTARGET_: case PUSHQVALUE_: case PUSHQMODULUS_: case PUSHQTOLERANCE_: case PUSHMMODULUS_: case PUSHQVOLCONST_: case PUSHQPARAMETER_1_: case PUSHQFIXED_: case PUSHQENERGY_: case PUSHQCONSERVED_: case PUSHQINFO_ONLY_: list[listtop].type = type; list[listtop].op1.quant_id = left; list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case PUSHMVALUE_: list[listtop].type = type; list[listtop].op1.meth_id = left; if ( reading_comp_quant_flag ) { int i; struct gen_quant *q = GEN_QUANT(cur_quant); METH_INSTANCE(left)->flags |= Q_COMPOUND; /* see if in quantity's list */ for ( i = 0 ; i < q->method_count ; i++ ) if ( q->meth_inst[i] == left ) break; if ( i == q->method_count ) { /* add to list */ METH_INSTANCE(left)->quant = cur_quant; METH_INSTANCE(left)->quant_index = q->method_count; attach_method_num(cur_quant,left); } } list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case VIEW_MATRIX_LVALUE_: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].right = right - listtop; break; case SET_VIEW_MATRIX_: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].right = right - listtop; list[listtop].op2.assigntype = assigntype; list[listtop].stack_delta = -3; break; case SET_QMODULUS_: case SET_QTOLERANCE_: case SET_MMODULUS_: case SET_QVOLCONST_: case SET_QTARGET_: case SET_QPARAMETER_1_: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].op1.quant_id = right; list[listtop].op2.assigntype = assigntype; list[listtop].stack_delta = -1; break; case DYNAMIC_LOAD_FUNC_: list[listtop].type = DYNAMIC_LOAD_FUNC_; list[listtop].op1.funcptr = globals(left)->value.funcptr; list[listtop].op2.name_id = left; list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case PUSHDELTA_: case PUSH_PARAM_SCALE: list[listtop].type = type; list[listtop].op1.name_id = left; list[listtop].flags |= EPHEMERAL; list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case PUSH_PARAM_FIXED: list[listtop].type = type; list[listtop].op1.name_id = left; list[listtop].flags |= EPHEMERAL; list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case PUSH_PARAM_EXTRA_: if ( (right != V_VELOCITY_ATTR) && (right != V_FORCE_ATTR) ) kb_error(2473,"Illegal attribute of variable.\n",COMMAND_ERROR); list[listtop].type = type; list[listtop].op1.name_id = left; /* which parameter */ list[listtop].op2.extranum = right; /* which attribute */ list[listtop].flags |= EPHEMERAL; list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case PUSHGLOBAL_: { struct global *g = globals(left); list[listtop].type = PUSHGLOBAL_; list[listtop].op1.name_id = left; list[listtop].flags |= EPHEMERAL; if ( (g->flags & METHOD_NAME) && reading_comp_quant_flag ) { struct method_instance *mi=METH_INSTANCE(left); mi->flags |= Q_COMPOUND; quant_flags[basic_gen_methods[mi->gen_method].type] |= GEN_QUANT(cur_quant)->flags & (Q_ENERGY|Q_FIXED|Q_INFO|Q_CONSERVED); attach_method_num(cur_quant,left); } list[listtop].datatype = (g->type <= MAX_NUMERIC_TYPE) ? REAL_TYPE : g->type; list[listtop].stack_delta = 1; break; } case PUSH_PERM_GLOBAL_: case PERM_STRINGGLOBAL_: { struct global *g; list[listtop].type = type; list[listtop].op1.name_id = left; list[listtop].stack_delta = 1; g = globals(left); list[listtop].datatype = g->type <= MAX_NUMERIC_TYPE ? REAL_TYPE : g->type; break; } case PERM_PROCEDURE_: list[listtop].type = type; list[listtop].op1.name_id = left; break; case STRINGGLOBAL_: list[listtop].type = type; list[listtop].op1.name_id = left; list[listtop].flags |= EPHEMERAL; list[listtop].stack_delta = 1; list[listtop].datatype = STRING_TYPE; break; case PROCEDURE_: list[listtop].type = type; list[listtop].op1.name_id = left; list[listtop].flags |= EPHEMERAL; break; case SET_CONSTRAINT_GLOBAL: case UNSET_CONSTRAINT_GLOBAL: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].stack_delta = -1; break; case SET_CONSTRAINT_NAME_GLOBAL: case UNSET_CONSTRAINT_NAME_GLOBAL: list[listtop].type = type; list[listtop].stack_delta = 0; list[listtop].op3.connum = globals(left)->value.cnum; break; case ON_CONSTRAINT_: case HIT_CONSTRAINT_: case ON_BOUNDARY_: list[listtop].type = type; list[listtop].left = left - listtop; list[listtop].flags |= EPHEMERAL; list[listtop].stack_delta = 0; list[listtop].datatype = REAL_TYPE; break; case ON_CONSTRAINT_NAME: case HIT_CONSTRAINT_NAME: list[listtop].type = type; list[listtop].op3.connum = globals(left)->value.cnum; /* actual constraint number */ list[listtop].flags |= EPHEMERAL; list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case ON_BOUNDARY_NAME: list[listtop].type = type; list[listtop].op3.bdrynum = globals(left)->value.bnum; list[listtop].flags |= EPHEMERAL; list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case ON_QUANTITY_: list[listtop].type = type; list[listtop].op2.quant_id = left; list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case ON_METHOD_INSTANCE_: list[listtop].type = type; list[listtop].op2.meth_id = left; list[listtop].stack_delta = 1; list[listtop].datatype = REAL_TYPE; break; case INDEXED_COORD_: list[listtop].type = type; list[listtop].left = left - listtop; if ( list[left].op1.indexcount != 1 ) kb_error(2568,"Coordinate can have only one index.\n",COMMAND_ERROR); list[listtop].datatype = REAL_TYPE; break; case PRINT_VERTEXNORMAL_: list[listtop].type = type; list[listtop].left = left - listtop; /* element */ break; case GET_VERTEXNORMAL_: list[listtop].type = type; list[listtop].left = left - listtop; /* index set */ if ( list[left].op1.indexcount != 1 ) kb_error(2569,"Vertexnormal can have only one index.\n", COMMAND_ERROR); list[listtop].datatype = REAL_TYPE; break; case INDEXED_ATTRIBUTE: /* get extra attribute value */ { struct extra *ex; etype = (unsigned)right >> YYTYPESHIFT; right = right & YYSHIFTMASK; ex = EXTRAS(etype) + right; list[listtop].type = GET_EXTRA_ATTR_; list[listtop].op2.eltype = etype; list[listtop].op3.extranum = right; /* which extra */ list[listtop].left = left - listtop; /* index */ list[listtop].stack_delta = 1 - ex->array_spec.dim; if ( ex->array_spec.dim != list[left].op1.indexcount ) { sprintf(errmsg,"Attribute %s must have %d indices.\n", ex->name,ex->array_spec.dim); kb_error(2513,errmsg,COMMAND_ERROR); } list[listtop].flags |= EPHEMERAL; } break; case PRINT_ATTR_ARRAY_: /* print element attribute array or slice */ { struct extra *ex; int exnum; etype = int_val >> YYTYPESHIFT; exnum = int_val & YYSHIFTMASK; ex = EXTRAS(etype) + exnum; list[listtop].type = type; list[listtop].op1.indexcount = right ? list[right].op1.indexcount : 0; /* indices */ list[listtop].op2.eltype = etype; list[listtop].op3.extranum = exnum; /* which extra */ list[listtop].left = left - listtop; /* element */ if ( right ) list[listtop].right = right - listtop; /* index */ if ( ex->array_spec.dim < list[right].op1.indexcount ) { sprintf(errmsg,"Attribute %s must have at most %d indices.\n", ex->name,ex->array_spec.dim); kb_error(2644,errmsg,COMMAND_ERROR); } list[listtop].flags |= EPHEMERAL; list[listtop].stack_delta = -list[listtop].op1.indexcount; } break; case QUALIFIED_ATTRIBUTE: list[listtop].type = QUALIFIED_ATTRIBUTE; list[listtop].left = left-listtop; list[listtop].right = right-listtop; list[listtop].datatype = REAL_TYPE; etype = list[left].op1.eltype; /* element type */ /* element type error checking */ check_element_type(list[right].type,etype); break; case ATTRIBUTE: list[listtop].type = (NTYPE)left; /* some special treatments */ switch ( left ) { case COORD_: list[listtop].op2.coordnum = right-1; if ( right > SDIM ) kb_error(2475,"Coordinate dimension exceeds space dimension.\n", COMMAND_ERROR); break; case PARAM_: list[listtop].op2.coordnum = right-1; using_param_flag = 1; break; case GET_INSTANCE_: case GET_QUANTITY_: list[listtop].op2.quant_id = right; break; case GET_EXTRA_ATTR_: { struct extra *ex; etype = (unsigned)right >> YYTYPESHIFT; ex = EXTRAS(etype) + (right & YYSHIFTMASK); if ( ex->array_spec.dim > 0 ) { sprintf(errmsg, "\"%s\" is an indexed attribute; index is missing. Or use element name to print as full array.\n", ex->name); kb_error(2634,errmsg,Q_ERROR); } list[listtop].op2.eltype = etype; list[listtop].op3.extranum = (right & YYSHIFTMASK); list[listtop].flags |= EPHEMERAL; list[listtop].stack_delta = 1-ex->array_spec.dim; } break; } if ( left != GET_EXTRA_ATTR_ ) list[listtop].stack_delta = 1; break; case SET_ATTRIBUTE_: case SET_ATTRIBUTE_L: case SET_ATTRIBUTE_A: if ( elsym == NULL ) kb_error(1477,"Don't have element for attribute to apply to.\n", COMMAND_ERROR); list[listtop].type = type; if ( left ) list[listtop].left = left-listtop; /* value */ if ( right ) { /* index; check which attributes are legal */ list[listtop].right = right-listtop; switch ( attr_kind ) { case SET_EXTRA_ATTR_: list[listtop].flags |= EPHEMERAL; break; case SET_COORD_1: case SET_PARAM_1: break; default: kb_error(1478,"Attribute is not indexed.\n",COMMAND_ERROR); } } list[listtop].op1.localnum = elsym->localnum; list[listtop].op2.attr_kind = attr_kind; /* to node */ /* error checking on attributes and element types */ etype = elsym->type; switch ( attr_kind ) { case SET_EXTRA_ATTR_: { struct extra *ex; ex = EXTRAS(etype); for ( n = 0 ; n < web.skel[etype].extra_count ; n++,ex++ ) if ( stricmp(ex->name,set_extra_name) == 0 ) break; if ( n == web.skel[etype].extra_count ) { sprintf(errmsg,"Invalid extra attribute name '%s'.\n", set_extra_name); kb_error(1479,errmsg,COMMAND_ERROR); } list[listtop].op3.extra_info = (etype << ESHIFT) + n; if ( !(ex->flags & DIMENSIONED_ATTR) && right ) { sprintf(errmsg,"Cannot use index with attribute '%s'.\n",ex->name); kb_error(2499,errmsg,COMMAND_ERROR); } if ( (ex->flags & DIMENSIONED_ATTR) && !right ) { sprintf(errmsg,"Must use index with attribute '%s'.\n",ex->name); kb_error(1481,errmsg,COMMAND_ERROR); } if ( right && (list[right].op1.indexcount != ex->array_spec.dim) ) { sprintf(errmsg,"Attribute '%s' has %d indexes.\n", ex->name,ex->array_spec.dim); kb_error(2498,errmsg,COMMAND_ERROR); } list[listtop].stack_delta = -1-ex->array_spec.dim; } break; case SET_WRAP_: if ( etype != EDGE ) kb_error(2239,"Wrap only for edges.\n",COMMAND_ERROR); list[listtop].stack_delta = -1; break; case SET_COORD_: case SET_COORD_1: case SET_COORD_2: case SET_COORD_3: case SET_COORD_4: case SET_COORD_5: case SET_COORD_6: case SET_COORD_7: case SET_COORD_8: if ( attr_kind-SET_COORD_1 >= SDIM ) kb_error(2543,"Coordinate dimension exceeds space dimension.\n", COMMAND_ERROR); if ( etype != VERTEX ) kb_error(1482,"Coordinates only for vertices.\n",COMMAND_ERROR); list[listtop].stack_delta = -1; break; case SET_PARAM_: case SET_PARAM_1: case SET_PARAM_2: case SET_PARAM_3: case SET_PARAM_4: if ( etype != VERTEX ) kb_error(1483,"Boundary parameters only for vertices.\n",COMMAND_ERROR); list[listtop].stack_delta = -1; break; case SET_TAG_: if ( etype != FACET ) kb_error(1484,"Tag only for facets now.\n",COMMAND_ERROR); list[listtop].stack_delta = -1; break; case SET_OPACITY_: if ( etype != FACET ) kb_error(1485,"Opacity only for facets.\n",COMMAND_ERROR); list[listtop].stack_delta = -1; break; case SET_FRONTCOLOR_: case SET_BACKCOLOR_: if ( (etype != FACET) ) kb_error(1304,"Front or back color only for facets.\n",COMMAND_ERROR); list[listtop].stack_delta = -1; break; case SET_FRONTBODY_: case SET_BACKBODY_: if ( web.representation == STRING ) { if ( (etype != FACET) && (etype != EDGE) ) kb_error(1486, "Frontbody or backbody only for facets or edges in string model.\n", COMMAND_ERROR); } else if (etype != FACET) kb_error(1308,"Frontbody or backbody only for facets.\n",COMMAND_ERROR); list[listtop].stack_delta = -1; break; case SET_COLOR_: if ( !((etype == FACET) || (etype == EDGE)) ) kb_error(1487,"Color only for edges or facets.\n",COMMAND_ERROR); list[listtop].stack_delta = -1; break; case SET_VOLCONST_: if ( etype != BODY ) kb_error(1488,"Volconst only for bodies.\n",COMMAND_ERROR); list[listtop].stack_delta = -1; break; case SET_TARGET_: if ( etype != BODY ) kb_error(1489,"Target volume only for bodies.\n",COMMAND_ERROR); list[listtop].stack_delta = -1; break; case SET_VOLUME_: if ( etype != BODY ) kb_error(1490,"Target volume only for bodies.\n",COMMAND_ERROR); kb_error(1491, "Volume is read-only. Setting TARGET instead.\n", WARNING); list[listtop].op2.attr_kind = SET_TARGET_; list[listtop].stack_delta = -1; break; case SET_PRESSURE_: if ( etype != BODY ) kb_error(1492,"Pressure only for bodies.\n",COMMAND_ERROR); list[listtop].stack_delta = -1; break; case SET_PHASE_: case SET_CONSTRAINT_: case SET_BOUNDARY_: case SET_ORIGINAL_: list[listtop].stack_delta = -1; break; case SET_DENSITY_: if ( etype == VERTEX ) kb_error(1493,"No density for vertices.\n",COMMAND_ERROR); list[listtop].stack_delta = -1; break; case SET_FIXED_: if ( etype == FACETEDGE ) kb_error(2527,"No fixedness for facetedges.\n",COMMAND_ERROR); if ( etype == BODY ) kb_error(2528,"Use 'set body target ...' to fix volume.\n", COMMAND_ERROR); break; case SET_HIT_PARTNER_: if ( etype != VERTEX ) kb_error(3002,"Hit_partner is only for vertices.\n",COMMAND_ERROR); break; case SET_NO_DISPLAY_: if ( etype != FACET ) kb_error(1495,"No_display is only for facets.\n",COMMAND_ERROR); break; case SET_NO_REFINE_: if ( etype == BODY || etype == VERTEX ) kb_error(1496,"No no_refine for vertices or bodies.\n", COMMAND_ERROR); break; case SET_NONCONTENT_: if ( ((web.representation == STRING) && (etype != EDGE)) || ((web.representation != STRING) && (etype != FACET)) ) kb_error(2903,"Noncontent only applies to edges or facets.\n", COMMAND_ERROR); break; } /* error checking for arithmetic assigns */ if ( type == SET_ATTRIBUTE_A ) { switch ( attr_kind ) { case SET_FIXED_: case SET_TRIPLE_PT_: case SET_TETRA_PT_: case SET_AXIAL_POINT_: kb_error(2241,"Cannot assign value to this attribute with :=.\n", COMMAND_ERROR); } if ( assigntype != ASSIGN_ ) switch ( attr_kind ) { case SET_ORIENTATION_: case SET_PHASE_: case SET_OPACITY_: case SET_CONSTRAINT_: case SET_ORIGINAL_: case SET_COLOR_: case SET_FRONTCOLOR_: case SET_BACKCOLOR_: case SET_WRAP_: case SET_TAG_: case SET_FRONTBODY_: case SET_BACKBODY_: case SET_BOUNDARY_: kb_error(2242, "Cannot use arithmetic assign with this attribute.\n", COMMAND_ERROR); } } break; case EQ_: case NE_: case GE_: case LE_: case GT_: case LT_: list[listtop].right = right - listtop; list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].stack_delta = -1; list[listtop].datatype = REAL_TYPE; break; case AND_: /* for short-circuit evaluation, have to move test */ case OR_: /* node between operands */ { int top; top = listtop; list[listtop].type = type; subtree_swap(&right,&top); list[top].right = right - top; list[top].left = left - top; list[top].op1.skipsize = listtop - top; listtop++; list[listtop].type = CONJUNCTION_END; list[listtop].left = top - listtop; list[listtop].stack_delta = -1; list[listtop].datatype = REAL_TYPE; break; } case NOT_: list[listtop].left = left - listtop; list[listtop].type = type; list[listtop].datatype = REAL_TYPE; break; default: sprintf(errmsg,"Internal error: Unknown MAKENODE %d type %s\n",type,tokname(type)); kb_error(1329,errmsg,COMMAND_ERROR); } } /****************************************************************** * * Function: is_constant() * * Purpose: See if tree node type is a constant value. * Also checks that datatype is REAL_TYPE. */ int is_constant ARGS1((node), int node) { if ( list[node].datatype != REAL_TYPE ) kb_error(3711,"Operand datatype is not numeric.\n", datafile_flag ? DATAFILE_ERROR : COMMAND_ERROR); switch(list[node].type) { case PUSHPI: case PUSHE: case PUSHCONST: return 1; default: return 0; } } /********************************************************************** * * Function: check_element_type() * * purpose: see if attribute is legal for element */ void check_element_type ARGS2((attrib,etype), int attrib, int etype) { switch ( attrib ) { case GET_TAG_: if ( etype != FACET ) kb_error(1455,"Tag only for facets now.\n",COMMAND_ERROR); break; case GET_MIDV_: if ( etype != EDGE ) kb_error(1456,"Midv only for quadratic edges.\n",COMMAND_ERROR); break; case GET_SQ_MEAN_CURV_: if ( etype != VERTEX ) kb_error(1457,"Square mean curvature only for vertices.\n", COMMAND_ERROR); break; case GET_FRONTBODY_: case GET_BACKBODY_: if ( (etype != FACET) && !((etype==EDGE)&&(web.representation==STRING))) kb_error(1297,"Frontbody or backbody only for facets, or string model edges.\n", COMMAND_ERROR); break; case GET_FRONTCOLOR_: case GET_BACKCOLOR_: if ( (etype != FACET) ) kb_error(1458,"Frontcolor or backcolor only for facets.\n",COMMAND_ERROR); break; case GET_COLOR_: if ( !((etype == FACET) || (etype == EDGE)) ) kb_error(1459,"Color only for edges or facets.\n",COMMAND_ERROR); break; case BARE_: if ( (etype == BODY) || (etype == FACET) ) kb_error(1460,"No bareness for facets or bodies.\n",COMMAND_ERROR); break; case GET_SHOW_: if ( (etype != EDGE) && (etype != FACET) ) kb_error(2243,"\"Show\" only for edges or facets.\n",COMMAND_ERROR); break; case GET_ORIENTATION_: if ( (etype != EDGE) && (etype != FACET) ) kb_error(1461,"Orientation only for edges or facets.\n",COMMAND_ERROR); break; case GET_LENGTH_: if ( etype != EDGE ) kb_error(1462,"Length only for edges.\n",COMMAND_ERROR); break; case GET_MEANCURV_: if ( etype != VERTEX ) kb_error(2872,"Mean_curvature only for vertices.\n",COMMAND_ERROR); if ( web.representation == SIMPLEX ) kb_error(4567, "Mean_curvature attribute implemented only for string and soapfilm models.\n", RECOVERABLE); break; case GET_VERTEXNORMAL_: if ( etype != VERTEX ) kb_error(2244,"Facetnormal only for edges.\n",COMMAND_ERROR); break; case GET_FIXEDVOL_: if ( etype != BODY ) kb_error(1463,"Fixedvol only for bodies.\n",COMMAND_ERROR); break; case GET_WRAP_: if ( etype != EDGE ) kb_error(1464,"Wrap only for edges.\n",COMMAND_ERROR); break; case GET_MID_EDGE_: if ( etype != VERTEX ) kb_error(3112,"Mid_edge only for vertices.\n",COMMAND_ERROR); break; case GET_MID_FACET_: if ( etype != VERTEX ) kb_error(3113,"Mid_facet only for vertices.\n",COMMAND_ERROR); break; case GET_DIHEDRAL_: if ( web.representation == SOAPFILM ) { if (etype != EDGE) kb_error(1465,"Dihedral only for edges.\n",COMMAND_ERROR); } else if ( web.representation == STRING ) { if (etype != VERTEX) kb_error(1466,"Dihedral only for vertices.\n",COMMAND_ERROR); } else kb_error(1467, "Dihedral defined only for STRING and SOAPFILM models.\n",COMMAND_ERROR); break; case GET_AREA_: if ( etype != FACET ) kb_error(1468,"Area only for facets.\n",COMMAND_ERROR); break; case GET_EDGE_: if ( etype != FACETEDGE ) kb_error(1469,"Edge only for facetedges.\n",COMMAND_ERROR); break; case GET_FACET_: if ( etype != FACETEDGE ) kb_error(1470,"Facet only for facetedges.\n",COMMAND_ERROR); break; case GET_TARGET_: if ( etype != BODY ) kb_error(1471,"Target only for bodies.\n",COMMAND_ERROR); break; case GET_VOLCONST_: if ( etype != BODY ) kb_error(1472,"Volconst only for bodies.\n",COMMAND_ERROR); break; case GET_VOLUME_: if ( etype != BODY ) kb_error(1473,"Volume only for bodies.\n",COMMAND_ERROR); break; case GET_DENSITY_: if ( etype == VERTEX ) kb_error(1474,"No density for vertices.\n",COMMAND_ERROR); break; case SET_HIT_PARTNER_: if ( etype != VERTEX ) kb_error(3003,"Hit_partner only for vertices.\n", COMMAND_ERROR); break; case SET_NO_REFINE_: if ( etype == BODY || etype == VERTEX ) kb_error(1494,"No no_refine for vertices or bodies.\n", COMMAND_ERROR); break; case SET_NO_DISPLAY_: if ( etype != FACET ) kb_error(1320,"No_display is only for facets.\n", COMMAND_ERROR); break; } } /********************************************************************** * * function: check_array_dims_same() * * purpose: see if two arrays are of the same dimensionality (i.e * same number of indices, not number of elements). * Also of same type. * * return: 1 if same, 0 if not. */ int check_array_dims_same( int left, /* id number of array */ int right) /* id number of second array */ { struct array *alvalue; struct array *arvalue; int adim,atype,bdim,btype; if ( (left & GTYPEMASK) == ATTRIBNAME ) { struct extra *ex = EXTRAS(name_eltype(left)) + (left & GLOBMASK); alvalue = &(ex->array_spec); adim = alvalue->dim; atype = alvalue->datatype; } else { struct global *glvalue = globals(left); adim = glvalue->attr.arrayptr->dim; atype = glvalue->type; } if ( (right & GTYPEMASK) == ATTRIBNAME ) { struct extra *ex = EXTRAS(name_eltype(right)) + (right & GLOBMASK); arvalue = &(ex->array_spec); bdim = arvalue->dim; btype = arvalue->datatype; } else { struct global *grvalue = globals(right); arvalue = grvalue->attr.arrayptr; bdim = grvalue->attr.arrayptr->dim; btype = grvalue->type; } if ( atype != btype ) return 0; return adim == bdim; } /********************************************************************* * * function: check_recalc_attr() * * Purpose: see if attribute has RECALC_ATTR flag bit set. */ int check_recalc_attr(int name_id) { if ( (name_id & GTYPEMASK) == ATTRIBNAME ) { struct extra *ex = EXTRAS(name_eltype(name_id)) + (name_id & GLOBMASK); return ex->flags & RECALC_ATTR; } return 0; } /********************************************************************* * * function: check_readonly_attr() * * purpose: See if left side of assignment is to read-only attribute. */ void check_readonly_attr(int name_id) { if ( (name_id & GTYPEMASK) == ATTRIBNAME ) { if ( name_id == set_name_eltype(V_NORMAL_ATTR,VERTEX) ) kb_error(3017,"__vertex_normal is a read-only attribute.\n", COMMAND_ERROR); if ( name_id == set_name_eltype(E_VECTOR_ATTR,EDGE) ) kb_error(3016,"__e_vector is a read-only attribute.\n", COMMAND_ERROR); if ( name_id == set_name_eltype(F_NORMAL_ATTR,FACET) ) kb_error(3018,"__e_vector is a read-only attribute.\n", COMMAND_ERROR); } } /* end check_readonly_attr() */ /********************************************************************* * * function: check_special_attr() * * purpose: Tell user that calculated attributes on right side are * only implemented as lone terms. */ void check_special_attr(int name_id) { if ( (name_id & GTYPEMASK) == ATTRIBNAME ) { if ( name_id == set_name_eltype(V_NORMAL_ATTR,VERTEX) ) kb_error(3123,"__vertex_normal term must be lone term on right side.\n", COMMAND_ERROR); if ( name_id == set_name_eltype(E_VECTOR_ATTR,EDGE) ) kb_error(3024,"__e_vector term must be lone term on right side.\n", COMMAND_ERROR); if ( name_id == set_name_eltype(F_NORMAL_ATTR,FACET) ) kb_error(3124,"__e_vector term must be lone term on right side.\n", COMMAND_ERROR); } } evolver-2.30c.dfsg/src/display.c0000644000175300017530000005703011410765113017033 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************* * * File: display.c * * Purpose: Overall control of graphics file output. */ #include "include.h" /************************************************************** * * Function: display_file() * * Purpose: Makes graphical output display file of * current object. */ void display_file(arg) int arg; /* menu choice; -1 to invoke user dialog */ { char response[100]; REAL val; if ( web.zoom_radius < 9000.0 ) { sprintf(errmsg,"Inner clip radius (%g): ",(DOUBLE)inner_clip_rad); prompt(errmsg,response,sizeof(response)); if ( const_expr(response,&val) > 0 ) { inner_clip_rad = val; inner_clip_flag = 1; } } /* get user choice */ if ( arg < 0 ) { outstring("Choose output graphics format: \n"); outstring("1. Pixar file \n"); outstring("2. OOGL file\n"); outstring("3. PostScript file\n"); outstring("4. Triangle file \n"); outstring("5. Softimage file \n"); outstring("6. OFF file \n"); outstring("7. binary OFF file (for evmovie) \n"); #if defined(OOGL) && !defined(WIN32) outstring("8. Start simultaneous geomview \n"); outstring("9. End simultaneous geomview \n"); #endif outstring("A. Start OOGL pipe (you must start reader) \n"); outstring("B. End OOGL pipe\n"); outstring("0. cancel \n"); prompt("Choice: ",response,sizeof(response)); if ( response[0] == '3' ) { ps_colorflag = gridflag = labelflag = crossingflag = -1; } do_gfile(response[0],NULL); } else { if ( arg == 3 ) { ps_colorflag = gridflag = labelflag = crossingflag = -1; } do_gfile('0'+arg,NULL); } } /*********************************************************************** * * Function: do_gfile() * * purpose: Set up graphics function pointers and invoke proper * graphics drivers. */ void do_gfile(choice,fname) int choice; char *fname; { /* for saving old graphics state */ void (*old_edge)ARGS((struct tsort *)); void (*old_facet)ARGS((struct tsort *)); void (*old_gfacet)ARGS((struct graphdata *,facet_id)); void (*old_gedge)ARGS((struct graphdata *,edge_id)); void (*old_init)ARGS((void)); void (*old_finish)ARGS((void)); void (*old_start)ARGS((void)); void (*old_end)ARGS((void)); ENTER_GRAPH_MUTEX if ( torus_display_mode == TORUS_DEFAULT_MODE ) ask_wrap_display(); switch ( toupper(choice) ) { case 0: /* null file with painter, for bounding box */ /* save old graphics functions */ old_init = init_graphics; old_finish = finish_graphics; old_edge = display_edge; old_facet = display_facet; old_start = graph_start; old_end = graph_end; old_gfacet = graph_facet; old_gedge = graph_edge; /* set null graphics functions */ init_graphics = null_function; finish_graphics = null_function; graph_start = painter_start; graph_edge = painter_edge; display_edge = (void (*)ARGS((struct tsort *))) null_function; graph_facet = painter_facet; display_facet = (void (*)ARGS((struct tsort *)))null_function; graph_end = painter_end; need_bounding_box = 1; edgewidths = otherwidths; /* do output */ graphgen(); /* restore old graphics */ init_graphics = old_init; finish_graphics = old_finish; display_edge = old_edge; display_facet = old_facet; graph_start = old_start; graph_end = old_end; graph_facet = old_gfacet; graph_edge = old_gedge; need_bounding_box = 0; break; case '1': case 'P': /* Pixar */ if ( web.representation != SOAPFILM ) kb_error(1001,"Will do Pixar file only for SOAPFILM model.\n", RECOVERABLE); /* fall through, since MinneView same as Pixar */ case '2': case 'M': /* OOGL file */ if ( web.representation != SOAPFILM ) kb_error(1002,"Will do OOGL file only for SOAPFILM model.\n", RECOVERABLE); old_start = graph_start; old_end = graph_end; old_gfacet = graph_facet; graph_start = pix_start; graph_facet = pix_facet; graph_end = pix_end; /* do output */ graphgen(); /* restore old graphics */ graph_start = old_start; graph_end = old_end; graph_facet = old_gfacet; break; case '3': case 'S': /* PostScript */ /* save old graphics functions */ old_init = init_graphics; old_finish = finish_graphics; old_edge = display_edge; old_facet = display_facet; old_start = graph_start; old_end = graph_end; old_gfacet = graph_facet; old_gedge = graph_edge; /* set PostScript functions */ init_graphics = ps_init; finish_graphics = ps_finish; graph_start = painter_start; graph_edge = painter_edge; display_edge = ps_edge; graph_facet = painter_facet; display_facet = ps_facet; graph_end = painter_end; need_bounding_box = 1; edgewidths = pswidths; graph_capabilities = web.torus_display_period ? 0 : GC_ARCS; /* do output */ graphgen(); /* restore old graphics */ init_graphics = old_init; finish_graphics = old_finish; display_edge = old_edge; display_facet = old_facet; graph_start = old_start; graph_end = old_end; graph_facet = old_gfacet; graph_edge = old_gedge; need_bounding_box = 0; graph_capabilities = 0; break; case '4': case 'F': /* triangle file output */ /* save old graphics functions */ old_init = init_graphics; old_finish = finish_graphics; old_edge = display_edge; old_facet = display_facet; old_start = graph_start; old_end = graph_end; old_gfacet = graph_facet; old_gedge = graph_edge; /* set functions */ init_graphics = fil_init; finish_graphics = fil_finish; if ( web.representation == STRING ) { graph_start = fil_init; graph_edge = painter_edge; display_edge = fil_edge; graph_end = fil_finish; } else { graph_start = painter_start; graph_facet = painter_facet; display_facet = fil_facet; graph_end = painter_end; } edgewidths = otherwidths; /* do output */ graphgen(); /* restore old graphics */ init_graphics = old_init; finish_graphics = old_finish; display_edge = old_edge; display_facet = old_facet; graph_start = old_start; graph_end = old_end; graph_facet = old_gfacet; graph_edge = old_gedge; break; case '5': /* Softimage file */ softimage(); break; case '6': /* OFF format file */ old_start = graph_start; old_end = graph_end; old_gfacet = graph_facet; old_gedge = graph_edge; graph_start = OFF_start; graph_edge = OFF_edge; graph_facet = OFF_facet; graph_end = OFF_end; /* do output */ graphgen(); /* restore old graphics */ graph_start = old_start; graph_end = old_end; graph_facet = old_gfacet; graph_edge = old_gedge; break; case '7': /* binary OFF format file, for evmovie*/ binary_off_filename = fname; old_start = graph_start; old_end = graph_end; old_gfacet = graph_facet; old_gedge = graph_edge; graph_start = binary_OFF_start; graph_edge = binary_OFF_edge; graph_facet = binary_OFF_facet; graph_end = binary_OFF_end; /* do output */ graphgen(); /* restore old graphics */ graph_start = old_start; graph_end = old_end; graph_facet = old_gfacet; graph_edge = old_gedge; break; case '8': /* interactive geomview */ Begin_geomview(fname); go_display_flag = 1; /* default autodisplay */ break; case '9': /* end geomview */ case 'B': /* end geomview */ if ( geomview_flag || geompipe_flag ) End_geomview(); go_display_flag = 0; /* autodisplay off */ geompipe_flag = GEOM_TO_GEOMVIEW; break; case 'A': /* geomview to named pipe */ geompipe_flag = GEOM_NAMED_PIPE; Begin_geomview(fname); go_display_flag = 1; /* default autodisplay */ break; case 'C': /* geomview to command */ geompipe_flag = GEOM_PIPE_COMMAND; Begin_geomview(fname); go_display_flag = 1; /* default autodisplay */ break; case 'q': case '0': break; default: outstring("Invalid choice.\n"); break; } LEAVE_GRAPH_MUTEX inner_clip_flag = 0; /* turn off inner clipping */ } /***************************************************************** * * function: gray_level() * * purpose: compute gray level for facet by normal orientation * return value 0 < gray < 1. Scaling depends on internal * variable "brightness", which is mid-level gray. * */ REAL gray_level(normal) float *normal; { REAL cosine; REAL denom; denom = sqrt(dotf(normal,normal,3)); if ( denom == 0.0 ) return 0.0; cosine = normal[1]/denom; if ( (REAL)normal[2] < 0.0 ) return brightness - 0.9*(1-brightness)*cosine; return brightness + 0.9*(1-brightness)*cosine; } /******************************************************************** * * function: generate_transforms * * purpose: generate viewing transforms from generators to * desired depth */ static int trans_max = 500; static REAL ***work_transforms; void generate_transforms(depth) int depth; { int start,stop; /* range of transforms to apply gens to */ int i,j,k,m,n,level; MAT2D(temp_mat,MAXCOORD+1,MAXCOORD+1); /* clean up old stuff */ view_transforms = NULL; transform_count = 0; transform_depth = depth; if ( depth < 1 ) return; trans_max = 50; /* new stuff */ work_transforms = dmatrix3(depth+1,SDIM+1,SDIM+1); matcopy(work_transforms[0],identmat,SDIM+1,SDIM+1); restart: if ( view_transforms ) free_matrix3(view_transforms); view_transforms = dmatrix3(trans_max,SDIM+1,SDIM+1); matcopy(view_transforms[0],identmat,SDIM+1,SDIM+1); /* initialize generation */ for ( k = 0 ; k < transform_gen_count ; k++ ) matcopy(view_transforms[k+1],view_transform_gens[k],SDIM+1,SDIM+1); transform_count = 1+transform_gen_count; /* apply all generators to transforms of last generation */ stop = 1; for ( level = 2 ; level <= depth ; level++ ) { start = stop; stop = transform_count; for ( n = start ; n < stop ; n++ ) for ( i = 0 ; i < transform_gen_count ; i++ ) { mat_mult(view_transform_gens[i],view_transforms[n], view_transforms[transform_count],SDIM+1,SDIM+1,SDIM+1); /* see if already have, by crude linear search */ for ( j = 0 ; j < transform_count ; j++ ) { /* compare */ for ( k = 0 ; k < SDIM + 1 ; k++ ) for ( m = 0 ; m < SDIM+1 ; m++ ) if ( fabs(view_transforms[transform_count][k][m] -view_transforms[j][k][m]) > 1e-6 ) goto different; /* same */ goto same; different: ; /* continue search */ } /* now know have new transform */ transform_count++; if ( transform_count >= trans_max ) { trans_max *= 2; goto restart; } same: ; } } if ( view_transform_det ) myfree((char*)view_transform_det); view_transform_det = (int*)mycalloc(transform_count,sizeof(int)); set_view_transforms_global(); allocate_transform_colors(transform_count); if ( transform_parity ) myfree((char*)transform_parity); transform_parity = (int*)mycalloc(transform_count,sizeof(int)); for ( n = 0 ; n < transform_count ; n++ ) { if ( view_transforms[n][SDIM][SDIM] < 0.0 ) { transform_colors[n] = SWAP_COLORS; transform_colors_flag = 1; for ( i = 0 ; i <= SDIM ; i++ ) for ( j = 0 ; j <= SDIM ; j++ ) view_transforms[n][i][j] *= -1; } else transform_colors[n] = SAME_COLOR; matcopy(temp_mat,view_transforms[n],SDIM+1,SDIM+1); if ( determinant(temp_mat,SDIM+1) > 0.0 ) view_transform_det[n] = 1; else view_transform_det[n] = -1; } free_matrix3(work_transforms); } /* end generate_transforms() */ /************************************************************************** * * function: transform_gen_expr() * * purpose: generate viewing transforms from generators and * quasi-regular expression. * Expression syntax: * G is nonterminal, represents set of transforms * G ::= letter - corresponding generator, a - z, G = {I,a} * but a! generates only {a} * G ::= GG - all ordered products * G ::= G|G - union * G ::= nG - all ordered n-fold products * G ::= (G) - for precedence, same set * Leading ! in string means do not include identity transform, even * if generated by other transforms. * * Sets global variable view_transforms to matrix list allocated with dmatrix3() * Sets global variable transform_count */ struct stack { /* parsing stack */ int type; /* symbol type */ int num; /* if number */ int count; /* how many matrices */ REAL ***mats; /* set of matrices */ int *swapcolor; /* list of front-back swap color flags */ }; #define SMAX 100 /* types */ #define G_START 0 #define G_NUM 1 #define G_SET 2 #define G_OR 3 #define G_LEFT 4 #define G_RIGHT 5 #define G_LBRACKET 6 #define G_SINGLE 7 int matcomp ARGS((REAL***,REAL***)); int matcomp(a,b) /* lexicographic comparison of matrices */ REAL ***a,***b; { int i,j; REAL *aa,*bb; for ( i = 0 ; i <= SDIM ; i++ ) for ( j = 0,aa = a[0][i],bb = b[0][i] ; j <= SDIM ; j++,aa++,bb++ ) { if ( *aa < *bb-1e-6 ) return -1; else if ( *aa > *bb+1e-6 ) return 1; } return 0; /* equal, within error */ } void transform_gen_expr(expr) char *expr; { int errnum = 0; struct stack stack[SMAX]; int stacktop; char *c; /* next character */ int new_count; REAL ***new_mats; int i,j,k,n,m; int old_count; REAL ***old_mats; REAL ***id; MAT2D(temp_mat,MAXCOORD+1,MAXCOORD+1); int no_identity = 0; if ( expr[0] == 0 ) /* empty transform string */ { stacktop = 1 ; stack[1].count = 0; stack[stacktop].mats = dmatrix3(1,SDIM+1,SDIM+1); goto skip_stuff; } stacktop = 0; stack[stacktop].type = G_START; c = expr; if ( *c == '!' ) { no_identity = 1; c++; } while ( *c || (stacktop > 1) ) { /* test first for reduction */ if ( stack[stacktop].type == G_SET ) { switch ( stack[stacktop-1].type ) { case G_SET: /* form product */ new_count = stack[stacktop].count*stack[stacktop-1].count; new_mats = dmatrix3(new_count+1,SDIM+1,SDIM+1); for ( m = 0, k = 0 ; k < stack[stacktop-1].count ; k++ ) for ( j = 0 ; j < stack[stacktop].count ; j++,m++ ) mat_mult(stack[stacktop-1].mats[k],stack[stacktop].mats[j], new_mats[m],SDIM+1,SDIM+1,SDIM+1); free_matrix3(stack[stacktop-1].mats); free_matrix3(stack[stacktop].mats); stacktop--; stack[stacktop].count = mat_simplify(new_mats,new_count); stack[stacktop].mats = new_mats; stack[stacktop].type = G_SET; continue; case G_SINGLE: /* single matrix in [...] */ mat_mult(stack[stacktop-1].mats[1],stack[stacktop].mats[1], temp_mat,SDIM+1,SDIM+1,SDIM+1); free_matrix3(stack[stacktop].mats); stacktop--; matcopy(stack[stacktop].mats[1],temp_mat,SDIM+1,SDIM+1); continue; case G_LBRACKET: /* convert to single */ stack[stacktop].type = G_SINGLE; continue; case G_NUM: /* form powers */ if ( stack[stacktop-1].num < 2 ) { stack[stacktop-1] = stack[stacktop]; stacktop--; continue;} new_mats = stack[stacktop].mats; new_count = stack[stacktop].count; for ( k = 1 ; k < stack[stacktop-1].num ; k++ ) { old_count = new_count; old_mats = new_mats; new_count = new_count * stack[stacktop].count; new_mats = dmatrix3(new_count+1,SDIM+1,SDIM+1); for ( m =0, n = 0 ; n < old_count ; n++ ) for ( j = 0 ; j < stack[stacktop].count ; j++,m++ ) mat_mult(old_mats[n],stack[stacktop].mats[j], new_mats[m],SDIM+1,SDIM+1,SDIM+1); if ( k > 1 ) free_matrix3(old_mats); new_count = mat_simplify(new_mats,new_count); } free_matrix3(stack[stacktop].mats); stacktop--; stack[stacktop].count = new_count; stack[stacktop].mats = new_mats; stack[stacktop].type = G_SET; continue; case G_OR: /* form union */ if ( *c & (*c != '|') & (*c != ')') ) break; /* go to accept next */ new_count = stack[stacktop].count+stack[stacktop-2].count; new_mats = dmatrix3(new_count+1,SDIM+1,SDIM+1); for ( m = 0, k = 0 ; k < stack[stacktop-2].count ; k++,m++ ) matcopy(new_mats[m],stack[stacktop-2].mats[k],SDIM+1,SDIM+1); for ( j = 0 ; j < stack[stacktop].count ; j++,m++ ) matcopy(new_mats[m],stack[stacktop].mats[j],SDIM+1,SDIM+1); free_matrix3(stack[stacktop-2].mats); free_matrix3(stack[stacktop].mats); stacktop -= 2; stack[stacktop].count = mat_simplify(new_mats,new_count); stack[stacktop].mats = new_mats; stack[stacktop].type = G_SET; continue; } /* end switch */ } else if ( stack[stacktop].type == G_RIGHT ) { if ( ((stack[stacktop-1].type != G_SET) && (stack[stacktop-1].type != G_SINGLE)) || ((stack[stacktop-2].type != G_LEFT) && (stack[stacktop-2].type != G_LBRACKET)) ) { sprintf(errmsg, "Transform expression has unmatched parentheses.\n"); errnum = 2600; goto err_exit; } stacktop -= 2; stack[stacktop].type = G_SET; stack[stacktop].count = stack[stacktop+1].count; stack[stacktop].mats = stack[stacktop+1].mats; continue; } /* shift */ if ( *c == 0 ) { sprintf(errmsg,"Illegal transform expression syntax: %s\n",expr); errnum = 2601; goto err_exit; } if ( isalpha(*c) ) { n = toupper(*c) - 'A'; /* number of generator */ if ( n >= transform_gen_count ) { sprintf(errmsg,"No generator '%c'.\n",*c); errnum = 2602; goto err_exit; /* clean up */ } /* set up set */ stack[++stacktop].type = G_SET; if ( c[1] == '!' ) /* exclude identity */ { stack[stacktop].count = 1; stack[stacktop].mats = dmatrix3(1+1,SDIM+1,SDIM+1); matcopy(stack[stacktop].mats[0],view_transform_gens[n],SDIM+1,SDIM+1); c++; } else /* include identity */ { stack[stacktop].count = 2; stack[stacktop].mats = dmatrix3(2+1,SDIM+1,SDIM+1); matcopy(stack[stacktop].mats[0],identmat,SDIM+1,SDIM+1); matcopy(stack[stacktop].mats[1],view_transform_gens[n],SDIM+1,SDIM+1); } if ( transform_gen_swap[n] ) for ( i = 0 ; i <= SDIM ; i++ ) for ( j = 0 ; j <= SDIM ; j++ ) stack[stacktop].mats[1][i][j] *= -1; c++; } else if ( isdigit(*c) ) { stack[++stacktop].type = G_NUM; stack[stacktop].num = atoi(c); while ( isdigit(*c) ) c++; } else if ( *c == '(' ) { stack[++stacktop].type = G_LEFT; c++; } else if ( *c == ')' ) { stack[++stacktop].type = G_RIGHT; c++; } else if ( *c == '|' ) { stack[++stacktop].type = G_OR; c++; } else if ( *c == '[' ) { stack[++stacktop].type = G_LBRACKET; c++; } else if ( *c == ']' ) { stack[++stacktop].type = G_RIGHT; c++; } else if ( (*c==' ')||(*c=='\t') ) c++ ; else { sprintf(errmsg,"Illegal character '%c' in transform expression.\n", *c); errnum = 2603; goto err_exit; } } /* normal exit */ if ( (stacktop != 1) || (stack[1].type != G_SET) ) { sprintf(errmsg,"Illegal transform expression syntax: %s\n",expr); errnum = 2604; goto err_exit; } strncpy(transform_expr,expr,sizeof(transform_expr)); skip_stuff: /* put identity at the head of the list, if not excluded */ id = (REAL***)bsearch((char*)&identmat,(char*)stack[1].mats,stack[1].count, sizeof(REAL**), FCAST matcomp); if ( no_identity ) { if ( id ) *id = stack[1].mats[--stack[1].count]; } else { if ( id && (*id != stack[1].mats[0]) ) { REAL **mat = stack[1].mats[0]; stack[1].mats[0] = *id; *id = mat; } else if ( !id ) { for ( i = 0 ; i <= SDIM ; i++ ) for ( j = 0 ; j <= SDIM ; j++ ) { stack[1].mats[stack[1].count][i][j] = stack[1].mats[0][i][j]; stack[1].mats[0][i][j] = ( i==j ) ? 1.0 : 0.0; } stack[1].count++; } } ENTER_GRAPH_MUTEX; transform_count = stack[1].count; if ( view_transforms ) free_matrix3(view_transforms); view_transforms = stack[1].mats; set_view_transforms_global(); if ( view_transform_det ) myfree((char*)view_transform_det); view_transform_det = (int*)mycalloc(transform_count,sizeof(int)); allocate_transform_colors(transform_count); for ( n = 0 ; n < transform_count ; n++ ) { if ( view_transforms[n][SDIM][SDIM] < 0.0 ) { transform_colors[n] = SWAP_COLORS; transform_colors_flag = 1; for ( i = 0 ; i <= SDIM ; i++ ) for ( j = 0 ; j <= SDIM ; j++ ) view_transforms[n][i][j] *= -1; } else transform_colors[n] = SAME_COLOR; matcopy(temp_mat,view_transforms[n],SDIM+1,SDIM+1); if ( determinant(temp_mat,SDIM+1) > 0.0 ) view_transform_det[n] = 1; else view_transform_det[n] = -1; } LEAVE_GRAPH_MUTEX; return; err_exit: for ( i = 0 ; i <= stacktop ; i++ ) if ( stack[i].type == G_SET ) free_matrix3(stack[i].mats); kb_error(errnum,errmsg,RECOVERABLE); } /* end transform_gen_expr() */ /******************************************************************** * * function: mat_simplify() * * purpose: remove redundant matrices during transform_gen_expr() * * return value: number of distinct matrices left */ int mat_simplify(mats,count) REAL ***mats; int count; { int newcount; int i; qsort((char*)mats,count,sizeof(REAL**), FCAST matcomp); /* eliminate duplicates */ for ( newcount = 1, i = 1 ; i < count ; i++ ) { if ( matcomp(mats+newcount-1,mats+i) != 0 ) mats[newcount++] = mats[i]; } return newcount; } /* end mat_simplify() */ evolver-2.30c.dfsg/src/graphgen.c0000644000175300017530000032365311410765113017170 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************** * * File: graphgen.c * * Purpose: Generates triangles with normals and colors for * feeding to device-specific graphing routines. * */ #include "include.h" #include "ytab.h" /* prototypes to keep some compilers happy */ int INDEX_TO_RGBA ARGS((int)); int get_edge_color_2 ARGS((edge_id)); int get_facet_color_2 ARGS((facet_id)); int get_facet_backcolor_2 ARGS((facet_id)); int get_facet_frontcolor_2 ARGS((facet_id)); /************************************************************************* * Some preliminary stuff for handling RGBA colors. */ int INDEX_TO_RGBA(c) int c; { return ((int)(rgb_colors[c][0]*255)<<24) + ((int)(rgb_colors[c][1]*255)<<16) + ((int)(rgb_colors[c][2]*255)<<8) + ((int)(rgb_colors[c][3]*255)); } int get_edge_color_2(e_id) edge_id e_id; { if ( edge_rgb_color_attr > 0 ) { REAL *c = (REAL*)get_extra(e_id,edge_rgb_color_attr); return ((int)(c[0]*255)<<24) + ((int)(c[1]*255)<<16) + ((int)(c[2]*255)<<8) + (edge_alpha_flag ? ((int)(c[3]*255)) : 255); } else return get_edge_color(e_id); } int get_facet_frontcolor_2(f_id) facet_id f_id; { if ( facet_rgb_color_attr > 0 ) { REAL *c = (REAL*)get_extra(f_id,facet_rgb_color_attr); return ((int)(c[0]*255)<<24) + ((int)(c[1]*255)<<16) + ((int)(c[2]*255)<<8) + (facet_alpha_flag ? ((int)(c[3]*255)) : 255); } else return get_facet_frontcolor(f_id); } int get_facet_color_2(f_id) facet_id f_id; { return get_facet_frontcolor_2(f_id); } int get_facet_backcolor_2(f_id) facet_id f_id; { if ( facet_rgb_color_attr > 0 ) { if ( facet_rgb_backcolor_attr > 0 ) { REAL *c = (REAL*)get_extra(f_id,facet_rgb_backcolor_attr); return ((int)(c[0]*255)<<24) + ((int)(c[1]*255)<<16) + ((int)(c[2]*255)<<8) + (facetback_alpha_flag ? ((int)(c[3]*255)) : 255); } else return INDEX_TO_RGBA(get_facet_backcolor(f_id)); } else return get_facet_backcolor(f_id); } /* Data used in plotting circular arcs */ /* using fact that circle is inversion of line */ static REAL wlambda[17] = { 1e30,10,5,4,3,2,1.5,1.2,1,.85,.7,.6,.5,.35,.25,.12,0.}; /***************************************************************************** * * Function: graphgen() * * purpose: Generates data for each triangle and calls the display * function graph_facet(). * * * Return value: number of triangles plotted */ int graphgen() { int graphcount = 0; /* number of facets done */ int b,i; REAL *c; FILE *mapfd = NULL; int dummy; struct extra *ex; if ( torus_display_mode == TORUS_CLIPPED_MODE ) lazy_transforms_flag = 0 ; if ( slice_view_flag ) { /* check coefficients set */ for ( i = 0 ; i < SDIM ; i++ ) if ( slice_coeff[i] ) break; if ( i == SDIM ) kb_error(1900,"slice_view on, but slice_coeff[] not set.\n", RECOVERABLE); } if ( rgb_colors_flag ) { int three = 3; /* see if using RGBA colors for elements */ edge_rgb_color_attr = find_extra("ergb",&dummy); if ( edge_rgb_color_attr > 0 ) { ex = EXTRAS(EDGE)+edge_rgb_color_attr; if ( ex->array_spec.datacount < 3 ) expand_attribute(EDGE,edge_rgb_color_attr,&three); edge_alpha_flag = (ex->array_spec.datacount >= 4); } facet_rgb_color_attr = find_extra("frgb",&dummy); if ( facet_rgb_color_attr > 0 ) { ex = EXTRAS(FACET)+facet_rgb_color_attr; if ( ex->array_spec.datacount < 3 ) expand_attribute(FACET,facet_rgb_color_attr,&three); facet_alpha_flag = (ex->array_spec.datacount >= 4); } facet_rgb_backcolor_attr = find_extra("fbrgb",&dummy); if ( facet_rgb_backcolor_attr > 0 ) { ex = EXTRAS(FACET)+facet_rgb_backcolor_attr; if ( ex->array_spec.datacount < 3 ) expand_attribute(FACET,facet_rgb_backcolor_attr,&three); facetback_alpha_flag = (ex->array_spec.datacount >= 4); } } else /* turn off rgb */ { edge_rgb_color_attr = -1; facet_rgb_color_attr = -1; facet_rgb_backcolor_attr = -1; } (*graph_start)(); /* device-specific initialization */ if ( markedgedrawflag ) { edge_id e_id; FOR_ALL_EDGES(e_id) unset_attr(e_id,EDGE_DRAWN); } iterate_flag = 2; if ( box_flag ) for ( i = 0 ; i < SDIM ; i++ ) bounding_box[i][0] = bounding_box[i][1] = 0.0; if ( colorflag ) do { if ( strlen(cmapname) == 0 ) prompt("Enter name of colormap file: ",cmapname,sizeof(cmapname)); if ( cmapname[0] == 0 ) { outstring("No colormap used.\n"); colorflag = 0; } mapfd = path_open(cmapname,NOTDATAFILENAME); if ( mapfd ) { colormap = (maprow *)temp_calloc(4*web.bodycount,sizeof(REAL)); for ( b = 0 ; b < web.bodycount ; b++ ) { c = colormap[b]; #ifdef LONGDOUBLE if ( fscanf(mapfd,"%Lf %Lf %Lf %Lf", #else if ( fscanf(mapfd,"%lf %lf %lf %lf", #endif c,c+1,c+2,c+3) != 4 ) break; } if ( b < web.bodycount ) { sprintf(errmsg, "Colormap file has only %d entries for %d bodies.\n", b,web.bodycount); kb_error(1046,errmsg,WARNING); for ( ; b < web.bodycount ; b++ ) { c = colormap[b]; c[0] = c[1] = c[2] = c[3] = 0.5; } } } else perror(cmapname); } while ( mapfd == NULL ); /* call appropriate facet generator */ if ( web.symmetry_flag && (torus_display_mode==TORUS_CONNECTED_MODE) ) { if ( web.skel[BODY].count <= 0 ) { kb_error(1049, "There are no bodies to display connectedly. Reverting to raw mode.\n", WARNING); torus_display_mode = TORUS_RAW_MODE; } } if ( web.symmetry_flag && (torus_display_mode==TORUS_CONNECTED_MODE) ) { if ( web.representation == STRING ) torus_cells(); else torus_bodies(); bare_edges(); } else { if ( web.representation == SIMPLEX ) hi_dim_graph(); else if ( web.representation == STRING ) { if ( show_expr[FACET] && show_expr[FACET]->start ) plain_string_facets(); plain_edges(); /* so edges get drawn on top */ } else if ( web.representation == SOAPFILM ) { plain_facets(); bare_edges(); } } /* bounding box (torus only) */ if ( box_flag && (web.torus_flag || (torus_display_mode == TORUS_CLIPPED_MODE))) { int k,m,n,a[MAXCOORD]; struct graphdata gdata[2]; REAL **per = web.torus_display_period ? web.torus_display_period : web.torus_period; memset(gdata,0,2*sizeof(struct graphdata)); gdata[0].color = gdata[0].ecolor = (edge_rgb_color_attr > 0 ) ? INDEX_TO_RGBA(BLUE) : BLUE; gdata[0].etype = REGULAR_EDGE; if ( SDIM == 2 ) for ( a[0] = 0 ; a[0] <= 1 ; a[0]++ ) for ( a[1] = 0 ; a[1] <= 1 ; a[1]++ ) for ( k = 0 ; k < 2 ; k++ ) { if ( a[k] == 0 ) { for ( m = 0 ; m < SDIM ; m++ ) { for ( n = 0, gdata[0].x[m] = web.display_origin[m] ; n < SDIM ; n++ ) gdata[0].x[m] += a[n]*per[n][m]; gdata[1].x[m] = gdata[0].x[m] + per[k][m]; } (*graph_edge_transforms)(gdata,NULLID); /* (*graph_edge)(gdata,NULLID); */ } } else for ( a[0] = 0 ; a[0] <= 1 ; a[0]++ ) for ( a[1] = 0 ; a[1] <= 1 ; a[1]++ ) for ( a[2] = 0 ; a[2] <= 1 ; a[2]++ ) for ( k = 0 ; k < 3 ; k++ ) if ( a[k] == 0 ) { for ( m = 0 ; m < SDIM ; m++ ) { for ( n = 0, gdata[0].x[m] = web.display_origin[m] ; n < SDIM ; n++ ) gdata[0].x[m] += a[n]*per[n][m]; gdata[1].x[m] = gdata[0].x[m] + per[k][m]; } (*graph_edge_transforms)(gdata,NULLID); /*(*graph_edge)(gdata,NULLID);*/ } } else if ( box_flag ) { int k,m,a[MAXCOORD]; struct graphdata gdata[2]; memset(gdata,0,2*sizeof(struct graphdata)); gdata[0].color = gdata[0].ecolor = (edge_rgb_color_attr > 0 ) ? INDEX_TO_RGBA(BLACK) : BLACK; gdata[0].etype = REGULAR_EDGE; if ( SDIM == 2 ) { for ( a[0] = 0 ; a[0] <= 1 ; a[0]++ ) for ( a[1] = 0 ; a[1] <= 1 ; a[1]++ ) for ( k = 0 ; k < 2 ; k++ ) if ( a[k] == 0 ) { for ( m = 0 ; m < SDIM ; m++ ) { gdata[0].x[m] = bounding_box[m][a[m]]; gdata[1].x[m] = bounding_box[m][m==k?1:a[m]]; } graph_edge_clip(gdata,NULLID); } } else for ( a[0] = 0 ; a[0] <= 1 ; a[0]++ ) for ( a[1] = 0 ; a[1] <= 1 ; a[1]++ ) for ( a[2] = 0 ; a[2] <= 1 ; a[2]++ ) for ( k = 0 ; k < 3 ; k++ ) if ( a[k] == 0 ) { for ( m = 0 ; m < SDIM ; m++ ) { gdata[0].x[m] = bounding_box[m][a[m]]; gdata[1].x[m] = bounding_box[m][m==k?1:a[m]]; } graph_edge_clip(gdata,NULLID); } } (*graph_end)(); /* device-specific termination */ if ( colorflag ) temp_free((char *)colormap); return graphcount; } /* end graphgen() */ /***************************************************************** * * function: plain_facets() * * purpose: plots all facets one by one. * */ void plain_facets() { int i,j,ii,jj,k; facetedge_id fe,fe_id; edge_id e_id; facet_id f_id; body_id b0_id,b1_id; REAL **verts; /* for adjusted triangle vertices */ struct graphdata *gdata; int ctrlpts = web.skel[FACET].ctrlpts; int segs = 8; /* for smooth_graph */ int to_alloc = ctrlpts+1 > ((segs+1)*(segs+2))/2+1 ? ctrlpts+1 : ((segs+1)*(segs+2))/2+1; REAL ***points = NULL; int gorder = 1; /* order actually used in graphing subfacets */ gdata = (struct graphdata *)temp_calloc(to_alloc,sizeof(struct graphdata)); verts = (REAL **)temp_calloc(ctrlpts,sizeof(REAL *)); if ( web.modeltype == LAGRANGE ) { gorder = smooth_graph_flag ? segs : web.lagrange_order; points = temp_dmatrix3(gorder+1,gorder+1,SDIM); for ( i = 0 ; i < ctrlpts ; i++ ) verts[i] = gdata[i].x; } else for ( i = 0 ; i < FACET_VERTS ; i++ ) { if ( web.modeltype == QUADRATIC ) /* mdpts after verts in gdata */ { verts[2*i] = gdata[i].x; verts[2*i+1] = gdata[FACET_VERTS+i].x; } else verts[i] = gdata[i].x; } for ( i = 0 ; i <= ctrlpts ; i++ ) gdata[i].x[3] = 1.0; /* homogeneous coord */ MFOR_ALL_FACETS(f_id) { int nbrs; /* number of neighboring bodies */ int fattr = get_fattr(f_id); if ( breakflag ) break; #ifdef MPI_EVOLVER if ( !mpi_show_corona_flag && (id_task(f_id) != this_task) ) continue; #endif if ( (fattr & (BOUNDARY|CONSTRAINT)) && !bdry_showflag ) continue; if ( fattr & NODISPLAY ) continue; gdata[0].color = get_facet_color_2(f_id); gdata[0].backcolor = get_facet_backcolor_2(f_id); if ( no_wall_flag ) { /* skip facets with all three vertices on walls */ fe = get_facet_fe(f_id); if ( get_vattr(get_fe_headv(fe)) & (HIT_WALL|CONSTRAINT) ) if ( get_vattr(get_fe_tailv(fe)) & (HIT_WALL|CONSTRAINT) ) { fe = get_next_edge(fe); if ( get_vattr(get_fe_headv(fe)) & (HIT_WALL|CONSTRAINT) ) continue; } } if ( show_expr[FACET] && show_expr[FACET]->start ) { if ( !eval(show_expr[FACET],NULL,f_id,NULL) ) gdata[0].color = gdata[0].backcolor = UNSHOWN; /* maybe do edges */ } nbrs = (valid_id(get_facet_body(f_id)) ? 1 : 0) + (valid_id(get_facet_body(facet_inverse(f_id))) ? 1 : 0); if ( (nbrs >= 2) && !innerflag ) continue; if ( (nbrs < 2) && !outerflag ) continue; if ( colorflag ) /* get vertex color */ { b0_id = get_facet_body(f_id); b1_id = get_facet_body(facet_inverse(f_id)); for ( i = 0 ; i < ctrlpts ; i++ ) /* vertex loop */ { if ( valid_id(b0_id) ) gdata[i].backcolor = (facet_rgb_color_attr > 0 )? INDEX_TO_RGBA(loc_ordinal(b0_id)) : loc_ordinal(b0_id); else gdata[i].backcolor = 0; if ( valid_id(b1_id) ) gdata[i].color = ( facet_rgb_color_attr > 0 ) ? INDEX_TO_RGBA(loc_ordinal(b1_id)): loc_ordinal(b1_id); else gdata[i].color = 0; } } /* get vertices; verts is list of gdata.x pointers */ if ( labelflag != 0 ) get_facet_verts(f_id,verts,NULL); else get_facet_verts_special(f_id,verts,NULL); if ( web.modeltype == LAGRANGE ) { vertex_id *v = get_facet_vertices(f_id); for ( i = 0 ; i < ctrlpts ; i++ ) gdata[i].v_id = v[i]; } else { fe = get_facet_fe(f_id); for ( i = 0 ; i < FACET_VERTS ; i++, fe = get_next_edge(fe) ) { e_id = get_fe_edge(fe); if ( web.modeltype == LINEAR ) gdata[i].v_id = get_edge_tailv(e_id); else if ( web.modeltype == QUADRATIC ) { gdata[i].v_id = get_edge_tailv(e_id); gdata[FACET_VERTS+i].v_id = get_edge_midv(e_id); } } } /* do inner clipping, if called for */ if ( inner_clip_flag ) { for ( i = 0 ; i < ctrlpts ; i++ ) { REAL dist = 0.0; REAL *x = get_coord(web.zoom_v); for ( j = 0 ; j < SDIM ; j++ ) dist += (x[j]-verts[i][j])*(x[j]-verts[i][j]); if ( sqrt(dist) > inner_clip_rad ) break; /* it's a keeper */ } if ( i == ctrlpts ) continue; /* entirely inside */ } if ( gdata[0].color != gdata[0].backcolor ) { /* need normal for separation */ REAL dd; if ( web.modeltype == LAGRANGE ) vnormal(gdata[0].x,gdata[web.lagrange_order].x,gdata[ctrlpts-1].x, gdata[0].norm); else vnormal(gdata[0].x,gdata[1].x,gdata[2].x,gdata[0].norm); dd = sqrt(SDIM_dot(gdata[0].norm,gdata[0].norm)); if ( dd > 0.0 ) for ( i = 0 ; i < SDIM ; i++ ) gdata[0].norm[i] /= dd; } if ( normflag || thickenflag ) { fe = get_facet_fe(f_id); for ( i = 0 ; i < FACET_VERTS ; i++ ) { calc_vertex_smooth_normal(get_fe_tailv(fe),fe,gdata[i].norm); fe = get_next_edge(fe); } } /* check for special edges */ fe_id = get_facet_fe(f_id); if ( web.representation != SIMPLEX ) for ( i = 0 ; i < 3 ; i++, fe_id = get_next_edge(fe_id) ) { int eattr; gdata[i].etype = INVISIBLE_EDGE; /* default */ e_id = get_fe_edge(fe_id); if ( inverted(e_id) ) gdata[i].etype |= LABEL_REVERSED; gdata[i].ecolor = get_edge_color_2(e_id); gdata[i].id = e_id; eattr = get_eattr(e_id); if ( markedgedrawflag ) { if ( eattr & EDGE_DRAWN ) continue; } if ( get_edge_color(e_id) == CLEAR ) continue; if ( eattr & BOUNDARY ) gdata[i].etype |= BOUNDARY_EDGE; if ( equal_id(get_next_facet(fe_id),fe_id) ) /* valence 1 */ gdata[i].etype |= SINGLE_EDGE; else if ( !equal_id(get_next_facet(fe_id),get_prev_facet(fe_id)) ) gdata[i].etype |= TRIPLE_EDGE; /* triple line at least */ if ( (eattr & HIT_WALL) && !(fattr & CONSTRAINT) ) gdata[i].etype |= CONSTRAINT_EDGE; if ( (eattr & FIXED) && !(fattr & FIXED) ) gdata[i].etype |= FIXED_EDGE; if ( show_expr[EDGE] && show_expr[EDGE]->start ) { if ( eval(show_expr[EDGE],NULL,e_id,NULL) ) gdata[i].etype |= REGULAR_EDGE; else gdata[i].etype &= (~EBITS) | INVISIBLE_EDGE; } if ( markedgedrawflag && ((gdata[i].etype & EBITS) != INVISIBLE_EDGE) ) set_attr(e_id,EDGE_DRAWN); } if ( ridge_color_flag ) { REAL side[FACET_EDGES][MAXCOORD]; REAL otherside[FACET_EDGES][MAXCOORD]; fe = get_facet_fe(f_id); for ( i = 0 ; i < FACET_EDGES ; i++ ) { get_fe_side(fe,side[i]); get_fe_side(get_next_edge(get_next_facet(fe)),otherside[i]); fe = get_next_edge(fe); } for ( i = 0 ; i < FACET_EDGES ; i++ ) if ( triple_prod(side[i],side[(i+1)%FACET_EDGES],otherside[i]) < 0.0 ) gdata[i].ecolor = ( edge_rgb_color_attr > 0 ) ? INDEX_TO_RGBA(RIDGE) : RIDGE; else gdata[i].ecolor = ( edge_rgb_color_attr > 0 ) ? INDEX_TO_RGBA(VALLEY) : VALLEY; } if ( web.modeltype == LINEAR ) { /* call option-specific routine */ gdata->flags |= LABEL_FACET|LABEL_EDGE|LABEL_HEAD|LABEL_TAIL; option_facet(gdata,f_id); } else if ( (web.modeltype == QUADRATIC) ) { /* quadratic, plot as four subtriangles */ struct graphdata qdata[FACET_VERTS+1]; int flags = (gdata[0].flags | LIST_FACET) & ~(LABEL_FACET|LABEL_EDGE|LABEL_HEAD|LABEL_TAIL); memcpy((char*)qdata,(char*)gdata,3*sizeof(struct graphdata)); for( j=0 ; jstart || !eval(show_expr[FACET],NULL,f_id,NULL) ) continue; if ( (fattr & (BOUNDARY|CONSTRAINT)) && !bdry_showflag ) continue; if ( fattr & NODISPLAY ) continue; gdata[0].color = get_facet_color_2(f_id); gdata[0].backcolor = get_facet_backcolor_2(f_id); vcount = 0; /* get vertices; verts is list of gdata.x pointers */ fe = start_fe = get_facet_fe(f_id); if ( !valid_id(fe) ) continue; gdata[vcount].v_id = get_fe_tailv(fe); x = get_coord(gdata[vcount].v_id); for ( i = 0 ; i < SDIM ; i++ ) { gdata[vcount].x[i] = x[i]; } vcount++; do { REAL s[MAXCOORD]; if ( web.modeltype == LINEAR ) { gdata[vcount].v_id = get_fe_headv(fe); get_edge_side(get_fe_edge(fe),s); for ( i = 0 ; i < SDIM ; i++ ) gdata[vcount].x[i] = gdata[vcount-1].x[i] + s[i]; vcount++; } else if ( web.modeltype == QUADRATIC ) { REAL *tailx = gdata[vcount-1].x,*midxp; REAL midx[MAXCOORD],headx[MAXCOORD]; REAL w1[MAXCOORD],w2[MAXCOORD],w[MAXCOORD]; REAL mag,mag1,mag2; int k; int segments; REAL ang; gdata[vcount+1].v_id = get_fe_headv(fe); get_edge_side(get_fe_edge(fe),s); for ( i = 0 ; i < SDIM ; i++ ) headx[i] = tailx[i] + s[i]; midv = get_fe_midv(fe); midxp = get_coord(midv); if ( inverted(get_fe_edge(fe)) ) { REAL *headxp = get_coord(get_fe_headv(fe)); for ( i = 0 ; i < SDIM ; i++ ) midx[i] = headx[i] - (headxp[i]-midxp[i]); } else { REAL *tailxp = get_coord(get_fe_tailv(fe)); for ( i = 0 ; i < SDIM ; i++ ) midx[i] = tailx[i] + (midxp[i]-tailxp[i]); } for (i = 0 ; i < SDIM ; i++ ) { w1[i] = midx[i] - tailx[i]; w2[i] = headx[i] - tailx[i]; } mag1 = SDIM_dot(w1,w1); mag2 = SDIM_dot(w2,w2); for ( i = 0 ; i < SDIM ; i++ ) { w1[i] /= mag1; w2[i] /= mag2; } /* figure out how many segments to do */ ang = 4*acos(SDIM_dot(w1,w2)*sqrt(mag1*mag2)); segments = (int)(ang/(M_PI/180*string_curve_tolerance)); if ( segments < 2 ) segments = 2; if ( vcount+segments >= to_alloc-20 ) { to_alloc *= 2; gdata = (struct graphdata *)temp_realloc((char*)gdata, to_alloc*sizeof(struct graphdata)); tailx = gdata[vcount-1].x; /* since it moved */ } for ( k = 1 ; k <= segments ; k++ ) { if ( circular_arc_flag ) { for ( i = 0 ; i < SDIM ; i++ ) /* circle as inversion of line */ w[i] = w2[i] + (segments-k)/(REAL)k*(w1[i]-w2[i]); mag = SDIM_dot(w,w); for ( i = 0 ; i < SDIM ; i++ ) gdata[vcount].x[i] = tailx[i] + w[i]/mag; } else /* quadratic spline */ { REAL t = 2*k/(REAL)segments; REAL c1 = (t-1)*(t-2)/2; REAL c2 = t*(2-t); REAL c3 = t*(t-1)/2; for ( i = 0 ; i < SDIM ; i++ ) gdata[vcount].x[i] = c1*tailx[i]+c2*midx[i]+c3*headx[i]; } vcount++; } } else /* LAGRANGE */ { edge_id e_id = get_fe_edge(fe); vertex_id *v = get_edge_vertices(e_id); REAL *midx; gdata[vcount+web.lagrange_order-1].v_id = get_fe_headv(fe); get_edge_side(get_fe_edge(fe),s); for ( i = 0 ; i < SDIM ; i++ ) gdata[vcount+web.lagrange_order-1].x[i] = gdata[vcount-1].x[i] + s[i]; if ( inverted(get_fe_edge(fe)) ) { REAL *headx = get_coord(get_fe_headv(fe)); for ( n = 1 ; n < web.lagrange_order ; n++ ) { midx = get_coord(v[web.lagrange_order - n]); for ( i = 0 ; i < SDIM ; i++ ) gdata[vcount+n-1].x[i] = gdata[vcount+web.lagrange_order-1].x[i] - (headx[i]-midx[i]); } } else { REAL *tailx = get_coord(get_fe_tailv(fe)); for ( n = 1 ; n < web.lagrange_order ; n++ ) { midx = get_coord(v[n]); for ( i = 0 ; i < SDIM ; i++ ) gdata[vcount+n-1].x[i] = gdata[vcount-1].x[i] + (midx[i]-tailx[i]); } } vcount += web.lagrange_order; } if ( vcount >= to_alloc-20 ) { to_alloc *= 2; gdata = (struct graphdata *)temp_realloc((char*)gdata, to_alloc*sizeof(struct graphdata)); } fe = get_next_edge(fe); } while ( valid_id(fe) && !equal_id(fe,start_fe) ); graph_string_facet(f_id,gdata,vcount); } /* end FOR_ALL_FACETS */ temp_free((char*)gdata); } /* end plain_facets() */ /************************************************************************* * * function: option_facet() * * purpose: call facet plotter for specific options in effect */ void option_facet(gdata,f_id) struct graphdata *gdata; facet_id f_id; { if ( web.torus_flag ) { gdata[0].flags &= ~(LIST_FACET | SIMPLE_FACET); } if ( web.torus_flag && (torus_display_mode == TORUS_CLIPPED_MODE) ) { torus_clip(gdata,SDIM-1,f_id); } else { if ( !web.symmetry_flag && (web.representation != STRING) ) gdata[0].flags |= SIMPLE_FACET; graph_facet_transforms(gdata,f_id); } } /***************************************************************** * * function: graph_edge_transforms() * * purpose: graph edge and all transforms specified in datafile * */ void graph_edge_transforms(gdata,e_id) struct graphdata *gdata; edge_id e_id; { int i,j,k; struct graphdata gt[3]; int ctrl_pts = gdata[0].flags & EDGE_ARC ? 3 : 2; memset(gt,0,3*sizeof(struct graphdata)); for ( i = 0 ; i < ctrl_pts ; i++ ) gdata[i].x[SDIM] = 1.0; /* homogeneous coord */ if ( box_flag ) for ( j = 0 ; j < ctrl_pts ; j++ ) for ( k = 0 ; k < SDIM ; k++ ) { if ( gdata[j].x[k] < bounding_box[k][0] ) bounding_box[k][0] = gdata[j].x[k]; if ( gdata[j].x[k] > bounding_box[k][1] ) bounding_box[k][1] = gdata[j].x[k]; } if ( transforms_flag && !lazy_transforms_flag && view_transforms && (transform_colors_flag || (graph_facet != geomview_facet) ) ) for ( i = 0 ; i < transform_count ; i++ ) { gt[0] = gdata[0]; gt[1].v_id = gdata[1].v_id; for ( j = 0 ; j < ctrl_pts ; j++ ) { matvec_mul(view_transforms[i],gdata[j].x,gt[j].x, SDIM+1,SDIM+1); for ( k = 0 ; k <= SDIM ; k++ ) { gt[j].x[k] /= gt[j].x[SDIM]; /* normalize */ if ( box_flag ) { if ( gt[j].x[k] < bounding_box[k][0] ) bounding_box[k][0] = gt[j].x[k]; if ( gt[j].x[k] > bounding_box[k][1] ) bounding_box[k][1] = gt[j].x[k]; } } } if ( (web.representation == STRING) && transform_colors_flag ) gt[0].ecolor = transform_colors[i]; if ( (torus_display_mode == TORUS_CLIPPED_MODE) && web.torus_display_period && !web.torus_flag ) torus_edge_clip(gt,0); else graph_edge_clip(gt,e_id); } else { if ( (torus_display_mode == TORUS_CLIPPED_MODE) && web.torus_display_period && !web.torus_flag ) torus_edge_clip(gdata,0); else graph_edge_clip(gdata,e_id); } } /* graph_edge_transforms */ /***************************************************************** * * function: graph_facet_transforms() * * purpose: graph facet and all transforms specified in datafile * */ void graph_facet_transforms(gdata,f_id) struct graphdata *gdata; facet_id f_id; { struct graphdata gt[FACET_VERTS+1]; int i,j,k; memset(gt,0,(FACET_VERTS+1)*sizeof(struct graphdata)); for ( i = 0 ; i < FACET_VERTS ; i++ ) gdata[i].x[SDIM] = 1.0; /* homogeneous coord */ if ( box_flag ) for ( j = 0 ; j < FACET_VERTS ; j++ ) for ( k = 0 ; k < SDIM ; k++ ) { if ( gdata[j].x[k] < bounding_box[k][0] ) bounding_box[k][0] = gdata[j].x[k]; if ( gdata[j].x[k] > bounding_box[k][1] ) bounding_box[k][1] = gdata[j].x[k]; } if ( transforms_flag && !lazy_transforms_flag && view_transform_det && (transform_colors_flag || (graph_facet != geomview_facet) ) ) { for ( i = 0 ; i < transform_count ; i++ ) { if ( view_transform_det[i] > 0 ) { for ( j = 0 ; j < FACET_VERTS ; j++ ) { gt[j] = gdata[j]; matvec_mul(view_transforms[i],gdata[j].x,gt[j].x,SDIM+1,SDIM+1); for ( k = 0 ; k <= SDIM ; k++ ) gt[j].x[k] /= gt[j].x[SDIM]; /* normalize */ matvec_mul(view_transforms[i],gdata[j].norm,gt[j].norm, SDIM,SDIM); } } else { /* reverse orientation */ for ( j = 0 ; j < FACET_VERTS ; j++ ) { gt[j] = gdata[2-j]; matvec_mul(view_transforms[i],gdata[2-j].x,gt[j].x,SDIM+1,SDIM+1); for ( k = 0 ; k <= SDIM ; k++ ) gt[j].x[k] /= gt[j].x[SDIM]; /* normalize */ matvec_mul(view_transforms[i],gdata[2-j].norm,gt[j].norm,SDIM,SDIM); } gt[0].color = gdata[0].color; gt[0].backcolor = gdata[0].backcolor; gt[0].ecolor = gdata[1].ecolor; gt[1].ecolor = gdata[0].ecolor; gt[2].ecolor = gdata[2].ecolor; gt[0].etype = gdata[1].etype; gt[1].etype = gdata[0].etype; gt[2].etype = gdata[2].etype; gt[0].flags = gdata[0].flags; } if ( transform_colors[i] == SWAP_COLORS ) { int temp = gt[0].color; gt[0].color = gt[0].backcolor; gt[0].backcolor = temp; } else if ( transform_colors_flag && (transform_colors[i] != SAME_COLOR) ) gt[0].color = gt[0].backcolor = transform_colors[i]; if ( box_flag ) for ( j = 0 ; j < FACET_VERTS ; j++ ) for ( k = 0 ; k < SDIM ; k++ ) { if ( gt[j].x[k] < bounding_box[k][0] ) bounding_box[k][0] = gt[j].x[k]; if ( gt[j].x[k] > bounding_box[k][1] ) bounding_box[k][1] = gt[j].x[k]; } if ( (torus_display_mode == TORUS_CLIPPED_MODE) && web.torus_display_period && !web.torus_flag ) torus_clip(gt,SDIM-1,f_id); else if ( slice_view_flag ) slice_facet(gt); else if ( clip_view_flag ) clip_facet(gt,f_id,0); else (*graph_facet)(gt,f_id); } } else { if ( (torus_display_mode == TORUS_CLIPPED_MODE) && web.torus_display_period && !web.torus_flag ) torus_clip(gdata,SDIM-1,f_id); else if ( slice_view_flag ) slice_facet(gdata); else if ( clip_view_flag ) clip_facet(gdata,f_id,0); else (*graph_facet)(gdata,f_id); } } /***************************************************************** * * function: plain_edges() * * purpose: plots all edges one by one. * */ void plain_edges() { int i,k; edge_id e_id; REAL **verts; /* for adjusted triangle vertices */ struct graphdata *gdata; int ctrlpts = web.skel[EDGE].ctrlpts; int segs = smooth_graph_flag ? 8 : ctrlpts-1; /* for smooth plotting */ int to_alloc = ctrlpts > (segs+1) ? ctrlpts : (segs+1); gdata = (struct graphdata *)mycalloc(to_alloc,sizeof(struct graphdata)); verts = temp_dmatrix(0,to_alloc,0,SDIM); gdata[0].etype = REGULAR_EDGE; MFOR_ALL_EDGES(e_id) { if ( breakflag ) break; #ifdef MPI_EVOLVER if ( !mpi_show_corona_flag && (id_task(e_id) != this_task) ) continue; #endif if ( show_expr[EDGE] && show_expr[EDGE]->start ) if ( !eval(show_expr[EDGE],NULL,e_id,NULL) ) continue; gdata[0].flags = 0; /* get vertices */ get_edge_verts(e_id,verts,NULL); for ( i = 0 ; i < SDIM ; i++ ) { gdata[1].x[i] = verts[1][i]; gdata[0].x[i] = verts[0][i]; gdata[0].v_id = get_edge_tailv(e_id); gdata[1].v_id = get_edge_headv(e_id); if ( web.modeltype == QUADRATIC ) { gdata[2].x[i] = verts[2][i]; gdata[2].v_id = get_edge_midv(e_id); } else if ( web.modeltype == LAGRANGE ) { vertex_id *v = get_edge_vertices(e_id); for ( k = 0 ; k < ctrlpts ; k++ ) { gdata[k].x[i] = verts[k][i]; gdata[k].v_id = v[k]; } } } gdata[0].color = gdata[0].ecolor = get_edge_color_2(e_id); gdata[0].id = e_id; if ( inverted(e_id) ) gdata[i].etype |= LABEL_REVERSED; /* call device-specific routine */ if ( web.modeltype == LINEAR ) { gdata[0].flags |= LABEL_EDGE|LABEL_HEAD|LABEL_TAIL; if ( web.torus_flag && (torus_display_mode == TORUS_CLIPPED_MODE) ) torus_edge_clip(gdata,0); else if ( spherical_arc_flag ) { if ( graph_capabilities & GS_ARCS ) { gdata[0].flags |= SPHERE_ARC; (*graph_edge_transforms)(gdata,e_id); } else /* do by hand */ { REAL rr = SDIM_dot(gdata[0].x,gdata[0].x); REAL base[MAXCOORD]; REAL perp[MAXCOORD]; REAL ab = SDIM_dot(gdata[0].x,gdata[1].x); REAL angle,dangle; int n; if ( ab >= rr ) { sprintf(errmsg,"Edge %s endpoints are antipodal; cannot be graphed as spherical arc.\n", ELNAME(e_id)); kb_error(3733,errmsg,WARNING); return; } for ( i = 0 ; i < SDIM ; i++ ) { base[i] = gdata[0].x[i]; perp[i] = 1/sqrt(1-ab*ab/rr/rr)*(gdata[1].x[i] - ab/rr*gdata[0].x[i]); } angle = acos(ab/rr); dangle = M_PI/40; segs = (int)ceil(angle/dangle); dangle = angle/segs; for ( n = 0 ; n < segs ; n++ ) { if ( n > 0 ) for ( i = 0 ; i < SDIM ; i++ ) gdata[0].x[i] = gdata[1].x[i]; for ( i = 0 ; i < SDIM ; i++ ) gdata[1].x[i] = cos((n+1)*dangle)*base[i] + sin((n+1)*dangle)*perp[i]; (*graph_edge_transforms)(gdata,e_id); } } } else (*graph_edge_transforms)(gdata,e_id); } else if ( (web.modeltype == QUADRATIC) && circular_arc_flag ) { /* arc, so plot in segments, unless have circular arc mode */ REAL headx[MAXCOORD]; REAL tailx[MAXCOORD]; REAL midx[MAXCOORD]; REAL w1[MAXCOORD],mag1; REAL w2[MAXCOORD],mag2; REAL w[MAXCOORD],mag; REAL xx[MAXCOORD]; for (i = 0 ; i < SDIM ; i++ ) { headx[i] = gdata[2].x[i]; tailx[i] = gdata[0].x[i]; midx[i] = gdata[1].x[i]; w1[i] = midx[i] - tailx[i]; w2[i] = headx[i] - tailx[i]; } mag1 = SDIM_dot(w1,w1); mag2 = SDIM_dot(w2,w2); if ( graph_capabilities & GC_ARCS ) { gdata[0].flags |= EDGE_ARC; if ( web.torus_flag && (torus_display_mode == TORUS_CLIPPED_MODE)) torus_edge_clip(gdata,0); else (*graph_edge_transforms)(gdata,e_id); } else { int segments; REAL ang; for ( i = 0 ; i < SDIM ; i++ ) { w1[i] /= mag1; w2[i] /= mag2; } for ( i = 0 ; i < SDIM ; i++ ) gdata[0].x[i] = tailx[i]; /* figure out how many segments to do */ ang = 4*acos(SDIM_dot(w1,w2)*sqrt(mag1*mag2)); segments = (int)(ang/(M_PI/180*string_curve_tolerance)); if ( segments < 2 ) segments = 2; gdata[1].v_id = NULLID; for ( k = 1 ; k <= segments ; k++ ) { if ( k == segments/2 ) gdata[0].v_id = get_edge_midv(e_id); else if ( k > 1 ) gdata[0].v_id = NULLID; if ( k == segments ) gdata[1].v_id = get_edge_headv(e_id); for ( i = 0 ; i < SDIM ; i++ ) w[i] = w2[i] + (segments-k)/(REAL)k*(w1[i]-w2[i]); mag = SDIM_dot(w,w); for ( i = 0 ; i < SDIM ; i++ ) gdata[1].x[i] = xx[i] = tailx[i] + w[i]/mag; if ( labelflag ) { gdata[0].flags &= ~(LABEL_EDGE|LABEL_HEAD|LABEL_TAIL); if ( k == 1 ) gdata[0].flags |= LABEL_TAIL; if ( k == segments/2 ) gdata[0].flags |= LABEL_EDGE; if ( k == segments ) gdata[0].flags |= LABEL_HEAD; } if ( web.torus_flag && (torus_display_mode == TORUS_CLIPPED_MODE)) torus_edge_clip(gdata,0); else (*graph_edge_transforms)(gdata,e_id); for ( i = 0 ; i < SDIM ; i++ ) /* gdata[1].x was messed up by clip */ gdata[0].x[i] = xx[i]; } } } else if ( web.modeltype == QUADRATIC ) /* quadratic, so plot in segments */ { REAL headx[MAXCOORD]; REAL tailx[MAXCOORD]; REAL midx[MAXCOORD]; REAL w1[MAXCOORD],w2[MAXCOORD],mag1,mag2,ang; for ( i = 0 ; i < SDIM ; i++ ) { headx[i] = gdata[2].x[i]; tailx[i] = gdata[0].x[i]; midx[i] = gdata[1].x[i]; w1[i] = midx[i] - tailx[i]; w2[i] = headx[i] - tailx[i]; } /* figure out how many segments to do */ mag1 = SDIM_dot(w1,w1); mag2 = SDIM_dot(w2,w2); ang = 4*acos(SDIM_dot(w1,w2)/sqrt(mag1*mag2)); segs = (int)(ang/(M_PI/180*string_curve_tolerance)); if ( segs < 2 ) segs = 2; for ( k = 1 ; k <= segs ; k++ ) { for ( i = 0 ; i < SDIM ; i++ ) gdata[1].x[i] = (1-k/(REAL)segs)*(1-k*2./segs)*tailx[i] + k*4./segs*(1-k/(REAL)segs)*midx[i] - k/(REAL)segs*(1-k*2./segs)*headx[i]; if ( labelflag ) { gdata[0].flags &= ~(LABEL_EDGE|LABEL_HEAD|LABEL_TAIL); if ( k == 1 ) gdata[0].flags |= LABEL_TAIL; if ( k == segs/2+1 ) gdata[0].flags |= LABEL_EDGE; if ( k == segs ) gdata[0].flags |= LABEL_HEAD; } if ( web.torus_flag && (torus_display_mode == TORUS_CLIPPED_MODE)) torus_edge_clip(gdata,0); else (*graph_edge_transforms)(gdata,e_id); for ( i = 0 ; i < SDIM ; i++ ) /* gdata[1].x was messed up by clip */ gdata[0].x[i] = (1-k/(REAL)segs)*(1-k*2./segs)*tailx[i] + k*4./segs*(1-k/(REAL)segs)*midx[i] - k/(REAL)segs*(1-k*2./segs)*headx[i]; } } else if ( web.modeltype == LAGRANGE ) { /* plot subsegments */ if ( smooth_graph_flag || bezier_flag ) { /* plot lots of segments */ vertex_id headv = gdata[web.lagrange_order].v_id; for ( k = 0 ; k < segs ; k++ ) { gdata[k].ecolor = gdata[0].ecolor; gdata[k].etype = gdata[0].etype; /* were messed up by clip of previous segment */ if ( bezier_flag ) { bezier_eval_1d(web.lagrange_order,SDIM,k/(REAL)segs,verts, gdata[k].x); bezier_eval_1d(web.lagrange_order,SDIM,(k+1.)/segs,verts, gdata[k+1].x); } else { /* regular lagrange */ lagrange_eval_1d(web.lagrange_order,SDIM,k/(REAL)segs,verts, gdata[k].x); lagrange_eval_1d(web.lagrange_order,SDIM,(k+1.)/segs,verts, gdata[k+1].x); } gdata[k].flags &= ~(LABEL_EDGE|LABEL_HEAD|LABEL_TAIL); if ( k == 0 ) gdata[0].flags |= LABEL_TAIL; if ( k == segs/2 ) gdata[k].flags |= LABEL_EDGE; if ( k == segs-1 ) { gdata[k].flags |= LABEL_HEAD; gdata[k+1].v_id = headv; } else gdata[k+1].v_id = NULLID; if ( web.torus_flag && (torus_display_mode == TORUS_CLIPPED_MODE) ) torus_edge_clip(gdata+k,0); else (*graph_edge_transforms)(gdata+k,e_id); } } else /* just plot between control points */ { for ( k = 0 ; k < ctrlpts-1 ; k++ ) { gdata[k].ecolor = gdata[0].ecolor; gdata[k].etype = gdata[0].etype; for ( i = 0 ; i < SDIM ; i++ ) /* were messed up by clip */ { gdata[k].x[i] = verts[k][i]; gdata[k+1].x[i] = verts[k+1][i]; } gdata[k].flags &= ~(LABEL_EDGE|LABEL_HEAD|LABEL_TAIL); if ( k == 0 ) gdata[0].flags |= LABEL_TAIL; if ( k == (ctrlpts-1)/2 ) gdata[k].flags |= LABEL_EDGE; if ( k == ctrlpts-2 ) gdata[k].flags |= LABEL_HEAD; if ( web.torus_flag && (torus_display_mode == TORUS_CLIPPED_MODE) ) torus_edge_clip(gdata+k,0); else (*graph_edge_transforms)(gdata+k,e_id); } } } } free_temp_matrix(verts); myfree((char*)gdata); } /* end plain_edges() */ /***************************************************************** * * function: bare_edges() * * purpose: plots all facetless edges. * */ void bare_edges() { int i; edge_id e_id; REAL *verts[3]; struct graphdata gdata[3]; facetedge_id fe; memset((char*)gdata,0,3*sizeof(struct graphdata)); gdata[0].etype = BARE_EDGE; for ( i = 0 ; i < 2 ; i++ ) verts[i] = gdata[i].x; MFOR_ALL_EDGES(e_id) { if ( breakflag ) break; #ifdef MPI_EVOLVER if ( !mpi_show_corona_flag && (id_task(e_id) != this_task) ) continue; #endif gdata[0].id = e_id; fe = get_edge_fe(e_id); if ( valid_id(fe) && valid_id(get_fe_facet(fe)) ) continue; if ( show_expr[EDGE] && show_expr[EDGE]->start ) if ( !eval(show_expr[EDGE],NULL,e_id,NULL) ) continue; /* get vertices */ gdata[0].v_id = get_edge_tailv(e_id); gdata[1].v_id = get_edge_headv(e_id); verts[0] = get_coord(get_edge_tailv(e_id)); verts[1] = get_coord(get_edge_headv(e_id)); for ( i = 0 ; i < SDIM ; i++ ) { gdata[0].x[i] = verts[0][i]; } gdata[0].color = gdata[0].ecolor = get_edge_color_2(e_id); if ( web.symmetry_flag ) (*sym_wrap)(verts[1],gdata[1].x,get_edge_wrap(e_id)); else for ( i = 0 ; i < SDIM ; i++ ) gdata[1].x[i] = verts[1][i]; /* call device-specific routine */ gdata[0].flags = (LABEL_EDGE|LABEL_HEAD|LABEL_TAIL); if ( web.torus_flag && (torus_display_mode == TORUS_CLIPPED_MODE) ) torus_edge_clip(gdata,0); else (*graph_edge_transforms)(gdata,e_id); } } /***************************************************************** * * function: triple_edges() * * purpose: plots all multiple-facet edges if triple_show_flag set. * */ void triple_edges() { int i; edge_id e_id; facetedge_id fe_id; REAL *verts[3]; /* for adjusted triangle vertices */ struct graphdata gdata[3]; memset(gdata,0,3*sizeof(struct graphdata)); gdata[0].etype = TRIPLE_EDGE; for ( i = 0 ; i < 2 ; i++ ) verts[i] = gdata[i].x; MFOR_ALL_EDGES(e_id) { if ( breakflag ) break; #ifdef MPI_EVOLVER if ( !mpi_show_corona_flag && (id_task(e_id) != this_task) ) continue; #endif fe_id = get_edge_fe(e_id); if ( equal_id(get_next_facet(fe_id),get_prev_facet(fe_id)) ) continue; if ( show_expr[EDGE] && show_expr[EDGE]->start ) if ( !eval(show_expr[EDGE],NULL,e_id,NULL) ) continue; /* get vertices */ verts[0] = get_coord(get_edge_tailv(e_id)); verts[1] = get_coord(get_edge_headv(e_id)); verts[2] = get_coord(get_edge_midv(e_id)); for ( i = 0 ; i < SDIM ; i++ ) { gdata[0].x[i] = verts[0][i]; gdata[2].x[i] = verts[2][i]; } if ( web.symmetry_flag ) (*sym_wrap)(verts[1],gdata[1].x,get_edge_wrap(e_id)); else for ( i = 0 ; i < SDIM ; i++ ) gdata[1].x[i] = verts[1][i]; gdata[0].id = e_id; gdata[0].color = gdata[0].ecolor = get_edge_color_2(e_id); /* call device-specific routine */ gdata[0].flags |= LABEL_EDGE|LABEL_HEAD|LABEL_TAIL; if ( web.torus_flag && (torus_display_mode == TORUS_CLIPPED_MODE) ) torus_edge_clip(gdata,0); else (*graph_edge_transforms)(gdata,e_id); } } /********************************************************************** * * Function: torus_edge_clip() * * Purpose: Recursive routine to subdivide edges crossing * torus cell faces. Edges have been already * unwrapped by torus periods. */ void torus_edge_clip(gdata,m) struct graphdata *gdata; /* edge to check */ int m; /* coordinate number */ { struct graphdata gdata1[2]; /* for possible subdivided edge */ int i,k; REAL t,a,b; int wrap[2]; REAL **per=NULL; REAL **invper=NULL; REAL orig[MAXCOORD]; /* cell coords of display origin */ if ( gdata[0].flags & EDGE_ARC ) { torus_arc_clip(gdata,m); return; } memset(gdata1,0,2*sizeof(struct graphdata)); if ( web.torus_display_period && web.torus_period ) { /* per = web.torus_display_period; */ per = web.torus_period; invper = web.inverse_display_periods; } else if ( web.torus_period ) { per = web.torus_period; invper = web.inverse_periods; } else if ( web.torus_display_period ) { per = web.torus_display_period; invper = web.inverse_display_periods; } else graph_edge_clip(gdata,gdata->id); for ( i = 0 ; i < SDIM ; i++ ) orig[i] = SDIM_dot(invper[i],web.display_origin); /* see if any vertices outside cell in this coordinate */ for ( i = 0 ; i < 2 ; i++ ) wrap[i] = (int)floor(SDIM_dot(invper[m],gdata[i].x)-orig[m]); /* split, if necessary */ if ( wrap[0] != wrap[1] ) { int cut = (wrap[0] > wrap[1]) ? wrap[0] : wrap[1]; /* set up head of new edge */ gdata1[1] = gdata[1]; gdata1[0].id = gdata[0].id; gdata1[0].etype = gdata[0].etype; gdata1[0].flags = gdata[0].flags & ~LABEL_TAIL; gdata[0].flags &= ~LABEL_HEAD; gdata[1].v_id = NULLID; gdata1[0].v_id = NULLID; /* calculate new vertex */ a = SDIM_dot(invper[m],gdata[0].x)-orig[m]; b = SDIM_dot(invper[m],gdata1[1].x)-orig[m]; t = (cut - a)/(b - a); if ( t < 0.0 ) t = 0.0; /* clamp */ else if ( t > 1.0 ) t = 1.0; for ( k = 0 ; k < SDIM ; k++ ) gdata1[0].x[k] = gdata[1].x[k] = (1 - t)*gdata[0].x[k] + t*gdata1[1].x[k]; /* wrap new edge vertices properly */ for ( i = 0 ; i < 2 ; i++ ) for ( k = 0 ; k < SDIM ; k++ ) gdata1[i].x[k] -= wrap[1]*per[m][k]; /* kludge for display_periods to get back towards box */ /* if ( web.torus_display_period ) { int ww = (int)floor((SDIM_dot(invper[1-m],gdata1[0].x) + SDIM_dot(invper[1-m],gdata1[1].x))/2 - orig[1-m]); for ( i = 0 ; i < 2 ; i++ ) for ( k = 0 ; k < SDIM ; k++ ) gdata1[i].x[k] -= ww*per[1-m][k]; } */ /* send on for further check, or plot */ gdata1[0].color = gdata1[0].ecolor = gdata[0].color; if ( m == SDIM-1 ) { if ( web.torus_flag && (torus_display_mode == TORUS_CLIPPED_MODE) ) (*graph_edge_transforms)(gdata1,gdata[0].id); else graph_edge_clip(gdata1,gdata[0].id); } else torus_edge_clip(gdata1,m+1); } /* wrap vertices properly */ for ( i = 0 ; i < 2 ; i++ ) for ( k = 0 ; k < SDIM ; k++ ) gdata[i].x[k] -= wrap[0]*per[m][k]; /* kludge for display_periods to get back towards box */ /* if ( web.torus_display_period ) { int ww = (int)floor((SDIM_dot(invper[1-m],gdata[0].x) + SDIM_dot(invper[1-m],gdata[1].x))/2-orig[1-m]); for ( i = 0 ; i < 2 ; i++ ) for ( k = 0 ; k < SDIM ; k++ ) gdata[i].x[k] -= ww*per[1-m][k]; } */ /* send on original edge structure */ if ( m == SDIM-1 ) { if ( web.torus_flag && (torus_display_mode == TORUS_CLIPPED_MODE) ) (*graph_edge_transforms)(gdata,gdata[0].id); else graph_edge_clip(gdata,gdata->id); } else torus_edge_clip(gdata,m+1); } /* end torus_edge_clip() */ /********************************************************************** * * Function: torus_arc_clip() * * Purpose: Recursive routine to subdivide circular arc edges crossing * torus cell faces. Edges have been already * unwrapped by torus periods. */ void torus_arc_clip(gdata,m) struct graphdata *gdata; /* edge to check */ int m; /* coordinate number */ { struct graphdata gdata1[3]; /* for possible subdivided edge */ struct graphdata gdata2[3]; /* for possible subdivided edge */ int i,j; REAL a,b,c,h,k,u,v,x0,x1,x2,y0,y1,y2,t1,t2,discr; int wrap[3]; REAL **per; REAL **invper; REAL w1[MAXCOORD],w2[MAXCOORD],mag1,mag2,w1w2,center[2],radius; REAL angle1,angle2,det,theta1,theta2; if ( web.torus_display_period ) { per = web.torus_display_period; invper = web.inverse_display_periods; } else { per = web.torus_period; invper = web.inverse_periods; } for ( i = 0 ; i < 3 ; i++ ) gdata1[i] = gdata2[i] = gdata[i]; /* flags and stuff */ /* see where vertices are relative to cell in this coordinate */ for ( i = 0 ; i < 3 ; i++ ) wrap[i] = (int)floor(SDIM_dot(invper[m],gdata[i].x)); /* solve for geometric parameters */ for (i = 0 ; i < SDIM ; i++ ) { w1[i] = gdata[1].x[i] - gdata[0].x[i]; w2[i] = gdata[2].x[i] - gdata[0].x[i]; } det = w1[0]*w2[1] - w1[1]*w2[0]; mag1 = SDIM_dot(w1,w1); mag2 = SDIM_dot(w2,w2); w1w2 = w1[0]*w2[0] + w1[1]*w2[1]; if ( 4000*det*det < mag1*mag1*mag2 + mag1*mag2*mag2 - 2*mag1*w1w2*mag2 ) { /* practically straight line */ for ( i = 0 ; i < SDIM ; i++ ) gdata[1].x[i] = gdata[2].x[i]; gdata[0].flags &= ~EDGE_ARC; torus_edge_clip(gdata,m); return; } /* circle */ center[0] = gdata[0].x[0] + 0.5*(w2[1]*mag1 - w1[1]*mag2)/det; center[1] = gdata[0].x[1] + 0.5*(-w2[0]*mag1 + w1[0]*mag2)/det; radius = sqrt((mag1*mag1*mag2+mag1*mag2*mag2-2*mag1*w1w2*mag2)/4/det/det); angle1 = atan2(gdata[0].x[1]-center[1],gdata[0].x[0]-center[0]); angle2 = atan2(gdata[2].x[1]-center[1],gdata[2].x[0]-center[0]); if ( det < 0.0 ) /* swap to get counterclockwise order around circle */ { REAL temp = angle1; angle1 = angle2; angle2 = temp; wrap[0] = wrap[2]; /* only one we use */ temp = gdata[0].x[0]; gdata[0].x[0] = gdata[2].x[0]; gdata[2].x[0] = temp; temp = gdata[0].x[1]; gdata[0].x[1] = gdata[2].x[1]; gdata[2].x[1] = temp; } if ( angle2 < angle1 ) angle2 += 2*M_PI; /* shrink arc a bit to avoid numerical problems */ angle1 += 1e-10; angle2 -= 1e-10; wrap[0] = (int)floor(invper[m][0]*(center[0]+radius*cos(angle1)) + invper[m][1]*(center[1]+radius*sin(angle1))); /* intersect with line for low side */ h = center[0]; k = center[1]; u = per[1-m][0]; v = per[1-m][1]; x0 = 0.0; y0 = 0.0; a = u*u + v*v; b = 2*(u*(x0-h) + v*(y0-k)); c = (x0-h)*(x0-h) + (y0-k)*(y0-k) - radius*radius; discr = b*b - 4*a*c; if ( discr > 0.0 ) { /* potential intersection */ t1 = b > 0.0 ? -2*c/(b + sqrt(discr)) : (-b + sqrt(discr))/2/a; t2 = b > 0.0 ? (-b - sqrt(discr))/2/a : -2*c/(b - sqrt(discr)); x1 = x0 + t1*u; y1 = y0 + t1*v; x2 = x0 + t2*u; y2 = y0 + t2*v; theta1 = atan2(y1-center[1],x1-center[0]); theta2 = atan2(y2-center[1],x2-center[0]); if ( theta1 < angle1 ) theta1 += 2*M_PI; if ( theta2 < angle1 ) theta2 += 2*M_PI; if ( theta1 > theta2 ) { REAL temp = theta1; theta1 = theta2; theta2 = temp; temp = x1; x1 = x2; x2 = temp; temp = y1; y1 = y2; y2 = temp; } if ( theta1 >= angle2 ) { /* no intersection, but may have to wrap */ if ( wrap[0] < 0 ) for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) gdata[i].x[j] += per[m][j]; } else /* split in at least two */ { if ( theta2 > angle2 ) { /* split in half, top half in gdata in case of further split on top */ if ( wrap[0] >= 0 ) { gdata1[0].x[0] = x1; gdata1[0].x[1] = y1; gdata1[0].flags |= EDGE_ARC; gdata1[1].x[0] = center[0] + radius*cos((angle2+theta1)/2); gdata1[1].x[1] = center[1] + radius*sin((angle2+theta1)/2); gdata1[2] = gdata[2]; gdata[1].x[0] = center[0] + radius*cos((angle1+theta1)/2); gdata[1].x[1] = center[1] + radius*sin((angle1+theta1)/2); gdata[2].x[0] = x1; gdata[2].x[1] = y1; for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) gdata1[i].x[j] += per[m][j]; } else { gdata1[0] = gdata[0]; gdata1[1].x[0] = center[0] + radius*cos((angle1+theta1)/2); gdata1[1].x[1] = center[1] + radius*sin((angle1+theta1)/2); gdata1[2].x[0] = x1; gdata1[2].x[1] = y1; gdata[0].x[0] = x1; gdata[0].x[1] = y1; gdata[1].x[0] = center[0] + radius*cos((angle2+theta1)/2); gdata[1].x[1] = center[1] + radius*sin((angle2+theta1)/2); for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) gdata1[i].x[j] += per[m][j]; } } else /* split in three */ { if ( wrap[0] >= 0 ) { gdata2[0].x[0] = x2; gdata2[0].x[1] = y2; gdata2[1].x[0] = center[0] + radius*cos((angle2+theta2)/2); gdata2[1].x[1] = center[1] + radius*sin((angle2+theta2)/2); gdata2[2] = gdata[2]; gdata2[0].flags |= EDGE_ARC; gdata1[0].flags |= EDGE_ARC; gdata1[0].x[0] = x2; gdata1[0].x[1] = y2; gdata1[1].x[0] = center[0] + radius*cos((theta2+theta1)/2); gdata1[1].x[1] = center[1] + radius*sin((theta2+theta1)/2); gdata1[2].x[0] = x1; gdata1[2].x[1] = y1; gdata[1].x[0] = center[0] + radius*cos((angle1+theta1)/2); gdata[1].x[1] = center[1] + radius*sin((angle1+theta1)/2); gdata[2].x[0] = x1; gdata[2].x[1] = y1; for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) gdata1[i].x[j] += per[m][j]; } else { gdata2[0].x[0] = x2; gdata2[0].x[1] = y2; gdata2[1].x[0] = center[0] + radius*cos((angle2+theta2)/2); gdata2[1].x[1] = center[1] + radius*sin((angle2+theta2)/2); gdata2[2] = gdata[2]; gdata2[0].flags |= EDGE_ARC; gdata1[0] = gdata[0]; gdata1[1].x[0] = center[0] + radius*cos((angle1+theta1)/2); gdata1[1].x[1] = center[1] + radius*sin((angle1+theta1)/2); gdata1[2].x[0] = x1; gdata1[2].x[1] = y1; gdata[0].x[0] = x2; gdata[0].x[1] = y2; gdata[1].x[0] = center[0] + radius*cos((theta2+theta1)/2); gdata[1].x[1] = center[1] + radius*sin((theta2+theta1)/2); gdata[2].x[0] = x1; gdata[2].x[1] = y1; for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { gdata1[i].x[j] += per[m][j]; gdata2[i].x[j] += per[m][j]; } } /* send on for further check, or plot */ if ( m == SDIM-1 ) (*graph_edge_transforms)(gdata2,gdata[0].id); else torus_edge_clip(gdata2,m+1); } /* send on for further check, or plot */ if ( m == SDIM-1 ) (*graph_edge_transforms)(gdata1,gdata[0].id); else torus_edge_clip(gdata1,m+1); } } /* intersect with line for high side */ h = center[0]; k = center[1]; u = per[1-m][0]; v = per[1-m][1]; x0 = per[m][0]; y0 = per[m][1]; a = u*u + v*v; b = 2*(u*(x0-h) + v*(y0-k)); c = (x0-h)*(x0-h) + (y0-k)*(y0-k) - radius*radius; discr = b*b - 4*a*c; if ( discr > 0.0 ) { /* potential intersection */ t1 = b > 0.0 ? -2*c/(b + sqrt(discr)) : (-b + sqrt(discr))/2/a; t2 = b > 0.0 ? (-b - sqrt(discr))/2/a : -2*c/(b - sqrt(discr)); x1 = x0 + t1*u; y1 = y0 + t1*v; x2 = x0 + t2*u; y2 = y0 + t2*v; theta1 = atan2(y1-center[1],x1-center[0]) - 0.000000001; theta2 = atan2(y2-center[1],x2-center[0]); if ( theta1 < angle1 ) theta1 += 2*M_PI; if ( theta2 < angle1 ) theta2 += 2*M_PI; if ( theta1 > theta2 ) { REAL temp = theta1; theta1 = theta2; theta2 = temp; temp = x1; x1 = x2; x2 = temp; temp = y1; y1 = y2; y2 = temp; } if ( theta1 > angle2 ) { /* no intersection, but may have to wrap */ if ( wrap[0] > 0 ) for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) gdata[i].x[j] -= per[m][j]; } else { /* split in at least two */ if ( theta2 > angle2 ) { /* split in half, bottom half in gdata */ if ( wrap[0] <= 0 ) { gdata1[0].x[0] = x1; gdata1[0].x[1] = y1; gdata1[0].flags |= EDGE_ARC; gdata1[1].x[0] = center[0] + radius*cos((angle2+theta1)/2); gdata1[1].x[1] = center[1] + radius*sin((angle2+theta1)/2); gdata1[2] = gdata[2]; gdata[1].x[0] = center[0] + radius*cos((angle1+theta1)/2); gdata[1].x[1] = center[1] + radius*sin((angle1+theta1)/2); gdata[2].x[0] = x1; gdata[2].x[1] = y1; for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) gdata1[i].x[j] -= per[m][j]; } else { gdata1[0] = gdata[0]; gdata1[1].x[0] = center[0] + radius*cos((angle1+theta1)/2); gdata1[1].x[1] = center[1] + radius*sin((angle1+theta1)/2); gdata1[2].x[0] = x1; gdata1[2].x[1] = y1; gdata[0].x[0] = x1; gdata[0].x[1] = y1; gdata[1].x[0] = center[0] + radius*cos((angle2+theta1)/2); gdata[1].x[1] = center[1] + radius*sin((angle2+theta1)/2); for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) gdata1[i].x[j] -= per[m][j]; } } else /* split in three */ { if ( wrap[0] <= 0 ) { gdata2[0].x[0] = x2; gdata2[0].x[1] = y2; gdata2[1].x[0] = center[0] + radius*cos((angle2+theta2)/2); gdata2[1].x[1] = center[1] + radius*sin((angle2+theta2)/2); gdata2[2] = gdata[2]; gdata2[0].flags |= EDGE_ARC; gdata1[0].flags |= EDGE_ARC; gdata1[0].x[0] = x2; gdata1[0].x[1] = y2; gdata1[1].x[0] = center[0] + radius*cos((theta2+theta1)/2); gdata1[1].x[1] = center[1] + radius*sin((theta2+theta1)/2); gdata1[2].x[0] = x1; gdata1[2].x[1] = y1; gdata[1].x[0] = center[0] + radius*cos((angle1+theta1)/2); gdata[1].x[1] = center[1] + radius*sin((angle1+theta1)/2); gdata[2].x[0] = x1; gdata[2].x[1] = y1; for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) gdata1[i].x[j] -= per[m][j]; } else { gdata2[0].x[0] = x2; gdata2[0].x[1] = y2; gdata2[1].x[0] = center[0] + radius*cos((angle2+theta2)/2); gdata2[1].x[1] = center[1] + radius*sin((angle2+theta2)/2); gdata2[2] = gdata[2]; gdata2[0].flags |= EDGE_ARC; gdata1[0] = gdata[0]; gdata1[1].x[0] = center[0] + radius*cos((angle1+theta1)/2); gdata1[1].x[1] = center[1] + radius*sin((angle1+theta1)/2); gdata1[2].x[0] = x1; gdata1[2].x[1] = y1; gdata[0].x[0] = x2; gdata[0].x[1] = y2; gdata[1].x[0] = center[0] + radius*cos((theta2+theta1)/2); gdata[1].x[1] = center[1] + radius*sin((theta2+theta1)/2); gdata[2].x[0] = x1; gdata[2].x[1] = y1; for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { gdata1[i].x[j] -= per[m][j]; gdata2[i].x[j] -= per[m][j]; } } /* send on for further check, or plot */ if ( m == SDIM-1 ) (*graph_edge_transforms)(gdata2,gdata[0].id); else torus_edge_clip(gdata2,m+1); } /* send on for further check, or plot */ if ( m == SDIM-1 ) (*graph_edge_transforms)(gdata1,gdata[0].id); else torus_edge_clip(gdata1,m+1); } } /* send on original edge structure */ if ( m == SDIM-1 ) (*graph_edge_transforms)(gdata,gdata[0].id); else torus_edge_clip(gdata,m+1); } /* end torus_arc_clip() */ /********************************************************************** * * Function: torus_clip() * * Purpose: Recursive routine to subdivide triangles crossing * torus cell faces. Also handles unit cell wrapping * in general. */ void torus_clip(gdata,m,f_id) struct graphdata *gdata; /* triangle to check */ int m; /* coordinate number */ facet_id f_id; /* parent facet */ { struct graphdata gdata0[FACET_VERTS+1],gdata1[FACET_VERTS+1], gdata2[FACET_VERTS+1]; int i,k,mm; REAL t,d,a,b; int wrap[FACET_VERTS]; int cut,oddman,oddflag; int oddwrap; REAL w; REAL **per=NULL; REAL **invper=NULL; REAL orig[MAXCOORD]; if ( web.torus_display_period && web.torus_period ) { /* per = web.torus_display_period; */ per = web.torus_period; invper = web.inverse_display_periods; } else if ( web.torus_period ) { per = web.torus_period; invper = web.inverse_periods; } else if ( web.torus_display_period ) { per = web.torus_display_period; invper = web.inverse_display_periods; } else if ( slice_view_flag ) slice_facet(gdata); else if ( clip_view_flag ) clip_facet(gdata,f_id,0); else (*graph_facet)(gdata,f_id); for ( i = 0 ; i < SDIM ; i++ ) orig[i] = SDIM_dot(invper[i],web.display_origin); memset(gdata0,0,(FACET_VERTS+1)*sizeof(struct graphdata)); memset(gdata1,0,(FACET_VERTS+1)*sizeof(struct graphdata)); memset(gdata2,0,(FACET_VERTS+1)*sizeof(struct graphdata)); for ( i = 0 ; i < FACET_VERTS ; i++ ) gdata0[i] = gdata[i]; /* local copy */ /* see if any vertices outside cell in this coordinate */ for ( i = 0 ; i < FACET_VERTS ; i++ ) { w = SDIM_dot(invper[m],gdata0[i].x) - orig[m]; wrap[i] = (w < .5) ? ((int)floor(w+.00000001)):((int)floor(w-.00000001)); /* slight shift to prevent spurious wraps due to rounding */ } /* find odd vertex */ oddflag = 1; if ( (wrap[0] == wrap[1]) && (wrap[1] == wrap[2]) ) { oddflag = 0; oddman = 0; } else if ( wrap[0] == wrap[1] ) oddman = 2; else if ( wrap[1] == wrap[2] ) oddman = 0; else if ( wrap[2] == wrap[0] ) oddman = 1; else { sprintf(msg,"Triangle spans too many periods. Wraps %d %d %d\n", wrap[0],wrap[1],wrap[2]); erroutstring(msg); oddman = 0; } /* wrap new triangle vertices properly */ oddwrap = wrap[oddman]; for ( i = 0 ; i < FACET_VERTS ; i++ ) { wrap[i] -= oddwrap; for ( k = 0 ; k < SDIM ; k++ ) { d = oddwrap*per[m][k]; gdata0[i].x[k] -= d; } } /* kludge for display_periods to get back towards box */ if ( web.torus_flag && web.torus_display_period ) for ( mm = 0 ; mm < SDIM ; mm++ ) if ( mm != m ) { int ww = (int)floor((SDIM_dot(invper[mm],gdata0[0].x) + SDIM_dot(invper[mm],gdata0[1].x) + SDIM_dot(invper[mm],gdata0[2].x))/FACET_VERTS - orig[mm]); for ( i = 0 ; i < FACET_VERTS ; i++ ) for ( k = 0 ; k < SDIM ; k++ ) gdata0[i].x[k] -= ww*per[mm][k]; } /* split, if necessary */ if ( oddflag ) { int pair1 = (oddman+1)%FACET_VERTS; int pair2 = (oddman+2)%FACET_EDGES; gdata0[0].flags = 0; /* vertices the hard way */ /* find wrap multiple that cuts triangle */ if ( wrap[oddman] < wrap[pair1] ) cut = wrap[pair1]; else cut = wrap[oddman]; /* set up new triangles */ for ( i = 0 ; i < FACET_VERTS ; i++ ) { gdata2[i] = gdata0[i]; gdata1[i] = gdata0[i]; } /* calculate new vertices */ a = SDIM_dot(invper[m],gdata0[oddman].x)-orig[m]; b = SDIM_dot(invper[m],gdata1[pair1].x)-orig[m]; if ( fabs(a-b) < 0.00000001 ) t = 0.5; else t = (cut - a)/(b - a); if ( t < 0.0 ) t = 0.0; /* clamp; necessary due to wiggle room above */ else if ( t > 1.0 ) t = 1.0; for ( k = 0 ; k < SDIM ; k++ ) gdata1[oddman].x[k] = gdata0[pair1].x[k] = (1 - t)*gdata0[oddman].x[k] + t*gdata1[pair1].x[k]; b = SDIM_dot(invper[m],gdata2[pair2].x)-orig[m]; if ( fabs(a-b) < 0.00000001 ) t = 0.5; else t = (cut - a)/(b - a); if ( t < 0.0 ) t = 0.0; /* clamp; necessary due to wiggle room above */ else if ( t > 1.0 ) t = 1.0; for ( k = 0 ; k < SDIM ; k++ ) gdata2[oddman].x[k] = gdata0[pair2].x[k] = gdata1[pair2].x[k] = (1 - t)*gdata0[oddman].x[k] + t*gdata2[pair2].x[k]; gdata0[pair1].v_id = NULLID; gdata0[pair2].v_id = NULLID; gdata1[oddman].v_id = NULLID; gdata1[pair2].v_id = NULLID; gdata2[oddman].v_id = NULLID; /* new edge colors are clear */ gdata0[pair1].ecolor = CLEAR; gdata1[pair1].ecolor = gdata1[pair2].ecolor = CLEAR; gdata2[oddman].ecolor = CLEAR; gdata0[pair1].etype = INVISIBLE_EDGE; gdata1[pair1].etype = gdata1[pair2].etype = INVISIBLE_EDGE; gdata2[oddman].etype = INVISIBLE_EDGE; /* wrap new triangle vertices properly */ for ( i = 0 ; i < FACET_VERTS ; i++ ) for ( k = 0 ; k < SDIM ; k++ ) { d = wrap[pair1]*per[m][k]; gdata1[i].x[k] -= d; gdata2[i].x[k] -= d; } /* kludge for display_periods to get back towards box */ if ( web.torus_display_period ) for ( mm = 0 ; mm < SDIM ; mm++ ) if ( mm != m ) { int ww = (int)floor((SDIM_dot(invper[mm],gdata1[0].x) + SDIM_dot(invper[mm],gdata1[1].x) + SDIM_dot(invper[mm],gdata1[2].x))/FACET_VERTS - orig[mm]); for ( i = 0 ; i < FACET_VERTS ; i++ ) for ( k = 0 ; k < SDIM ; k++ ) gdata1[i].x[k] -= ww*per[mm][k]; ww = (int)floor((SDIM_dot(invper[mm],gdata2[0].x) + SDIM_dot(invper[mm],gdata2[1].x) + SDIM_dot(invper[mm],gdata2[2].x))/FACET_VERTS - orig[mm]); for ( i = 0 ; i < FACET_VERTS ; i++ ) for ( k = 0 ; k < SDIM ; k++ ) gdata2[i].x[k] -= ww*per[mm][k]; } /* send on for further check, or plot */ if ( m == 0 ) { if ( web.torus_flag && (torus_display_mode == TORUS_CLIPPED_MODE) ) { (*graph_facet_transforms)(gdata1,f_id); (*graph_facet_transforms)(gdata2,f_id); } else if ( slice_view_flag ) { slice_facet(gdata1); slice_facet(gdata2); } else if ( clip_view_flag ) { clip_facet(gdata1,f_id,0); clip_facet(gdata2,f_id,0); } else { (*graph_facet)(gdata1,f_id); (*graph_facet)(gdata2,f_id); } } else { torus_clip(gdata1,m-1,f_id); torus_clip(gdata2,m-1,f_id); } } /* send on original triangle structure */ if ( m == 0 ) { if ( web.torus_flag && (torus_display_mode == TORUS_CLIPPED_MODE) ) (*graph_facet_transforms)(gdata0,f_id); else if ( slice_view_flag ) slice_facet(gdata0); else if ( clip_view_flag ) clip_facet(gdata0,f_id,0); else (*graph_facet)(gdata0,f_id); } else torus_clip(gdata0,m-1,f_id); } /* end torus_clip */ /******************************************************************** * * function: bfcomp() * * purpose: comparison for ordering structures in torus_bodies() * */ int bfcomp(a,b) struct bodyface *a,*b; { if ( loc_ordinal(a->b_id) < loc_ordinal(b->b_id) ) return -1; if ( loc_ordinal(a->b_id) > loc_ordinal(b->b_id) ) return 1; if ( loc_ordinal(a->f_id) < loc_ordinal(b->f_id) ) return -1; if ( loc_ordinal(a->f_id) > loc_ordinal(b->f_id) ) return 1; if ( inverted(a->f_id) < inverted(b->f_id) ) return -1; if ( inverted(a->f_id) > inverted(b->f_id) ) return 1; return 0; } /********************************************************************** * * Function: torus_bodies() * * Purpose: To display SOAPFILM torus model in terms of connected * bodies. Finds all facets of each body and plots them * in a connected manner. */ void torus_bodies() { struct bodyface *allfaces; int allfacetop; int facemax; facetedge_id fe,fen,ff,fa,fan; body_id b_id; int i,j,k,kk,bj; int numleft; int oldnumleft; int startflag; struct bodyface *bf; struct graphdata *gdata; facet_id f_id; WRAPTYPE xwrap=0; /* for centering body */ int ctrlpts = web.skel[FACET].ctrlpts; REAL **xxx; /* for LAGRANGE unwrapped vertices */ int *bodystarts; /* spots in bodyface list */ gdata = (struct graphdata *)temp_calloc(ctrlpts+1,sizeof(struct graphdata)); xxx = temp_dmatrix(0,ctrlpts,0,SDIM); facemax = 2*web.skel[FACET].count; allfaces = (struct bodyface *) temp_calloc(facemax,sizeof(struct bodyface)); bodystarts = (int *)temp_calloc(web.skel[BODY].max_ord+3,sizeof(int)); allfacetop = 0; /* first, get a list of all facets bounding the body, with proper orientation. Some may be included twice with opposite orientations in case the body wraps around the torus. */ MFOR_ALL_FACETS(f_id) { body_id front,back; #ifdef MPI_EVOLVER if ( !mpi_show_corona_flag && (id_task(f_id) != this_task) ) continue; #endif front = get_facet_body(f_id); back = get_facet_body(facet_inverse(f_id)); if ( valid_id(front) ) { allfaces[allfacetop].b_id = front; allfaces[allfacetop++].f_id = f_id; } if ( valid_id(back) ) { allfaces[allfacetop].b_id = get_facet_body(facet_inverse(f_id)); allfaces[allfacetop++].f_id = facet_inverse(f_id); } if ( !valid_id(front) && !valid_id(back) ) { allfaces[allfacetop].b_id = web.skel[BODY].max_ord+1; /* big, so bare facets sort at end */ allfaces[allfacetop++].f_id = f_id; } } /* sort in facet order, so can find facets quickly */ qsort((char *)allfaces,allfacetop,sizeof(struct bodyface),FCAST bfcomp); /* find ranges for particular bodies */ bodystarts[0] = 0; i = loc_ordinal(allfaces[0].b_id); /* first body may not have ordinal 0 */ for ( j = 0 ; j <= i ; j++ ) bodystarts[j] = 0; for ( k = 1 ; k < allfacetop ; k++ ) { if ( allfaces[k].b_id != allfaces[k-1].b_id ) { i = loc_ordinal(allfaces[k].b_id); for ( ; j <= i ; j++ ) bodystarts[j] = k; } } for ( ; j <= web.skel[BODY].max_ord+2 ; j++ ) bodystarts[j] = k; /* Now go through list repeatedly, marking proper wraps for facet base vertices as can. */ for ( bj = 0 ; bj <= web.skel[BODY].max_ord+1 ; bj++ ) { int facetop; struct bodyface *faces; faces = allfaces + bodystarts[bj]; facetop = bodystarts[bj+1] - bodystarts[bj]; if ( facetop == 0 ) continue; if ( bj <= web.skel[BODY].max_ord ) b_id = get_ordinal_id(BODY,bj); else b_id = 0; if ( breakflag ) break; if ( valid_id(b_id) && show_expr[BODY] && show_expr[BODY]->start ) if ( !eval(show_expr[BODY],NULL,b_id,NULL) ) continue; numleft = oldnumleft = facetop; do { startflag = (numleft == oldnumleft); /* see if need to start body piece */ oldnumleft = numleft; for ( k = 0 ; k < facetop ; k++ ) { if ( faces[k].wrapflag ) continue; if ( startflag ) /* for starting disconnected body pieces */ { faces[k].wrap = 0; /* wrap to base cell */ faces[k].wrapflag = IS_WRAPPED; numleft--; startflag = 0; continue; } fe = get_facet_fe( faces[k].f_id ); for ( i = 0 ; i < FACET_VERTS ; i++ ) /* check for wrapped neighbor */ { struct bodyface key; int counter; if ( equal_id(fe,get_prev_facet(fe)) ) { /* valence 1 edge */ fe = get_next_edge(fe); continue; } fen = fe_inverse(get_prev_facet(fe)); ff = get_fe_facet(fen); if ( !equal_id(get_facet_body(ff),b_id) ) continue; key.f_id = ff; key.b_id = b_id; bf = (struct bodyface *)bsearch((char *)&key,(char *)faces, facetop, sizeof(struct bodyface),FCAST bfcomp); if ( bf == NULL ) { sprintf(errmsg, "INTERNAL ERROR, torus_bodies(): face %s missing neighbor face %s on body %s.\n", ELNAME(faces[k].f_id),ELNAME1(ff), ELNAME2(b_id)); kb_error(1050,errmsg,WARNING); fe = get_next_edge(fe); faces[k].wrapflag = BAD_BAD_BAD; numleft--; continue; } if ( bf-> wrapflag != IS_WRAPPED) { fe = get_next_edge(fe); continue; } /* now have wrapped neighbor */ /* start at base point of neighbor and follow edges accumulating wraps until new base point is reached */ faces[k].wrap = bf->wrap; fan = get_facet_fe(bf->f_id); fen = get_next_edge(fen); /* so tail of fan winds up at tail of fe */ counter = 0; while ( !equal_id(fan,fen) ) { /* walk around neighbor */ faces[k].wrap = (*sym_compose)(faces[k].wrap,get_fe_wrap(fan)); fan = get_next_edge(fan); if ( ++counter >= 3 ) break; /* just in case */ } fa = get_facet_fe(faces[k].f_id); counter = 0; while ( !equal_id(fa,fe) ) { /* walk backward around new facet */ fe = get_prev_edge(fe); faces[k].wrap = (*sym_compose)(faces[k].wrap, (*sym_inverse)(get_fe_wrap(fe))); if ( ++counter >= 3 ) break; /* just in case */ } faces[k].wrapflag = 1; numleft--; break; } } } while ( numleft > 0 ); /* Adjusting body wrap */ if ( !web.torus_flag || !(get_battr(b_id) & HAVE_CENTEROFMASS) ) { /* try to center body in cell by finding most common wrap */ struct { WRAPTYPE wrap; int count; } xxwrap[50]; int wrapcount = 1; /* number of different wraps */ memset((char*)xxwrap,0,sizeof(xxwrap)); for ( k = 0 ; k < facetop ; k++ ) { for ( i = 0 ; i < wrapcount ; i++ ) if ( xxwrap[i].wrap == faces[k].wrap ) { xxwrap[i].count++; break; } if ( (i == wrapcount) && (i < 50-1) ) { xxwrap[wrapcount].wrap = faces[k].wrap; xxwrap[wrapcount++].count = 1; } } for ( k = 0, i = 0 ; i < wrapcount ; i++ ) if ( xxwrap[i].count > xxwrap[k].count ) k = i; xwrap = (*sym_inverse)(xxwrap[k].wrap); } if ( web.torus_flag ) { int vcount; REAL *cm; REAL new_cm[MAXCOORD]; REAL u[MAXCOORD]; facet_id f_id; facetedge_id fe; vertex_id v_id; REAL adjust; REAL x[MAXCOORD]; /* get vertex-wise center of mass */ vcount = 0; cm = get_body_cm(b_id); /* in unit cell coordinates */ if ( !(get_battr(b_id) & HAVE_CENTEROFMASS) ) { for ( i = 0 ; i < SDIM ; i++ ) cm[i] = 0.5; set_attr(b_id,HAVE_CENTEROFMASS); } for ( i = 0 ; i < SDIM ; i++ ) new_cm[i] = 0.0; for ( kk = 0 ; kk < facetop ; kk++ ) { f_id = faces[kk].f_id; fe = get_facet_fe(f_id); v_id = get_fe_tailv(fe); (*sym_wrap)(get_coord(v_id),x,faces[kk].wrap); for ( i = 0 ; i < SDIM ; i++ ) new_cm[i] += x[i]; vcount++; } for ( i = 0 ; i < SDIM ; i++ ) new_cm[i] /= vcount; /* wrap to best agreement with old (which should be middle first time) */ if ( get_battr(b_id) & HAVE_CENTEROFMASS ) { matvec_mul(web.inverse_periods,new_cm,u,SDIM,SDIM); xwrap = 0; for ( i = SDIM-1 ; i >= 0 ; i-- ) { xwrap <<= TWRAPBITS; adjust = floor(cm[i]-u[i]+0.5); xwrap |= (int)(adjust) & WRAPMASK; cm[i] = u[i] + adjust; /* for next time around */ } } else /* save most common wrap (for backwards compatibility) */ { (*sym_wrap)(new_cm,x,xwrap); matvec_mul(web.inverse_periods,x,cm,SDIM,SDIM); } } /* now plot all the facets */ for ( kk = 0 ; kk < facetop ; kk++ ) { WRAPTYPE wrap; int fattr; facetedge_id fe_id; edge_id e_id; if ( breakflag ) break; f_id = faces[kk].f_id; fattr = get_fattr(f_id); if ( fattr & NODISPLAY ) continue; fe = get_facet_fe(f_id); wrap = (*sym_compose)(faces[kk].wrap,xwrap); if ( web.modeltype == LAGRANGE ) { vertex_id *v = get_facet_vertices(f_id); WRAPTYPE lwrap; get_facet_verts(f_id,xxx,NULL); /* for positive facet */ if ( inverted(f_id) ) lwrap = (*sym_compose)(get_fe_wrap(fe),wrap); else lwrap = wrap; for ( i = 0 ; i < ctrlpts ; i++ ) { int ii; if ( inverted(f_id) ) { int row=0,col; int n; n = 0; while ( n + (web.lagrange_order-row) < i ) { n += web.lagrange_order-row+1; row++; } col = i - n; ii = n + (web.lagrange_order-row-col); } else ii = i; gdata[i].v_id = v[ii]; (*sym_wrap)(xxx[ii],gdata[i].x,lwrap); if ( colorflag ) gdata[i].color = gdata[i].backcolor = (facet_rgb_color_attr > 0 ) ? INDEX_TO_RGBA(loc_ordinal(b_id)) : loc_ordinal(b_id); else { if ( inverted(f_id) ) { gdata[i].color = get_facet_backcolor_2(f_id); gdata[i].backcolor = get_facet_frontcolor_2(f_id); } else { gdata[i].color = get_facet_frontcolor_2(f_id); gdata[i].backcolor = get_facet_backcolor_2(f_id); } } } } else for ( i = 0 ; i < FACET_VERTS ; i++ ) /* vertex loop */ { REAL *verts; vertex_id v_id; e_id = get_fe_edge(fe); gdata[i].v_id = v_id = get_edge_tailv(e_id); verts = get_coord(v_id); (*sym_wrap)(verts,gdata[i].x,wrap); if ( colorflag ) gdata[i].color = gdata[i].backcolor = (facet_rgb_color_attr > 0 ) ? INDEX_TO_RGBA(loc_ordinal(b_id)) : loc_ordinal(b_id); else { gdata[i].color = get_facet_frontcolor_2(f_id); gdata[i].backcolor = get_facet_backcolor_2(f_id); } /* wrap vertices that belong with base of edge */ if ( web.modeltype == QUADRATIC ) { if ( !inverted(e_id) ) { verts = get_coord(get_edge_midv(e_id)); (*sym_wrap)(verts,gdata[FACET_VERTS+i].x,wrap); } gdata[FACET_VERTS+i].color = gdata[i].color; gdata[FACET_VERTS+i].backcolor = gdata[i].backcolor; gdata[FACET_VERTS+i].v_id = get_edge_midv(e_id); } wrap = (*sym_compose)(wrap,get_fe_wrap(fe)); if ( web.modeltype == QUADRATIC && inverted(e_id) ) { verts = get_coord(get_edge_midv(e_id)); (*sym_wrap)(verts,gdata[FACET_VERTS+i].x,wrap); } fe = get_next_edge(fe); } if ( show_expr[FACET] && show_expr[FACET]->start ) if ( !eval(show_expr[FACET],NULL,f_id,NULL) ) gdata[0].color = gdata[0].backcolor = UNSHOWN; /* maybe do edges */ /* do inner clipping, if called for */ if ( inner_clip_flag ) { for ( i = 0 ; i < FACET_VERTS ; i++ ) { REAL dist = 0.0; REAL *x = get_coord(web.zoom_v); for ( j = 0 ; j < SDIM ; j++ ) dist += (x[j]-gdata[i].x[j])*(x[j]-gdata[i].x[j]); if ( sqrt(dist) > inner_clip_rad ) break; /* it's a keeper */ } if ( i == FACET_VERTS ) continue; /* entirely inside */ } if ( gdata[0].color != gdata[0].backcolor ) { /* need normal for separation */ REAL dd; if ( web.modeltype == LAGRANGE ) vnormal(gdata[0].x,gdata[web.lagrange_order].x,gdata[ctrlpts-1].x, gdata[0].norm); else vnormal(gdata[0].x,gdata[1].x,gdata[2].x,gdata[0].norm); dd = sqrt(SDIM_dot(gdata[0].norm,gdata[0].norm)); for ( i = 0 ; i < SDIM ; i++ ) gdata[0].norm[i] /= dd; } fe = get_facet_fe(f_id); if ( normflag || thickenflag ) { fe = get_facet_fe(f_id); for ( i = 0 ; i < FACET_VERTS ; i++ ) { calc_vertex_smooth_normal(get_fe_tailv(fe),fe,gdata[i].norm); fe = get_next_edge(fe); } } /* check for special edges */ fe_id = get_facet_fe(f_id); for ( i = 0 ; i < 3 ; i++, fe_id = get_next_edge(fe_id) ) { int eattr; gdata[i].etype = INVISIBLE_EDGE; /* default */ gdata[i].id = e_id = get_fe_edge(fe_id); gdata[i].ecolor = get_edge_color_2(e_id); eattr = get_eattr(e_id); if ( get_edge_color(e_id) == CLEAR ) continue; if ( eattr & BOUNDARY ) gdata[i].etype |= BOUNDARY_EDGE; if ( equal_id(get_next_facet(fe_id),fe_id) ) /* valence 1 */ gdata[i].etype |= SINGLE_EDGE; else if ( !equal_id(get_next_facet(fe_id),get_prev_facet(fe_id)) ) gdata[i].etype |= TRIPLE_EDGE; /* triple line at least */ if ( (eattr & HIT_WALL) && !(fattr & CONSTRAINT) ) gdata[i].etype |= CONSTRAINT_EDGE; if ( (eattr & FIXED) && !(fattr & FIXED) ) gdata[i].etype |= FIXED_EDGE; if ( show_expr[EDGE] && show_expr[EDGE]->start ) { if ( eval(show_expr[EDGE],NULL,e_id,NULL) ) gdata[i].etype |= REGULAR_EDGE; else gdata[i].etype = INVISIBLE_EDGE; } } /* call device-specific routine */ if ( web.modeltype == LINEAR ) { /* call option-specific routine */ gdata->flags |= LABEL_FACET; option_facet(gdata,f_id); } else if ( web.modeltype == QUADRATIC ) /* plot as four subtriangles */ { struct graphdata qdata[FACET_VERTS+1]; memcpy((char*)qdata,(char*)gdata,3*sizeof(struct graphdata)); qdata[0].flags = 0; for( j = 0 ; j < SDIM ; j++ ) { qdata[1].x[j] = gdata[3].x[j]; qdata[2].x[j] = gdata[5].x[j]; } qdata[1].etype=INVISIBLE_EDGE; qdata[1].v_id = gdata[3].v_id; qdata[2].v_id = gdata[5].v_id; qdata[0].flags = 0; option_facet(qdata,f_id); for(j=0;jstart ) { facet_galloc = 100; facet_gdata = (struct graphdata *)temp_calloc(facet_galloc, sizeof(struct graphdata)); } /* if ( web.torus_display_period ) { invper = web.inverse_display_periods; } else */ { invper = web.inverse_periods; } MFOR_ALL_FACETS(f_id) { int show_this_facet = 0; #ifdef MPI_EVOLVER if ( !mpi_show_corona_flag && (id_task(f_id) != this_task) ) continue; #endif if ( show_expr[FACET] && show_expr[FACET]->start ) { if ( !eval(show_expr[FACET],NULL,f_id,NULL) ) continue; else show_this_facet = 1; } if ( breakflag ) break; facet_gcount = 0; fe_id = get_facet_fe(f_id); x = get_coord(get_fe_tailv(fe_id)); for ( i = 0 ; i < SDIM ; i++ ) gdata[0].x[i] = x[i]; /* wrap back to center */ if ( web.torus_flag ) { int ww[MAXCOORD]; for ( i = 0 ; i < SDIM ; i++ ) ww[i] = (int)floor(SDIM_dot(invper[i],gdata[0].x)); for ( i = 0, wrap = 0 ; i < SDIM ; i++ ) { wrap <<= TWRAPBITS; wrap |= ww[i] & WRAPMASK; } (*sym_wrap)(x,gdata[0].x,wrap); } if ( show_this_facet ) { facet_gdata[0] = gdata[0]; facet_gcount = 1; } fe = fe_id; do { /* follow edges, keeping track of wrap */ if ( !valid_id(fe) ) break; gdata[0].id = e_id = get_fe_edge(fe); gdata[0].color = gdata[0].ecolor = get_edge_color_2(gdata[0].id); gdata[0].etype = REGULAR_EDGE; gdata[0].flags = 0; gdata[0].v_id = get_fe_tailv(fe); gdata[1].v_id = get_fe_headv(fe); if ( show_this_facet && (facet_gcount > facet_galloc-20) ) { facet_galloc *= 2; facet_gdata = (struct graphdata *)temp_realloc((char*)facet_gdata, facet_galloc*sizeof(struct graphdata)); } if ( web.modeltype == LINEAR ) { wrap = (*sym_compose)(wrap,get_fe_wrap(fe)); } else if ( web.modeltype == QUADRATIC ) { gdata[1] = gdata[0]; if ( inverted(e_id) ) wrap = (*sym_compose)(wrap,get_fe_wrap(fe)); x = get_coord(get_fe_midv(fe)); (*sym_wrap)(x,gdata[1].x,wrap); gdata[0].flags &= LABEL_TAIL; if ( circular_arc_flag && (graph_capabilities & GC_ARCS) ) { x = get_coord(get_fe_headv(fe)); if ( !inverted(e_id) ) wrap = (*sym_compose)(wrap,get_fe_wrap(fe)); (*sym_wrap)(x,gdata[2].x,wrap); gdata[0].flags |= EDGE_ARC; (*graph_edge_transforms)(gdata,gdata[0].id); gdata[0] = gdata[2]; goto next_edge; } else { REAL headx[MAXCOORD]; REAL tailx[MAXCOORD]; REAL midx[MAXCOORD]; REAL w1[MAXCOORD],mag1; REAL w2[MAXCOORD],mag2; REAL w[MAXCOORD],mag; REAL xx[MAXCOORD]; REAL ang; int segments; int k; x = get_coord(get_fe_headv(fe)); if ( !inverted(e_id) ) wrap = (*sym_compose)(wrap,get_fe_wrap(fe)); (*sym_wrap)(x,gdata[2].x,wrap); for (i = 0 ; i < SDIM ; i++ ) { headx[i] = gdata[2].x[i]; tailx[i] = gdata[0].x[i]; midx[i] = gdata[1].x[i]; w1[i] = midx[i] - tailx[i]; w2[i] = headx[i] - tailx[i]; } mag1 = SDIM_dot(w1,w1); mag2 = SDIM_dot(w2,w2); for ( i = 0 ; i < SDIM ; i++ ) { w1[i] /= mag1; w2[i] /= mag2; } /* figure out how many segments to do */ ang = 4*acos(SDIM_dot(w1,w2)*sqrt(mag1*mag2)); segments = (int)(ang/(M_PI/180*string_curve_tolerance)); if ( segments < 2 ) segments = 2; if ( show_this_facet ) while (facet_gcount+segments > facet_galloc-20 ) { facet_galloc *= 2; facet_gdata = (struct graphdata *)temp_realloc((char*)facet_gdata, facet_galloc*sizeof(struct graphdata)); } for ( i = 0 ; i < SDIM ; i++ ) gdata[0].x[i] = tailx[i]; for ( k = 1 ; k <= segments ; k++ ) { if ( circular_arc_flag ) { /* use circle as inversion of line */ for ( i = 0 ; i < SDIM ; i++ ) w[i] = w2[i] + (segments-k)/(REAL)k*(w1[i]-w2[i]); mag = SDIM_dot(w,w); for ( i = 0 ; i < SDIM ; i++ ) gdata[1].x[i] = xx[i] = tailx[i] + w[i]/mag; } else /* quadratic spline */ { REAL t = 2*k/(REAL)segments; REAL c1 = (t-1)*(t-2)/2; REAL c2 = t*(2-t); REAL c3 = t*(t-1)/2; for ( i = 0 ; i < SDIM ; i++ ) gdata[1].x[i] = xx[i] = c1*tailx[i]+c2*midx[i]+c3*headx[i]; } if ( show_this_facet ) facet_gdata[facet_gcount++] = gdata[1]; if ( labelflag ) { gdata[0].flags &= ~(LABEL_EDGE|LABEL_HEAD|LABEL_TAIL); if ( k == 1 ) gdata[0].flags |= LABEL_TAIL; if ( k == segments/2 ) gdata[0].flags |= LABEL_EDGE; if ( k == segments ) gdata[0].flags |= LABEL_HEAD; } (*graph_edge_transforms)(gdata,e_id); for ( i = 0 ; i < SDIM ; i++ ) /* gdata[1].x was messed by clip */ gdata[0].x[i] = xx[i]; } goto next_edge; } } else if ( web.modeltype == LAGRANGE ) { int k; vertex_id *v = get_edge_vertices(e_id); gdata[1] = gdata[0]; if ( inverted(e_id) ) { wrap = (*sym_compose)(wrap,get_fe_wrap(fe)); for ( k = web.lagrange_order - 1; k > 0 ; k-- ) { x = get_coord(v[k]); (*sym_wrap)(x,gdata[1].x,wrap); if ( k == web.lagrange_order/2 ) gdata[0].flags |= LABEL_EDGE; if ( k == web.lagrange_order - 2 ) gdata[0].flags |= LABEL_HEAD; if ( show_this_facet ) facet_gdata[facet_gcount++] = gdata[1]; (*graph_edge_transforms)(gdata,gdata[0].id); gdata[0] = gdata[1]; } } else { for ( k = 1; k < web.lagrange_order ; k++ ) { x = get_coord(v[k]); (*sym_wrap)(x,gdata[1].x,wrap); if ( k == web.lagrange_order/2 ) gdata[0].flags |= LABEL_EDGE; if ( k == web.lagrange_order - 2 ) gdata[0].flags |= LABEL_HEAD; if ( show_this_facet ) facet_gdata[facet_gcount++] = gdata[1]; (*graph_edge_transforms)(gdata,gdata[0].id); gdata[0] = gdata[1]; } wrap = (*sym_compose)(wrap,get_fe_wrap(fe)); } } /* now the head vertex for everybody */ x = get_coord(get_fe_headv(fe)); (*sym_wrap)(x,gdata[1].x,wrap); if ( show_this_facet ) facet_gdata[facet_gcount++] = gdata[1]; gdata[0].flags |= LABEL_EDGE|LABEL_TAIL; (*graph_edge_transforms)(gdata,gdata[0].id); gdata[0] = gdata[1]; next_edge: fe = get_next_edge(fe); } while ( !equal_id(fe,fe_id) ); if ( show_this_facet ) graph_string_facet(f_id,facet_gdata,facet_gcount); } if ( facet_gdata ) temp_free((char*)facet_gdata); } /* end torus_cells() */ /************************************************************************** * * Function: graph_string_facet() * * Purpose: In string model, graph one polygonal facet from list * of vertices in graphdata structures. */ void graph_string_facet(f_id,facet_gdata,facet_gcount) facet_id f_id; struct graphdata *facet_gdata; /* the list */ int facet_gcount; /* how many in the list */ { struct graphdata gdata[FACET_VERTS+1]; /* for an individual triangle, plus wrap */ int i,n; memset((char*)gdata,0,sizeof(gdata)); gdata[0].color = get_facet_frontcolor(f_id); gdata[0].backcolor = get_facet_backcolor(f_id); /* first vertex to be average of others */ for ( n = 0 ; n < facet_gcount ; n++ ) { for ( i = 0 ; i < SDIM ; i++ ) gdata[0].x[i] += facet_gdata[n].x[i]; facet_gdata[n].etype = INVISIBLE_EDGE; } for ( i = 0 ; i < SDIM ; i++ ) gdata[0].x[i] /= facet_gcount; for ( n = 0 ; n < facet_gcount-1 ; n++ ) { gdata[1] = facet_gdata[n]; gdata[2] = facet_gdata[n+1]; option_facet(gdata,f_id); } gdata[1] = facet_gdata[facet_gcount-1]; gdata[2] = facet_gdata[0]; option_facet(gdata,f_id); } /************************************************************************ * * function: slice_edge() * * purpose: Graph point, since slice of edge is point. * To be called after transforms done. */ void slice_edge(gdata) struct graphdata *gdata; { } /************************************************************************ * * function: slice_facet() * * purpose: graph 1D edge resulting from slicing plane through 2D facet. * To be called after transforms done. */ void slice_facet(gdata) struct graphdata *gdata; { struct graphdata sliceg[FACET_VERTS]; int i,j; int m = 0; /* how many plane crossings found */ memset(sliceg,0,sizeof(sliceg)); sliceg[0].etype = REGULAR_EDGE; sliceg[0].ecolor = gdata[0].color; for ( i = 0 ; i < FACET_VERTS ; i++ ) { int ii = (i < FACET_VERTS-1) ? i+1 : 0 ; REAL denom=0; REAL numer=slice_coeff[SDIM]; REAL lambda; for ( j = 0 ; j < SDIM ; j++ ) { numer -= slice_coeff[j]*gdata[ii].x[j]; denom += slice_coeff[j]*(gdata[i].x[j] - gdata[ii].x[j]); } if ( denom == 0.0 ) continue; lambda = numer/denom; if ( (lambda < 0.0) || (lambda > 1.0) ) continue; for ( j = 0 ; j < SDIM ; j++ ) sliceg[m].x[j] = lambda*gdata[i].x[j] + (1-lambda)*gdata[ii].x[j]; m++; } if ( m == 2 ) (*graph_edge)(sliceg,NULLID); else if ( m == 3 ) (*graph_facet)(sliceg,NULLID); } /************************************************************************ * * function: graph_edge_clip() * * purpose: wrapper for clip or slice stage */ void graph_edge_clip(g,id) struct graphdata *g; edge_id id; { if ( slice_view_flag ) slice_edge(g); else if ( clip_view_flag ) clip_edge(g,id,0); else (*graph_edge)(g,id); } /************************************************************************ * * function: clip_edge() * * purpose: Enforce clipping planes as defined in clip_coeff[][], * for clip_view toggle. * To be called after transforms done. */ void clip_edge(gdata,id,which) struct graphdata *gdata; edge_id id; /* original edge */ int which; /* which clipping plane at this stage */ { struct graphdata clipg[EDGE_VERTS]; int i,j; int innies[EDGE_VERTS],outies[EDGE_VERTS]; int in_count = 0,out_count = 0; /* Find which vertices are on which side */ for ( i = 0 ; i < EDGE_VERTS ; i++ ) { REAL value = clip_coeff[which][SDIM]; for ( j = 0 ; j < SDIM ; j++ ) value -= clip_coeff[which][j]*gdata[i].x[j]; if ( value >= 0.0 ) innies[in_count++] = i; else outies[out_count++] = i; } if ( in_count == 0 ) return; /* nothing to show */ if ( in_count == EDGE_VERTS ) /* don't have to clip */ { if ( which < MAXCLIPS-1 ) clip_edge(gdata,id,which+1); else (*graph_edge)(gdata,id); return; } /* have to clip */ clipg[0] = clipg[1] = gdata[0]; /* id, color, type, etc. */ if ( innies[0] == 1 ) for ( j = 0 ; j < SDIM ; j++ ) clipg[0].x[j] = gdata[1].x[j]; /* find intersections of clip plane with edge */ { REAL lambda; REAL denom=0; REAL numer=clip_coeff[which][SDIM]; for ( j = 0 ; j < SDIM ; j++ ) { numer -= clip_coeff[which][j]*gdata[0].x[j]; denom += clip_coeff[which][j]*(gdata[1].x[j] - gdata[0].x[j]); } if ( denom == 0.0 ) lambda = 0; else lambda = numer/denom; if ( lambda < 0.0 ) lambda = 0.0; else if ( lambda > 1.0 ) lambda = 1.0; for ( j = 0 ; j < SDIM ; j++ ) clipg[1].x[j] = lambda*gdata[1].x[j] + (1-lambda)*gdata[0].x[j]; } if ( which < MAXCLIPS-1 ) clip_edge(clipg,id,which+1); else (*graph_edge)(clipg,id); } /* end clip_edge() */ /************************************************************************ * * function: clip_facet() * * purpose: Enforce clipping planes as defined in clip_coeff[][], * for clip_view toggle. * To be called after transforms done. */ void clip_facet(gdata,f_id,which) struct graphdata *gdata; facet_id f_id; /* original facet */ int which; /* which clipping plane at this stage */ { struct graphdata clipg[2][FACET_VERTS]; int i,j; int innies[FACET_VERTS]; /* which vertices in clip space */ int in_count=0; int outies[FACET_VERTS]; /* which vertices out of clip space */ int out_count=0; int oddie; /* odd vertex, in or out */ int pair[2]; /* the other pair */ /* Find which vertices are on which side and */ for ( i = 0 ; i < FACET_VERTS ; i++ ) { REAL value = clip_coeff[which][SDIM]; for ( j = 0 ; j < SDIM ; j++ ) value -= clip_coeff[which][j]*gdata[i].x[j]; if ( value >= 0.0 ) innies[in_count++] = i; else outies[out_count++] = i; } if ( in_count == 0 ) return; /* nothing to show */ if ( in_count == 3 ) /* don't have to clip */ { if ( which < MAXCLIPS-1 ) clip_facet(gdata,f_id,which+1); else (*graph_facet)(gdata,f_id); return; } /* have to clip, maybe 2 facets result */ clipg[0][0] = clipg[0][1] = gdata[0]; /* facet id, color, normsl, etc. */ clipg[0][0].etype = INVISIBLE_EDGE; clipg[0][1].etype = INVISIBLE_EDGE; clipg[0][0].ecolor = CLEAR; clipg[0][1].ecolor = CLEAR; if ( in_count == 1 ) { oddie = innies[0]; pair[0] = outies[0]; pair[1] = outies[1]; } else /* in_count == 2 */ { oddie = outies[0]; pair[0] = innies[0]; pair[1] = innies[1]; } /* find intersections of clip plane with facet edges */ for ( i = 0 ; i < 2 ; i++ ) { REAL lambda; REAL denom=0; REAL numer=clip_coeff[which][SDIM]; for ( j = 0 ; j < SDIM ; j++ ) { numer -= clip_coeff[which][j]*gdata[pair[i]].x[j]; denom += clip_coeff[which][j]*(gdata[oddie].x[j] - gdata[pair[i]].x[j]); } if ( denom == 0.0 ) lambda = 0; else lambda = numer/denom; if ( lambda < 0.0 ) lambda = 0.0; else if ( lambda > 1.0 ) lambda = 1.0; for ( j = 0 ; j < SDIM ; j++ ) clipg[0][i].x[j] = lambda*gdata[oddie].x[j] + (1-lambda)*gdata[pair[i]].x[j]; } if ( in_count == 1 ) { /* third vertex is innie */ if ( innies[0] == 0 ) { clipg[0][2] = clipg[0][1]; clipg[0][1] = clipg[0][0]; clipg[0][0] = gdata[0]; clipg[0][2].ecolor = gdata[2].ecolor; clipg[0][2].etype = gdata[2].etype; } else if ( innies[0] == 1 ) { clipg[0][2] = clipg[0][1]; clipg[0][1] = gdata[1]; clipg[0][0].etype = gdata[0].etype; clipg[0][0].ecolor = gdata[0].ecolor; } else clipg[0][2] = gdata[innies[0]]; } else /* two innies, so have to split */ { clipg[1][0] = clipg[1][1] = gdata[0]; /* facet id, color, etc */ clipg[1][0].etype = INVISIBLE_EDGE; clipg[1][1].etype = INVISIBLE_EDGE; clipg[1][0].ecolor = CLEAR; clipg[1][1].ecolor = CLEAR; if ( outies[0] == 0 ) { clipg[0][2] = clipg[0][1]; clipg[0][1] = gdata[1]; clipg[0][0].etype = gdata[0].etype; clipg[0][0].ecolor = gdata[0].ecolor; clipg[0][1].etype = INVISIBLE_EDGE; clipg[0][1].ecolor = CLEAR; clipg[1][0] = clipg[0][2]; clipg[1][1] = gdata[1]; clipg[1][2] = gdata[2]; } else if ( outies[0] == 1 ) { clipg[0][2] = gdata[0]; clipg[1][0] = gdata[0]; clipg[1][0].etype = INVISIBLE_EDGE; clipg[1][0].ecolor = CLEAR; clipg[1][1] = clipg[0][1]; clipg[1][1].etype = gdata[1].etype; clipg[1][1].ecolor = gdata[1].ecolor; clipg[1][2] = gdata[2]; } else { clipg[0][2] = clipg[0][1]; clipg[0][1] = gdata[1]; clipg[1][0] = gdata[0]; clipg[1][1] = gdata[1]; clipg[1][1].etype = INVISIBLE_EDGE; clipg[1][1].ecolor = CLEAR; clipg[1][2] = clipg[0][0]; clipg[1][2].etype = gdata[2].etype; clipg[1][2].ecolor = gdata[2].ecolor; } if ( which < MAXCLIPS-1 ) clip_facet(clipg[1],f_id,which+1); else (*graph_facet)(clipg[1],f_id); } if ( which < MAXCLIPS-1 ) clip_facet(clipg[0],f_id,which+1); else (*graph_facet)(clipg[0],f_id); } /* end clip_facet() */ evolver-2.30c.dfsg/src/filgraph.c0000644000175300017530000003470611410765113017167 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /***************************************************************** * * File: filgraph.c * * Purpose: Triangle list file output. * Format: One triangle per line, * x1 y1 x2 y2 x3 y3 w1 w2 w3 d * vertex coordinates, edge widths, and face density. * * Also OFF format output file. */ #include "include.h" static FILE *fd; /***************************************************************** * * Function: fil_init() * * Purpose: Get file name from user. */ void fil_init() { char file_name[200]; for (;;) { prompt("Enter file name: ",file_name,sizeof(file_name)); if ( file_name[0] == 0 ) { kb_error(4006,"File aborted.\n",RECOVERABLE); } fd = fopen(file_name,"w"); if ( fd == NULL ) { perror(file_name); continue; } else return; } } /************************************************************ * * Function: fil_edge() * * Purpose: Graphs one edge, already transformed. */ void fil_edge(t) struct tsort *t; { fprintf(fd," %f %f ",(DOUBLE)t->x[0][0],(DOUBLE)t->x[0][1]); fprintf(fd," %f %f ",(DOUBLE)t->x[1][0],(DOUBLE)t->x[1][1]); fprintf(fd," %f %f ",(DOUBLE)t->x[1][0],(DOUBLE)t->x[1][1]); fprintf(fd," 0.03 0.03 0.03 0.0 \n"); } /************************************************************ * * Function: fil_facet() * * Purpose: Graphs one facet, already transformed. */ void fil_facet(t) struct tsort *t; { REAL cosine; edge_id e_id; facetedge_id fe_id; int type; int i; if ( t->color == CLEAR ) return; fprintf(fd,"%f %f",(DOUBLE)t->x[0][0],(DOUBLE)t->x[0][1]); fprintf(fd," %f %f",(DOUBLE)t->x[1][0],(DOUBLE)t->x[1][1]); fprintf(fd," %f %f",(DOUBLE)t->x[2][0],(DOUBLE)t->x[2][1]); fe_id = get_facet_fe(t->f_id); for ( i = 0 ; i < 3 ; i++ ) { e_id = get_fe_edge(fe_id); if ( get_eattr(e_id) & FIXED ) type = 3; else if ( equal_id(get_next_facet(fe_id),fe_id) ) type = 1; /* edge of some sort */ else if ( !equal_id(get_next_facet(fe_id),get_prev_facet(fe_id)) ) type = 4; /* triple line at least */ else type = 0; /* ordinary internal grid line */ fprintf(fd," %1d",type); fe_id = get_next_edge(fe_id); } if ( valid_id(t->f_id) ) { cosine = t->normal[1]/sqrt(dotf(t->normal,t->normal,3)); if ( (REAL)t->normal[2] < 0.0 ) cosine = -cosine; } else cosine = 0.0; fprintf(fd," %f\n",(DOUBLE)cosine); } /************************************************************* * * Function: fil_finish() * * Purpose: End output. */ void fil_finish() { fclose(fd); } /*************************************************************************** OFF format file. */ struct OFF_vertex_t { REAL x[3]; int orig; } *OFF_verts; int OFF_verts_alloc; struct OFF_edge_t { int v[2]; int color; int orig; } *OFF_edges; int OFF_edges_alloc; struct OFF_facet_t { int v[3]; int color; } *OFF_facets; int OFF_facets_alloc; int OFF_vertex_count; int OFF_edge_count; int OFF_facet_count; REAL OFF_eps = 1e-6; int OFF_comp(a,b) struct OFF_vertex_t *a,*b; { int i; for ( i = 0 ; i < 3; i++ ) { if ( a->x[i] < b->x[i] - OFF_eps ) return -1; if ( a->x[i] > b->x[i] + OFF_eps ) return 1; } return 0; } int OFF_edge_comp(a,b) struct OFF_edge_t *a,*b; { int i; for ( i = 0 ; i < EDGE_VERTS ; i++ ) { if ( a->v[i] < b->v[i] ) return -1; if ( a->v[i] > b->v[i] ) return 1; } return 0; } /***************************************************************** * * Function: OFF_start() * * Purpose: Get file name from user. */ void OFF_start() { char file_name[200]; for (;;) { prompt("Enter file name: ",file_name,sizeof(file_name)); if ( file_name[0] == 0 ) { kb_error(4005,"File aborted.\n",RECOVERABLE); } fd = fopen(file_name,"w"); if ( fd == NULL ) { perror(file_name); kb_error(1034,"",RECOVERABLE); } else break; } OFF_vertex_count = 0; OFF_edge_count = 0; OFF_facet_count = 0; OFF_verts_alloc = 2*web.skel[EDGE].count + 3*web.skel[FACET].count; OFF_verts = (struct OFF_vertex_t *)temp_calloc(OFF_verts_alloc, sizeof(struct OFF_vertex_t)); OFF_facets_alloc = 3*web.skel[FACET].count; OFF_facets = (struct OFF_facet_t *)temp_calloc(OFF_facets_alloc, sizeof(struct OFF_facet_t)); } /************************************************************ * * Function: OFF_edge() * * Purpose: Graphs one edge, already transformed. */ void OFF_edge(g,e_id) struct graphdata *g; edge_id e_id; { int e_color; e_color = g[0].ecolor; if ( e_color == CLEAR ) return; if ( (e_color < 0) || (e_color >= IRIS_COLOR_MAX) ) e_color = DEFAULT_EDGE_COLOR; } /****************************************************************** * * function: OFF_facet() * * purpose: graph one facet. */ void OFF_facet(g,f_id) struct graphdata *g; facet_id f_id; { int i,k; if ( OFF_vertex_count > OFF_verts_alloc - 5 ) { OFF_verts = (struct OFF_vertex_t*)temp_realloc((char*)OFF_verts, 2*OFF_verts_alloc*sizeof(struct OFF_vertex_t)); OFF_verts_alloc *= 2; } if ( OFF_facet_count > OFF_facets_alloc - 5 ) { OFF_facets = (struct OFF_facet_t*)temp_realloc((char*)OFF_facets, 2*OFF_facets_alloc*sizeof(struct OFF_facet_t)); OFF_facets_alloc *= 2; } for ( i = 0 ; i < FACET_VERTS ; i++ ) { for ( k = 0 ; k < 3 ; k++ ) OFF_verts[OFF_vertex_count].x[k] = g[i].x[k]; OFF_facets[OFF_facet_count].v[i] = OFF_vertex_count; OFF_vertex_count++; } OFF_facet_count++; } /************************************************************* * * Function: OFF_end() * * Purpose: End output. */ void OFF_end() { int i,j,keep; int *translate; /* unify vertices */ for ( i = 0 ; i < OFF_vertex_count ; i++ ) OFF_verts[i].orig = i; qsort((char*)OFF_verts,OFF_vertex_count,sizeof(struct OFF_vertex_t), FCAST OFF_comp); translate = (int*)temp_calloc(OFF_vertex_count,sizeof(int)); translate[OFF_verts[0].orig] = 0; for ( i = 1, keep = 0 ; i < OFF_vertex_count ; i++ ) { if ( OFF_comp(OFF_verts+i,OFF_verts+keep) != 0 ) OFF_verts[++keep] = OFF_verts[i]; translate[OFF_verts[i].orig] = keep; } OFF_vertex_count = keep+1; for ( i = 0 ; i < OFF_facet_count ; i++ ) { for ( j = 0 ; j < 3 ; j++ ) OFF_facets[i].v[j] = translate[OFF_facets[i].v[j]]; /* test for degenerate facet */ if ( OFF_facets[i].v[0] == OFF_facets[i].v[1] || OFF_facets[i].v[1] == OFF_facets[i].v[2] || OFF_facets[i].v[2] == OFF_facets[i].v[0] ) { /* found degenerate, so replace with last in list */ OFF_facets[i] = OFF_facets[--OFF_facet_count]; i--; } } fprintf(fd,"OFF\n%d %d %d\n",OFF_vertex_count,OFF_facet_count,OFF_edge_count); for ( i = 0 ; i < OFF_vertex_count ; i++ ) fprintf(fd,"%f %f %f\n",OFF_verts[i].x[0],OFF_verts[i].x[1], OFF_verts[i].x[2]); for ( i = 0 ; i < OFF_facet_count ; i++ ) fprintf(fd,"3 %d %d %d\n",OFF_facets[i].v[0],OFF_facets[i].v[1], OFF_facets[i].v[2]); fclose(fd); temp_free((char*)OFF_verts); temp_free((char*)OFF_facets); temp_free((char*)translate); } /*************************************************************************** Binary OFF format file for evmovie. */ /***************************************************************** * * Function: binary_OFF_start() * * Purpose: Get file name from user. */ char *binary_off_filename; /* set by binary_off_file command */ void binary_OFF_start() { OFF_vertex_count = 0; OFF_edge_count = 0; OFF_facet_count = 0; OFF_verts_alloc = 2*web.skel[EDGE].count + 3*web.skel[FACET].count + 10; OFF_verts = (struct OFF_vertex_t *)temp_calloc(OFF_verts_alloc, sizeof(struct OFF_vertex_t)); OFF_edges_alloc = 2*web.skel[EDGE].count + 10; OFF_edges = (struct OFF_edge_t *)temp_calloc(OFF_edges_alloc, sizeof(struct OFF_edge_t)); OFF_facets_alloc = web.skel[FACET].count + 10; OFF_facets = (struct OFF_facet_t *)temp_calloc(OFF_facets_alloc, sizeof(struct OFF_facet_t)); } /************************************************************ * * Function: binary_OFF_edge() * * Purpose: Graphs one edge, already transformed. */ void binary_OFF_edge(g,e_id) struct graphdata *g; edge_id e_id; { int i,k; int e_color; if ( OFF_vertex_count > OFF_verts_alloc - 5 ) { OFF_verts = (struct OFF_vertex_t*)temp_realloc((char*)OFF_verts, 2*OFF_verts_alloc*sizeof(struct OFF_vertex_t)); OFF_verts_alloc *= 2; } if ( OFF_edge_count > OFF_edges_alloc - 5 ) { OFF_edges = (struct OFF_edge_t*)temp_realloc((char*)OFF_edges, 2*OFF_edges_alloc*sizeof(struct OFF_edge_t)); OFF_edges_alloc *= 2; } e_color = g[0].ecolor; if ( e_color == CLEAR ) return; if ( (e_color < 0) || (e_color >= IRIS_COLOR_MAX) ) e_color = DEFAULT_EDGE_COLOR; for ( i = 0 ; i < EDGE_VERTS ; i++ ) { for ( k = 0 ; k < 3 ; k++ ) OFF_verts[OFF_vertex_count].x[k] = g[i].x[k]; OFF_edges[OFF_edge_count].v[i] = OFF_vertex_count; OFF_vertex_count++; } OFF_edges[OFF_facet_count].color = e_color; OFF_edge_count++; } /****************************************************************** * * function: binary_OFF_facet() * * purpose: graph one facet. */ void binary_OFF_facet(gdata,f_id) struct graphdata *gdata; facet_id f_id; { int i,k; struct graphdata ggdata[2]; if ( OFF_vertex_count > OFF_verts_alloc - 5 ) { OFF_verts = (struct OFF_vertex_t*)temp_realloc((char*)OFF_verts, 2*OFF_verts_alloc*sizeof(struct OFF_vertex_t)); OFF_verts_alloc *= 2; } if ( OFF_facet_count > OFF_facets_alloc - 5 ) { OFF_facets = (struct OFF_facet_t*)temp_realloc((char*)OFF_facets, 2*OFF_facets_alloc*sizeof(struct OFF_facet_t)); OFF_facets_alloc *= 2; } for ( i = 0 ; i < FACET_VERTS ; i++ ) { for ( k = 0 ; k < 3 ; k++ ) OFF_verts[OFF_vertex_count].x[k] = gdata[i].x[k]; OFF_facets[OFF_facet_count].v[i] = OFF_vertex_count; OFF_vertex_count++; } OFF_facets[OFF_facet_count].color = gdata[0].color; OFF_facet_count++; /* do edges */ for ( i = 0 ; i < FACET_EDGES ; i++ ) { if ( ((gdata[i].etype&EBITS)|REGULAR_EDGE) == REGULAR_EDGE ) continue; ggdata[0] = gdata[i]; ggdata[0].color = gdata[i].ecolor; ggdata[1] = gdata[i==2 ? 0 : i+1]; binary_OFF_edge(ggdata,NULLID); } } /************************************************************************** * * Function: binary_OFF_end() * * Purpose: Sort and merge vertices and edges; write output file */ void binary_OFF_end() { int i,j,k,keep; int *translate; /* unify vertices */ for ( i = 0 ; i < OFF_vertex_count ; i++ ) OFF_verts[i].orig = i; qsort((char*)OFF_verts,OFF_vertex_count,sizeof(struct OFF_vertex_t), FCAST OFF_comp); translate = (int*)temp_calloc(OFF_vertex_count,sizeof(int)); translate[OFF_verts[0].orig] = 0; for ( i = 1, keep = 0 ; i < OFF_vertex_count ; i++ ) { if ( OFF_comp(OFF_verts+i,OFF_verts+keep) != 0 ) OFF_verts[++keep] = OFF_verts[i]; translate[OFF_verts[i].orig] = keep; } OFF_vertex_count = keep+1; /* renumber edge and facet vertices */ for ( i = 0 ; i < OFF_edge_count ; i++ ) { for ( j = 0 ; j < EDGE_VERTS ; j++ ) OFF_edges[i].v[j] = translate[OFF_edges[i].v[j]]; /* get vertices in canonical order */ if ( OFF_edges[i].v[0] > OFF_edges[i].v[1] ) { int tmp = OFF_edges[i].v[0]; OFF_edges[i].v[0] = OFF_edges[i].v[1]; OFF_edges[i].v[1] = tmp; } } for ( i = 0 ; i < OFF_facet_count ; i++ ) { for ( j = 0 ; j < FACET_VERTS ; j++ ) OFF_facets[i].v[j] = translate[OFF_facets[i].v[j]]; } /* unify edges */ /* sort edges */ for ( i = 0 ; i < OFF_edge_count ; i++ ) OFF_edges[i].orig = i; qsort((char*)OFF_edges,OFF_edge_count,sizeof(struct OFF_edge_t), FCAST OFF_edge_comp); translate = (int*)temp_realloc((char*)translate,OFF_edge_count*sizeof(int)); for ( i = 1, keep = 0 ; i < OFF_edge_count ; i++ ) { if ( OFF_edge_comp(OFF_edges+i,OFF_edges+keep) != 0 ) OFF_edges[++keep] = OFF_edges[i]; } OFF_edge_count = keep+1; if ( binary_off_filename == NULL ) /* prompt for file name */ for (;;) { char file_name[200]; prompt("Enter file name (supply your own extension): ",file_name,sizeof(file_name)); if ( file_name[0] == 0 ) { kb_error(4005,"File aborted.\n",RECOVERABLE); } fd = fopen(file_name,"wb"); if ( fd == NULL ) { perror(file_name); kb_error(1034,"",RECOVERABLE); } else break; } else /* use supplied name */ { fd = fopen(binary_off_filename,"wb"); if ( fd == NULL ) { perror(binary_off_filename); kb_error(1034,"",RECOVERABLE); } binary_off_filename = NULL; /* for next time around */ } #ifdef ASCII_OFF /* ASCII version, with color indexes for edges and facets */ fprintf(fd,"COFF\n%d %d %d\n",OFF_vertex_count,OFF_facet_count,OFF_edge_count); for ( i = 0 ; i < OFF_vertex_count ; i++ ) fprintf(fd,"%f %f %f\n",OFF_verts[i].x[0],OFF_verts[i].x[1], OFF_verts[i].x[2]); for ( i = 0 ; i < OFF_facet_count ; i++ ) fprintf(fd,"3 %d %d %d %d\n",OFF_facets[i].v[0],OFF_facets[i].v[1], OFF_facets[i].v[2],OFF_facets[i].color); for ( i = 0 ; i < OFF_edge_count ; i++ ) fprintf(fd,"2 %d %d %d\n",OFF_edges[i].v[0],OFF_edges[i].v[1], OFF_edges[i].color); #else /* Binary version */ fprintf(fd,"OFF BINARY (by Surface Evolver, for evmovie)\n"); fwrite(&OFF_vertex_count,sizeof(int),1,fd); fwrite(&OFF_facet_count,sizeof(int),1,fd); fwrite(&OFF_edge_count,sizeof(int),1,fd); for ( i = 0 ; i < OFF_vertex_count ; i++ ) { float xx[3]; for ( k = 0 ; k < 3 ; k++ ) xx[k] = (float)(OFF_verts[i].x[k]); fwrite(xx,sizeof(float),3,fd); } for ( i = 0 ; i < OFF_facet_count ; i++ ) { int data[5]; data[0] = 3; for ( k = 0 ; k < 3 ; k++ ) data[k+1] = OFF_facets[i].v[k]; data[4] = OFF_facets[i].color; fwrite(data,sizeof(int),5,fd); } for ( i = 0 ; i < OFF_edge_count ; i++ ) { int data[4]; data[0] = 2; for ( k = 0 ; k < 2 ; k++ ) data[k+1] = OFF_edges[i].v[k]; data[3] = OFF_edges[i].color; fwrite(data,sizeof(int),4,fd); } #endif fclose(fd); temp_free((char*)OFF_verts); temp_free((char*)OFF_edges); temp_free((char*)OFF_facets); temp_free((char*)translate); } evolver-2.30c.dfsg/src/datafile.lex0000644000175300017530000024536511410765113017517 0ustar hazelscthazelsct%{ /******************************************************************** * * File: datafile.lex, lexyy.c * * Contents: lexical analyzer for evolver data files. * Constructed from datafile.lex by lex. */ /* will have my own input() and output() */ #ifdef FLEX_SCANNER #define YY_INPUT(buf,result,max_size) \ { \ int c = kb_input(); \ result = (c == 0) ? YY_NULL : (buf[0] = c, 1); \ } #else /* lex scanner */ #undef input #undef unput #define input kb_input #define unput rawunput #endif #include "include.h" #include "lex.h" #include "ytab.h" static int previous_char = 0; /* for CR conversion */ static int BUFFSIZE = 0; /* size of lex buffer */ static char *buff = NULL; /* lex buffer, expandable */ static int spot = 0; #define ERRBUFFSIZE 80 static char errbuff[ERRBUFFSIZE+4]; /* for reporting spot of error */ static int errspot; extern int ubuff_spot; struct ckey { char *name; int token; } const_expr_keywords[] = { {"pi",PI_}, {"e",E_}, {"g",G_} }; void savein ARGS((int)); void get_more_input ARGS((void)); int keyword_compare ARGS(( struct ckey *, struct ckey *)); /* Wrapper for yylex() to buffer look-ahead token */ #define UNPUTMAX 10 int unputted_tok[UNPUTMAX]; /* unput token number */ int unput_tok_count; /* 1 if token was unput */ YYSTYPE yylval; char kb_input_new ARGS((void)); void kb_unput ARGS((char)); int kblex ARGS((void)); #define KB_INPUT kb_input_new #define KB_UNPUT kb_unput /* for generation of bad tokens for error message generation */ /* for use with -E command line option */ int next_err_token = 0; /* token in file for which to generate error */ int token_count; /* resets on file initialization */ int err_tok_gen_flag; /* whether -E option in effect */ int kb_yylex(lvalp) YYSTYPE *lvalp; /* for pure parser destination for yylval */ { if ( lvalp ) { PROF_FINISH(yyparse); /* exclude from yyparse time */ } PROF_START(yylex); if ( unput_tok_count ) { tok = unputted_tok[--unput_tok_count]; } else { /* clean out previous data */ /* memset(&yylval,0,sizeof(yylval)); time hog*/ /* tok = yylex(); */ if ( err_tok_gen_flag ) { if ( token_count == next_err_token ) { tok = GEN_ERROR_TOKEN; if ( yytext ) strcpy(yytext,"generated error"); next_err_token++; token_count += 10000000; /* just one error per file */ return tok; } token_count++; } tok = kblex(); } switch ( tok ) { case '(' : parens++; break; case ')' : parens--; break; case '{' : brace_depth++; break; case '}' : brace_depth--; break; } if ( lvalp ) { { /* non-movsd substitute for *lvalp = yylval; */ int i; int *dest = (int*)lvalp; int *src = (int*)&yylval; for ( i = 0 ; i < sizeof(yylval)/sizeof(int) ; i++ ) *(dest++) = *(src++); } PROF_START(yyparse); } PROF_FINISH(yylex); return tok; } /* For unputting unneeded look-ahead token */ void unput_tok() { if ( unput_tok_count >= UNPUTMAX ) kb_error(2324,"Internal error: unputted_tok stack overflow.\n",RECOVERABLE); unputted_tok[unput_tok_count++] = tok; /* fprintf(stderr,"UNPUT %3d %s %s\n",tok,tokname(tok),yytext); */ /* tok = UNPUTTED_; */ /* some places depend on knowing unput lookahead */ } #ifndef NOPROTO int rawinput ARGS((void)); int yyback(int *,int); /* #define yyoutput yyout_unused */ /* #define yyunput yyunput_unused */ #endif extern FILE *data_fd; extern char *cmdptr; int line_no = 1; int macro_flag; /* tells macro() identifier is being #define'd */ /* return values for macro() */ #define NO_MACRO 0 #define WAS_MACRO_DEF 1 #define MACRO_EXPANDED 2 char *whitespace = " \t\r,:;"; /* whitespace */ #define SUBMAX 500 int macro_max; int macro_subs_top; /* index of top of string space */ int macro_subs_max; /* space allocated for strings */ struct dkey { char *name; int token; } datafile_keywords[] = { {"content_rank",CONTENT_RANK_}, {"clip_coeff",CLIP_COEFF}, {"slice_coeff",SLICE_COEFF}, {"high_constraint",V_HIGH_CONSTRAINT}, {"high_boundary",V_HIGH_BOUNDARY}, {"suppress_warning",SUPPRESS_WARNING_}, {"unsuppress_warning",UNSUPPRESS_WARNING_}, {"volume_method_name",VOLUME_METHOD_NAME_}, {"partner_hitting",PARTNER_HITTING_}, {"procedure",PROCEDURE_WORD_}, {"display_origin",DISPLAY_ORIGIN_}, {"length_method_name",LENGTH_METHOD_NAME_}, {"area_method_name",AREA_METHOD_NAME_}, {"hessian_special_normal_vector",HESSIAN_SPECIAL_NORMAL_VECTOR_}, {"keep_originals",KEEP_ORIGINALS_}, {"element_modulus",ELEMENT_MODULUS_}, {"swap_colors",SWAP_COLORS_}, {"self",SELF_}, {"conserved",CONSERVED_}, {"actual_volume",ACTUAL_VOLUME_}, {"keep_macros",KEEP_MACROS_}, {"pdelta",DELTA_ }, {"tolerance",TOLERANCE_}, {"lagrange_multiplier",LAGRANGE_MULTIPLIER_ }, {"evolver_version",VERSION_}, {"orientation",ORIENTATION_}, {"ignore_constraints",IGNORE_CONSTRAINTS_}, {"ignore_fixed",IGNORE_FIXED_}, {"load_library",LOAD_LIBRARY_}, {"interp_bdry_param",INTERP_BDRY_PARAM_}, {"axial_point",AXIAL_POINT_}, {"lagrange",LAGRANGE_}, {"lagrange_order",LAGRANGE_ORDER_}, {"parameter_1",PARAMETER_1_}, {"optimizing_parameter",OPTIMIZING_PARAMETER_}, {"optimising_parameter",OPTIMIZING_PARAMETER_}, {"everything_quantities",EVERYTHING_QUANTITIES_}, {"value",VALUE_}, {"target",TARGET_}, {"define",DEFINE_}, {"attribute",ATTRIBUTE_}, {"method_instance",METHOD_INSTANCE_}, {"method",METHOD_}, {"scalar_integrand",SCALAR_INTEGRAND_}, {"vector_integrand",VECTOR_INTEGRAND_}, {"k_vector_order",K_VEC_ORDER_}, {"form_integrand",FORM_INTEGRAND_}, {"mobility",MOBILITY_}, {"mobility_tensor",MOBILITY_TENSOR_}, {"bare",BARE_}, {"boundary_curvature",BOUNDARY_CURVATURE_}, {"modulus",MODULUS_}, {"info_only",INFO_ONLY_}, {"global_method",GLOBAL_METHOD_}, {"global",GLOBAL_}, {"area_fixed",AREA_FIXED_}, {"fixed_area",AREA_FIXED_}, {"view_matrix",VIEW_MATRIX_}, {"view_transforms",VIEW_TRANSFORMS_}, {"view_transform_generators",VIEW_TRANSFORM_GENS_}, {"homothety",HOMOTHETY_}, {"approximate_curvature",APPROX_CURV_}, {"approx_curvature",APPROX_CURV_}, {"phasefile",PHASEFILE_}, {"phase",PHASE_}, {"autopop",AUTOPOP_}, {"autopop_quartic",AUTOPOP_QUARTIC_}, {"autochop",AUTOCHOP_}, {"total_time",TOTAL_TIME_}, {"effective_area",EFFECTIVE_AREA_}, {"runge_kutta",RUNGE_KUTTA_}, {"color",COLOR_}, {"backcolor",BACKCOLOR_}, {"frontcolor",FRONTCOLOR_}, {"mean_curvature_integral",MEAN_CURV_INT_}, {"normal_curvature",NORMAL_CURVATURE_}, {"square_curvature",SQUARE_CURVATURE_}, {"squared_curvature",SQUARE_CURVATURE_}, {"square_gaussian_curvature",SQGAUSS_}, {"squared_gaussian_curvature",SQGAUSS_}, {"gauss_curvature",GAUSS_CURVATURE_}, {"insulating_knot_energy",INSULATING_KNOT_ENERGY_}, {"conducting_knot_energy",CONDUCTING_KNOT_ENERGY_}, {"space_dimension",SPACE_DIMENSION_}, {"surface_dimension",SURFACE_DIMENSION_}, {"simplex_representation",SIMPLEX_REP_}, {"metric",METRIC_}, {"klein_metric",KLEIN_METRIC_}, {"conformal_metric",CONFORMAL_}, {"fixed",FIXED_}, {"no_refine",NO_REFINE_}, {"hit_partner",HIT_PARTNER_}, {"no_display",NODISPLAY_}, {"noncontent",NONCONTENT_}, {"efixed",EFIXED_}, {"symmetry_group",SYMMETRY_GROUP_}, {"wrap",WRAP_}, {"torus",TORUS_}, {"torus_filled",TORUS_FILLED_}, {"torus_periods",TORUS_PERIODS_}, {"periods",PERIODS_}, {"display_periods",DISPLAY_PERIODS_}, {"string",STRING_}, {"soapfilm",SOAPFILM_}, {"wulff",WULFF_}, {"boundary",BOUNDARY_}, {"boundaries",BOUNDARY_}, {"constraint",CONSTRAINT_}, {"constraints",CONSTRAINT_}, {"surface_energy",SURFACE_ENERGY_}, {"formula",FUNCTION_}, {"function",FUNCTION_}, {"parameter",PARAMETERS_}, {"parameters",PARAMETERS_}, {"parameter_file",PARAMETER_FILE_}, {"symmetric_content",SYMMETRIC_CONTENT_}, {"integral_order",V_INTEGRAL_ORDER}, {"integral_order_1d",V_INTEGRAL_ORDER_1D}, {"integral_order_2d",V_INTEGRAL_ORDER_2D}, {"integration_order",V_INTEGRAL_ORDER}, {"integration_order_1d",V_INTEGRAL_ORDER_1D}, {"integration_order_2d",V_INTEGRAL_ORDER_2D}, {"constraint_tolerance",CONSTRAINT_TOLERANCE_ }, {"convex",CONVEX_}, {"nonwall",NONWALL_}, {"nonnegative",NONNEGATIVE_}, {"nonpositive",NONPOSITIVE_}, {"global",GLOBAL_}, {"energy",ENERGY_}, {"content",CONTENT_}, {"quadratic",QUADRATIC_}, {"linear",LINEAR_}, {"area_normalization",MEAN_CURV_}, {"jiggle",JIGGLE_}, {"diffusion",DIFFUSION_}, {"merit_factor",MERITFACTOR_}, {"gravity_constant",GRAV_CONST_}, {"spring_constant",SPRING_CONSTANT_}, {"gap_constant",GAP_CONSTANT_}, {"scale",SCALE_}, {"pscale",SCALE_}, {"temperature",TEMPERATURE_}, {"pressure",PRESSURE_}, {"volume",VOLUME_}, {"density",DENSITY_}, {"tension",DENSITY_}, {"nodisplay",NODISPLAY_}, {"scale_limit",SCALE_LIMIT_}, {"zoom_vertex",ZOOM_VERTEX_}, {"zoom_radius",ZOOM_RADIUS_}, {"quantity",QUANTITY_ }, {"volconst",VOLCONST_ }, {"read",READ_}, {"and",AND_}, {"or",OR_}, {"not",NOT_}, {"mod",'%'}, {"imod",IMOD_}, {"idiv",IDIV_}, {"dot_product",DOT_}, {"pi",PI_}, {"e",E_}, {"g",G_}, {"tag",TAG_}, {"original",ORIGINAL_}, {"vertices",VERTICES_}, {"vertex",VERTICES_}, {"edges",EDGES_}, {"edge",EDGES_}, {"faces",FACES_}, {"face",FACES_}, {"facets",FACES_}, {"facet",FACES_}, {"bodies",BODIES_}, {"body",BODIES_}, {"facet_edges",FACETEDGES_}, {"facet_edge",FACETEDGES_}, {"facetedges",FACETEDGES_}, {"facetedge",FACETEDGES_} }; struct ckey mathfunc_keywords[] = { {"wrap_inverse",WRAP_INVERSE_}, {"ellipticK",ELLIPTICK}, {"ellipticE",ELLIPTICE}, {"log",LOG}, {"exp",EXP}, {"sin",SIN}, {"cos",COS}, {"tan",TAN}, {"asin",ASIN}, {"acos",ACOS}, {"atan",ATAN}, {"sinh",SINH}, {"cosh",COSH}, {"tanh",TANH}, {"asinh",ASINH}, {"acosh",ACOSH}, {"atanh",ATANH}, {"sqrt",SQRT}, {"sqr",SQR}, {"ceil",CEIL_}, {"floor",FLOOR_}, {"abs",ABS} }; struct ckey mathfunc2_keywords[] = { /* two arguments */ {"wrap_compose",WRAP_COMPOSE_}, {"atan2",ATAN2_}, {"incompleteEllipticF",INCOMPLETE_ELLIPTICF}, {"incompleteEllipticE",INCOMPLETE_ELLIPTICE}, {"maximum",MAXIMUM_}, {"minimum",MINIMUM_}, {"pow",POW} }; struct ckey command_keywords[] = { {"valid_constraint",VALID_CONSTRAINT_}, {"valid_boundary",VALID_BOUNDARY_}, {"profiling",PROFILING_}, {"reset_profiling",RESET_PROFILING_}, {"suppress_warning",SUPPRESS_WARNING_}, {"unsuppress_warning",UNSUPPRESS_WARNING_}, {"delete_text",DELETE_TEXT_}, {"display_text", DISPLAY_TEXT_ }, {"simplex_to_fe", SIMPLEX_TO_FE_ }, {"addload", ADDLOAD_ }, {"whereami",WHEREAMI_}, {"breakpoint",BREAKPOINT_}, {"breakpoints",BREAKPOINT_}, {"abort",ABORT_}, {"subcommand",SUBCOMMAND_}, {"global",GLOBAL_}, {"repartition",REPARTITION_}, {"free_discards",FREE_DISCARDS_}, {"dump_memlist",DUMP_MEMLIST_}, {"reverse_orientation",REVERSE_ORIENTATION_}, {"matrix_multiply",MATRIX_MULTIPLY_}, {"matrix_inverse",MATRIX_INVERSE_}, {"matrix_determinant",MATRIX_DETERMINANT_}, {"mid_edge",MID_EDGE_}, {"mid_facet",MID_FACET_}, {"flush_counts",FLUSH_COUNTS_}, {"valid_element",VALID_ELEMENT_}, {"reset_counts",RESET_COUNTS_}, {"vertex_merge",MERGE_VERTEX_}, {"edge_merge",MERGE_EDGE_}, {"facet_merge",MERGE_FACET_}, {"mpi_task",MPI_TASK_ATTR_}, {"mean_curvature",MEAN_CURVATURE_}, {"element_modulus",ELEMENT_MODULUS_}, {"ignore_constraints",IGNORE_CONSTRAINTS_}, {"ignore_fixed",IGNORE_FIXED_}, {"global_method",GLOBAL_METHOD_}, {"scalar_integrand",SCALAR_INTEGRAND_}, {"vector_integrand",VECTOR_INTEGRAND_}, {"k_vector_order",K_VEC_ORDER_}, {"form_integrand",FORM_INTEGRAND_}, {"info_only",INFO_ONLY_}, {"method",METHOD_}, {"parallel_exec",PARALLEL_EXEC_}, {"task_exec",TASK_EXEC_}, {"pop",POP_}, {"pop_tri_to_edge",POP_TRI_TO_EDGE_}, {"pop_edge_to_tri",POP_EDGE_TO_TRI_}, {"pop_quad_to_quad",POP_QUAD_TO_QUAD_}, {"local",LOCAL_}, {"for",FOR_}, {"warning_messages",WARNING_MESSAGES_}, {"equiangulate",EQUIANGULATE_}, {"no_refine",NO_REFINE_}, {"hit_partner",HIT_PARTNER_}, {"no_display",NODISPLAY_}, {"vertexnormal",VERTEXNORMAL_}, {"colorfile",COLORFILE_}, {"date_and_time",DATE_AND_TIME_}, {"exprint",EXPRINT_}, {"energy",ENERGY_}, {"info_only",INFO_ONLY_}, {"conserved",CONSERVED_}, {"exec",EXEC_}, {"wrap_vertex",WRAP_VERTEX_}, {"self",SELF_}, {"is_defined",IS_DEFINED_}, {"nodisplay",NODISPLAY_}, {"no_display",NODISPLAY_}, {"function",FUNCTION_}, {"reorder_storage",REORDER_STORAGE_}, {"renumber_all",RENUMBER_ALL_}, /* {"view_matrix",VIEW_MATRIX_}, */ {"pause",PAUSE_}, {"pdelta",DELTA_ }, {"pscale",SCALE_ }, {"scale",SCALE_ }, {"tolerance",TOLERANCE_}, {"method_instance",METHOD_INSTANCE_}, {"logfile",LOGFILE_}, {"keylogfile",KEYLOGFILE_}, {"frontbody",FRONTBODY_}, {"backbody",BACKBODY_}, {"new_vertex",NEWVERTEX_}, {"new_edge",NEWEDGE_}, {"new_facet",NEWFACET_}, {"new_body",NEWBODY_}, {"noncontent",NONCONTENT_}, /* {"inverse_periods",INVERSE_PERIODS_}, */ {"return",RETURN_}, {"postscript",POSTSCRIPT_}, {"ooglfile",OOGLFILE_}, {"binary_off_file",BINARY_OFF_FILE_}, {"vertex_average",VERTEX_AVERAGE_}, {"raw_vertex_average",RAW_VERTEX_AVERAGE_}, {"rawest_vertex_average",RAWEST_VERTEX_AVERAGE_}, {"axial_point",AXIAL_POINT_}, {"metis_factor",METIS_FACTOR_}, {"lagrange",LAGRANGE_}, {"metis",METIS_}, {"metis_readjust",METIS_READJUST_}, {"body_metis",BODY_METIS_}, {"kmetis",KMETIS_}, {"ometis",OMETIS_}, {"sizeof",SIZEOF_}, {"move",MOVE_}, {"geompipe",GEOMPIPE_}, {"convert_to_quantities",CONVERT_TO_QUANTS_}, {"edgeswap",EDGESWAP_}, {"t1_edgeswap",T1_EDGESWAP_}, /* {"torus_periods",TORUS_PERIODS_}, */ {"break",BREAK_}, {"volfixed",FIXEDVOL_}, {"continue",CONTINUE_}, {"volconst",VOLCONST_ }, {"ritz",RITZ_}, {"orientation",ORIENTATION_}, {"eigenprobe",EIGENPROBE_}, {"lanczos",LANCZOS_}, {"tetra_point",TETRA_POINT_}, {"triple_point",TRIPLE_POINT_}, {"value",VALUE_}, {"target",TARGET_}, {"datafilename",DATAFILENAME_}, {"geomview",GEOMVIEW_}, {"saddle",HESSIAN_SADDLE_}, {"midv",MIDV_}, {"define",DEFINE_}, {"attribute",ATTRIBUTE_}, {"attributes",ATTRIBUTE_}, {"string",STRING_}, {"sobolev",SOBOLEV_}, {"sobolev_seek",SOBOLEV_SEEK_}, {"dirichlet",DIRICHLET_}, {"dirichlet_seek",DIRICHLET_SEEK_}, {"wrap",WRAP_}, {"total",TOTAL_}, {"history",HISTORY_}, {"fix",FIX_}, {"unfix",UNFIX_}, {"bare",BARE_}, {"phase",PHASE_}, {"foreach",FOREACH_}, {"rebody", REBODY_}, {"burchard", BURCHARD_}, {"close_show",CLOSE_SHOW_}, {"show_off",CLOSE_SHOW_}, /* {"view_transforms",VIEW_TRANSFORMS_}, */ /* {"view_transform_swap_colors",VIEW_TRANSFORM_SWAP_COLORS_}, */ /* {"view_transform_parity",VIEW_TRANSFORM_PARITY_}, */ {"transform_depth",TRANSFORM_DEPTH_}, {"transform_expr",TRANSFORM_EXPR_}, {"modulus",MODULUS_}, {"boundary",BOUNDARY_}, {"on_constraint",ON_CONSTRAINT_}, {"hit_constraint",HIT_CONSTRAINT_}, {"on_boundary",ON_BOUNDARY_}, {"on_quantity",ON_QUANTITY_}, {"on_method_instance",ON_METHOD_INSTANCE_}, {"hessian",HESSIAN_}, {"hessian_seek",HESSIAN_SEEK_}, {"hessian_menu",HESSIAN_MENU_}, {"help",HELP_}, {"quit",QUIT_}, {"exit",QUIT_}, {"bye",QUIT_}, {"dump",DUMP_}, {"load",LOAD_}, {"permload",PERMLOAD_}, {"quantity",QUANTITY_}, {"spring_constant",GAP_CONSTANT_}, {"gap_constant",GAP_CONSTANT_}, {"notch",NOTCH_}, {"while",WHILE_}, {"do",DO_}, {"if",IF_}, {"then",THEN_}, {"else",ELSE_}, {"histogram",HISTOGRAM_}, {"loghistogram",LOGHISTOGRAM_}, {"show_expr",SHOW_EXPR_}, {"show_trans",SHOW_TRANS_}, {"dihedral",DIHEDRAL_}, {"max",MAX_}, {"min",MIN_}, {"avg",AVG_}, {"sum",SUM_}, {"count",COUNT_}, {"print",PRINT_}, {"eprint",EPRINT_}, {"printf",PRINTF_}, {"errprintf",ERRPRINTF_}, {"sprintf",SPRINTF_}, {"binary_printf",BINARY_PRINTF_}, {"procedures",PROCEDURES_}, {"procedure",PROCEDURE_WORD_}, {"on",ON_}, {"counts",COUNTS_}, {"extrapolate",EXTRAPOLATE_}, {"off",OFF_}, {"areaweed",AREAWEED_}, {"edgeweed",EDGEWEED_}, {"edge_divide",EDGEDIVIDE_}, {"alice",ALICE_}, {"stability_test",STABILITY_TEST_}, {"zoom",ZOOM_}, {"utest",UTEST_}, {"system",SYSTEM_}, {"chdir",CHDIR_}, {"longj",LONG_JIGGLE_}, {"rawv",RAW_VERAVG_}, {"rawestv",RAWEST_VERAVG_}, {"go",GO_}, {"refine",REFINE_}, {"check",CHECK_}, {"show_vol",SHOW_VOL_}, {"id",ID_}, {"oid",OID_}, {"and",AND_}, {"or",OR_}, {"not",NOT_}, {"mod",'%'}, {"imod",IMOD_}, {"idiv",IDIV_}, {"dot_product",DOT_}, {"pi",PI_}, {"e",E_}, {"g",G_}, {"tag",TAG_}, {"original",ORIGINAL_}, {"fixed",FIXED_}, {"vertices",VERTICES_}, {"vertex",VERTICES_}, {"edges",EDGES_}, {"edge",EDGES_}, {"facets",FACETS_}, {"facet",FACETS_}, {"faces",FACETS_}, {"face",FACETS_}, {"facet_edges",FACETEDGES_}, {"facet_edge",FACETEDGES_}, {"facetedges",FACETEDGES_}, {"facetedge",FACETEDGES_}, {"bodies",BODIES_}, {"body",BODIES_}, {"topinfo",TOPINFO_}, {"bottominfo",BOTTOMINFO_}, {"length",LENGTH_ }, {"valence",VALENCE_ }, {"area",AREA_ }, {"volume",VOLUME_ }, {"sqcurve",SQ_MEAN_CURV_}, {"where",WHERE_ }, {"list",LIST_ }, {"show",SHOW_}, {"showq",SHOWQ_}, {"delete",DELETE_ }, {"dissolve",DISSOLVE_ }, {"refine",REFINE_ }, {"shell",SHELL_}, {"constraint",CONSTRAINT_}, {"pressure",PRESSURE_}, {"color",COLOR_}, {"backcolor",BACKCOLOR_}, {"frontcolor",FRONTCOLOR_}, {"opacity",OPACITY_}, {"volume",VOLUME_}, {"density",DENSITY_}, {"tension",DENSITY_}, {"set",SET_}, {"read",READ_}, {"unset",UNSET_}, {"recalc",RECALC_}, {"optimize",OPTIMIZE_}, {"optimise",OPTIMIZE_}, {"autochop",AUTOCHOP_}, }; struct ckey togglenames[] = { {"immediate_autopop",IMMEDIATE_AUTOPOP_}, {"star_finagling",STAR_FINAGLING_}, {"force_deletion",FORCE_DELETION_}, {"function_quantity_sparse",FUNCTION_QUANTITY_SPARSE_}, {"slice_view",SLICE_VIEW_}, {"clip_view",CLIP_VIEW_}, {"quietload",QUIETLOAD_}, {"big_endian",BIG_ENDIAN_}, {"little_endian",LITTLE_ENDIAN_}, {"full_bounding_box",FULL_BOUNDING_BOX_}, {"pop_disjoin",POP_DISJOIN_}, {"pop_enjoin",POP_ENJOIN_}, {"pop_to_edge",POP_TO_EDGE_}, {"pop_to_face",POP_TO_FACE_}, {"mpi_debug",MPI_DEBUG_}, {"smooth_graph",SMOOTH_GRAPH_}, {"bezier_basis",BEZIER_BASIS_}, {"break_after_warning",BREAK_AFTER_WARNING_}, {"blas_flag",BLAS_FLAG_}, {"diffusion",DIFFUSION_}, {"augmented_hessian",AUGMENTED_HESSIAN_}, {"sparse_constraints",SPARSE_CONSTRAINTS_}, {"visibility_test", VISIBILITY_TEST_}, {"circular_arc_draw",CIRCULAR_ARC_DRAW_}, {"rgb_colors",RGB_COLORS_FLAG_}, {"kraynikpopvertex",KRAYNIKPOPVERTEX_FLAG_}, {"kraynikpopedge",KRAYNIKPOPEDGE_FLAG_}, {"sobolev_mode",SOBOLEV_MODE_}, {"dirichlet_mode",DIRICHLET_MODE_}, {"verbose",VERBOSE_}, {"torus_filled",TORUS_FILLED_}, {"ambient_pressure",AMBIENT_PRESSURE_}, {"backcull",BACKCULL_}, {"interp_normals",INTERP_NORMALS_}, {"volgrads_every",VOLGRADS_EVERY_}, {"zener_drag",ZENER_DRAG_}, {"hessian_special_normal",HESSIAN_SPECIAL_NORMAL_}, {"hessian_normal_perp",HESSIAN_NORMAL_PERP_}, {"show_all_quantities",SHOW_ALL_QUANTITIES_}, {"pscolorflag",PSCOLORFLAG_}, {"ps_colorflag",PSCOLORFLAG_}, {"ps_gridflag",GRIDFLAG_}, {"gridflag",GRIDFLAG_}, {"crossingflag",CROSSINGFLAG_}, {"labelflag",LABELFLAG_}, {"ps_labelflag",LABELFLAG_}, {"hessian_normal_one",HESSIAN_NORMAL_ONE_}, {"interp_bdry_param",INTERP_BDRY_PARAM_}, {"hessian_double_normal",HESSIAN_DOUBLE_NORMAL_}, {"h_inverse_metric",H_INVERSE_METRIC_}, {"squared_gradient",SQUARED_GRADIENT_}, {"linear_metric",LINEAR_METRIC_}, {"metric_convert",METRIC_CONVERT_}, {"quantities_only",QUANTITIES_ONLY_}, {"ysmp",YSMP_}, {"bunch_kaufman",BUNCH_KAUFMAN_}, {"bunch_kauffman",BUNCH_KAUFMAN_}, {"hessian_normal",HESSIAN_NORMAL_}, {"jiggle",JIGGLE_TOGGLE_}, {"assume_oriented",ASSUME_ORIENTED_}, {"ribiere",RIBIERE_CG_}, {"area_normalization",MEAN_CURV_}, {"force_pos_def",FORCE_POS_DEF_}, {"autodisplay",AUTODISPLAY_}, {"lagrange",LAGRANGE_}, {"quadratic",QUADRATIC_}, {"linear",LINEAR_}, {"show_inner",SHOW_INNER_}, {"area_fixed",AREA_FIXED_}, {"fixed_area",AREA_FIXED_}, {"clipped",CLIPPED_CELLS_}, {"clipped_cells",CLIPPED_CELLS_}, {"raw_cells",RAW_CELLS_}, {"connected",CONNECTED_CELLS_}, {"connected_cells",CONNECTED_CELLS_}, {"show_outer",SHOW_OUTER_}, {"colormap",COLORMAP_}, {"thicken",THICKEN_}, {"hessian_diff",HESSIAN_DIFF_}, {"normal_motion",NORMAL_MOTION_}, {"runge_kutta",RUNGE_KUTTA_}, {"deturck",DETURCK_}, {"kusner",KUSNER_}, {"view_4d",VIEW_4D_}, {"conf_edge",CONF_EDGE_SQCURV_}, {"mean_curvature_integral",MEAN_CURV_INT_}, {"sqgauss",SQGAUSS_}, {"autopop",AUTOPOP_}, {"autopop_quartic",AUTOPOP_QUARTIC_}, {"old_area",OLD_AREA_}, {"approx_curv",APPROX_CURV_}, {"check_increase",CHECK_INCREASE_}, {"debug",DEBUG_}, {"memdebug",MEMDEBUG_}, {"itdebug",ITDEBUG_}, {"gravity",GRAVITY_}, {"effective_area",EFFECTIVE_AREA_}, {"estimate",ESTIMATE_}, {"post_project",POST_PROJECT_}, {"transforms",TRANSFORMS_}, {"quiet",QUIET_}, {"quietgo",QUIETGO_}, {"hessian_quiet",HESSIAN_QUIET_}, {"conj_grad",CONJ_GRAD_}, {"homothety",HOMOTHETY_}, {"facet_colors",FACET_COLORS_}, {"shading",SHADING_}, {"div_normal_curvature",DIV_NORMAL_CURVATURE_}, {"normal_curvature",NORMAL_CURVATURE_}, {"boundary_curvature",BOUNDARY_CURVATURE_}, {"self_similar",SELF_SIMILAR_}, {"gv_binary",GV_BINARY_}, {"metric_conversion",METRIC_CONVERSION_}, {"autorecalc",AUTORECALC_}, {"pinning",PINNING_} }; struct ckey colornames[] = { {"clear",CLEAR}, /* transparent */ {"transparent",CLEAR}, /* transparent */ {"black",BLACK}, /* dark colors */ {"blue",BLUE}, {"green",GREEN}, {"cyan",CYAN}, {"red",RED}, {"magenta",MAGENTA}, {"brown",BROWN}, {"lightgray",LIGHTGRAY}, {"lightgrey",LIGHTGRAY}, {"gray",LIGHTGRAY}, {"grey",LIGHTGRAY}, {"darkgray",DARKGRAY}, /* light colors */ {"darkgrey",DARKGRAY}, /* light colors */ {"lightblue",LIGHTBLUE}, {"lightgreen",LIGHTGREEN}, {"lightcyan",LIGHTCYAN}, {"lightred",LIGHTRED}, {"lightmagenta",LIGHTMAGENTA}, {"yellow",YELLOW}, {"white",WHITE} }; struct ckey internal_variables[] = { {"autochop_length",V_AUTOCHOP_LENGTH}, {"string_curve_tolerance",V_STRING_CURVE_TOLERANCE}, {"corona_state",V_CORONA_STATE}, {"mpi_maxtask",V_MPI_MAXTASK}, {"this_task",V_THIS_TASK}, /* MPI task number */ {"window_aspect_ratio",V_WINDOW_ASPECT_RATIO}, {"mindeg_debug_level",V_MINDEG_DEBUG_LEVEL}, {"mindeg_min_region_size",V_MINDEG_MIN_REGION_SIZE}, {"mindeg_margin",V_MINDEG_MARGIN}, {"fix_count",V_FIX_COUNT}, {"unfix_count",V_UNFIX_COUNT}, {"edge_delete_count",V_EDGE_DELETE_COUNT}, {"facet_delete_count",V_FACET_DELETE_COUNT}, {"vertex_dissolve_count",V_VERTEX_DISSOLVE_COUNT}, {"edge_dissolve_count",V_EDGE_DISSOLVE_COUNT}, {"facet_dissolve_count",V_FACET_DISSOLVE_COUNT}, {"body_dissolve_count",V_BODY_DISSOLVE_COUNT}, {"edge_refine_count",V_EDGE_REFINE_COUNT}, {"facet_refine_count",V_FACET_REFINE_COUNT}, {"vertex_pop_count",V_VERTEX_POP_COUNT}, {"edge_pop_count",V_EDGE_POP_COUNT}, {"pop_tri_to_edge_count",V_POP_TRI_TO_EDGE_COUNT}, {"pop_edge_to_tri_count",V_POP_EDGE_TO_TRI_COUNT}, {"pop_quad_to_quad_count",V_POP_QUAD_TO_QUAD_COUNT}, {"edgeswap_count",V_EDGESWAP_COUNT}, {"t1_edgeswap_count",V_T1_EDGESWAP_COUNT}, {"ps_labelsize",V_PS_LABELSIZE_}, {"ps_stringwidth",V_PS_STRINGWIDTH_}, {"ps_fixededgewidth",V_PS_FIXEDEDGEWIDTH_}, {"ps_tripleedgewidth",V_PS_TRIPLEEDGEWIDTH_}, {"ps_conedgewidth",V_PS_CONEDGEWIDTH_}, {"ps_bareedgewidth",V_PS_BAREEDGEWIDTH_}, {"ps_gridedgewidth",V_PS_GRIDEDGEWIDTH_}, {"scrollbuffersize",V_SCROLLBUFFERSIZE_}, {"visibility_debug",V_VISIBILITY_DEBUG_}, {"check_count",V_CHECK_COUNT_}, {"breakflag",V_BREAKFLAG_}, {"everything_quantities",EVERYTHING_QUANTITIES_}, {"gravity_constant",GRAV_CONST_}, {"hessian_slant_cutoff",V_HESSIAN_SLANT_CUTOFF}, {"ambient_pressure_value",V_AMBIENT_PRESSURE}, {"last_error",V_LAST_ERROR}, {"memory_arena",V_MEMARENA}, {"memory_used",V_MEMUSED}, {"background",V_BACKGROUND}, {"brightness",V_BRIGHTNESS}, {"diffusion_coeff",V_DIFFUSION}, {"transform_count",V_TRANSFORM_COUNT}, {"scale_limit",V_SCALE_LIMIT}, {"clock",V_CLOCK}, {"cpu_counter",V_CPU_COUNTER}, {"target_tolerance",V_TARGET_TOLERANCE}, {"thickness",V_THICKNESS}, {"spring_constant",V_GAP_CONSTANT}, {"gap_constant",V_GAP_CONSTANT}, {"lagrange_order",V_LAGRANGE_ORDER}, {"last_eigenvalue",V_LAST_EIGENVALUE}, {"last_hessian_scale",V_LAST_HESSIAN_SCALE}, {"integral_order",V_INTEGRAL_ORDER}, {"integral_order_1d",V_INTEGRAL_ORDER_1D}, {"integral_order_2d",V_INTEGRAL_ORDER_2D}, {"integration_order",V_INTEGRAL_ORDER}, {"integration_order_1d",V_INTEGRAL_ORDER_1D}, {"integration_order_2d",V_INTEGRAL_ORDER_2D}, {"random_seed",V_RANDOM_SEED}, {"random",V_RANDOM}, {"linear_metric_mix",V_LINEAR_METRIC_MIX}, {"quadratic_metric_mix",V_QUADRATIC_METRIC_MIX}, {"pickvnum",V_PICKVNUM}, {"pickenum",V_PICKENUM}, {"pickfnum",V_PICKFNUM}, {"eigen_pos",V_EIGENPOS}, {"eigen_neg",V_EIGENNEG}, {"eigen_zero",V_EIGENZERO}, {"eigenpos",V_EIGENPOS}, {"eigenneg",V_EIGENNEG}, {"eigenzero",V_EIGENZERO}, {"total_time",V_TIME}, {"jiggle_temperature",V_JIG_TEMP}, {"iteration_counter",V_ITER_COUNTER}, {"scale_scale",V_SCALE_SCALE}, {"vertex_count",V_VERTEXCOUNT}, {"edge_count",V_EDGECOUNT}, {"facet_count",V_FACETCOUNT}, {"body_count",V_BODYCOUNT}, {"facetedge_count",V_FACETEDGECOUNT}, {"total_energy",V_ENERGY}, {"total_area",V_AREA}, {"total_length",V_LENGTH}, {"scale",V_SCALE}, {"space_dimension",V_SPACE_DIMENSION}, {"surface_dimension",V_SURFACE_DIMENSION}, {"torus",V_TORUS}, {"symmetry_group",V_SYMMETRY_GROUP}, {"simplex_representation",V_SIMPLEX}, {"constraint_tolerance",V_TOLERANCE}, {"hessian_epsilon",V_HESS_EPSILON}, {"equi_count",V_EQUI_COUNT}, {"delete_count",V_DELETE_COUNT}, {"refine_count",V_REFINE_COUNT}, {"notch_count",V_NOTCH_COUNT}, {"dissolve_count",V_DISSOLVE_COUNT}, {"pop_count",V_POP_COUNT}, {"where_count",V_WHERE_COUNT} }; %} /* %option array */ D [0-9] PD [1-9] H [0-9A-Fa-f] E [DEde][+-]?{D}+ W [ \t\r\032] %p 4000 %a 3000 %% "/*" { /* eat comment */ register int c; in_comment = 1; for ( ; ; ) { while ( (c = input()) != '*' && c != 0 ) if ( c == '\n' ) line_no++; /* eat up text of comment */ if ( c == '*' ) { while ( (c = input()) == '*' ) ; if ( c == '/' ) break; /* found the end */ if ( c == '\n' ) line_no++; } if ( c == 0 && yywrap() ) { kb_error(2325,"End-of-file in comment\n",RECOVERABLE ); break; } } in_comment = 0; } "//".* /* comment */ ; \n { line_no++; } _anti_line_no_ { errspot -= 15; line_no--; } ^{W}*-?{D}+ { char *c = strtok(yytext,whitespace); yylval.r = atof(strtok(yytext,whitespace)); yylval.i = atoi(strtok(yytext,whitespace)); if ( lists_flag==LISTS_FULL ) return(tok=LEAD_INTEGER_); else if ( uminus_flag || c[0] != '-' ) return(tok = (fabs(yylval.r) > MAXINT) ? REAL_ : INTEGER_); else { unput_string(c+1); c[1] = 0; return (tok = minus_type(c[0])); } } {W}+[+-]{D}+ { char *c = strtok(yytext,whitespace); yylval.i = atoi(c); yylval.r = atof(strtok(c,whitespace)); yylval.qnum = 0; if ((lists_flag!=LISTS_OFF) && uminus_flag && (parens==0)) return(tok = (fabs(yylval.r) > MAXINT) ? REAL_ : INTEGER_); else { unput_string(c+1); c[1] = 0; return (tok = minus_type(c[0])); } } {D}+ { yylval.i = atoi(yytext); yylval.r = atof(strtok(yytext,whitespace)); yylval.qnum = 0; return(tok = (fabs(yylval.r) > MAXINT) ? REAL_ : INTEGER_); } ^{W}*{D}+"@"{D}+ { yylval.i = atoi(yytext); yylval.qnum = atoi(strchr(yytext,'@')+1); return(tok = LEAD_INTEGER_AT_); } {W}+-?{D}+"@"{D}+ { yylval.i = atoi(yytext); yylval.qnum = atoi(strchr(yytext,'@')+1); return(tok = INTEGER_AT_); } 0x{H}+ { sscanf(yytext+2,"%x",&yylval.i); yylval.r = (REAL)(yylval.i); return(tok = INTEGER_); } /* hex */ [01]+[Bb] { char *c = yytext; /* binary */ yylval.i = 0; while ( isdigit(*c) ) { yylval.i = 2*yylval.i + *c - '0'; c++;} yylval.r = (REAL)(yylval.i); return(tok = INTEGER_); } {D}+"."{D}+[a-z]+ return VERSIONTOKEN_; ^{W}*[+-]{D}+"."{D}*({E})? | ^{W}*[+-]{D}*"."{D}+({E})? | ^{W}*[+-]{D}+{E} | {W}+[+-]{D}+"."{D}*({E})? | {W}+[+-]{D}*"."{D}+({E})? | {W}+[+-]{D}+{E} { char *c = strtok(yytext,whitespace); yylval.r = atof(c); if ((lists_flag!=LISTS_OFF) && uminus_flag && (parens==0)) return(tok = REAL_); else { unput_string(c+1); c[1] = 0; verb_flag = 0; return (tok = minus_type(c[0])); } } ^{W}*{D}+"."{D}*({E})? | ^{W}*{D}*"."{D}+({E})? | ^{W}*{D}+{E} | {D}+"."{D}*({E})? | {D}*"."{D}+({E})? | {D}+{E} { yylval.r = atof(yytext); return(tok = REAL_); } \" { /* read quoted string */ int n; in_quote = 1; for (n=0;;n++) { int c; c = input(); if ( c == '\n' ) line_no++; if ( c == '"' && (n == 0 || (yytext[n-1] != '\\')) ) break; if ( n < 198 ) yytext[n] = c; else if ( n==199 ) kb_error(2326,"Quoted string over 198 characters.",WARNING); } yytext[n] = 0; reduce_string(yytext); in_quote = 0; return(tok = QUOTATION_); } {W}+-{W}+ verb_flag = 0; return (tok = minus_type('-')); {W}+-$ verb_flag = 0; return (tok = minus_type('-')); {W}+- { if (datafile_flag && uminus_flag && (parens == 0) ) return(tok = UMINUS_); else { verb_flag = 0; return ( tok = minus_type('-') );} } {W}+-= { verb_flag = 2; yylval.i = SUBASSIGN_; return (tok= ASSIGNOP_);} ^-{W} { return ( tok = minus_type('-')); } ^- { if (datafile_flag && uminus_flag && (parens == 0) ) return ( tok = UMINUS_); else { verb_flag = 0; return ( tok = minus_type('-') ); } } ":=" { verb_flag = 2; return (tok= ASSIGN_);} ";=" { verb_flag = 2; kb_error(2327,"You mistyped ';=' for ':='?\n",WARNING); return (tok= ASSIGN_); } "+=" { verb_flag = 2; yylval.i = PLUSASSIGN_; return (tok= ASSIGNOP_);} "-=" { verb_flag = 2; yylval.i = SUBASSIGN_; return (tok= ASSIGNOP_);} "*=" { verb_flag = 2; yylval.i = MULTASSIGN_; return (tok= ASSIGNOP_);} "/=" { verb_flag = 2; yylval.i = DIVASSIGN_; return (tok= ASSIGNOP_);} "::=" { verb_flag = 2; return (tok = PERM_ASSIGN_);} ":::=" { verb_flag = 2; return (tok = REDEFINE_); } - { return (tok = minus_type('-')); } [<>+*/,.?%^\[\]] { verb_flag = 0 ; return(tok = yytext[0]); } ";" { verb_flag = 1; return tok = ';'; } "{" { verb_flag = 1; return(tok = yytext[0]); } "}" { return(tok = yytext[0]); } "=" { return tok = (datafile_flag ? '=' : EQ_); } "(" { verb_flag = 0; return(tok = yytext[0]); } ")" { return(tok = yytext[0]); } "`" { return(tok = yytext[0]); } "**" { return(tok = '^'); } "==" { return(tok = EQ_); } "!=" { return(tok = NE_); } "<=" { return(tok = LE_); } ">=" { return(tok = GE_); } "||" { return(tok = OR_); } "&&" { return(tok = AND_); } "!" { return(tok = NOT_); } "|" { return (tok = PIPE_); /* pipe followed by quoted string */ } ">>" { return (tok = REDIRECT_); /* for redirection */ } ">>>" { return (tok = REDIRECTOVER_); /* for overwrite redirection */ } "`"" "*"," { return (tok = BACKQUOTE_COMMA_); } #define { if (!datafile_flag) kb_error(1880,"#define valid only in top of datafile.\n",WARNING); macro_flag = 1; } #include{W}*\"[^"]*\" { /* nested include */ char *name = strchr(yytext,'"')+1;/* initial quote + 1 */ *strchr(name,'"') = 0; /* replace final quote by 0 */ push_commandfd(NULL,name); } [A-Za-z_][A-Za-z0-9_]* { strncpy(yylval.lexeme,yytext,31); if ( !macro() ) return identcase(yytext); } "'"[A-Za-z]"'" { yylval.i = yytext[1]; return tok = SINGLE_LETTER_; } {W} ; ":" { if ( cond_expr_flag ) return tok = ':';} . { if ( isprint(yytext[0]) ) sprintf(errmsg,"Illegal token: %c\n",yytext[0]); else sprintf(errmsg,"Illegal token: 0x%02X\n",yytext[0]); yyerror(errmsg); return tok=LEXERROR; } %% /* My own input() function to handle case insensitivity and macros */ /* and line counting */ /* for saving input for command definition */ int inputbuffersize = 0; int inputbufferspot = 0; /* where next character will go */ int inputsave_flag = 0; /* so only save when reading command */ char *inputbuffer; void savein(c) int c; { if ( !inputsave_flag ) return; if ( inputbufferspot >= inputbuffersize ) { int newsize = inputbuffersize?2*inputbuffersize:1000; inputbuffer = my_list_realloc(inputbuffer,newsize,ETERNAL_BLOCK); inputbuffersize = newsize; } inputbuffer[inputbufferspot++] = (char)c; } void reset_inputbuffer() { inputbufferspot = 0; inputsave_flag = 1; } #define MOREMAX 1000 char morebuff[MOREMAX+2]; int rawinput() { int c=0,retval; if ( cmdptr ) /* read from string */ { if ( *cmdptr == 0 ) /* newline at end of each line */ { if ( previous_char != '\n' ) { retval = previous_char = '\n'; errbuff[errspot++] = retval; savein(retval); } else retval = 0; goto rawreturn; } c = *(cmdptr++); if ( c == MOREIN ) /* for really long input lines */ { #ifdef USE_READLINE //CSL prompt(CONTPROMPT,morebuff,MOREMAX); #else getstring(morebuff,MOREMAX); #endif cmdptr = morebuff; if ( !*cmdptr ) { if (!in_quote && !in_comment) errbuff[errspot++] = '\n'; retval = '\n'; savein(retval); goto rawexit; } c = *(cmdptr++); } } else /* read from file */ { for(;;) { if ( commandfd == NULL ) /* in case read after close */ { retval = previous_char = 0; goto rawreturn; } c = getc(commandfd); if ( c != -1 ) break; pop_commandfd(); /* EOF, so pop #include stack */ if ( !datafile_flag ) /* EOF for a command file */ { retval = previous_char = 0; goto rawreturn; } } } /* taking care of various line-ending combinations */ if ( (c == '\n') && (previous_char == '\r') ) return rawinput(); previous_char = c; if ( c == '\r' ) c = '\n'; errbuff[errspot++] = (char)c; retval = c; savein(retval); rawexit: if ( errspot >= ERRBUFFSIZE ) /* need partial reset */ { memcpy(errbuff,errbuff+ERRBUFFSIZE/2,ERRBUFFSIZE/2); errspot = ERRBUFFSIZE/2; } rawreturn: return retval; } void get_more_input() {int n; #ifdef USE_READLINE //CSL prompt(MOREPROMPT,morebuff,MOREMAX); #else if ( !topflag && (!quiet_load_flag || (commandfd == stdin)) ) outstring("more> "); getstring(morebuff,MOREMAX); #endif n = strlen(morebuff); if ( (morebuff[n-1] != MOREIN) && (morebuff[n-1] != '\n') ) morebuff[n] = '\n'; morebuff[n+1] = 0; catfulltext(morebuff); cmdptr = morebuff; } int kb_input() { int c; if ( spot > 0 ) { c = buff[--spot]; goto input_exit; } retry: c = rawinput(); if ( (c == '\\') ) /* line splicing */ { /* have to do line splicing as input filter to remove special character of line start */ int cc = c; c = rawinput(); if ( c == 0 ) { get_more_input(); c = ' '; goto input_exit; } if ( c == '\n' ) { int ccc; line_no++; ccc = rawinput(); if ( ccc == 0 ) { get_more_input(); c = ' '; goto input_exit; } rawunput(ccc); c = ' '; } else { rawunput(c); c = cc; /* not linesplicing */ } } if ( c == 0 && in_comment ) { get_more_input(); goto retry; } if ( c == 0 && in_quote ) { get_more_input(); goto retry; } input_exit: return c; } void rawunput(c) int c; { if ( spot >= BUFFSIZE - 1 ) { buff = my_list_realloc(buff,BUFFSIZE+500,ETERNAL_BLOCK); BUFFSIZE += 500; } buff[spot++] = (char)c; } void unput_string(s) char *s; { char *c; c = s ; while ( *c ) c++; /* find end of token */ while ( c != s ) { --c; unput(*c); } } void dump_buff(str,size) /* for error reporting */ char *str; size_t size; { int place; strcpy(str," Input line so far: \n"); size -= strlen(str)+3; errbuff[errspot] = 0; for ( place = errspot-1 ; place >= 0 ; place-- ) { if (errbuff[place]==0 ) errbuff[place] = ' '; /* nulls creep in */ if (errbuff[place]=='\n' && place < errspot-2) break; } strncat(str,errbuff+place+1,size); if ( str[strlen(str)-1] != '\n' ) strcat(str,"\n"); } int yywrap() { /* Called at end of input. Return 1 if really done, 0 if not */ spot = 0; /* clean buffer */ if ( (parens > 0) || (brace_depth > 0) || in_quote || in_comment || in_function || in_control_structure /* || loopdepth */ ) { get_more_input(); return 0; } if ( !help_flag ) switch ( tok ) { case '+': case '-': case '*': /* might be wrap symbols */ if ( read_wrap_flag ) return 1; else { get_more_input(); return 0 ; } break; case UMINUS_: case ',': case '=': case '?': case ':': case OR_: case AND_: case NOT_: case EQ_: case '>': case '<': case LE_: case GE_: case NE_: case '/': case '%': case '^': case ON_CONSTRAINT_: case HIT_CONSTRAINT_: case ON_BOUNDARY_: case DO_ : case FOR_: case WHILE_ : case IF_ : case ELSE_: case THEN_ : case SET_: case DEFINE_: case WHERE_: case ASSIGN_ : case PERM_ASSIGN_: case REDEFINE_ : case FUNCTION_: case PROCEDURE_WORD_: get_more_input(); return 0; break; default: return 1; } return 1; /* done */ } void macro_init() { if ( macro_subs ) myfree((char *)macro_subs); if ( macros ) myfree((char *)macros); macro_subs = NULL; macros = NULL; macro_count = macro_subs_top = macro_max = macro_subs_max = 0; } void yylex_init() { unput_tok_count = 0; ubuff_spot = 0; spot = 0; errspot = 0; yylval.i = 0; yylval.r = 0.0; in_function = brace_depth = in_quote = in_comment = 0; #ifdef FLEX_SCANNER if ( yy_current_buffer) YY_FLUSH_BUFFER; #endif } /************************************************************* * * Function: record_macro() * * Purpose: Record macro definition. * */ void record_macro() { int len; char *mspot; int cont_flag; int backslash_spot; macro_flag = 0; if ( macro_count >= macro_max ) { if ( macro_max == 0 ) { macro_max = 10; macros = (struct macro *)mycalloc(macro_max,sizeof(struct macro)); } else { macros = (struct macro *)kb_realloc((char *)macros, 2*macro_max*sizeof(struct macro)); macro_max *= 2; } } strncpy(macros[macro_count].name,yytext,MACRONAMESIZE); if ( macro_subs_max == 0 ) { macro_subs_max = 2*SUBMAX; macro_subs = (char *)mycalloc(macro_subs_max,1); } /* read in macro string */ for ( len = 0, cont_flag = 0 ; ; len++ ) { int c = KB_INPUT(); if ( len == 0 && (c == ' ' || c == '\t') ) { len--; continue; /* skip leading whitespace */ } if ( c == '\\' ) { cont_flag = 1; backslash_spot = len; } else if ( c != ' ' && c != '\t' && c != '\n' && c != 0 ) cont_flag = 0; if ( (c == '\0') || (c == '\n') ) { line_no++; if ( !cont_flag ) break; cont_flag = 0; len = backslash_spot; macro_subs[macro_subs_top+len] = 0; len--; continue; } if ( macro_subs_top+len >= macro_subs_max-2 ) { macro_subs = (char *)kb_realloc(macro_subs,macro_subs_max+2*SUBMAX); macro_subs_max += 2*SUBMAX; } macro_subs[macro_subs_top+len] = (char)c; } mspot = macro_subs+macro_subs_top; /* strip terminal comment */ if ( strstr(mspot,"//") ) { *strstr(mspot,"//") = 0; len = strlen(mspot); } else mspot[len] = 0; /* strip trailing blanks */ while ( (len > 0) && (mspot[len-1] == ' ') ) mspot[--len] = 0; macros[macro_count].subsize = len; macros[macro_count++].offset = macro_subs_top; macro_subs_top += len+1; } /************************************************************* * * Function: macro() * * Purpose: See if yytext is a macro, and do substitution. * * Return: 1 if macro, 0 if not. * */ int macro() { int n,k; char *c; if ( macro_flag ) { record_macro(); return WAS_MACRO_DEF; } if ( strcmp(yytext,"_anti_line_no_") == 0 ) { line_no--; /* kludge line backup */ errspot -= 15; return 1; } if ( !keep_macros_flag && !datafile_flag ) return 0; /* no macros in commands */ /* find name, simple linear search */ for ( n = macro_count-1 ; n >= 0 ; n-- ) /* use latest definition */ if ( strcmp(yytext,macros[n].name) == 0 ) break; if ( n < 0 ) return 0; /* not found */ /* insert string into input in reverse order so reads ok */ c = macro_subs+macros[n].offset; for ( k = macros[n].subsize - 1 ; k >= 0 ; k-- ) KB_UNPUT(c[k]); KB_UNPUT(' '); /* substitution starts with whitespace; for proper preservation of unary minus leading in the macro */ return MACRO_EXPANDED; } /* for deciding type of minus sign or plus sign */ int minus_type(c) int c; /* '+' or '-' */ { int result; switch ( tok ) { case LEAD_INTEGER_: case SIGNED_NUMBER_: case INTEGER_: case REAL_: case PI_: case E_: case G_: case PARAM_: case ARRAYIDENT_: case COORD_: case VALUE_: case BARE_: case IDENT_: case QUANTITY_NAME_: case WRAP_: case PERM_IDENT_: case LENGTH_: case DIHEDRAL_: case VALENCE_: case AREA_: case VOLUME_: case DENSITY_: case TAG_: case ID_: case ORIGINAL_: case SQ_MEAN_CURV_: case OID_: case INTERNAL_VARIABLE_: case COLOR_: case TOGGLEVALUE_: case EXTRA_ATTRIBUTE_: case PRESSURE_: case ARRAY_ATTRIBUTE_: case FRONTCOLOR_: case BACKCOLOR_: case TARGET_: case NO_REFINE_: case VOLCONST_: case FRONTBODY_: case NONCONTENT_: case BACKBODY_: case TETRA_POINT_: case TRIPLE_POINT_: case MIDV_: case PHASE_: case MODULUS_: case METHOD_NAME_: case NODISPLAY_: case HIT_PARTNER_: case ']': case ')': result = c; break; default: result = (c=='-' ? UMINUS_ : UPLUS_) ; break; } return result; } /**************************************************************************** * * function identcase() * * purpose: handle case of identifier token, maybe macro, keyword, etc. */ int keyword_compare(a,b) struct ckey *a,*b; { return stricmp(a->name,b->name); } int identcase(lexeme) char *lexeme; { int k,i; int type; struct sym *s; static int sorted = 0; /* flag for sorting keywords */ struct ckey *keyptr; struct ckey key; /* for bsearch key */ char *c,*p; dll_func_type h; yylval.i = 0; if ( ! sorted ) { qsort((char*)datafile_keywords, sizeof(datafile_keywords)/ sizeof(struct dkey),sizeof(struct dkey), FCAST keyword_compare); qsort((char*)command_keywords, sizeof(command_keywords)/ sizeof(struct ckey),sizeof(struct ckey), FCAST keyword_compare); qsort((char*)togglenames, sizeof(togglenames)/ sizeof(struct ckey),sizeof(struct ckey), FCAST keyword_compare); qsort((char*)colornames, sizeof(colornames)/ sizeof(struct ckey),sizeof(struct ckey), FCAST keyword_compare); qsort((char*)internal_variables, sizeof(internal_variables)/ sizeof(struct ckey),sizeof(struct ckey), FCAST keyword_compare); qsort((char*)mathfunc_keywords, sizeof(mathfunc_keywords)/ sizeof(struct ckey),sizeof(struct ckey), FCAST keyword_compare); qsort((char*)mathfunc2_keywords, sizeof(mathfunc2_keywords)/ sizeof(struct ckey),sizeof(struct ckey), FCAST keyword_compare); sorted = 1; } if ( strcmp(lexeme,"_command_") == 0 ) { return tok = COMMAND_START_ ; } if ( strcmp(lexeme,"_expr_") == 0 ) { return tok = EXPRESSION_START_; } /* strip whitespace from front of lexeme */ c = strtok(lexeme,whitespace); if ( c != lexeme ) { p = lexeme; while ( *c ) *(p++) = *(c++); *p = 0;} if ( strlen(lexeme) == 1 ) { char ch = lexeme[0]; if ( verb_flag == 1 || tok==PRINT_ || (verb_flag == 2 && (toupper(ch) < 'W')) ) { yylval.i=lexeme[0]; if ( single_redefine[yylval.i].start ) return tok = SINGLE_REDEFD_; switch ( yylval.i ) { case 't': case 'l': case 'j': case 'P': case 'M': case 'w': case 'n': case 'm': case 'b': case 'k': case 'K': case 'p': case 'y': return ( tok = SINGLE_LETTER_ARG_ ); case 'G': if ( verb_flag ) return ( tok = SINGLE_LETTER_ARG_ ); else return tok=G_; default: return ( tok = SINGLE_LETTER_ ); } } else switch ( toupper(lexeme[0]) ) { case 'X': case 'Y': case 'Z': case 'W': yylval.i = ((toupper(lexeme[0]) - 'X' + 4) % 4) + 1; return COORD_; } } if ( toupper(lexeme[0]) == 'X' ) { char *c; for ( c = lexeme+1 ; isdigit(*c) ; c++ ) ; if ( *c == 0 ) { yylval.i = atoi(lexeme+1); if ( yylval.i != 0 ) return COORD_; } } if ( toupper(lexeme[0]) == 'P' && isdigit(lexeme[1]) && !lexeme[2] ) { yylval.i = atoi(lexeme+1); return PARAM_; } if ( lexeme[0] == 'g' ) { char *c; for ( c = lexeme+1 ; isdigit(*c) ; c++ ) ; if ( *c == 0 ) { yylval.i = atoi(lexeme+1); return GO_COUNT_; } } if ( strncmp(lexeme,"usr",3) == 0 ) { char *c; for ( c = lexeme+3 ; isdigit(*c) ; c++ ) ; if ( *c == 0 ) { yylval.i = atoi(lexeme+3); return USERFUNC_; } } key.name = lexeme; #define BSEARCH(namelist)\ (struct ckey*)bsearch((char*)&key,(char*)(namelist),\ sizeof(namelist)/sizeof(struct ckey),\ sizeof(struct ckey),FCAST keyword_compare); keyptr = BSEARCH(colornames); if ( keyptr ) { yylval.r = yylval.i = keyptr->token; return (tok = INTEGER_); } /* search math funcs */ keyptr = BSEARCH(mathfunc_keywords); if ( keyptr ) { yylval.i = keyptr->token; return (tok = MATHFUNC_); } keyptr = BSEARCH(mathfunc2_keywords); if ( keyptr ) { yylval.i = keyptr->token; return (tok = MATHFUNC2_); } if ( const_expr_flag ) { for ( k=0 ; ktoken;} keyptr = BSEARCH(command_keywords); if ( keyptr ) { sprintf(errmsg, "Use of the keyword '%s' as an identifier is very ill-advised!\n", lexeme); kb_error(2328,errmsg, WARNING); } } else /* search keywords and internal variables */ { keyptr = BSEARCH(command_keywords); if ( keyptr ) { if ( kb_stricmp(lexeme,"HELP") == 0 ) help_flag = 1; yylval.i = tok = keyptr->token; switch ( tok ) { /* set verb_flag when expecting a command */ case THEN_: case ELSE_: case DO_: verb_flag = 1; break; /* clear verb_flag when expecting an expression */ case PRINT_: case IF_: case WHILE_: case VIEW_TRANSFORMS_: case TRANSFORM_DEPTH_: case METIS_: case KMETIS_: case OMETIS_: case EDGEWEED_: case AREAWEED_: case EDGEDIVIDE_: case LANCZOS_: case RITZ_: case EIGENPROBE_: case MOVE_: case ZOOM_: case LAGRANGE_: case SET_: case PRINTF_: case LIST_: case DELETE_: case VERTEX_AVERAGE_: case BINARY_PRINTF_: case DISSOLVE_: case REFINE_: case EDGESWAP_: case FIX_: case UNFIX_: case SPRINTF_: case EPRINT_: case HISTOGRAM_: case LOGHISTOGRAM_: case FOREACH_: case SHOW_EXPR_: case UNSET_: case HESSIAN_SADDLE_: case HESSIAN_SEEK_: case NOTCH_: case AUTOCHOP_: case AUTOPOP_: case OPTIMIZE_: verb_flag = 0; break; case SHOW_: if ( verb_flag ) tok = SHOWVERB_; verb_flag = 0; break; case TRANSFORM_EXPR_: if ( verb_flag ) tok = TRANSFORM_EXPR_VERB_; verb_flag = 0; break; } return tok; } } for ( i = 0 ; i < NUMDATATYPES ; i++ ) if ( stricmp(lexeme,datatype_name[i]) == 0 ) { yylval.i = DATATYPE_; yylval.datatype = i; return tok = DATATYPE_; } keyptr = BSEARCH(togglenames); if ( keyptr ) { yylval.i = keyptr->token; if ( verb_flag != 0 ) tok = TOGGLENAME_; else tok = TOGGLEVALUE_; return tok; } keyptr = BSEARCH(internal_variables); if ( keyptr ) { yylval.i = keyptr->token; return tok = INTERNAL_VARIABLE_; } /* search local variables */ yylval.i = lookup_local_var(lexeme); if ( yylval.i ) { struct global *g; g = globals(yylval.i); if ( g->flags & SUBROUTINE ) return ( tok = PROCEDURE_ ); else if ( g->flags & ORDINARY_PARAM ) { switch ( g->type ) { case VERTEX_TYPE: yylval.etype = VERTEX; return ( tok = ELEMENT_IDENT_ ); case ELEMENTID_TYPE: yylval.etype = -1; return ( tok = ELEMENT_IDENT_ ); case EDGE_TYPE: yylval.etype = EDGE; return ( tok = ELEMENT_IDENT_ ); case FACET_TYPE: yylval.etype = FACET; return ( tok = ELEMENT_IDENT_ ); case BODY_TYPE: yylval.etype = BODY; return ( tok = ELEMENT_IDENT_ ); case FACETEDGE_TYPE: yylval.etype = FACETEDGE; return ( tok = ELEMENT_IDENT_ ); default: return ( tok = IDENT_ ); } } else if ( g->flags & FUNCTION_NAME ) return ( tok = FUNCTION_IDENT_ ); else if ( g->flags & PROCEDURE_NAME ) return ( tok = PROCEDURE_IDENT_ ); else if ( g->flags & STRINGVAL ) return ( tok = STRINGGLOBAL_ ); else if ( g->flags & QUANTITY_NAME ) /* can't happen */ return ( tok = QUANTITY_NAME_ ); else if ( g->flags & METHOD_NAME ) /* can't happen */ return ( tok = METHOD_NAME_ ); else if ( g->flags & CONSTRAINT_NAME ) /* can't happen */ return ( tok = CONSTRAINT_NAME_ ); else if ( g->flags & BOUNDARY_NAME ) /* can't happen */ return ( tok = BOUNDARY_NAME_ ); else if ( g->flags & DYNAMIC_LOAD_FUNC ) /* can't happen */ return ( tok = DYNAMIC_LOAD_FUNC_ ); else if ( g->flags & ARRAY_PARAM ) return ( tok = ARRAYIDENT_ ); else if ( g->flags & GLOB_LOCALVAR ) return IDENT_; else return (tok = NEWIDENT_); } /* search symbol table */ s = symbol_lookup(lexeme); if ( s ) { yysym = s; yylval.i = yysym-symtable; verb_flag = 0; return (tok = SYMBOL_ ); } /* search parameter names */ yylval.i = lookup_global_hash(lexeme,0,0,HASH_LOOK); if ( yylval.i != 0 ) { int nametype = yylval.i & NAMETYPEMASK; struct global *g; yylval.i &= INDEXMASK; /* get plain index */ if ( nametype == QUANTITYNAME ) return tok = QUANTITY_NAME_; else if ( nametype == METHODNAME ) return tok = METHOD_NAME_; else if ( nametype == PERM_NAME ) { yylval.i |= PERMGLOBAL; if ( perm_globals(yylval.i)->flags & SUBROUTINE ) return ( tok = PERM_PROCEDURE_ ); else if ( perm_globals(yylval.i)->flags & STRINGVAL ) return ( tok = PERM_STRINGGLOBAL_ ); else if ( perm_globals(yylval.i)->flags & ARRAY_PARAM ) return ( tok = ARRAYIDENT_ ); else return (tok = PERM_IDENT_); } yylval.i |= EPHGLOBAL; g = globals(yylval.i); if ( g->flags & SUBROUTINE ) return ( tok = PROCEDURE_ ); else if ( g->flags & FUNCTION_NAME ) return ( tok = FUNCTION_IDENT_ ); else if ( g->flags & PROCEDURE_NAME ) return ( tok = PROCEDURE_IDENT_ ); else if ( g->flags & STRINGVAL ) return ( tok = STRINGGLOBAL_ ); else if ( g->flags & QUANTITY_NAME ) return ( tok = QUANTITY_NAME_ ); else if ( g->flags & METHOD_NAME ) return ( tok = METHOD_NAME_ ); else if ( g->flags & CONSTRAINT_NAME ) return ( tok = CONSTRAINT_NAME_ ); else if ( g->flags & BOUNDARY_NAME ) return ( tok = BOUNDARY_NAME_ ); else if ( g->flags & DYNAMIC_LOAD_FUNC ) return ( tok = DYNAMIC_LOAD_FUNC_ ); else if ( g->flags & ARRAY_PARAM ) return ( tok = ARRAYIDENT_ ); else { switch ( g->type ) { case VERTEX_TYPE: yylval.etype = VERTEX; return ( tok = ELEMENT_IDENT_ ); case ELEMENTID_TYPE: yylval.etype = -1; return ( tok = ELEMENT_IDENT_ ); case EDGE_TYPE: yylval.etype = EDGE; return ( tok = ELEMENT_IDENT_ ); case FACET_TYPE: yylval.etype = FACET; return ( tok = ELEMENT_IDENT_ ); case BODY_TYPE: yylval.etype = BODY; return ( tok = ELEMENT_IDENT_ ); case FACETEDGE_TYPE: yylval.etype = FACETEDGE; return ( tok = ELEMENT_IDENT_ ); default: return ( tok = IDENT_ ); } } } /* search extra attributes */ for ( type = 0 ; type < NUMELEMENTS ; type++ ) { struct extra *ex; int i; for ( i = 0, ex = EXTRAS(type) ; i < web.skel[type].extra_count ; i++ , ex++ ) if ( stricmp(lexeme, ex->name) == 0 ) { strncpy(idname,lexeme,sizeof(idname)); /* save text */ yylval.qnum = i; yylval.etype = type; return tok = ex->array_spec.dim ? ARRAY_ATTRIBUTE_ : EXTRA_ATTRIBUTE_; } } /* search dynamic load libraries */ h = search_libraries(lexeme); if ( h ) { yylval.i = add_global(lexeme); globals(yylval.i)->flags |= DYNAMIC_LOAD_FUNC; globals(yylval.i)->value.funcptr = h; return ( tok = DYNAMIC_LOAD_FUNC_ ); } /* if here, then not keyword */ strncpy(idname,lexeme,sizeof(idname)); /* save text */ if ( strlen(lexeme) == 1 ) { sprintf(errmsg, "Use of single letter command '%c' is illegal here!\n", lexeme[0]); kb_error(2329,errmsg, WARNING); if ( !datafile_flag) return tok = lexeme[0]; } yylval.i = 0; return(tok = NEWIDENT_) ; } /* end identcase() */ /****************************************************************** * * function: keywordname() * * purpose: find keyword of given token number. * */ char *keywordname(toknum) int toknum; { int k,i,imax; for ( k = 0 ; k < sizeof(command_keywords)/sizeof(struct ckey) ; k++ ) if ( toknum == command_keywords[k].token ) { static char name[32]; strncpy(name,command_keywords[k].name,31); return name; } for ( k = 0 ; k < sizeof(togglenames)/sizeof(struct ckey) ; k++ ) if ( toknum == togglenames[k].token ) { static char name[32]; strncpy(name,togglenames[k].name,31); return name; } for ( k = 0 ; k < sizeof(internal_variables)/sizeof(struct ckey) ; k++ ) if ( toknum == internal_variables[k].token ) { static char name[32]; strncpy(name,internal_variables[k].name,31); return name; } imax = sizeof(colornames)/sizeof(struct ckey); for ( i = 0 ; i < imax ; i++ ) if ( colornames[i].token == toknum ) return colornames[i].name; imax = sizeof(datafile_keywords)/sizeof(struct dkey); for ( i = 0 ; i < imax ; i++ ) if ( datafile_keywords[i].token == toknum ) return datafile_keywords[i].name; imax = sizeof(mathfunc_keywords)/sizeof(struct ckey); for ( i = 0 ; i < imax ; i++ ) if ( mathfunc_keywords[i].token == toknum ) return mathfunc_keywords[i].name; imax = sizeof(mathfunc2_keywords)/sizeof(struct ckey); for ( i = 0 ; i < imax ; i++ ) if ( mathfunc2_keywords[i].token == toknum ) return mathfunc2_keywords[i].name; /* unfound */ return tokname(toknum); } /**************************************************************************** * * function identtype() * * purpose: find type of an identifier. This version used by * is_variable() etc. commands. */ int identtype(word) char *word; { int type; struct sym *s; struct ckey *keyptr; struct ckey key; /* for bsearch key */ if ( strlen(word) == 1 ) { if ( single_redefine[yylval.i].start ) return tok = SINGLE_REDEFD_; switch ( word[0] ) { case 't': case 'l': case 'j': case 'P': case 'M': case 'w': case 'n': case 'm': case 'b': case 'k': case 'K': case 'p': case 'y': return ( tok = SINGLE_LETTER_ARG_ ); case 'G': if ( verb_flag ) return tok = SINGLE_LETTER_ARG_; else return tok = G_; break; default: return ( tok = SINGLE_LETTER_ ); } } key.name = word; keyptr = BSEARCH(colornames); if ( keyptr ) return INTEGER_; /* search math funcs */ keyptr = BSEARCH(mathfunc_keywords); if ( keyptr ) { return MATHFUNC_; } keyptr = BSEARCH(mathfunc2_keywords); if ( keyptr ) { return MATHFUNC2_; } /* search keywords and internal variables */ { keyptr = BSEARCH(command_keywords); if ( keyptr ) { return tok; } keyptr = BSEARCH(togglenames); if ( keyptr ) return tok; keyptr = BSEARCH(internal_variables); if ( keyptr ) { return INTERNAL_VARIABLE_; } } /* search local variables */ yylval.i = lookup_local_var(word); if ( yylval.i ) { struct global *g; g = globals(yylval.i); if ( g->flags & SUBROUTINE ) return ( tok = PROCEDURE_ ); else if ( g->flags & ORDINARY_PARAM ) return ( tok = IDENT_ ); else if ( g->flags & STRINGVAL ) return ( tok = STRINGGLOBAL_ ); else if ( g->flags & QUANTITY_NAME ) /* can't happen */ return ( tok = QUANTITY_NAME_ ); else if ( g->flags & METHOD_NAME ) /* can't happen */ return ( tok = METHOD_NAME_ ); else if ( g->flags & CONSTRAINT_NAME ) /* can't happen */ return ( tok = CONSTRAINT_NAME_ ); else if ( g->flags & BOUNDARY_NAME ) /* can't happen */ return ( tok = BOUNDARY_NAME_ ); else if ( g->flags & DYNAMIC_LOAD_FUNC ) /* can't happen */ return ( tok = DYNAMIC_LOAD_FUNC_ ); else if ( g->flags & ARRAY_PARAM ) return ( tok = ARRAYIDENT_ ); else if ( g->flags & GLOB_LOCALVAR ) return IDENT_; else return (tok = NEWIDENT_); } /* search symbol table */ s = symbol_lookup(word); if ( s ) { yysym = s; yylval.i = yysym-symtable; verb_flag = 0; return (tok = SYMBOL_ ); } /* search parameter names */ yylval.i = lookup_perm_global(word); if ( yylval.i >= 0 ) { if ( perm_globals(yylval.i)->flags & SUBROUTINE ) return ( tok = PERM_PROCEDURE_ ); else if ( globals(yylval.i)->flags & STRINGVAL ) return ( tok = PERM_STRINGGLOBAL_ ); else return (tok = PERM_IDENT_); } yylval.i = lookup_global(word); if ( yylval.i >= 0 ) { if ( globals(yylval.i)->flags & SUBROUTINE ) return ( tok = PROCEDURE_ ); else if ( globals(yylval.i)->flags & STRINGVAL ) return ( tok = STRINGGLOBAL_ ); else if ( globals(yylval.i)->flags & QUANTITY_NAME ) return ( tok = QUANTITY_NAME_ ); else if ( globals(yylval.i)->flags & METHOD_NAME ) return ( tok = METHOD_NAME_ ); else if ( globals(yylval.i)->flags & DYNAMIC_LOAD_FUNC ) return ( tok = DYNAMIC_LOAD_FUNC_ ); else if ( globals(yylval.i)->flags & ARRAY_PARAM ) return ( tok = ARRAYIDENT_ ); else return (tok = IDENT_); } /* search extra attributes */ for ( type = 0 ; type <= BODY ; type++ ) { struct extra *ex; int i; for ( i = 0, ex = EXTRAS(type) ; i < web.skel[type].extra_count ; i++ , ex++ ) if ( stricmp(word, ex->name) == 0 ) { strncpy(idname,word,sizeof(idname)); /* save text */ yylval.qnum = i; yylval.etype = type; return tok = ex->array_spec.dim ? ARRAY_ATTRIBUTE_ : EXTRA_ATTRIBUTE_; } } /* if here, then not keyword */ yylval.i = 0; return(tok = NEWIDENT_) ; } /* end identtype() */ /********************************************************************* * * function: kblex() * * purpose: my own lexical analyzer, faster (for Evolver) and * more comprehensible. */ /* States */ #define L_START 1 #define L_SLASH 2 #define L_LINECOMMENT 3 #define L_DOT 4 #define L_DIGITS 5 #define L_DIGITS_DOT 6 #define L_WHITESPACE 7 #define L_WHITESIGN 8 #define L_LINESTART 9 #define L_LEAD_INTEGER 10 #define L_LEAD_INTEGER_AT 11 #define L_INTEGER_AT 12 #define L_DIGITS_E 13 #define L_ALPHANUM 14 #define L_COLON 15 #define L_SEMICOLON 16 #define L_PLUS 17 #define L_MINUS 18 #define L_STAR 19 #define L_BANG 20 #define L_PIPE 21 #define L_AND 22 #define L_GREATER 23 #define L_COLONCOLON 24 #define L_SINGLE_QUOTE 25 #define L_HEXNUMBER 26 #define L_ZERO 27 #define L_GRGR 28 #define L_BACKQUOTE 29 #define L_SHARP 30 #define L_COLONCOLONCOLON 31 #define L_QUOTE 32 #define L_EQUAL 33 #define L_LESS 34 #define L_DIGITS_E_DIGITS 35 #define L_LEAD_MINUS 36 #define L_BACKSLASH 37 /* Unput buffer */ int ubuff_max = 0; int ubuff_spot = 0; /* index of first vacant spot in unput_buff */ char *unput_buff; /* the token */ /*char *yytext;*/ int yytext_max; char kb_input_new() { char c; if ( ubuff_spot ) { c = unput_buff[--ubuff_spot]; unput_buff[ubuff_spot] = 0; return c; } c = rawinput(); while ( c == 0 ) { if ( yywrap() ) return 0; else c = rawinput(); } return c; } void kb_unput(c) char c; { if ( ubuff_spot >= ubuff_max ) { if ( unput_buff ) { ubuff_max *= 2; unput_buff = realloc(unput_buff,ubuff_max); } else { ubuff_max = 16000; unput_buff = calloc(ubuff_max,sizeof(char)); } } unput_buff[ubuff_spot++] = c; } int kblex() { int state; char nextchar; char *yyspot; /* first empty spot in yytext */ int retval; PROF_START(kblex); if ( !yytext ) { yytext_max = 200; yytext = calloc(yytext_max,sizeof(char)); } yyspot = yytext; memset(&yylval,0,sizeof(yylval)); state = tok ? L_START : L_LINESTART; for(;;) { int len = yyspot-yytext; if ( len >= yytext_max-2 ) { yytext_max *= 2; yytext = realloc(yytext,yytext_max); yyspot = yytext + len; } *(yyspot++) = nextchar = kb_input_new(); switch ( state ) { case L_START: if ( nextchar == '0' ) state = L_ZERO; else if ( isdigit(nextchar) ) state = L_DIGITS; else if ( isalpha(nextchar) ) state = L_ALPHANUM; else switch ( nextchar ) { case 0: tok=0; goto kblex_exit; case '\n' : line_no++; yyspot = yytext; state = L_LINESTART; break; case ' ': case '\t': yyspot--; state = L_WHITESPACE; break; case '/': state = L_SLASH; break; case '.': state = L_DOT; break; case '_': state = L_ALPHANUM; break; case ':': state = L_COLON; break; case ';': state = L_SEMICOLON; break; case '+': state = L_PLUS; break; case '-': state = L_MINUS; break; case '*': state = L_STAR; break; case '!': state = L_BANG; break; case '|': state = L_PIPE; break; case '&': state = L_AND; break; case '>': state = L_GREATER; break; case '\'': state = L_SINGLE_QUOTE; break; case '"': state = L_QUOTE; break; case '`': state = L_BACKQUOTE; break; case '=': state = L_EQUAL; break; case '<': state = L_LESS; break; case '\\': state = L_BACKSLASH; break; case '#': state = L_SHARP; break; case '(': verb_flag = 0; *yyspot = 0; tok = nextchar; goto kblex_exit; case ')': case '^': case ',': case '?': case '%': case '[': case ']': case '}': case '@': *yyspot = 0; tok = nextchar; goto kblex_exit; case '{': verb_flag = 1; *yyspot = 0; tok = nextchar; goto kblex_exit; default: sprintf(errmsg,"Illegal character '%c'\n",nextchar); kb_error(3658,errmsg,RECOVERABLE); } break; case L_BACKSLASH: /* allow trailing whitespace */ while ( nextchar == ' ' || nextchar == '\t' ) nextchar = kb_input_new(); if ( nextchar == '\n' ) { yyspot = yytext; state = L_START; line_no++; break; } kb_error(3659, "Illegal backslash. Backslash is line continuation character.\n", RECOVERABLE); break; case L_EQUAL: if ( nextchar == '=' ) { *yyspot = 0; tok = EQ_; goto kblex_exit; } kb_unput(nextchar); yyspot[-1] = 0; tok = (datafile_flag ? '=' : EQ_); goto kblex_exit; case L_QUOTE: { /* read quoted string */ int n; in_quote = 1; for (n=0;;n++) { if ( nextchar == '\n' ) line_no++; if ( nextchar == '"' && (n == 0 || (yytext[n-1] != '\\')) ) break; if ( n >= yytext_max - 2 ) { yytext_max *= 2; yytext = realloc(yytext,yytext_max); } yytext[n] = nextchar; nextchar = kb_input_new(); } yytext[n] = 0; reduce_string(yytext); in_quote = 0; tok = QUOTATION_; goto kblex_exit; } break; case L_LESS: if ( nextchar == '=' ) { *yyspot = 0; tok = LE_; goto kblex_exit; } else { kb_unput(nextchar); yyspot[-1] = 0; tok = '<'; goto kblex_exit; } break; case L_GREATER: if ( nextchar == '>' ) state = L_GRGR; else if ( nextchar == '=' ) { *yyspot = 0; tok = GE_; goto kblex_exit; } else { kb_unput(nextchar); yyspot[-1] = 0; tok = '>'; goto kblex_exit; } break; case L_GRGR: if ( nextchar == '>' ) { *yyspot = 0; tok = REDIRECTOVER_; goto kblex_exit; } else { kb_unput(nextchar); yyspot[-1] = 0; tok = REDIRECT_; goto kblex_exit; } break; case L_SINGLE_QUOTE: if ( nextchar == '\'' ) kb_error(3577,"Empty single quotes.\n",RECOVERABLE); if ( nextchar == 0 || nextchar == '\n' ) kb_error(3570,"Dangling single quote.\n",RECOVERABLE); yylval.i = nextchar; nextchar = kb_input_new(); if ( nextchar != '\'' ) kb_error(3583,"Single quote not closed after one character.\n", RECOVERABLE); yytext[1] = 0; tok = SINGLE_LETTER_; goto kblex_exit; break; case L_BACKQUOTE: while ( nextchar == ' ' ) yyspot[-1] = nextchar = kb_input_new(); if ( nextchar == ',' ) { *yyspot = 0; tok = BACKQUOTE_COMMA_; goto kblex_exit; } yyspot[-1] = 0; kb_unput(nextchar); tok = '`'; goto kblex_exit; break; case L_SHARP: while ( isalpha(nextchar) ) *(yyspot++) = nextchar = kb_input_new(); yyspot[-1] = 0; if ( strcmp(yytext,"#define") == 0 ) { if (!datafile_flag) kb_error(2853,"#define valid only in top of datafile.\n",WARNING); macro_flag = 1; state = L_START; yyspot = yytext; break; } if ( strcmp(yytext,"#include") == 0 ) { while ( nextchar != '"' ) nextchar = kb_input_new(); yyspot = yytext; do *(yyspot++) = nextchar = kb_input_new(); while ( nextchar != '"' ); yyspot[-1] = 0; push_commandfd(NULL,yytext); state = L_LINESTART; yyspot = yytext; break; } kb_error(3681,"Illegal # directive.\n",RECOVERABLE); break; case L_ZERO: if ( toupper(nextchar) == 'X' ) state = L_HEXNUMBER; else if ( nextchar == '.' ) state = L_DIGITS_DOT; else if ( isdigit(nextchar) ) state = L_DIGITS; else { kb_unput(nextchar); yyspot[-1] = 0; yylval.i = 0; verb_flag = 0; tok = INTEGER_; goto kblex_exit; } break; case L_HEXNUMBER: if ( isdigit(nextchar) || (toupper(nextchar) >= 'A' && toupper(nextchar) <= 'F') ) break; yyspot[-1] = 0; kb_unput(nextchar); sscanf(yytext+2,"%x",&yylval.i); yylval.r = (REAL)yylval.i; verb_flag = 0; tok = INTEGER_; goto kblex_exit; case L_WHITESPACE: if ( nextchar == '+' || nextchar == '-' ) { state = L_WHITESIGN; break; } if ( nextchar == ' ' || nextchar == '\t' ) { yyspot--; break; } else { state = L_START; kb_unput(nextchar); yyspot--; } break; case L_WHITESIGN: kb_unput(nextchar); if ( !isspace(nextchar) && (yytext[0] == '-') ) { if ( datafile_flag && uminus_flag && (parens == 0) ) { tok = UMINUS_; goto kblex_exit; } } /* else go back and treat as ordinary - */ yyspot--; state = yytext[0] == '+' ? L_PLUS : L_MINUS; break; case L_LINESTART: if ( nextchar == 0 ) { tok = 0; goto kblex_exit; } if ( nextchar == ' ' || nextchar == '\t' ) yyspot--; else if ( isdigit(nextchar) ) state = L_LEAD_INTEGER; else if ( nextchar == '-' ) state = L_LEAD_MINUS; else { kb_unput(nextchar); state = L_START; yyspot = yytext; } break; case L_LEAD_MINUS: if ( isdigit(nextchar) ) state = L_LEAD_INTEGER; else { kb_unput(nextchar); state = L_WHITESIGN; yyspot--; } break; case L_LEAD_INTEGER: if ( isdigit(nextchar) ) break; if ( nextchar == '.' ) { state = L_DIGITS_DOT; break; } if ( (toupper(nextchar) == 'E') || (toupper(nextchar) == 'D') ) { state = L_DIGITS_E; break; } if ( nextchar == '@' ) { state = L_LEAD_INTEGER_AT; break; } /* have LEAD_INTEGER_ */ kb_unput(nextchar); yyspot[-1] = 0; yylval.r = atof(yytext); yylval.i = atoi(yytext); verb_flag = 0; if ( lists_flag==LISTS_FULL ) { tok = LEAD_INTEGER_; goto kblex_exit; } else if ( uminus_flag || yytext[0] != '-' ) { tok = (fabs(yylval.r) > MAXINT) ? REAL_ : INTEGER_; goto kblex_exit; } else { for ( yyspot -= 2 ; yyspot != yytext ; yyspot-- ) kb_unput(*yyspot); yytext[1] = 0; tok = minus_type(yytext[0]); goto kblex_exit; } break; case L_DIGITS: if ( isdigit(nextchar) ) break; if ( nextchar == '.' ) { state = L_DIGITS_DOT; break; } if ( (toupper(nextchar) == 'E') || (toupper(nextchar) == 'D') ) { state = L_DIGITS_E; break; } if ( nextchar == '@' ) { state = L_INTEGER_AT; break; } if ( toupper(nextchar) == 'B' ) { char *c; yylval.i = 0; for ( c = yytext; *c=='0' || *c=='1' ; c++ ) yylval.i = 2*yylval.i + (*c - '0'); if ( c == yyspot-1 ) { yylval.r = (REAL)yylval.i; verb_flag = 0; tok = INTEGER_; goto kblex_exit; } /* else fall through to ordinary integer */ } /* have INTEGER_ */ kb_unput(nextchar); yyspot[-1] = 0; yylval.r = atof(yytext); yylval.i = atoi(yytext); verb_flag = 0; tok = (fabs(yylval.r) > MAXINT) ? REAL_ : INTEGER_; goto kblex_exit; case L_DIGITS_DOT: if ( (toupper(nextchar) == 'E') || (toupper(nextchar) == 'D') ) { state = L_DIGITS_E; break; } if ( isdigit(nextchar) ) break; if ( isalpha(nextchar) ) { *yyspot = 0; tok = VERSIONTOKEN_; goto kblex_exit; } /* have complete REAL */ kb_unput(nextchar); yyspot[-1] = 0; yylval.r = atof(yytext); verb_flag = 0; tok = REAL_; goto kblex_exit; case L_DOT: if ( isdigit(nextchar) ) { state = L_DIGITS_DOT; break; } /* else just a dot */ kb_unput(nextchar); yyspot[-1] = 0; tok = '.'; goto kblex_exit; case L_LEAD_INTEGER_AT: if ( isdigit(nextchar) ) break; /* have token */ kb_unput(nextchar); yyspot[-1] = 0; yylval.i = atoi(yytext); yylval.qnum = atoi(strchr(yytext,'@')+1); if ( yylval.i == 0 ) kb_error(3554,"Missing task number.",DATAFILE_ERROR); verb_flag = 0; tok = LEAD_INTEGER_AT_; goto kblex_exit; case L_INTEGER_AT: if ( isdigit(nextchar) ) break; /* have token */ kb_unput(nextchar); yyspot[-1] = 0; yylval.i = atoi(yytext); yylval.qnum = atoi(strchr(yytext,'@')+1); if ( yylval.i == 0 ) kb_error(3553,"Missing task number.",DATAFILE_ERROR); verb_flag = 0; tok = INTEGER_AT_; goto kblex_exit; case L_SLASH: switch ( nextchar ) { case '*': { /* eat comment */ register int c; in_comment = 1; state = L_START; for ( ; ; ) { while ( (c = kb_input_new()) != '*' && c != 0 ) if ( c == '\n' ) { line_no++; /* eat up text of comment */ state = L_LINESTART; } if ( c == '*' ) { while ( (c = kb_input_new()) == '*' ) ; if ( c == '/' ) break; /* found the end */ if ( c == '\n' ) { line_no++; state = L_LINESTART; } } if ( c == 0 && yywrap() ) { kb_error(2861,"End-of-file in comment\n",RECOVERABLE ); break; } } in_comment = 0; yyspot = yytext; } break; case '/': state = L_LINECOMMENT; break; case '=': *yyspot = 0; yylval.i = DIVASSIGN_; verb_flag = 0; tok=ASSIGNOP_; goto kblex_exit; default: kb_unput(nextchar); yyspot[-1] = 0; verb_flag = 0; tok = '/'; goto kblex_exit; } break; /* end L_SLASH */ case L_DIGITS_E: if ( isdigit(nextchar) || nextchar == '+' || nextchar == '-' ) { state = L_DIGITS_E_DIGITS; break; } kb_error(3664,"Missing exponent for scientific notation.\n", RECOVERABLE); break; case L_DIGITS_E_DIGITS: if ( isdigit(nextchar) ) break; /* have token */ kb_unput(nextchar); yyspot[-1] = 0; yylval.r = atof(yytext); verb_flag = 0; tok = REAL_; goto kblex_exit; case L_ALPHANUM: if ( isalnum(nextchar) || nextchar == '_' ) break; /* have token */ kb_unput(nextchar); yyspot[-1] = 0; strncpy(yylval.lexeme,yytext,31); retval = macro(); if ( retval == WAS_MACRO_DEF ) { /* macro() ate up rest of line */ state = L_LINESTART; yyspot = yytext; } else if ( retval == MACRO_EXPANDED ) { state = L_START; yyspot = yytext; } else { tok = identcase(yytext); goto kblex_exit; } break; case L_COLON: if ( nextchar == '=' ) { verb_flag = 2; yyspot[-1] = 0; tok = ASSIGN_; goto kblex_exit; } if ( nextchar == ':' ) { state = L_COLONCOLON; break; } kb_unput(nextchar); yyspot[-1] = 0; if ( cond_expr_flag ) { tok = ':'; goto kblex_exit; } /* else treat as whitespace */ yyspot = yytext; state = L_START; break; case L_COLONCOLON: if ( nextchar == '=' ) { verb_flag = 2; yyspot[-1] = 0; tok = PERM_ASSIGN_; goto kblex_exit; } if ( nextchar == ':' ) { state = L_COLONCOLONCOLON; break; } kb_error(3568,"Illegal token '::'\n",RECOVERABLE); break; case L_COLONCOLONCOLON: if ( nextchar == '=' ) { verb_flag = 2; yyspot[-1] = 0; tok = REDEFINE_; goto kblex_exit; } kb_error(3569,"Illegal token ':::'\n",RECOVERABLE); break; case L_SEMICOLON: if ( nextchar == '=' ) { verb_flag = 2; kb_error(2868,"You mistyped ';=' for ':='?\n",WARNING); tok= ASSIGN_; goto kblex_exit; } kb_unput(nextchar); yyspot[-1] = 0; verb_flag = 1; tok = ';'; goto kblex_exit; case L_PLUS: verb_flag = 0; if ( nextchar == '=' ) { verb_flag = 0; *yyspot = 0; yylval.i = PLUSASSIGN_; tok = ASSIGNOP_; goto kblex_exit; } kb_unput(nextchar); yyspot[-1] = 0; tok = '+'; goto kblex_exit; case L_MINUS: verb_flag = 0; if ( nextchar == '=' ) { verb_flag = 0; yylval.i = SUBASSIGN_; *yyspot = 0; tok= ASSIGNOP_; goto kblex_exit; } verb_flag = 0; kb_unput(nextchar); tok = minus_type('-'); goto kblex_exit; case L_STAR: verb_flag = 0; if ( nextchar == '=' ) { verb_flag = 0; *yyspot = 0; yylval.i = MULTASSIGN_; tok = ASSIGNOP_; goto kblex_exit; } if ( nextchar == '*' ) { verb_flag = 0; *yyspot = 0; tok = '^'; goto kblex_exit; } kb_unput(nextchar); yyspot[-1] = 0; tok = '*'; goto kblex_exit; case L_BANG: verb_flag = 0; if ( nextchar == '=' ) { *yyspot = 0; tok = NE_; goto kblex_exit; } kb_unput(nextchar); yyspot[-1] = 0; tok = NOT_; goto kblex_exit; case L_PIPE: if ( nextchar == '|' ) { *yyspot = 0; tok = OR_; goto kblex_exit; } kb_unput(nextchar); yyspot[-1] = 0; tok = PIPE_; goto kblex_exit; case L_AND: if ( nextchar == '&' ) { *yyspot = 0; tok = AND_; goto kblex_exit; } kb_error(3566,"Illegal token '&'\n",RECOVERABLE); break; case L_LINECOMMENT: if ( (nextchar == '\n') || (nextchar == 0) ) { line_no++; state = L_LINESTART; yyspot = yytext; break; } yyspot--; /* don't save comment token */ break; default: fprintf(stderr,"Unhandled state: %d\n",state); *yyspot = 0; tok = 1; goto kblex_exit; } } kblex_exit: PROF_FINISH(kblex); return tok; } evolver-2.30c.dfsg/src/eval_sec.c0000644000175300017530000017211311410765113017147 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /***************************************************************** * * File: eval_sec.c * * Purpose: To evaluate first and second derivatives of expressions * */ /* NOTE: Compiling with full optimization in Visual Studio .NET beta 1 takes a very long time, like an hour. Seems to be due to "global optimization" option. Set optimization to custom, just for speed, in file properties in project. */ #include "include.h" #include "ytab.h" /* for easy assignments */ #define FIRST for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] #define SECOND for ( i = 0 ; i < pcount ; i++ )\ for ( j = 0 ; j < pcount ; j++ ) stacktop->second[i][j] /* to save a little code size */ void zero_seconds ARGS((int, struct dstack *)); void zero_seconds (pcount, stacktop) int pcount; struct dstack *stacktop; { int i,j; FIRST = 0.0; SECOND = 0.0; } #ifdef MAC_CW #define ESTACKSIZE 20 #else #define ESTACKSIZE 100 #endif /***************************************************************** * * Function eval_second() * * Purpose: runtime tree_evaluation of expression and all of its * partial derivatives and second derivatives. * */ void eval_second(ex,params,pcount,fval,partials,seconds,q_id) struct expnode *ex; /* expression tree */ REAL *params; /* vector of paramters */ int pcount; /* number of variables */ REAL *fval; /* function value */ REAL *partials; /* values of partials */ REAL **seconds; /* second derivatives */ element_id q_id; /* reference element, if any */ { int n,m; /* i, j missing since want them local for optimization */ REAL x,y; struct dstack localstack[ESTACKSIZE]; struct dstack *stacktop = localstack; struct treenode *node; element_id id; struct locallist_t *localbase = ex->locals; if ( pcount > 2*MAXCOORD ) kb_error(1009,"More variables than 2*MAXCOORD in eval_second().\n",RECOVERABLE); stacktop->value = 0.0; /* in case of empty expression */ zero_seconds(pcount,stacktop); if ( ex ) for ( node = ex->start+1 ; ; node++ ) { switch ( node->type ) { case SETUP_FRAME_: break; case EXPRLIST_: break; /* leave expression on stack */ case FUNCTION_CALL_: { struct thread_data *td = GET_THREAD_DATA; REAL value; int i,j; /* push arguments on eval() stack */ for ( i = 0 ; i < node->op2.argcount ; i++ ) *(++(td->stack_top)) = stacktop[i-node->op2.argcount+1].value; value = eval(&globals(node->op1.name_id)->value.proc, NULL,NULLID,NULL); td->stack_top -= node->op2.argcount; /* pop arguments */ (++stacktop)->value = value; FIRST = 0.0; SECOND = 0.0; break; } case FUNCTION_CALL_RETURN_: { /* nothing to do here since FUNCTION_CALL used eval() */ break; } case INDEXSET_: break; /* just accumulate index values */ case ARRAY_HEAD_: break; /* let indices accumulate */ case ARRAYEVAL: { struct array *a = globals(node->op2.name_id)->attr.arrayptr; REAL value=0.0; int i,j,offset; void *lvalue; for ( i = 0 ; i < a->dim ; i++ ) { int k = (int)stacktop[i+1-a->dim].value; if ( k < 1 ) { sprintf(errmsg,"Array index %d of array %s is %d. Indexes start at 1.", i+1,globals(node->op2.name_id)->name,k); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2534,errmsg,RECOVERABLE); } if ( k > a->sizes[i] ) { sprintf(errmsg,"Array index %d of array %s is %d; exceeds bound of %d.", i+1,globals(node->op2.name_id)->name,k,a->sizes[i]); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2535,errmsg,RECOVERABLE); } } for ( i = 1, offset = (int)stacktop[1-a->dim].value-1 ; i < a->dim ; i++ ) { offset *= a->sizes[i]; offset += (int)stacktop[i+1-a->dim].value-1; /* 1-based indexing */ } stacktop -= a->dim; lvalue = ((char *)a) + a->datastart + offset*a->itemsize; switch ( a->datatype ) { case REAL_TYPE: value = *(REAL*)(lvalue); break; case INTEGER_TYPE: value = *(int*)(lvalue); break; case UINT_TYPE: value = *(unsigned int*)(lvalue); break; case CHAR_TYPE: value = *(char*)(lvalue); break; case UCHAR_TYPE: value = *(unsigned char*)(lvalue); break; case SHORT_TYPE: value = *(short int*)(lvalue); break; case USHORT_TYPE: value = *(unsigned short int*)(lvalue); break; case LONG_TYPE: value = *(long*)(lvalue); break; case ULONG_TYPE: value = *(unsigned long*)(lvalue); break; case PTR_TYPE: value = (unsigned long)*(char**)(lvalue); break; case VERTEX_TYPE: case EDGE_TYPE: case FACET_TYPE: case BODY_TYPE: case FACETEDGE_TYPE: case ELEMENTID_TYPE: value = (int)*(element_id*)(lvalue); break; default: value = *(int*)(lvalue); break; } (++stacktop)->value = value; FIRST=0.0; SECOND = 0.0; } break; case BACKQUOTE_START_: { struct expnode bqnode = *ex; bqnode.start = node; bqnode.root = node+node->op1.skipsize; bqnode.locals = localbase; eval(&bqnode,params,q_id,NULL); node += node->op1.skipsize-1; } break; case BACKQUOTE_END_ : break; /* just a placeholder */ case ACOMMANDEXPR_: /* backquoted command at start of expression */ break; case SET_GLOBAL_: { struct global *g = globals(node->op1.name_id); int i,j; g->value.real = stacktop->value; if ( g->attr.varstuff.gradhess == NULL ) g->attr.varstuff.gradhess = (REAL*)mycalloc(MAXCOORD*(MAXCOORD+1), sizeof(REAL)); for ( i = 0 ; i < pcount ; i++ ) g->attr.varstuff.gradhess[i] = stacktop->deriv[i]; for ( i = 0 ; i < pcount ; i++ ) for ( j = 0 ; j < pcount ; j++ ) (g->attr.varstuff.gradhess+MAXCOORD)[i*MAXCOORD+j] = stacktop->second[i][j]; stacktop--; break; } case REPLACECONST: zero_seconds(pcount,stacktop); stacktop->value = node->op1.real; break; case COND_TEST_: if ( (stacktop--)->value == 0. ) { /* jump */ node += node->op1.skipsize; } break; case COND_EXPR_: /* did first command, so skip second */ node += node->op1.skipsize; break; case COND_ELSE_: break; case MAXIMUM_: stacktop--; if (stacktop[0].value < stacktop[1].value) stacktop[0] = stacktop[1]; break; case MINIMUM_: stacktop--; if (stacktop[0].value > stacktop[1].value) stacktop[0] = stacktop[1]; break; case TOGGLEVALUE: (++stacktop)->value = (REAL)get_toggle_value(node->op1.toggle_state); zero_seconds(pcount,stacktop); break; case SIZEOF_: (++stacktop)->value = (REAL)EXTRAS(node->op2.eltype)[node->op1.extranum].array_spec.datacount; zero_seconds(pcount,stacktop); break; case PUSHCONST: case PUSHPI: case PUSHE: (++stacktop)->value = node->op1.real; zero_seconds(pcount,stacktop); break; case PUSHG: (++stacktop)->value = web.gravflag ? web.grav_const : 0.0; zero_seconds(pcount,stacktop); break; case PUSHGLOBAL_: case PUSHADJUSTABLE: { struct global *g = globals(node->op1.name_id); int i,j; if ( g->flags & FILE_VALUES ) (++stacktop)->value = g->value.file.values[int_val]; else if ( g->flags & STRINGVAL ) (++stacktop)->value = 0.0; else (++stacktop)->value = g->value.real; if ( g->attr.varstuff.gradhess ) { for ( i = 0 ; i < pcount ; i++ ) stacktop->deriv[i] = g->attr.varstuff.gradhess[i]; for ( i = 0 ; i < pcount ; i++ ) for ( j = 0 ; j < pcount ; j++ ) stacktop->second[i][j] = (g->attr.varstuff.gradhess+MAXCOORD)[i*MAXCOORD+j]; } else zero_seconds(pcount,stacktop); } break; case PUSHPARAM: { int i,j; (++stacktop)->value = params[node->op1.coordnum]; for ( i = 0 ; i < pcount ; i++ ) if ( i == node->op1.coordnum ) stacktop->deriv[i] = 1.0; else stacktop->deriv[i] = 0.0; SECOND = 0.0; break; } case PARAM_: { int i,j; if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; (++stacktop)->value = get_param(id)[node->op2.coordnum]; for ( i = 0 ; i < SDIM ; i++ ) if ( i == node->op2.coordnum ) stacktop->deriv[i] = 1.0; else stacktop->deriv[i] = 0.0; SECOND = 0; break; } case PUSHQPRESSURE_: (++stacktop)->value = GEN_QUANT(node->op1.quant_id)->pressure; zero_seconds(pcount,stacktop); break; case PUSHQTARGET_: (++stacktop)->value = GEN_QUANT(node->op1.quant_id)->target; zero_seconds(pcount,stacktop); break; case PUSHQMODULUS_: (++stacktop)->value = GEN_QUANT(node->op1.quant_id)->modulus; zero_seconds(pcount,stacktop); break; case PUSHQTOLERANCE_: (++stacktop)->value = GEN_QUANT(node->op1.quant_id)->tolerance; zero_seconds(pcount,stacktop); break; case PUSHMMODULUS_: (++stacktop)->value = METH_INSTANCE(node->op1.meth_id)->modulus; zero_seconds(pcount,stacktop); break; case PUSHQVALUE_: { struct gen_quant *q = GEN_QUANT(node->op1.quant_id); if ((q->timestamptimestampflags&(Q_INFO|Q_ENERGY|Q_FIXED)); (++stacktop)->value = q->value; zero_seconds(pcount,stacktop); } break; case PUSHMVALUE_: { struct method_instance *mi = METH_INSTANCE(node->op1.meth_id); int i,j; if ( mi->flags & Q_COMPOUND ) { if ( compound_hess_flag == CH_GRADS ) { if ( quantity_function_sparse_flag ) { /* saving up second partials of quantity wrt methods */ (++stacktop)->value = mi->value; FIRST = 0.0; stacktop->deriv[mi->quant_index] = 1.0; SECOND = 0.0; } else /* old dense way */ { volgrad *vgptri = get_vertex_vgrad(comp_quant_vi); volgrad *vgptrj = get_vertex_vgrad(comp_quant_vj); for ( ; vgptri ; vgptri = vgptri->chain ) if ( vgptri->qnum == mi->self_id ) break; for ( ; vgptrj ; vgptrj = vgptrj->chain ) if ( vgptrj->qnum == mi->self_id ) break; (++stacktop)->value = mi->value; for ( i = 0 ; i < SDIM ; i++ ) { stacktop->deriv[i] = (vgptri ? vgptri->grad[i] : 0.0); stacktop->deriv[i+SDIM] = (vgptrj ? vgptrj->grad[i] : 0.0); } SECOND = 0.0; } } if ( compound_hess_flag == CH_HESS ) { (++stacktop)->value = mi->value; FIRST = 0.0; if ( mi->stamp == comp_quant_stamp ) for ( i = 0 ; i < pcount ; i++ ) for ( j = 0 ; j < pcount ; j++ ) stacktop->second[i][j] = mi->hess[comp_quant_vertexi][comp_quant_vertexj][i][j]; else SECOND = 0.0; } } else { if ((mi->timestamptimestampvalue = mi->value; zero_seconds(pcount,stacktop); } } break; case PUSHQVOLCONST_: (++stacktop)->value = GEN_QUANT(node->op1.quant_id)->volconst; zero_seconds(pcount,stacktop); break; case PUSH_NAMED_QUANTITY: case PUSH_METHOD_INSTANCE_: (++stacktop)->value = (REAL)node->op1.quant_id; zero_seconds(pcount,stacktop); break; case GET_INTERNAL_: (++stacktop)->value = get_internal_variable(node->op1.name_id); zero_seconds(pcount,stacktop); break; case DYNAMIC_LOAD_FUNC_: (*node->op1.funcptr)(FUNC_SECOND,params,(struct dstack*)(++stacktop)); break; case USERFUNC: { MAT2D(tmp,MAXCOORD,MAXCOORD); int i,j; stacktop++; stacktop->value = (*userfunc_seconds[node->op1.userfunc])(params,stacktop->deriv,tmp); SECOND = tmp[i][j]; } break; case INDEXED_ELEMENT_: { int ord = (int)((stacktop--)->value); id = get_ordinal_id(node->op1.eltype,abs(ord)-1); if ( !valid_id(id) ) { sprintf(errmsg,"%s index %d is not valid.\n", typenames[node->op1.eltype],ord); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1201,errmsg,RECOVERABLE); } if ( ord < 0 ) invert(id); *(element_id*)get_localp(node->op2.localnum) = id; break; } case QUALIFIED_ATTRIBUTE: break; /* just a no-op in execution */ /*need logical expressions for conditional expressions */ case GT_: stacktop--; stacktop[0].value = (REAL)(stacktop[0].value > stacktop[1].value); zero_seconds(pcount,stacktop); break; case LT_: stacktop--; stacktop[0].value = (REAL)(stacktop[0].value < stacktop[1].value); zero_seconds(pcount,stacktop); break; case LE_: stacktop--; stacktop[0].value = (REAL)(stacktop[0].value <= stacktop[1].value); zero_seconds(pcount,stacktop); break; case GE_: stacktop--; stacktop[0].value = (REAL)(stacktop[0].value >= stacktop[1].value); zero_seconds(pcount,stacktop); break; case NE_: stacktop--; stacktop[0].value = (REAL)(stacktop[0].value != stacktop[1].value); zero_seconds(pcount,stacktop); break; case EQ_: stacktop--; stacktop[0].value = (REAL)(stacktop[0].value == stacktop[1].value); zero_seconds(pcount,stacktop); break; case AND_: /* short-circuit */ if ( stacktop->value == 0.0 ) { (++stacktop)->value = 0.0; zero_seconds(pcount,stacktop); node += node->op1.skipsize; } break; case CONJUNCTION_END: /* short-circuiting results in second arg being answer */ stacktop--; *stacktop = stacktop[1]; if ( stacktop->value != 0.0 ) stacktop->value = 1.0; break; case OR_: /* short-circuit */ if ( stacktop->value != 0.0 ) { (++stacktop)->value = 1.0; zero_seconds(pcount,stacktop); node += node->op1.skipsize; } break; case NOT_: stacktop[0].value = (REAL)(!stacktop[0].value); zero_seconds(pcount,stacktop); break; case PLUS: { int i,j; stacktop--; stacktop[0].value += stacktop[1].value; FIRST += stacktop[1].deriv[i]; SECOND += stacktop[1].second[i][j]; break; } case MINUS: case EQUATE: { int i,j; stacktop--; stacktop[0].value -= stacktop[1].value; FIRST -= stacktop[1].deriv[i]; SECOND -= stacktop[1].second[i][j]; break; } case TIMES: { int i,j; stacktop--; SECOND = stacktop[0].second[i][j]*stacktop[1].value + stacktop[0].deriv[i]*stacktop[1].deriv[j] + stacktop[0].deriv[j]*stacktop[1].deriv[i] + stacktop[0].value*stacktop[1].second[i][j]; FIRST = stacktop[1].value*stacktop[0].deriv[i] + stacktop[0].value*stacktop[1].deriv[i]; stacktop[0].value *= stacktop[1].value; break; } case DIVIDE: { int i,j; stacktop--; if ( stacktop[1].value == 0.0 ) { sprintf(errmsg,"Division by zero.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1011,errmsg,RECOVERABLE); } SECOND = (stacktop[0].second[i][j]*stacktop[1].value - stacktop[0].deriv[i]*stacktop[1].deriv[j] - stacktop[0].deriv[j]*stacktop[1].deriv[i] - stacktop[0].value*stacktop[1].second[i][j] + 2*stacktop[0].value*stacktop[1].deriv[i] *stacktop[1].deriv[j]/stacktop[1].value) /stacktop[1].value/stacktop[1].value; FIRST = (stacktop[1].value*stacktop[0].deriv[i] - stacktop[0].value*stacktop[1].deriv[i]) /stacktop[1].value/stacktop[1].value; stacktop[0].value /= stacktop[1].value; break; } case REALMOD: { int i,j; stacktop--; if ( stacktop[1].value == 0.0 ) { sprintf(errmsg,"Modulus base 0.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1012,errmsg,RECOVERABLE); } y = floor(stacktop[0].value/stacktop[1].value); /* integer part */ FIRST = stacktop[0].deriv[i] - y*stacktop[1].deriv[i]; SECOND = stacktop[0].second[i][j] - y*stacktop[1].second[i][j]; stacktop[0].value -= y*stacktop[1].value; break; } case IMOD_: stacktop--; if ( stacktop[1].value == 0.0 ) { sprintf(errmsg,"Modulus base 0.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1013,errmsg,RECOVERABLE); } stacktop[0].value = floor(stacktop[0].value) - floor(floor(stacktop[0].value)/floor(stacktop[1].value)) *floor(stacktop[1].value); zero_seconds(pcount,stacktop); break; case IDIV_: stacktop--; if ( (int)stacktop[1].value == 0 ) { sprintf(errmsg,"Division by zero.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1014,errmsg,RECOVERABLE); } stacktop[0].value = (REAL)((int)(stacktop[0].value)/(int)(stacktop[1].value)); zero_seconds(pcount,stacktop); break; case INTPOW: if ( node->op1.intpow == 0 ) { stacktop->value = 1.; zero_seconds(pcount,stacktop); } else if ( node->op1.intpow == 1 ) { /* no action necessary */ } else { REAL f1,f2=1.0; int i,j; x = stacktop->value; if ( node->op1.intpow > 1 ) /* get n-2 power first */ for ( n = 1 ; n < node->op1.intpow-1 ; n++ ) f2 *= x; else if ( node->op1.intpow < 0 ) { if ( x == 0.0 ) { sprintf(errmsg,"Negative power zero.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1015,errmsg,RECOVERABLE); } for ( f2 = 1/x, n = 0 ; n <= -node->op1.intpow ; n++ ) f2 /= x; } f1 = f2*x; stacktop->value = f1*x; SECOND = node->op1.intpow*(node->op1.intpow-1)*f2 *stacktop->deriv[i]*stacktop->deriv[j] + node->op1.intpow*f1*stacktop->second[i][j]; FIRST = node->op1.intpow*f1*stacktop->deriv[i]; } break; case POW: stacktop--; x = stacktop[0].value; y = stacktop[1].value; if ( x == 0.0 ) { stacktop->value = 0.0; if ( y > 2.0 ) { int i,j; FIRST = 0.0; SECOND = 0.0; } else if ( y < 2.0 ) { sprintf(errmsg,"Negative power (%f) of zero in second derivative.\n", (DOUBLE)(y-2)); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2005,errmsg,RECOVERABLE); } } else if ( (x < 0) && ( (REAL)(int)y != y ) ) { sprintf(errmsg,"Nonintegral power (%f) of negative number.\n",(double)y); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2006,errmsg,RECOVERABLE); } else { int i,j; stacktop->value = pow(x,y); SECOND = ((log(fabs(x))*stacktop[1].deriv[i]+y/x*stacktop[0].deriv[i]) *(log(fabs(x))*stacktop[1].deriv[j]+y/x*stacktop[0].deriv[j]) +log(fabs(x))*stacktop->second[i][j] + stacktop[0].deriv[i]* stacktop[1].deriv[j]/x + stacktop[0].deriv[j]* stacktop[1].deriv[i]/x + stacktop[0].deriv[i]* stacktop[0].deriv[j]*y/x/x + stacktop[0].second[i][j]*y/x)*stacktop->value; FIRST = (log(fabs(x))*stacktop[1].deriv[i] + y/x*stacktop[0].deriv[i]) *stacktop->value; } break; /* This case causes a 2-min slowdown in VisualStudio.NET opt compile time*/ case ATAN2_: { REAL denom; int i,j; stacktop--; y = stacktop[0].value; x = stacktop[1].value; stacktop->value = atan2(y,x); denom = x*x + y*y; SECOND = (x*stacktop[0].second[i][j] +stacktop[1].deriv[j]*stacktop[0].deriv[i] -stacktop[0].deriv[j]*stacktop[1].deriv[i] -y*stacktop[1].second[i][j])/denom - 2*(x*stacktop[0].deriv[i]-y*stacktop[1].deriv[i]) *(x*stacktop[1].deriv[j]+y*stacktop[0].deriv[j])/denom/denom; FIRST = (x*stacktop[0].deriv[i]-y*stacktop[1].deriv[i])/denom; break; } case SQR: { int i,j; SECOND = 2*(stacktop->deriv[i]*stacktop->deriv[j] + stacktop->value*stacktop->second[i][j]); FIRST *= 2*stacktop->value; stacktop->value *= stacktop->value; break; } case SQRT: { int i,j; if ( stacktop->value < 0.0 ) { sprintf(errmsg,"Square root of negative number.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2567,errmsg,RECOVERABLE); } x = sqrt(stacktop->value); if ( x == 0.0 ) { SECOND = 0.0; FIRST = 0.0;} else { SECOND = (stacktop->second[i][j]/2 - stacktop->deriv[i]* stacktop->deriv[j]/stacktop->value/4)/x; FIRST /= 2*x; } stacktop->value = x; break; } case CEIL_: { int i,j; stacktop->value = ceil(stacktop->value); FIRST = 0; SECOND = 0; break; } case FLOOR_: { int i,j; stacktop->value = floor(stacktop->value); FIRST = 0; SECOND = 0; break; } case ABS: if ( stacktop->value < 0.0 ) { int i,j; FIRST *= -1 ; SECOND *= -1; stacktop->value = -stacktop->value; } break; case SIN: { int i,j; x = cos(stacktop->value); y = sin(stacktop->value); SECOND = -y*stacktop->deriv[i]*stacktop->deriv[j] + x*stacktop->second[i][j]; FIRST *= x; stacktop->value = y; break; } case COS: { int i,j; x = cos(stacktop->value); y = sin(stacktop->value); SECOND = -x*stacktop->deriv[i]*stacktop->deriv[j] - y*stacktop->second[i][j]; FIRST *= -y; stacktop->value = x; break; } case ATAN: { int i,j; x = (1 + stacktop->value*stacktop->value); SECOND = -2*stacktop->value/x/x*stacktop->deriv[i] *stacktop->deriv[j] +stacktop->second[i][j]/x; FIRST /= x; stacktop->value = atan(stacktop->value); break ; } case EXP: { int i,j; stacktop->value = exp(stacktop->value); SECOND = (stacktop->deriv[i]*stacktop->deriv[j] + stacktop->second[i][j])*stacktop->value; FIRST *= stacktop->value; break; } case LOG: { int i,j; if ( stacktop->value <= 0.0 ) { sprintf(errmsg,"Log of nonpositive number.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2497,errmsg,RECOVERABLE); } SECOND = (stacktop->second[i][j] - stacktop->deriv[i]*stacktop->deriv[j]/stacktop->value) /stacktop->value; FIRST /= stacktop->value; stacktop->value = log(stacktop->value) ; break; } case ASIN: { int i,j; if ( fabs(stacktop->value) >= 1.0 ) { sprintf(errmsg,"Asin argument magnitude greater than 1.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2570,errmsg,RECOVERABLE); } x = 1 - stacktop->value*stacktop->value; y = sqrt(x); SECOND = stacktop->second[i][j]/y + stacktop->deriv[i]*stacktop->deriv[j]*stacktop->value/x/y; FIRST /= y; stacktop->value = asin(stacktop->value); break ; } case ACOS: { int i,j; if ( fabs(stacktop->value) >= 1.0 ) { sprintf(errmsg,"Acos argument magnitude greater than 1.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2571,errmsg,RECOVERABLE); } x = 1 - stacktop->value*stacktop->value; y = sqrt(x); SECOND = -stacktop->second[i][j]/y - stacktop->deriv[i]*stacktop->deriv[j]*stacktop->value/x/y; FIRST /= -y; stacktop->value = acos(stacktop->value); break; } case TAN: { int i,j; stacktop->value = tan(stacktop->value); x = 1+stacktop->value*stacktop->value; /* sec^2 */ SECOND = x*stacktop->second[i][j] + 2*x*stacktop->value*stacktop->deriv[i]*stacktop->deriv[j]; FIRST *= x; break; } case SINH: { int i,j; y = exp(stacktop->value); x = (y+1/y)/2; y = (y-1/y)/2; SECOND = y*stacktop->deriv[i]*stacktop->deriv[j] + x*stacktop->second[i][j]; FIRST *= x; stacktop->value = y; break ; } case COSH: { int i,j; y = exp(stacktop->value); x = (y-1/y)/2; y = (y+1/y)/2; SECOND = y*stacktop->deriv[i]*stacktop->deriv[j] + x*stacktop->second[i][j]; FIRST *= x; stacktop->value = y; break; } case TANH: { int i,j; y = exp(stacktop->value); x = (y-1/y)/2; y = (y+1/y)/2; SECOND = -2*x/y/y/y*stacktop->deriv[i]*stacktop->deriv[j] + 1/y/y*stacktop->second[i][j]; FIRST /= y*y ; stacktop->value = x/y; break; } case ASINH: { int i,j; y = 1/sqrt(1+stacktop->value*stacktop->value); x = -stacktop->value*y*y*y; SECOND = x*stacktop->deriv[i]*stacktop->deriv[j] + y*stacktop->second[i][j]; FIRST *= y; stacktop->value = log(stacktop->value + sqrt(stacktop->value*stacktop->value + 1)); break; } case ACOSH: { int i,j; if ( stacktop->value <= 1.0 ) { sprintf(errmsg,"Acosh argument less than 1.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2572,errmsg,RECOVERABLE); } y = 1/sqrt(stacktop->value*stacktop->value - 1); x = -stacktop->value*y*y*y; SECOND = x*stacktop->deriv[i]*stacktop->deriv[j] + y*stacktop->second[i][j]; FIRST *= y; stacktop->value = 2*log(sqrt(stacktop->value + 1) + sqrt(stacktop->value - 1)) - log(2.0); break; } case ATANH: { int i,j; if ( fabs(stacktop->value) >= 1 ) { sprintf(errmsg,"Atanh argument magnitude greater than 1.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2573,errmsg,RECOVERABLE); } y = 1/(1 - stacktop->value*stacktop->value); x = 2*stacktop->value*y*y; SECOND = x*stacktop->deriv[i]*stacktop->deriv[j] + y*stacktop->second[i][j]; FIRST *= y; stacktop->value = log(stacktop->value + 1)/2 - log(stacktop->value - 1)/2; break; } case CHS: { int i,j; SECOND *= -1; FIRST = -stacktop->deriv[i]; stacktop->value = -stacktop->value; break; } case INV: { int i,j; if ( stacktop->value == 0.0 ) { sprintf(errmsg,"Division by zero.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2574,errmsg,RECOVERABLE); } SECOND = (2*stacktop->deriv[i]*stacktop->deriv[i]/stacktop->value - stacktop->second[i][j])/stacktop->value/stacktop->value; FIRST /= stacktop->value*stacktop->value; stacktop->value = 1/stacktop->value; break; } case ELLIPTICK: { int i,j; x = stacktop->value; stacktop->value = ellipticK(x); SECOND = ellipticKdmdm(x)*stacktop->deriv[i]*stacktop->deriv[j] + ellipticKdm(x)*stacktop->second[i][j]; FIRST = ellipticKdm(x)*stacktop->deriv[i]; break; } case ELLIPTICE: { int i,j; x = stacktop->value; stacktop->value = ellipticE(x); SECOND = ellipticEdmdm(x)*stacktop->deriv[i]*stacktop->deriv[j] + ellipticEdm(x)*stacktop->second[i][j]; FIRST = ellipticEdm(x)*stacktop->deriv[i]; break; } case INCOMPLETE_ELLIPTICF: { REAL phi,m,val,dm,dphi,ddm,ddphi,dmdphi; int i,j; stacktop--; phi = stacktop[0].value; m = stacktop[1].value; val = incompleteEllipticFseconds(phi,m, &dphi,&dm,&ddphi,&ddm,&dmdphi); stacktop->value = val; SECOND = ddphi*stacktop[0].deriv[i]*stacktop[0].deriv[j] + ddm*stacktop[1].deriv[i]*stacktop[1].deriv[j] + dmdphi*(stacktop[0].deriv[i]*stacktop[1].deriv[j] + stacktop[1].deriv[i]*stacktop[0].deriv[j]) + dphi*stacktop[0].second[i][j] + dm*stacktop[1].second[i][j]; FIRST = dphi*stacktop[0].deriv[i]+dm*stacktop[1].deriv[i]; break; } case INCOMPLETE_ELLIPTICE: { REAL phi,m,val,dm,dphi,ddm,ddphi,dmdphi; int i,j; stacktop--; phi = stacktop[0].value; m = stacktop[1].value; val = incompleteEllipticEseconds(phi,m, &dphi,&dm,&ddphi,&ddm,&dmdphi); stacktop->value = val; SECOND = ddphi*stacktop[0].deriv[i]*stacktop[0].deriv[j] + ddm*stacktop[1].deriv[i]*stacktop[1].deriv[j] + dmdphi*(stacktop[0].deriv[i]*stacktop[1].deriv[j] + stacktop[1].deriv[i]*stacktop[0].deriv[j]) + dphi*stacktop[0].second[i][j] + dm*stacktop[1].second[i][j]; FIRST = dphi*stacktop[0].deriv[i]+dm*stacktop[1].deriv[i]; break; } /* attribute values */ case GET_SQ_MEAN_CURV_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; stacktop->value = vertex_sq_mean_curvature(id); zero_seconds(pcount,stacktop); break; case GET_MEANCURV_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; stacktop->value = vertex_mean_curvature(id); zero_seconds(pcount,stacktop); break; case LENGTH_: case GET_LENGTH_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; calc_edge(id); ++stacktop; stacktop->value = get_edge_length(id); zero_seconds(pcount,stacktop); break; case GET_DIHEDRAL_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; if ( id_type(id) == EDGE ) stacktop->value = dihedral(id); else if ( id_type(id) == VERTEX ) stacktop->value = vertex_angle(id); else stacktop->value = 0.0; zero_seconds(pcount,stacktop); break; case VALENCE_: case GET_VALENCE_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch ( id_type(id) ) { case VERTEX: stacktop->value = (REAL)get_vertex_evalence(id); break; case EDGE: stacktop->value = (REAL)get_edge_valence(id); break; case FACET: stacktop->value = (REAL)get_facet_valence(id); break; case BODY: stacktop->value = (REAL)get_body_valence(id); break; } zero_seconds(pcount,stacktop); break; case GET_EDGE_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = (REAL)(ordinal(get_fe_edge(id))+1); zero_seconds(pcount,stacktop); break; case GET_FACET_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = (REAL)(ordinal(get_fe_facet(id))+1); zero_seconds(pcount,stacktop); break; case AREA_: case GET_AREA_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = get_facet_area(id); zero_seconds(pcount,stacktop); break; case GET_WRAP_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = (REAL)get_edge_wrap(id); zero_seconds(pcount,stacktop); break; case GET_PRESSURE_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch ( id_type(id) ) { case BODY: stacktop->value = get_body_pressure(id); break; default: sprintf(errmsg,"Pressure only for bodies.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1003,errmsg,RECOVERABLE); } zero_seconds(pcount,stacktop); break; case GET_USERATTR_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = user_attribute(id); zero_seconds(pcount,stacktop); break; case GET_QUANTITY_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = quantity_attribute(id,node->op2.quant_id); zero_seconds(pcount,stacktop); break; case GET_INSTANCE_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = instance_attribute(id,node->op2.meth_id); zero_seconds(pcount,stacktop); break; case GET_PHASE_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch ( id_type(id) ) { case FACET: stacktop->value = (REAL)get_f_phase(id); break; case BODY: stacktop->value = (REAL)get_b_phase(id); break; default: sprintf(errmsg,"Phase of wrong type element.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1010,errmsg,RECOVERABLE); } zero_seconds(pcount,stacktop); break; case DENSITY_: case GET_DENSITY_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch ( id_type(id) ) { case EDGE: stacktop->value = get_edge_density(id); break; case FACET: stacktop->value = get_facet_density(id); break; case BODY: stacktop->value = get_body_density(id); break; default: sprintf(errmsg,"Density of wrong type element.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1018,errmsg,RECOVERABLE); } zero_seconds(pcount,stacktop); break; case GET_STAR_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch ( id_type(id) ) { case VERTEX: stacktop->value = get_vertex_star(id); break; case EDGE: stacktop->value = get_edge_star(id); break; default: stacktop->value = 0.0; break; } zero_seconds(pcount,stacktop); break; case VOLUME_: case GET_VOLUME_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = get_body_volume(id); zero_seconds(pcount,stacktop); break; case GET_VOLCONST_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; (++stacktop)->value = get_body_volconst(id); zero_seconds(pcount,stacktop); break; case GET_TARGET_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; (++stacktop)->value = get_body_fixvol(id); zero_seconds(pcount,stacktop); break; case GET_MPI_TASK_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; (++stacktop)->value = id_task(id); break; case ID_: case GET_ID_: case GET_OID_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; if ( (node->type == GET_OID_) && inverted(id) ) stacktop->value = -(REAL)(ordinal(id)+1); else stacktop->value = (REAL)(ordinal(id)+1); zero_seconds(pcount,stacktop); break; case ORIGINAL_: case GET_ORIGINAL_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = valid_id(id)?(REAL)ordinal(get_original(id))+1:0; zero_seconds(pcount,stacktop); break; case GET_COLOR_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch ( id_type(id) ) { case EDGE: stacktop->value = (REAL)get_edge_color(id); break; case FACET: stacktop->value = (REAL)get_facet_color(id); break; default: stacktop->value = 0.0; } zero_seconds(pcount,stacktop); break; case GET_FRONTCOLOR_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch ( id_type(id) ) { case FACET: stacktop->value = (REAL)get_facet_frontcolor(id); break; default: stacktop->value = 0.0; } zero_seconds(pcount,stacktop); break; case GET_BACKCOLOR_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch ( id_type(id) ) { case FACET: stacktop->value = (REAL)get_facet_backcolor(id); break; default: stacktop->value = 0.0; } zero_seconds(pcount,stacktop); break; case GET_FRONTBODY_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch ( id_type(id) ) { case BODY: stacktop->value = (REAL)ordinal(get_facet_body(id))+1; break; default: stacktop->value = 0.0; } zero_seconds(pcount,stacktop); break; case GET_BACKBODY_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch ( id_type(id) ) { case BODY: stacktop->value = (REAL)ordinal(get_facet_body(inverse_id(id)))+ 1; break; default: stacktop->value = 0.0; } zero_seconds(pcount,stacktop); break; case TAG_: case GET_TAG_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = (REAL)get_tag(id); zero_seconds(pcount,stacktop); break; case BARE_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = (get_attr(id) & BARE_NAKED) ? 1.0 : 0.0; zero_seconds(pcount,stacktop); break; case GET_MIDV_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = ordinal(get_edge_midv(id)) + 1.; zero_seconds(pcount,stacktop); break; case FIXED_: case GET_FIXED_: if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; stacktop->value = (get_attr(id) & FIXED) ? 1.0 : 0.0; zero_seconds(pcount,stacktop); break; case GET_EXTRA_ATTR_: { struct extra *ext; int spot,k; int i; n = node->op3.extranum; if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ext = EXTRAS(node->op2.eltype) + n; /* get index */ spot = 0; for ( k = 0 ; k < ext->array_spec.dim ; k++ ) { int j = (int)(stacktop[-ext->array_spec.dim+k+1].value); spot *= ext->array_spec.sizes[k]; if ( (j < 1) || (j > ext->array_spec.sizes[k]) ) { sprintf(errmsg, "Attribute %s index %d is %d; maximum is %d (in %s).\n", ext->name,k+1,j,ext->array_spec.sizes[k],ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2578,errmsg,RECOVERABLE); } spot += (int)(stacktop[-ext->array_spec.dim+k+1].value) - 1; } stacktop -= ext->array_spec.dim; if ( id_type(id) != node->op2.eltype ) { if ( (id_type(id)==EDGE) && (node->op2.eltype==VERTEX) && params ) { ext = EXTRAS(VERTEX) + n; (++stacktop)->value = interp_edge_attribute(id,ext,spot,(int)params[2*SDIM]); FIRST = 0.0; zero_seconds(pcount,stacktop); break; } else if ( (id_type(id)==FACET) && (node->op2.eltype==VERTEX) && params ) { ext = EXTRAS(VERTEX) + n; (++stacktop)->value = interp_facet_attribute(id,ext,spot,(int)params[2*SDIM]); FIRST = 0.0; zero_seconds(pcount,stacktop); break; } else { sprintf(errmsg, "Attribute %s is %s attribute, not %s attribute (in %s).\n", EXTRAS(node->op2.eltype)[n].name, typenames[node->op2.eltype], typenames[id_type(id)],ex->name); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(2538,errmsg,RECOVERABLE); } } if ( ext->code.start ) { int oldflag = autorecalc_flag; autorecalc_flag = 0; eval(&ext->code,NULL,id,NULL); /* side-effect fills in values */ autorecalc_flag = oldflag; } switch ( ext->type ) { case REAL_TYPE: (++stacktop)->value = ((REAL*)get_extra(id,n))[spot]; break; case INTEGER_TYPE: case CONSTRAINT_TYPE: case BOUNDARY_TYPE: case QUANTITY_TYPE: case INSTANCE_TYPE: case PROCEDURE_TYPE: (++stacktop)->value = (REAL)((int*)get_extra(id,n))[spot]; break; case UINT_TYPE: (++stacktop)->value = (REAL)((unsigned int*)get_extra(id,n))[spot]; break; case USHORT_TYPE: (++stacktop)->value = (REAL)((unsigned short*)get_extra(id,n))[spot]; break; case SHORT_TYPE: (++stacktop)->value = (REAL)((short*)get_extra(id,n))[spot]; break; case ULONG_TYPE: (++stacktop)->value = (REAL)((unsigned long*)get_extra(id,n))[spot]; break; case LONG_TYPE: (++stacktop)->value = (REAL)((long*)get_extra(id,n))[spot]; break; case UCHAR_TYPE: (++stacktop)->value = (REAL)((unsigned char*)get_extra(id,n))[spot]; break; case CHAR_TYPE: (++stacktop)->value = (REAL)((char*)get_extra(id,n))[spot]; break; case ELEMENTID_TYPE: case VERTEX_TYPE: case EDGE_TYPE: case FACET_TYPE: case BODY_TYPE: case FACETEDGE_TYPE: (++stacktop)->value = (REAL)((element_id*)get_extra(id,n))[spot]; break; } FIRST = 0.0; zero_seconds(pcount,stacktop); break; } case ON_CONSTRAINT_: { int testcon = (int)(stacktop--)->value; if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch(id_type(id)) { case VERTEX: stacktop->value = (REAL)v_on_constraint(id,testcon); break; case EDGE : stacktop->value = (REAL)e_on_constraint(id,testcon); break; case FACET : stacktop->value = (REAL)f_on_constraint(id,testcon); break; default: sprintf(errmsg, "Can't do constraints on this type element.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1052,errmsg,RECOVERABLE); } } zero_seconds(pcount,stacktop); break; case HIT_CONSTRAINT_: { int testcon = (int)(stacktop--)->value; if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch(id_type(id)) { case VERTEX: stacktop->value = (REAL)get_v_constraint_status(id,testcon); break; default: sprintf(errmsg, "Can do hit_constraints only on vertices.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1053,errmsg,RECOVERABLE); } } zero_seconds(pcount,stacktop); break; case ON_BOUNDARY_: { struct boundary *b=NULL; int testb = (int)(stacktop--)->value; if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; ++stacktop; switch(id_type(id)) { case VERTEX: b = get_boundary(id); break; case EDGE : b = get_edge_boundary(id); break; case FACET : b = get_facet_boundary(id); break; default: sprintf(errmsg, "Can't do boundary on this type element.\n"); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1054,errmsg,RECOVERABLE); } stacktop->value = (b == web.boundaries+testb) ? 1.0 : 0.0; } zero_seconds(pcount,stacktop); break; /* whole-array syntax */ case ARRAYIDENT_: /* push datastart for array */ { struct global *glvalue = globals(node->op2.name_id); struct array *alvalue = glvalue->attr.arrayptr; if ( glvalue->flags & FIXED_SIZE_ARRAY ) *(REAL**)(++stacktop) = (REAL*)get_localp(glvalue->attr.arrayptr->datastart); else *(char**)(++stacktop) = (char*)alvalue + alvalue->datastart; break; } case ATTRIB_LVALUE_: /* push datastart for attribute array */ { element_id id; n = node->op2.name_id & GLOBMASK; /* attribute number */ if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; *(char**)(++stacktop) = (char*)get_extra(id,n); } break; case ARRAY_VERTEX_NORMAL_: case ARRAY_EDGE_VECTOR_: case ARRAY_FACET_NORMAL_: { element_id id; int i; REAL *datastart = (REAL*)get_localp(node->op3.localnum); *(REAL**)(++stacktop) = datastart; if ( node->flags & IS_RVALUE ) { if ( node->op1.localnum ) id = *(element_id*)get_localp(node->op1.localnum); else id = q_id; switch ( node->type ) { case ARRAY_VERTEX_NORMAL_: { MAT2D(normal,MAXCOORD,MAXCOORD); REAL mag; int normcount; normcount = new_calc_vertex_normal(id,normal); project_vertex_normals(id,normal,normcount); mag = sqrt(SDIM_dot(normal[0],normal[0])); if ( mag == 0.0 ) mag = 1; for ( i = 0 ; i < SDIM ; i++ ) datastart[i] = normal[0][i]/mag; break; } case ARRAY_EDGE_VECTOR_: get_edge_side(id,datastart); break; case ARRAY_FACET_NORMAL_: get_facet_normal(id,datastart); break; } } } break; case ARRAY_LVALUE_INDEXED_: break; case ARRAY_RVALUE_ : break; case DOT_: /* dot product */ { struct array *a,*b; int name1 = node->op2.name_id; int name2 = node->op3.name_id; REAL *datastart1,*datastart2; REAL sum; int i,count; a = get_name_arrayptr(name1,NULL,localbase); b = get_name_arrayptr(name2,NULL,localbase); count = (a->datacount < b->datacount) ? a->datacount : b->datacount; datastart1 = *(REAL**)(stacktop--); datastart2 = *(REAL**)(stacktop--); for ( sum = 0.0, i = 0 ; i < count ; i++ ) sum += datastart1[i]*datastart2[i]; (++stacktop)->value = sum; zero_seconds(pcount,stacktop); break; } case ARRAY_EVAL_: /* rexpr: arraylvalue indexset */ { /* use info on stack to push value of array element. stack: datastart index-values -> rexpr */ struct array *a; REAL value=0.0; int i,offset; void *lvalue; char *datastart; a = get_name_arrayptr(node->op2.name_id,NULL,NULL); for ( i = 0 ; i < a->dim ; i++ ) { int k = (int)stacktop[i+1-a->dim].value; if ( k < 1 ) { sprintf(errmsg, "Array index %d of array %s is %d. Indexes start at 1.\n", i+1,get_name_name(node->op2.name_id,localbase),k); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3010,errmsg,RECOVERABLE); } if ( k > a->sizes[i] ) { sprintf(errmsg,"Array index %d of array %s is %d; exceeds bound of %d.\n", i+1,get_name_name(node->op2.name_id,localbase),k,a->sizes[i]); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(3009,errmsg,RECOVERABLE); } } for ( i = 1, offset = (int)stacktop[1-a->dim].value-1 ; i < a->dim ; i++ ) { offset *= a->sizes[i]; offset += (int)stacktop[i+1-a->dim].value-1; /* 1-based indexing */ } stacktop -= a->dim; datastart = *(char**)(stacktop--); lvalue = datastart + offset*a->itemsize; switch ( a->datatype ) { case REAL_TYPE: value = *(REAL*)(lvalue); break; case INTEGER_TYPE: value = *(int*)(lvalue); break; case UINT_TYPE: value = *(unsigned int*)(lvalue); break; case SHORT_TYPE: value = *(short int*)(lvalue); break; case USHORT_TYPE: value = *(unsigned short int*)(lvalue); break; case LONG_TYPE: value = *(long int*)(lvalue); break; case ULONG_TYPE: value = *(unsigned long int*)(lvalue); break; case CHAR_TYPE: value = *(char*)(lvalue); break; case UCHAR_TYPE: value = *(unsigned char*)(lvalue); break; case PTR_TYPE: value = (unsigned long int)*(char**)(lvalue); break; case VERTEX_TYPE: case EDGE_TYPE: case FACET_TYPE: case BODY_TYPE: case FACETEDGE_TYPE: case ELEMENTID_TYPE: break; default: value = *(int*)(lvalue); break; } (++stacktop)->value = value; zero_seconds(pcount,stacktop); break; } /* end whole-array syntax */ default: sprintf(errmsg,"Bad expression eval_second() node type: %s.", tokname(node->type)); sprintf(errmsg+strlen(errmsg),"(source file %s, line %d)\n", file_names[node->file_no],node->line_no); kb_error(1016,errmsg,RECOVERABLE); break; } if ( node == ex->root ) break; } *fval = stacktop->value; for ( n = 0 ; n < pcount ; n++ ) { partials[n] = stacktop->deriv[n]; for ( m = 0 ; m < pcount ; m++ ) seconds[n][m] = stacktop->second[n][m]; } return; } /* end eval_second() */ evolver-2.30c.dfsg/src/quantity.h0000644000175300017530000007272311410765113017257 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /****************************************************************** * * File: quantity.h * * Purpose: Header file for general quantities. */ struct qgrad { REAL *g;}; struct qhess { REAL **gg;}; #ifndef MAXMETH #define MAXMETH 4 #endif #ifdef NOPROTO #define QINT #define QINFO #else #define QINT int #define QINFO struct qinfo * typedef struct method_instance *MIPTR; #endif extern int gen_quant_list_max; extern int gen_quant_free_left; extern int meth_inst_list_max; #define MAXVCOUNT 100 /* structure for passing info to element methods */ struct qinfo { element_id id; int flags; /* see below */ int method; /* instance number; unreliable to use pointer here */ int vcount; /* number of vertices involved */ vertex_id v[MAXVCOUNT]; /* vertex list */ REAL *x[MAXVCOUNT]; /* vertex coordinates */ WRAPTYPE wraps[MAXVCOUNT]; /* wraps of individual vertices */ REAL **xx; /* room if facet coords need unwrapping */ REAL **u; /* room for affine coords of vertices in torus model */ REAL **uu[3]; /* ptrs to edge vertex coords for torus volume */ REAL **ugrad; /* room for affine gradient in torus model */ REAL **uugrad[3]; /* ptrs to edge vertex grads for torus volume */ REAL ****uhess; /* room for affine hessian in torus model */ REAL ****uuhess[3]; /* ptrs to edge vertex hessians for torus volume */ REAL **gauss_pt; /* for gaussian integration */ int gauss_num; REAL ***sides; /* edge or facet sides, or tangents at gauss pts in quad */ REAL **ss; /* matrix of dot products of sides */ REAL normal[MAXCOORD]; /* facet normal */ REAL **grad; /* vertex gradients */ REAL ****hess; /* vertex hessians */ int axial_order; /* number of times vertex repeats around axial point */ struct linsys *S; /* linear system associated with hessian */ }; /* flags values */ #define INCOMPLETE_STAR 1 /* for type of initialization */ #define METHOD_VALUE 1767 #define METHOD_GRADIENT 4321 #define METHOD_HESSIAN 8763 typedef void (*INIT_METHOD)ARGS((QINT,MIPTR)); typedef REAL (*VALUE_METHOD)(QINFO); typedef REAL (*GRAD_METHOD)(QINFO); typedef REAL (*HESS_METHOD)(QINFO); typedef void (*CLEANUP_METHOD) ARGS((void)); /* structure for one quantity */ #define QNAMESIZE 128 struct gen_quant { char name[QNAMESIZE]; /* for identification */ int num; /* number in gen_quant list */ int flags; /* see defines below */ /* used for Q_ENERGY,Q_FIXED, Q_CONSERVED, or Q_INFO */ REAL target; /* for fixed quantities */ REAL value; /* current value */ REAL oldvalue; /* value for restore_coords() */ REAL abstotal; /* total abs val of elements, for constraint tol */ REAL modulus; /* overall multiplier */ REAL tolerance; /* for fixed quantity */ REAL pressure; /* Lagrange multiplier */ REAL oldpressure; /* for restore_coords() */ REAL volconst; /* additive constant, for torus volume */ body_id b_id; /* body this is volume of, if any */ int vol_number; /* equivalent body number for fixvol */ int fixnum; /* order in fixvol computations */ struct qgrad *grad; /* gradients at vertices */ struct qhess *hess; /* hessian on vertices and edges */ int vhead_index; /* index into vhead list for hessian */ int method_count; /* number of methods */ int *meth_inst; /* method instances contributing */ int meth_inst_space[MAXMETH]; /* in case of only a few instances */ struct expnode expr; /* for compound formula */ int next_compound; /* -1 for end of list */ long timestamp; /* when quantity last calculated */ }; #define Q_ENERGY 1 /* part of energy */ #define Q_FIXED 2 /* is a constraint */ #define Q_INFO 4 /* only for information */ #define Q_CONSERVED 8 /* eliminate degree of freedom, but don't evaluate */ #define Q_DOTHIS 0x0010 #define Q_RENORMALIZE 0x0020 /* adjust torus volume */ #define Q_COMPOUND 0x0100 /* quantity defined by compound formula */ #define STANDARD_QUANTITY 0x1000 #define DEFAULT_QUANTITY 0x2000 #define Q_PRESSURE_SET 0x8000 #define Q_REDUNDANT 0x10000 /* skip this quantity due to torus_filled */ #define TORUS_MODULO_MUNGE 0x40000 #define Q_DELETED 0x80000 #define Q_FORWARD_DEF 0x100000 /* structure for one quantity calculating method */ /* split so can use same method for many quantities, */ /* or several methods for one quantity */ struct gen_quant_method { char name[QNAMESIZE]; /* for easy user identification */ int type; /* type of element applies to */ int flags; int spec_flags; /* what needs to be specified in datafile */ INIT_METHOD init; /* initialization */ VALUE_METHOD value; /* function for value on element */ GRAD_METHOD gradient; /* function for gradient on element */ HESS_METHOD hessian; /* function for hessian on element */ CLEANUP_METHOD cleanup; /* clean up any allocated memory, etc. */ }; /* flags for needed element atributes */ /* (low bits used in global_meth_inst_flags for Q_ENERGY etc.) */ #define NEED_SIDE 0x10 #define NEED_NORMAL 0x20 #define NEED_WINGS 0x40 #define NEED_GAUSS 0x80 #define NEED_STAR 0x100 #define NEED_STRING_STAR 0x200 #define NEED_MARKED_WINGS 0x400 #define NEED_PART_STAR 0x800 #define NEED_FULL_STAR 0x1000 #define ALL_NEEDS 0xFFF0 /* flags bit for orientability */ #define ORIENTABLE_METHOD 0x10000 /*#define TORUS_MODULO_MUNGE 0x40000 also used in flags */ /* bits for spec_flags */ #define NOSPEC 0 #define SPEC_SCALAR 0x0001 #define SPEC_VECTOR 0x0002 #define SPEC_2FORM 0x0004 #define SPEC_EXTRADIM 0x0008 #define SPEC_USE_DENSITY 0x0010 #define SPEC_KVECTOR 0x0020 /* structure for instance of method */ #define MAXMEXPR ((MAXCOORD*MAXCOORD)/2) /* enough for 2-forms */ #define MNAMESIZE 128 struct method_instance { char name[MNAMESIZE]; int self_id; /* for use in vgrads */ int type; /* element type */ int flags; int gen_method; /* parent method */ int quant; /* quantity this is a part of */ int connum; /* constraint or bdry this may be associated with */ int quant_index; /* order of method in quantity list */ int global_low_rank; /* index in list of all instances used in low rank updates */ int vec_order; /* dimension of k-vector */ int elmodulus; /* number of extra attribute to use as element modulus */ struct expnode *expr[MAXMEXPR]; /* whatever interpreted expressions needed */ REAL modulus; /* adjustable multiplier */ REAL value; /* total value of this instance */ REAL oldvalue; /* value for restore_coords() */ REAL newvalue; /* accumulating value of this instance */ REAL abstotal; /* total of abs values of elements */ REAL value_addends[MAXADDENDS]; /* for binary tree addition */ #ifdef SHARED_MEMORY REAL procvalue[MAXPROCS]; /* separate multiprocessor values */ REAL procabstotal[MAXPROCS]; /* separate multiprocessor values */ #endif REAL stamp; /* for coordination with eval_all() and eval_sec() */ REAL **grad; /* at current vertex, for compound quants */ REAL ****hess; /* at current vertex, for compound quants */ REAL parameter_1; /* parameter particular to this instance */ vertex_id *vlist; /* for use in compound Hessian */ long timestamp; /* when value last calculated */ #ifdef PROF_START /* for profiling execution time */ int value_call_count; int grad_call_count; int hess_call_count; double value_elapsed_time; /* in seconds */ double grad_elapsed_time; double hess_elapsed_time; #endif /* struct method_instance *next; */ /* linked list */ }; /* flags, (avoid quantity flag bits) */ #define IMPLICIT_INSTANCE 0x200000 #define DEFAULT_INSTANCE 0x400000 #define METH_PARAMETER_1 0x800000 #define BODY_INSTANCE 0x1000000 #define IGNORE_CONSTR 0x2000000 #define FAKE_IMPLICIT 0x4000000 #define IGNORE_FIXED 0x8000000 #define ELEMENT_MODULUS_FLAG 0x10000000 #define USE_DENSITY 0x20000000 #define GLOBAL_INST 0x40000000 /* start for self_id, to distinguish from bodies and quantities */ #define METHBASE 0x10000 #define METH_INST ((struct method_instance *)(dymem + dy_meth_inst)) #define METH_INSTANCE(n) (meth_inst_list[abs(n)]) #define dy_meth_inst web.dy_meth_inst_w extern struct method_instance *Meth_inst; #define meth_inst_alloc web.meth_inst_alloc_w /* number allocated */ #define meth_inst_count web.meth_inst_count_w /* number defined */ #define LOW_INST 1 #define dy_gen_quants web.dy_gen_quants_w #define GEN_QUANT(n) (gen_quant_list[n]) extern struct gen_quant *Gen_quants; #define gen_quant_count web.gen_quant_count_w /* used */ #define gen_quant_alloc web.gen_quant_alloc_w /* allocated */ extern int compound_quant_list_head; /* -1 for end of list */ /* new system of general quantity allocation */ extern struct gen_quant *gen_quant_free; extern struct gen_quant **gen_quant_list; extern int gen_quant_free_left; /* new system of general instance allocation */ extern struct method_instance *meth_inst_free; extern struct method_instance **meth_inst_list; extern int meth_inst_free_left; /* for list of available methods */ extern struct gen_quant_method basic_gen_methods[]; /* global method instances, applying to every element of type */ #define MAXGLOBINST 100 #define global_meth_inst_flags web.global_meth_inst_flags_w #define global_meth_inst web.global_meth_inst_w /* lists */ #define global_meth_inst_count web.global_meth_inst_count_w /* flags telling which quantity calculations necessary */ /* flag set for Q_ENERGY,Q_FIXED, Q_CONSERVED, or Q_INFO if any element needs a quantity calculated */ #define quant_flags web.quant_flags_w void q_edge_setup ARGS((struct linsys *,QINFO,int)); void q_facet_setup ARGS((struct linsys *,QINFO,int)); void q_vertex_setup ARGS((struct linsys *,QINFO,int)); void q_body_setup ARGS((struct linsys *,QINFO,int)); void q_facetedge_setup ARGS((struct linsys *,QINFO,int)); extern void (*q_setup[NUMELEMENTS]) ARGS((struct linsys *,QINFO,int)); void q_edge_setup_q ARGS((QINFO,int)); void q_facet_setup_q ARGS((QINFO,int)); void q_vertex_setup_q ARGS((QINFO,int)); void q_body_setup_q ARGS((QINFO,int)); void q_edge_setup_lagrange ARGS((QINFO,int)); void q_facet_setup_lagrange ARGS((QINFO,int)); /******************************************************************** * Declarations of available methods. */ extern REAL null_q_value(QINFO); extern REAL null_q_grad(QINFO); extern REAL null_q_hess(QINFO); extern void q_edge_tension_init ARGS((QINT,struct method_instance*)); extern REAL q_edge_tension_value(QINFO); extern REAL q_edge_tension_gradient(QINFO); extern REAL q_edge_tension_hessian(QINFO); extern REAL edge_length_q_value(QINFO); extern REAL edge_length_q_grad(QINFO); extern REAL edge_length_q_hess(QINFO); extern REAL lagrange_edge_tension_value(QINFO); extern REAL lagrange_edge_tension_grad(QINFO); extern REAL lagrange_edge_tension_hess(QINFO); extern void circular_arc_length_init ARGS((QINT,struct method_instance*)); extern REAL circular_arc_length_value(QINFO); extern REAL circular_arc_length_grad(QINFO); extern REAL circular_arc_length_hess(QINFO); extern void circular_arc_area_init ARGS((QINT,struct method_instance*)); extern REAL circular_arc_area_value(QINFO); extern REAL circular_arc_area_grad(QINFO); extern REAL circular_arc_area_hess(QINFO); extern void spherical_arc_length_init ARGS((QINT,struct method_instance*)); extern REAL spherical_arc_length_value(QINFO); extern REAL spherical_arc_length_grad(QINFO); extern REAL spherical_arc_length_hess(QINFO); extern void spherical_arc_area_init ARGS((QINT,struct method_instance*)); extern REAL spherical_arc_area_n_value(QINFO); extern REAL spherical_arc_area_n_grad(QINFO); extern REAL spherical_arc_area_n_hess(QINFO); extern REAL spherical_arc_area_s_value(QINFO); extern REAL spherical_arc_area_s_grad(QINFO); extern REAL spherical_arc_area_s_hess(QINFO); extern REAL null_length_value(QINFO); extern REAL null_length_grad(QINFO); extern REAL null_length_hess(QINFO); extern REAL null_area_value(QINFO); extern REAL null_area_grad(QINFO); extern REAL null_area_hess(QINFO); extern REAL q_edge_area(QINFO); extern REAL q_edge_area_grad(QINFO); extern REAL q_edge_area_hess(QINFO); extern REAL q_edge_area_q(QINFO); extern REAL q_edge_area_q_grad(QINFO); extern REAL q_edge_area_q_hess(QINFO); extern REAL q_edge_area_lagrange(QINFO); extern REAL q_edge_area_lagrange_grad(QINFO); extern REAL q_edge_area_lagrange_hess(QINFO); extern REAL q_edge_torus_area(QINFO); extern REAL q_edge_torus_area_grad(QINFO); extern REAL q_edge_torus_area_hess(QINFO); extern REAL q_edge_torus_area_q(QINFO); extern REAL q_edge_torus_area_q_grad(QINFO); extern REAL q_edge_torus_area_q_hess(QINFO); extern REAL q_edge_torus_area_lagrange(QINFO); extern REAL q_edge_torus_area_lagrange_grad(QINFO); extern REAL q_edge_torus_area_lagrange_hess(QINFO); extern REAL gap_energy(QINFO); extern REAL gap_grads(QINFO); extern REAL dihedral_hooke_energy(QINFO); extern REAL dihedral_hooke_grad(QINFO); extern REAL dihedral_hooke_hess(QINFO); extern void wulff_method_init ARGS((QINT,struct method_instance*)); extern REAL facet_wulff_value(QINFO); extern REAL facet_wulff_grad(QINFO); extern REAL klein_length_method(QINFO); extern REAL klein_length_method_grad(QINFO); extern REAL klein_area_method(QINFO); extern REAL klein_area_method_grad(QINFO); extern void q_facet_tension_init ARGS((QINT,struct method_instance*)); extern REAL q_facet_tension_value(QINFO); extern REAL q_facet_tension_gradient(QINFO); extern REAL q_facet_tension_hessian(QINFO); extern void q_facet_tension_u_init ARGS((QINT,struct method_instance*)); extern REAL q_facet_tension_u_value(QINFO); extern REAL q_facet_tension_u_gradient(QINFO); extern REAL q_facet_tension_u_hessian(QINFO); extern REAL q_facet_tension_q(QINFO); extern REAL q_facet_tension_q_grad(QINFO); extern REAL q_facet_tension_q_hess(QINFO); extern REAL q_facet_tension_uq(QINFO); extern REAL q_facet_tension_uq_grad(QINFO); extern REAL q_facet_tension_uq_hess(QINFO); extern REAL lagrange_facet_tension_value(QINFO); extern REAL lagrange_facet_tension_grad(QINFO); extern REAL lagrange_facet_tension_hess(QINFO); extern void metric_area_init ARGS((QINT,struct method_instance*)); extern REAL metric_area_value(QINFO); extern REAL metric_area_grad(QINFO); extern REAL metric_area_hess(QINFO); extern REAL area_square_value(QINFO); extern REAL area_square_gradient(QINFO); extern void q_facet_volume_init ARGS((QINT,struct method_instance*)); extern REAL q_facet_volume(QINFO); extern REAL q_facet_volume_grad(QINFO); extern REAL q_facet_volume_hess(QINFO); extern REAL q_facet_volume_q(QINFO); extern REAL q_facet_volume_q_grad(QINFO); extern REAL q_facet_volume_q_hess(QINFO); extern REAL q_facet_torus_volume(QINFO); extern REAL q_facet_torus_volume_grad(QINFO); extern REAL q_facet_torus_volume_hess(QINFO); extern REAL q_facet_torus_volume_q(QINFO); extern REAL q_facet_torus_volume_q_grad(QINFO); extern REAL q_facet_torus_volume_q_hess(QINFO); extern REAL lagrange_facet_volume(QINFO); extern REAL lagrange_facet_volume_grad(QINFO); extern REAL lagrange_facet_volume_hess(QINFO); extern REAL q_facet_torus_volume_lagr(QINFO); extern REAL q_facet_torus_volume_lagr_grad(QINFO); extern REAL q_facet_torus_volume_lagr_hess(QINFO); extern void pos_area_hess_init ARGS((QINT,struct method_instance*)); extern REAL pos_area_hess(QINFO); extern void sobolev_area_init ARGS((QINT,struct method_instance*)); extern REAL sobolev_area_hess(QINFO); extern void dirichlet_area_init ARGS((QINT,struct method_instance*)); extern REAL dirichlet_area_hess(QINFO); extern void gauss_integral_init ARGS((QINT,struct method_instance*)); extern REAL gauss_int_gradient(QINFO); extern REAL gauss_int_energy(QINFO); extern void sqgauss_method_init ARGS((QINT,struct method_instance*)); extern REAL sqgauss_method_value(QINFO); extern REAL sqgauss_method_grad(QINFO); extern void levine_energy_init ARGS((QINT,struct method_instance*)); extern REAL levine_energy_value(QINFO); extern REAL levine_energy_grad(QINFO); extern void star_sqgauss_method_init ARGS((QINT,struct method_instance*)); extern REAL star_sqgauss_method_value(QINFO); extern REAL star_sqgauss_method_grad(QINFO); extern REAL star_sqgauss_method_hess(QINFO); extern void star_gauss_method_init ARGS((QINT,struct method_instance*)); extern REAL star_gauss_method_value(QINFO); extern REAL star_gauss_method_grad(QINFO); extern REAL star_gauss_method_hess(QINFO); extern void sqcurve_string_init ARGS((QINT,struct method_instance*)); extern REAL sqcurve_string_value(QINFO); extern REAL sqcurve_string_grad(QINFO); extern REAL sqcurve_string_hess(QINFO); extern void sqcurve_string_marked_init ARGS((QINT,struct method_instance*)); extern REAL curve_power; extern int curve_power_param; #define CURVE_POWER_NAME "curvature_power" extern void sqcurve2_string_init ARGS((QINT,struct method_instance*)); extern REAL sqcurve2_string_value(QINFO); extern REAL sqcurve2_string_grad(QINFO); extern REAL sqcurve2_string_hess(QINFO); extern void sqcurve3_string_init ARGS((QINT,struct method_instance*)); extern REAL sqcurve3_string_value(QINFO); extern REAL sqcurve3_string_grad(QINFO); extern REAL sqcurve3_string_hess(QINFO); extern void sq_mean_curv_cyl_init ARGS((QINT,struct method_instance*)); extern REAL sq_mean_curv_cyl_value(QINFO); extern REAL sq_mean_curv_cyl_grad(QINFO); extern REAL sq_mean_curv_cyl_hess(QINFO); extern void sq_gauss_curv_cyl_init ARGS((QINT,struct method_instance*)); extern REAL sq_gauss_curv_cyl_value(QINFO); extern REAL sq_gauss_curv_cyl_grad(QINFO); extern REAL sq_gauss_curv_cyl_hess(QINFO); extern void circle_willmore_init ARGS((QINT,struct method_instance*)); extern REAL circle_willmore_value(QINFO); extern REAL circle_willmore_grad(QINFO); extern REAL circle_willmore_hess(QINFO); extern void mean_int_init ARGS((QINT,struct method_instance*)); extern REAL mean_int_value(QINFO); extern REAL mean_int_gradient(QINFO); extern REAL mean_int_hessian(QINFO); extern void mean_int_a_init ARGS((QINT,struct method_instance*)); extern REAL mean_int_a_value(QINFO); extern REAL mean_int_a_gradient(QINFO); extern REAL mean_int_a_hessian(QINFO); extern REAL vertex_scalar_integral(QINFO); extern REAL vertex_scalar_integral_grad(QINFO); extern REAL vertex_scalar_integral_hess(QINFO); extern REAL edge_scalar_integral(QINFO); extern REAL edge_scalar_integral_grad(QINFO); extern REAL edge_scalar_integral_hess(QINFO); extern REAL edge_scalar_integral_q(QINFO); extern REAL edge_scalar_integral_q_grad(QINFO); extern REAL edge_scalar_integral_q_hess(QINFO); extern REAL edge_scalar_integral_lagr(QINFO); extern REAL edge_scalar_integral_lagr_grad(QINFO); extern REAL edge_scalar_integral_lagr_hess(QINFO); extern REAL edge_vector_integral(QINFO); extern REAL edge_vector_integral_grad(QINFO); extern REAL edge_vector_integral_hess(QINFO); extern REAL edge_vector_integral_q(QINFO); extern REAL edge_vector_integral_q_grad(QINFO); extern REAL edge_vector_integral_q_hess(QINFO); extern REAL edge_vector_integral_lagrange(QINFO); extern REAL edge_vector_integral_lagrange_grad(QINFO); extern REAL edge_vector_integral_lagrange_hess(QINFO); extern void edge_general_init ARGS((QINT,struct method_instance*)); extern REAL edge_general_value(QINFO); extern REAL edge_general_grad(QINFO); extern REAL edge_general_hess(QINFO); extern REAL edge_general_value_lagrange(QINFO); extern REAL edge_general_grad_lagrange(QINFO); extern REAL edge_general_hess_lagrange(QINFO); extern void facet_scalar_integral_init ARGS((QINT,struct method_instance*)); extern REAL facet_scalar_integral(QINFO); extern REAL facet_scalar_integral_grad(QINFO); extern REAL facet_scalar_integral_hess(QINFO); extern REAL facet_scalar_integral_q(QINFO); extern REAL facet_scalar_integral_q_grad(QINFO); extern REAL facet_scalar_integral_q_hess(QINFO); extern REAL facet_scalar_integral_lagr(QINFO); extern REAL facet_scalar_integral_lagr_grad(QINFO); extern REAL facet_scalar_integral_lagr_hess(QINFO); extern void facet_vector_integral_init ARGS((QINT,struct method_instance*)); extern REAL facet_vector_integral(QINFO); extern REAL facet_vector_integral_grad(QINFO); extern REAL facet_vector_integral_hess(QINFO); extern REAL lagrange_vector_integral(QINFO); extern REAL lagrange_vector_integral_grad(QINFO); extern REAL lagrange_vector_integral_hess(QINFO); extern void simplex_vector_integral_init ARGS((QINT,struct method_instance*)); extern REAL simplex_vector_integral(QINFO); extern REAL simplex_vector_integral_grad(QINFO); extern REAL simplex_vector_integral_hess(QINFO); extern void simplex_k_vector_integral_init ARGS((QINT,struct method_instance*)); extern REAL simplex_k_vector_integral(QINFO); extern REAL simplex_k_vector_integral_grad(QINFO); extern REAL simplex_k_vector_integral_hess(QINFO); extern REAL lagrange_k_vector_integral(QINFO); extern REAL lagrange_k_vector_integral_grad(QINFO); extern REAL lagrange_k_vector_integral_hess(QINFO); extern REAL facet_vector_integral_q(QINFO); extern REAL facet_vector_integral_q_grad(QINFO); extern REAL facet_vector_integral_q_hess(QINFO); extern void facet_2form_integral_init ARGS((QINT,struct method_instance*)); extern REAL facet_2form_integral(QINFO); extern REAL facet_2form_integral_grad(QINFO); extern REAL facet_2form_integral_hess(QINFO); extern REAL facet_2form_integral_lagrange(QINFO); extern REAL facet_2form_integral_lagrange_grad(QINFO); extern REAL facet_2form_integral_lagrange_hess(QINFO); extern void facet_2form_sq_integral_init ARGS((QINT,struct method_instance*)); extern REAL facet_2form_sq_integral(QINFO); extern REAL facet_2form_sq_integral_grad(QINFO); extern void facet_general_init ARGS((QINT,struct method_instance*)); extern REAL facet_general_value(QINFO); extern REAL facet_general_grad(QINFO); extern REAL facet_general_hess(QINFO); extern REAL facet_general_value_lagr(QINFO); extern REAL facet_general_grad_lagr(QINFO); extern REAL facet_general_hess_lagr(QINFO); extern void stress_integral_init ARGS((QINT,struct method_instance*)); extern REAL stress_integral(QINFO); extern REAL stress_integral_grad(QINFO); extern void sqcurve_method_init ARGS((QINT,struct method_instance*)); extern REAL sqcurve_method_value(QINFO); extern REAL sqcurve_method_grad(QINFO); extern void sqcurve_method_cleanup ARGS((void)); extern void star_sqcurve_method_init ARGS((QINT,struct method_instance*)); extern REAL star_sqcurve_method_value(QINFO); extern REAL star_sqcurve_method_grad(QINFO); extern REAL star_sqcurve_method_hess(QINFO); extern void laplacian_mean_curvature_init ARGS((QINT,struct method_instance*)); extern REAL laplacian_mean_curvature_value(QINFO); extern REAL laplacian_mean_curvature_grad(QINFO); extern void stokes2d_init ARGS((QINT,struct method_instance*)); extern REAL stokes2d_value(QINFO); extern REAL stokes2d_grad(QINFO); extern REAL stokes2d_hess(QINFO); extern REAL stokes2d_laplacian(QINFO); extern void hooke_energy_init ARGS((QINT,struct method_instance*)); extern REAL hooke_energy(QINFO); extern REAL hooke_energy_gradient(QINFO); extern REAL hooke_energy_hessian(QINFO); extern void hooke2_energy_init ARGS((QINT,struct method_instance*)); extern REAL hooke2_energy(QINFO); extern REAL hooke2_energy_gradient(QINFO); extern REAL hooke2_energy_hessian(QINFO); extern void hooke3_energy_init ARGS((QINT,struct method_instance*)); extern REAL hooke3_energy(QINFO); extern REAL hooke3_energy_gradient(QINFO); extern REAL hooke3_energy_hessian(QINFO); extern void local_hooke_init ARGS((QINT,struct method_instance*)); extern REAL local_hooke(QINFO); extern REAL local_hooke_gradient(QINFO); extern void linear_elastic_init ARGS((QINT,struct method_instance*)); extern REAL linear_elastic_energy(QINFO); extern REAL linear_elastic_gradient(QINFO); extern REAL linear_elastic_hessian(QINFO); extern void general_linear_elastic_init ARGS((QINT,struct method_instance*)); extern REAL general_linear_elastic_energy(QINFO); extern REAL general_linear_elastic_gradient(QINFO); extern REAL general_linear_elastic_hessian(QINFO); extern void linear_elastic_B_init ARGS((QINT,struct method_instance*)); extern REAL linear_elastic_B_energy(QINFO); extern REAL linear_elastic_B_gradient(QINFO); extern REAL linear_elastic_B_hessian(QINFO); extern void relaxed_elastic_init ARGS((QINT,struct method_instance*)); extern REAL relaxed_elastic_energy(QINFO); extern REAL relaxed_elastic_gradient(QINFO); extern REAL relaxed_elastic_hessian(QINFO); extern REAL relaxed_elastic1_energy(QINFO); extern REAL relaxed_elastic1_gradient(QINFO); extern REAL relaxed_elastic1_hessian(QINFO); extern REAL relaxed_elastic2_energy(QINFO); extern REAL relaxed_elastic2_gradient(QINFO); extern REAL relaxed_elastic2_hessian(QINFO); extern void relaxed_elastic_A_init ARGS((QINT,struct method_instance*)); extern REAL relaxed_elastic_A_energy(QINFO); extern REAL relaxed_elastic_A_gradient(QINFO); extern REAL relaxed_elastic_A_hessian(QINFO); extern REAL relaxed_elastic1_A_energy(QINFO); extern REAL relaxed_elastic1_A_gradient(QINFO); extern REAL relaxed_elastic1_A_hessian(QINFO); extern REAL relaxed_elastic2_A_energy(QINFO); extern REAL relaxed_elastic2_A_gradient(QINFO); extern REAL relaxed_elastic2_A_hessian(QINFO); extern void dirichlet_elastic_init ARGS((QINT,struct method_instance*)); extern REAL dirichlet_elastic_energy(QINFO); extern REAL dirichlet_elastic_gradient(QINFO); extern REAL dirichlet_elastic_hessian(QINFO); extern void SVK_init ARGS((QINT,struct method_instance*)); extern REAL SVK_energy(QINFO); extern REAL SVK_gradient(QINFO); extern REAL SVK_hessian(QINFO); extern void Neo_Hookean_init ARGS((QINT,struct method_instance*)); extern REAL Neo_Hookean_energy(QINFO); extern REAL Neo_Hookean_gradient(QINFO); extern REAL Neo_Hookean_hessian(QINFO); extern void knot_energy_init ARGS((QINT,struct method_instance*)); extern void knot_power_init ARGS((QINT,struct method_instance*)); extern REAL knot_energy(QINFO); extern REAL knot_energy_gradient(QINFO); extern REAL knot_energy_hessian(QINFO); extern REAL knot_thickness(QINFO); extern REAL knot_thickness2(QINFO); extern REAL knot_thickness_0(QINFO); extern REAL knot_thickness_0_gradient(QINFO); extern REAL knot_thickness_p(QINFO); extern REAL knot_thickness_p_gradient(QINFO); extern REAL knot_thickness_p2(QINFO); extern REAL knot_thickness_p2_gradient(QINFO); extern REAL knot_local_thickness(QINFO); extern void charge_gradient_init ARGS((QINT,struct method_instance*)); extern REAL charge_gradient(QINFO); extern REAL charge_gradient_gradient(QINFO); extern void uniform_knot_energy_init ARGS((QINT,struct method_instance*)); extern REAL uniform_knot_energy(QINFO); extern REAL uniform_knot_energy_gradient(QINFO); extern REAL edge_edge_knot_energy(QINFO); extern REAL edge_edge_knot_energy_gradient(QINFO); extern REAL edge_min_knot_energy(QINFO); extern REAL uniform_normalization(QINFO); extern REAL uniform_binormalization(QINFO); extern REAL edge_normalization(QINFO); extern REAL simon_normalization(QINFO); extern void facet_knot_energy_init ARGS((QINT,struct method_instance*)); extern REAL facet_knot_energy(QINFO); extern REAL facet_knot_energy_gradient(QINFO); extern void bi_surface_init ARGS((QINT,struct method_instance*)); extern REAL bi_surface_energy(QINFO); extern REAL bi_surface_gradient(QINFO); extern void facet_knot_energy_fix_init ARGS((QINT,struct method_instance*)); extern REAL facet_knot_energy_fix(QINFO); extern REAL facet_knot_energy_fix_gradient(QINFO); extern REAL buck_knot_energy(QINFO); extern REAL buck_knot_energy_gradient(QINFO); extern REAL proj_knot_energy(QINFO); extern REAL proj_knot_energy_gradient(QINFO); extern REAL sin_knot_energy(QINFO); extern REAL sin_knot_energy_gradient(QINFO); extern REAL circle_knot_energy(QINFO); extern REAL circle_knot_energy_gradient(QINFO); extern REAL average_crossing(QINFO); extern REAL writhe(QINFO); extern REAL writhe_gradient(QINFO); extern REAL twist(QINFO); extern void sphere_knot_energy_init ARGS((QINT,struct method_instance*)); extern REAL sphere_knot_energy(QINFO); extern REAL sphere_knot_energy_gradient(QINFO); extern REAL johndust_energy(QINFO); extern REAL johndust_gradient(QINFO); extern void curvature_forces_init ARGS((QINT,struct method_instance*)); extern REAL curvature_forces_energy(QINFO); extern REAL curvature_forces(QINFO); /* extern INIT_METHOD ackerman_init; */ extern void ackerman_init ARGS((QINT,MIPTR)); extern REAL ackerman_energy(QINFO); extern REAL ackerman_forces(QINFO); extern void carter_energy_init ARGS((QINT,struct method_instance*)); extern REAL carter_energy(QINFO); extern REAL carter_energy_gradient(QINFO); extern void full_gravity_init ARGS((QINT,struct method_instance*)); extern void gravity_init ARGS((QINT,struct method_instance*)); extern REAL gravity_energy(QINFO); extern REAL gravity_grads(QINFO); extern REAL gravity_hessian(QINFO); extern void string_gravity_init ARGS((QINT,struct method_instance*)); extern REAL string_gravity_energy(QINFO); extern REAL string_gravity_grads(QINFO); extern REAL string_gravity_hessian(QINFO); extern void curvature_binormal_init ARGS((QINT,struct method_instance*)); extern REAL curvature_binormal_energy(QINFO); extern REAL curvature_binormal_force(QINFO); extern void ddd_gamma_sq_init ARGS((QINT,struct method_instance*)); extern REAL ddd_gamma_sq_energy(QINFO); extern REAL ddd_gamma_sq_gradient(QINFO); extern REAL true_average_crossing(QINFO); extern REAL true_writhe(QINFO); extern REAL spherical_area_value(QINFO); extern REAL spherical_area_grad(QINFO); evolver-2.30c.dfsg/src/tokname.c0000644000175300017530000000143311410765113017020 0ustar hazelscthazelsct /* to be appended to end of ytab.c so it knows about yytoks */ /****************************************************************** * * function: tokname() * * purpose: find name of given token number. Uses yytoks[] * list used by debugging. * */ char *tokname(toknum) int toknum; { char *name; #ifndef NO_YACC_DEBUG #ifdef YYBISON name = yytname[YYTRANSLATE(toknum)]; return name; #else int yy_i; for ( yy_i = 0; yytoks[yy_i].t_val >= 0; yy_i++ ) if ( yytoks[yy_i].t_val == toknum ) return yytoks[yy_i].t_name; /* unfound */ name = " "; sprintf(name,"%4d (unnamed)",toknum); return name; #endif #else /* unfound */ name = " "; sprintf(name,"%4d (unnamed)",toknum); return name; #endif } evolver-2.30c.dfsg/src/teix.c0000644000175300017530000011640411410765113016340 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************** * * File: tiex.c * * Contents: Miscellaneous stuff: * curvature forces * Laplacian of mean curvature * multigrid */ #include "include.h" /******************************************************************** * * Function: curvature_forces() * * Purpose: Forces as function of curvatures. * Finds mean and gaussian curvature at each vertex, * and normal. * */ struct teix_gvert *tgverts; void curvature_forces_init(mode,mi) int mode; struct method_instance *mi; { vertex_id v[3]; edge_id e_id; facet_id f_id; facetedge_id fe; int fixcount; int i,j; REAL area; REAL side[3][MAXCOORD]; REAL ss[3]; REAL st[3]; REAL angle; REAL c; struct teix_gvert *gv,*vc[3]; REAL normal[MAXCOORD]; if ( tgverts ) myfree((char*)tgverts); tgverts = (struct teix_gvert*)mycalloc(web.skel[VERTEX].max_ord+1, sizeof(struct teix_gvert)); /* accumulate angles and stuff around vertices */ FOR_ALL_FACETS(f_id) { fe = get_facet_fe(f_id); fixcount = 0; for ( i = 0; i < FACET_VERTS ; i++,fe=get_next_edge(fe) ) { e_id = get_fe_edge(fe); get_edge_side(e_id,side[i]); v[i] = get_edge_tailv(e_id); if ( get_vattr(v[i]) & (FIXED|BOUNDARY) ) fixcount++; } for ( i = 0 ; i < FACET_VERTS ; i++ ) { ss[i] = SDIM_dot(side[i],side[i]); st[i] = SDIM_dot(side[i],side[(i+2)%3]); } area = 0.5*sqrt(ss[0]*ss[1]-st[1]*st[1]); cross_prod(side[0],side[1],normal); for ( i = 0 ; i < FACET_VERTS ; i++ ) { REAL dd = ss[i]*ss[(i+2)%3]; gv = tgverts + loc_ordinal(v[i]); if ( dd > 0.0 ) { c = -st[i]/sqrt(ss[i]*ss[(i+2)%3]); if ( fabs(c) <= 1.0 ) { angle = acos(c); gv->angle += angle; } } gv->area += area/3; gv->star_area += area/(3-fixcount); for ( j = 0 ; j < SDIM ; j++ ) gv->normal[j] += normal[j]; } /* for mean curvature */ for ( i = 0 ; i < FACET_VERTS ; i++ ) vc[i] = tgverts + loc_ordinal(v[i]); for ( i = 0 ; i < SDIM ; i++ ) { vc[0]->force[i] += (ss[1]*side[2][i]-st[2]*side[1][i])/4/area; vc[1]->force[i] += (ss[2]*side[0][i]-st[0]*side[2][i])/4/area; vc[2]->force[i] += (ss[0]*side[1][i]-st[1]*side[0][i])/4/area; } } } REAL curvature_forces_energy(v_info) struct qinfo *v_info; { return 0.0; /* fake it */ } REAL curvature_forces(v_info) struct qinfo *v_info; { REAL K; /* gauss curvature of vertex */ REAL H; /* mean curvature of vertex, average of sectional curvatures */ struct teix_gvert *vg = tgverts + loc_ordinal(v_info->id); REAL norm; REAL f; /* function of H and K */ int i; if ( get_vattr(v_info->id) & (FIXED|BOUNDARY) ) return 0.0; /* normalize normal */ norm = sqrt(SDIM_dot(vg->normal,vg->normal)); for ( i = 0 ; i < SDIM ; i++ ) vg->normal[i] /= norm; K = (2*M_PI - vg->angle)/vg->area; H = SDIM_dot(vg->force,vg->normal)/2/vg->area; /* fix here for function of H and K */ f = (H>0.0) ? exp(K) : -exp(K); /* to detect misorientation */ /* end of fixing */ /* force as multiple of normal */ for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] = -f*vg->normal[i]; return 0.0; /* fake energy */ } /**************************************************************************** * * Function: vertex_mean_curvature() * * Purpose: calculate mean curvature at a vertex, as force divided by area, * area being volume gradient. Actually, does dot product, etc. */ REAL vertex_mean_curvature(v_id) vertex_id v_id; { REAL meanc = 0.0; REAL force[MAXCOORD],projf[MAXCOORD]; REAL area = 0.0,farea; REAL s1[MAXCOORD],s2[MAXCOORD]; REAL vnorm[MAXCOORD]; int i; for ( i = 0 ; i < MAXCOORD ; i++ ) force[i] = 0.0; if ( web.representation == STRING ) { edge_id start_e, e_id; REAL len; e_id = start_e = get_vertex_edge(v_id); if ( !valid_id(e_id) ) return 0.0; do { REAL s11; get_edge_side(e_id,s1); s11 = dot(s1,s1,SDIM); len = sqrt(s11); for ( i = 0 ; i < SDIM ; i++ ) force[i] += 1/len*s1[i]; e_id = get_next_tail_edge(e_id); } while ( !equal_element(start_e,e_id) ); force_project(force,v_id,projf); } else if ( web.representation == SOAPFILM ) { facetedge_id fe_id,start_fe; fe_id = start_fe = get_vertex_first_facet(v_id); /* really returns fe */ if ( !valid_id(fe_id) ) return 0.0; do { facetedge_id fe; REAL s11,s12,s22; fe = fe_id; while ( !equal_id(v_id,get_fe_headv(fe)) ) fe = get_next_edge(fe); get_edge_side(get_fe_edge(fe),s1); get_edge_side(get_fe_edge(get_prev_edge(fe)),s2); s11 = dot(s1,s1,SDIM); s12 = dot(s1,s2,SDIM); s22 = dot(s2,s2,SDIM); farea = sqrt(s11*s22-s12*s12); for ( i = 0 ; i < SDIM ; i++ ) force[i] += 1/farea*(s1[i]*s22 - s12*s2[i])/2; fe_id = get_next_vertex_facet(v_id,fe_id); } while ( !equal_element(start_fe,fe_id) ); force_project(force,v_id,projf); } area = calc_vertex_normal(v_id,NULLID,vnorm); /* norm comes back unit */ if ( area == 0.0 ) meanc = 0.0; else if ( web.representation == SOAPFILM ) meanc = dot(force,vnorm,SDIM)/area*3/2.0; else meanc = dot(force,vnorm,SDIM)/area*2; return meanc; } /* end vertex_mean_curvature */ /**************************************************************************** * * Function: sd_vertex_mean_curvature() * * Purpose: calculate mean curvature at a vertex, as force divided by area, * area being volume gradient. Actually, does dot product, etc. * * Special version for surface diffusion: takes edge/facet tensions into * account, and uses total-volume grad as vertex normal. */ REAL sd_vertex_mean_curvature(v_id) vertex_id v_id; { REAL meanc = 0.0; REAL force[MAXCOORD],projf[MAXCOORD]; REAL area = 0.0,farea; REAL s1[MAXCOORD],s2[MAXCOORD]; REAL vnorm[MAXCOORD]; int i; for ( i = 0 ; i < MAXCOORD ; i++ ) force[i] = 0.0; if ( web.representation == STRING ) { edge_id start_e, e_id; REAL len; e_id = start_e = get_vertex_edge(v_id); if ( !valid_id(e_id) ) return 0.0; do { REAL tension = get_edge_density(e_id); REAL s11; get_edge_side(e_id,s1); s11 = dot(s1,s1,SDIM); len = sqrt(s11); for ( i = 0 ; i < SDIM ; i++ ) force[i] -= tension/len*s1[i]; e_id = get_next_tail_edge(e_id); } while ( !equal_element(start_e,e_id) ); force_project(force,v_id,projf); } else if ( web.representation == SOAPFILM ) { facetedge_id fe_id,start_fe; fe_id = start_fe = get_vertex_first_facet(v_id); if ( !valid_id(fe_id) ) return 0.0; do { facetedge_id fe; REAL s11,s12,s22; REAL tension = get_facet_density(get_fe_facet(fe_id)); fe = fe_id; for ( i = 0 ; i < FACET_VERTS ; i++ ) { if ( equal_id(v_id,get_fe_headv(fe)) ) break; fe = get_next_edge(fe); } if ( i == FACET_VERTS ) { sprintf(errmsg,"Internal error: sd_vertex_mean_curvature at vertex %s.\n", ELNAME(v_id)); kb_error(3745,errmsg,RECOVERABLE); } get_edge_side(get_fe_edge(fe),s1); get_edge_side(get_fe_edge(get_prev_edge(fe)),s2); s11 = dot(s1,s1,SDIM); s12 = dot(s1,s2,SDIM); s22 = dot(s2,s2,SDIM); farea = sqrt(s11*s22-s12*s12); for ( i = 0 ; i < SDIM ; i++ ) force[i] += tension/farea*(s1[i]*s22 - s12*s2[i])/2; fe_id = get_next_vertex_facet(v_id,fe_id); } while ( !equal_element(start_fe,fe_id) ); force_project(force,v_id,projf); } area = vertex_total_vol_grad(v_id,vnorm); /* norm comes back unit */ if ( area == 0.0 ) meanc = 0.0; else if ( web.representation == SOAPFILM ) meanc = dot(force,vnorm,SDIM)/area*3/2.0; else meanc = dot(force,vnorm,SDIM)/area*2; return meanc; } /* end sd_vertex_mean_curvature() */ /****************************************************************************** Laplacian of mean curvature method, for surface diffusion. The curvature at each vertex is calculated as a scalar, in the same way as for area_normalized area gradient, i.e. area gradient dotted with volume gradient, divided by the star area. The Laplacian is calculated as described by physical diffusionn. String model: The gradient of curvature along each edge. The flow of "stuff" along each edge is proportional to the gradient. There is a resultant net flux into the "star" of a vertex. The flux divided by the volume gradient of the vertex gives the velocity, i.e. the Laplacian of curvature. Soapfilm model: The three curvatures at the vertices define a curvature gradient vector. There results a flux of stuff into the star of the vertex. This is divided by the volume gradient. ******************************************************************************/ /************************************************************************ * * function: laplacian_mean_curvature_init(); * * purpose: Calculate area gradient at each vertex, for later use by * individual vertex method. */ #define LMC_MC_ATTR_NAME "lmc_mean_curvature" int lmc_mc_attr; #define LMC_MOBILITY_ATTR_NAME "lmc_mobility" int lmc_mobility_attr; void laplacian_mean_curvature_init(mode,mi) int mode; struct method_instance *mi; { int one = 1; vertex_id v_id; if ( (web.representation != STRING) && (web.representation != SOAPFILM) ) kb_error(4085,"Can do laplacian_mean_curvature only in STRING and SOAPFILM models.\n", RECOVERABLE); if ( lmc_mc_attr < 0 ) lmc_mc_attr = add_attribute(VERTEX,LMC_MC_ATTR_NAME,REAL_TYPE,0,&one,0,NULL); /* create mobility attribute, if user didn't */ if ( web.representation == STRING ) { edge_id e_id; lmc_mobility_attr = find_attribute(EDGE,LMC_MOBILITY_ATTR_NAME); if ( lmc_mobility_attr < 0 ) { lmc_mobility_attr = add_attribute(EDGE,LMC_MOBILITY_ATTR_NAME,REAL_TYPE,0,&one,0,NULL); FOR_ALL_EDGES(e_id) *(REAL*)get_extra(e_id,lmc_mobility_attr) = 1.0; } } else { facet_id f_id; lmc_mobility_attr = find_attribute(FACET,LMC_MOBILITY_ATTR_NAME); if ( lmc_mobility_attr < 0 ) { lmc_mobility_attr = add_attribute(FACET,LMC_MOBILITY_ATTR_NAME,REAL_TYPE,0,&one,0,NULL); FOR_ALL_FACETS(f_id) *(REAL*)get_extra(f_id,lmc_mobility_attr) = 1.0; } } /* mean curvatures at vertices */ FOR_ALL_VERTICES(v_id) *(REAL*)get_extra(v_id,lmc_mc_attr) = sd_vertex_mean_curvature(v_id); } REAL laplacian_mean_curvature_value(v_info) struct qinfo *v_info; { REAL energy = 0.0; /* Try integral of square gradient of mean curvature as energy, even though it is not exact. */ if ( web.representation == STRING ) { REAL h1,h; edge_id e_id,start_e; h = *(REAL*)get_extra(v_info->v[0],lmc_mc_attr); e_id = start_e = get_vertex_edge(v_info->v[0]); do { REAL len1; vertex_id v1; len1 = get_edge_length(e_id); v1 = get_edge_headv(e_id); h1 = *(REAL*)get_extra(v1,lmc_mc_attr); energy += (h-h1)*(h-h1)/len1/2; /* /2 since double counting */ e_id = get_next_tail_edge(e_id); } while ( !equal_id(e_id,start_e) ); } if ( web.representation == SOAPFILM ) { REAL h1,h,h2; REAL side1[MAXCOORD],side2[MAXCOORD]; edge_id edge1,edge2; vertex_id v1,v2; facet_id f_id; facetedge_id fe,start_fe; REAL mobility; h = *(REAL*)get_extra(v_info->v[0],lmc_mc_attr); fe = start_fe = get_vertex_first_facet(v_info->v[0]); do { REAL s11,s12,s22,det; f_id = get_fe_facet(fe); mobility = *(REAL*)get_f_extra(f_id,lmc_mobility_attr); if ( mobility != 0.0 ) { edge1 = get_fe_edge(fe); get_edge_side(edge1,side1); v1 = get_edge_headv(edge1); h1 = *(REAL*)get_extra(v1,lmc_mc_attr); edge2 = get_fe_edge(inverse_id(get_prev_edge(fe))); get_edge_side(edge2,side2); v2 = get_edge_headv(edge2); h2 = *(REAL*)get_extra(v2,lmc_mc_attr); s11 = SDIM_dot(side1,side1); s12 = SDIM_dot(side1,side2); s22 = SDIM_dot(side2,side2); det = sqrt(s11*s22 - s12*s12); energy += ((h1-h)*(h1-h)*s22 - 2*(h1-h)*(h2-h)*s12 + (h2-h)*(h2-h)*s11)/det/6; } fe = get_next_vertex_facet(v_info->v[0],fe); } while ( !equal_id(fe,start_fe) ); } /* end soapfilm */ return energy; } REAL laplacian_mean_curvature_grad(v_info) struct qinfo *v_info; { int i; REAL normal[MAXCOORD]; if ( web.representation == STRING ) { /* compute Laplacian and multiply by normal */ REAL len1; vertex_id v1; REAL h1,h; REAL lap = 0; REAL flux; /* into vertex star */ REAL vgradmag; REAL mobility; edge_id e_id,start_e; vgradmag = vertex_total_vol_grad(v_info->v[0],normal); if ( vgradmag == 0 ) return 0.0; /* internal vertex probably */ h = *(REAL*)get_extra(v_info->v[0],lmc_mc_attr); e_id = start_e = get_vertex_edge(v_info->v[0]); flux = 0.0; do { len1 = get_edge_length(e_id); v1 = get_edge_headv(e_id); h1 = *(REAL*)get_extra(v1,lmc_mc_attr); mobility = *(REAL*)get_e_extra(e_id,lmc_mobility_attr); flux += mobility*(h-h1)/len1; e_id = get_next_tail_edge(e_id); } while ( !equal_id(e_id,start_e) ); /* now multiply by normal */ lap = flux/vgradmag; for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] = lap*normal[i]; return 0; } /* Just soapfilm gets here */ if ( web.representation == SOAPFILM ) { REAL vgradmag; REAL h1,h,h2; REAL lap; REAL side1[MAXCOORD],side2[MAXCOORD]; REAL flux; edge_id edge1,edge2; vertex_id v1,v2; facet_id f_id; facetedge_id fe,start_fe; REAL mobility; vgradmag = vertex_total_vol_grad(v_info->v[0],normal)/3; if ( vgradmag == 0.0 ) return 0.0; /* internal vertex, probably */ /* compute Laplacian and multiply by normal */ h = *(REAL*)get_extra(v_info->v[0],lmc_mc_attr); flux = 0.0; fe = start_fe = get_vertex_first_facet(v_info->v[0]); do { REAL s11,s12,s22,det; f_id = get_fe_facet(fe); mobility = *(REAL*)get_f_extra(f_id,lmc_mobility_attr); if ( mobility != 0.0 ) { edge1 = get_fe_edge(fe); get_edge_side(edge1,side1); v1 = get_edge_headv(edge1); h1 = *(REAL*)get_extra(v1,lmc_mc_attr); edge2 = get_fe_edge(inverse_id(get_prev_edge(fe))); get_edge_side(edge2,side2); v2 = get_edge_headv(edge2); h2 = *(REAL*)get_extra(v2,lmc_mc_attr); s11 = SDIM_dot(side1,side1); s12 = SDIM_dot(side1,side2); s22 = SDIM_dot(side2,side2); det = sqrt(s11*s22 - s12*s12); flux += mobility*((s11-2*s12+s22)*h + (s12-s22)*h1 + (s12-s11)*h2)/det; } fe = get_next_vertex_facet(v_info->v[0],fe); } while ( !equal_id(fe,start_fe) ); lap = flux/vgradmag/2; /* now multiply by unit normal */ for ( i = 0 ; i < SDIM ; i++ ) v_info->grad[0][i] = lap*normal[i]; } /* end soapfilm */ return 0.0; } /* end laplacian_mean_curvature_grad() */ /****************************************************************************** MULTIGRID stuff. *****************************************************************************/ /* Structure to hold multigrid info */ struct multigrid_s { struct linsys *fine; /* high resolution system */ struct linsys *coarse; /* coarse system */ int *marks; /* for recording which are coarse */ int coarse_count; /* number of coarse variables */ int *fine_to_coarse; /* index translation */ int *coarse_to_fine; /* and the other way */ /* interpolation, first index is fine variable */ int *interp_IA; int *interp_JA; REAL *interp_A; /* transpose interpolation, first indes is coarse variable */ int *interp_tr_IA; int *interp_tr_JA; REAL *interp_tr_A; /* strongly connected neighbors */ int *snbr_starts; int *snbr_counts; int *snbr_lists; REAL *wnbr_asums; /* sums of weak neighbor coefficients */ }; struct multigrid_s * init_multigrid ARGS(( struct linsys *)); void multigrid_cleanup ARGS((struct multigrid_s **)); void choose_coarse_points ARGS((struct multigrid_s*)); void interpolation_weights ARGS((struct multigrid_s*)); void interpolation_transpose ARGS((struct multigrid_s*)); void coarse_matrix ARGS((struct multigrid_s*)); void do_multigrid(S) struct linsys *S; { struct multigrid_s *mg; mg = init_multigrid(S); multigrid_cleanup(&mg); } /* lambda count handling, for tracking largest lambda */ #define LAMBDAMAX 100 struct lambda_s { struct lambda_s *prev,*next; int value; } *lambda_list, *lambda_heads[LAMBDAMAX]; int lambdamax; int lambda_count; static REAL omega = 0.25; /* "strong" connection cutoff ratio */ void print_lambda_lists() { int n; struct lambda_s *p; for ( n = 0 ; n < LAMBDAMAX ; n++ ) if ( lambda_heads[n] ) { printf(" Lambda %d: ",n); for ( p = lambda_heads[n] ; p ; p = p->next ) printf(" %d",p-lambda_list); printf("\n"); } } void lambda_init() { lambda_count = 0; lambdamax = -1; memset(lambda_heads,0,sizeof(lambda_heads)); } void lambda_insert(n,value) int n; /* spot in list */ int value; { if ( value >= LAMBDAMAX ) value = LAMBDAMAX-1; lambda_list[n].value = value; lambda_list[n].prev = NULL; lambda_list[n].next = lambda_heads[value]; if ( lambda_heads[value] ) lambda_heads[value]->prev = lambda_list + n; lambda_heads[value] = lambda_list + n; if ( value > lambdamax ) lambdamax = value; lambda_count++; } void lambda_delete(spot) int spot; /* in list */ { int value = lambda_list[spot].value; if ( lambda_list[spot].prev ) lambda_list[spot].prev->next = lambda_list[spot].next; else { lambda_heads[value] = lambda_list[spot].next; if ( (lambda_heads[value] == NULL) && (value == lambdamax) ) { /* reset lambdamax */ for ( ; (lambdamax >= 0) && (lambda_heads[lambdamax] == NULL) ; lambdamax-- ) ; } } if ( lambda_list[spot].next ) lambda_list[spot].next->prev = lambda_list[spot].prev; lambda_count--; } void increment_lambda(spot) int spot; /* in list */ { lambda_delete(spot); lambda_list[spot].value++; lambda_insert(spot,lambda_list[spot].value); } void decrement_lambda(spot) int spot; /* in list */ { lambda_delete(spot); lambda_list[spot].value++; lambda_insert(spot); } int get_highest_lambda() { int ret = lambda_heads[lambdamax] - lambda_list; lambda_delete(ret); return ret; } static int unmark = 0; static int coarse_mark = 1; static int fine_mark = 2; /************************************************************************** * * Function: init_multigrid() * * Purpose: Set up multigrid structures, restriction and prolongation * matrices. * * Assumes input system described in N, IA, JA, A. */ struct multigrid_s * init_multigrid( fine ) struct linsys *fine; { /* Select coarse points by going through fine points in order, and taking point as coarse point unless it has been marked as a neighbor of a previously selected coarse point. */ struct multigrid_s *mg = (struct multigrid_s *)temp_calloc(1, sizeof(struct multigrid_s)); mg->fine = fine; /* Following algebraic multigrid method of Ruge & Stuben in Multigrid Methods (1987) ed. McCormick, chapter 4 */ lambda_init(); choose_coarse_points(mg); interpolation_weights(mg); interpolation_transpose(mg); coarse_matrix(mg); return mg; } /* end init_multigrid */ /************************************************************************** * * function: choose_coarse_points() * * purpose: Choose subset of points to be the smaller coarse set. * Follows Ruge/Stuben algorithm. * * input: fine system, as pointed to by mg multigrid structure. * * output: */ void choose_coarse_points(mg) struct multigrid_s *mg; { REAL *amaxes; /* maximum matrix values on row */ int *nbr_count; /* number of neighbors of each fine variable */ int n,i,j; struct linsys *fine = mg->fine; int cspot; int *stnbr_starts,*stnbr_counts,*stnbr_lists; int *wnbr_starts,*wnbr_counts,*wnbr_lists; /* Coarse point choice, stage 1 (algorithm A1 on p. 102) */ mg->marks = (int*)temp_calloc(fine->N,sizeof(int)); /* Get maximum magnitudes in rows, so can find "strong" neighbors */ /* And count neighbors, while we're at it */ amaxes = (REAL*)temp_calloc(fine->N,sizeof(REAL)); nbr_count = (int*)temp_calloc(fine->N,sizeof(int)); for ( n = 0 ; n < fine->N ; n++ ) for ( j = fine->IA[n]-A_OFF+1 ; j < fine->IA[n+1]-A_OFF ; j++ ) { double mag = fabs(fine->A[j]); int jvar; if ( mag > amaxes[n] ) amaxes[n] = mag; jvar = fine->JA[j] - A_OFF; if ( mag > amaxes[jvar] ) amaxes[jvar] = mag; nbr_count[n]++; nbr_count[jvar]++; } /* "Strong" cutoff is taken to be 0.25 of max */ for ( n = 0 ; n < fine->N ; n++ ) amaxes[n] *= omega; /* Record strong neighbors, and reciprocal strong neighbors */ /* and weakly connected neighbors, for later */ mg->snbr_starts = (int*)temp_calloc(fine->N+1,sizeof(int)); mg->snbr_counts = (int*)temp_calloc(fine->N,sizeof(int)); mg->snbr_lists = (int*)temp_calloc(2*fine->IA[fine->N],sizeof(int)); stnbr_starts = (int*)temp_calloc(fine->N+1,sizeof(int)); stnbr_counts = (int*)temp_calloc(fine->N,sizeof(int)); stnbr_lists = (int*)temp_calloc(2*fine->IA[fine->N],sizeof(int)); wnbr_starts = (int*)temp_calloc(fine->N+1,sizeof(int)); wnbr_counts = (int*)temp_calloc(fine->N,sizeof(int)); wnbr_lists = (int*)temp_calloc(2*fine->IA[fine->N],sizeof(int)); mg->wnbr_asums = (REAL*)temp_calloc(fine->N,sizeof(REAL)); for ( n = 0 ; n < fine->N ; n++ ) { mg->snbr_starts[n+1] = mg->snbr_starts[n] + nbr_count[n]; stnbr_starts[n+1] = stnbr_starts[n] + nbr_count[n]; wnbr_starts[n+1] = wnbr_starts[n] + nbr_count[n]; } for ( n = 0 ; n < fine->N ; n++ ) for ( j = fine->IA[n]-A_OFF+1 ; j < fine->IA[n+1]-A_OFF ; j++ ) { double mag = fabs(fine->A[j]); int jvar = fine->JA[j] - A_OFF; if ( mag > amaxes[n] ) { /* strong nbr */ mg->snbr_lists[mg->snbr_starts[n] + mg->snbr_counts[n]++] = jvar; stnbr_lists[stnbr_starts[jvar] + stnbr_counts[jvar]++] = n; } else /* weak neighbor */ { wnbr_lists[wnbr_starts[n] + wnbr_counts[n]++] = jvar; mg->wnbr_asums[n] += fine->A[j]; } if ( mag > amaxes[jvar] ) { /* strong nbr */ mg->snbr_lists[mg->snbr_starts[jvar] + mg->snbr_counts[jvar]++] = n; stnbr_lists[stnbr_starts[n] + stnbr_counts[n]++] = jvar; } else /* weak neighbor */ { wnbr_lists[wnbr_starts[jvar] + wnbr_counts[jvar]++] = n; mg->wnbr_asums[jvar] += fine->A[j]; } } /* set up lambdas in heap so can track max lambda */ lambda_list = (struct lambda_s *)temp_calloc(fine->N,sizeof(struct lambda_s)); for ( n = 0 ; n < fine->N ; n++ ) lambda_insert(n,stnbr_counts[n]); /* iterate marking coarse points */ while ( lambda_count > 0 ) { int k; /* remove from unmarked list and mark as coarse */ i = get_highest_lambda(); mg->marks[i] = coarse_mark; mg->coarse_count++; printf("Coarse mark %d\n",i); /* mark fine neighbors */ for ( j = 0 ; j < stnbr_counts[i] ; j++ ) { int nbr = stnbr_lists[stnbr_starts[i] + j]; if ( mg->marks[nbr] != unmark ) continue; mg->marks[nbr] = fine_mark; printf(" Fine mark %d\n",nbr); lambda_delete(nbr); for ( k = 0 ; k < mg->snbr_counts[nbr] ; k++ ) { int nbrnbr = mg->snbr_lists[mg->snbr_starts[nbr] + k]; if ( mg->marks[nbrnbr] != unmark ) continue; increment_lambda(nbrnbr); } } for ( j = 0 ; j < mg->snbr_counts[i] ; j++ ) { int nbr = mg->snbr_lists[mg->snbr_starts[i] + j]; if ( mg->marks[nbr] != unmark ) continue; decrement_lambda(nbr); } } /* Finalize choice of coarse points */ /* (algorithm A3, p. 102; weight calculation separate) */ for ( n = 0 ; n < fine->N ; n++ ) { int sfcount; /* number of strong fine neigbors, not coarse */ int ctwiddle_count; int ctwiddle=0; /* candidate coarse variable */ int c_nbrs[1000]; int strong_fnbrs[1000]; int c_nbr_count; int k; if ( mg->marks[n] != fine_mark ) continue; /* A3.3: set C_i = S_i intersect C, Ds_i = S_i - C_i */ ctwiddle_count = 0; sfcount = 0; c_nbr_count = 0; for ( k = 0 ; k < mg->snbr_counts[n] ; k++ ) { int snbr = mg->snbr_lists[mg->snbr_starts[n] + k]; if ( mg->marks[snbr] == coarse_mark ) { c_nbrs[c_nbr_count++] = snbr; } else { strong_fnbrs[sfcount++] = snbr; /* Ds_i */ } } step4: /* A3.5 */ for ( j = 0 ; j < sfcount ; j++ ) { /* A3.6 test S_j intersect C_i */ int jnbr = strong_fnbrs[j]; int meet_flag = 0; int nn,jj; for ( nn = 0 ; (meet_flag == 0) && (nn < c_nbr_count) ; nn++ ) for ( jj = 0 ; jj < mg->snbr_counts[jnbr] ; jj++ ) if ( c_nbrs[nn] == mg->snbr_lists[mg->snbr_starts[jnbr]+jj] ) { meet_flag = 1; break; } if ( meet_flag == 0 ) { /* step 7 */ if ( ctwiddle_count ) /* punt, and make it coarse */ { mg->marks[n] = coarse_mark; mg->coarse_count++; break; } ctwiddle = jnbr; ctwiddle_count = 1; c_nbrs[c_nbr_count++] = jnbr; strong_fnbrs[j] = strong_fnbrs[--sfcount]; goto step4; /* try again */ } } /* end step A3.5 */ /* step A3.9 */ if ( ctwiddle_count ) { mg->marks[ctwiddle] = coarse_mark; mg->coarse_count++; } } /* end step A3.2 */ /* coarse system aliases of coarse variables */ mg->coarse = (struct linsys *)temp_calloc(1,sizeof(struct linsys)); mg->fine_to_coarse = (int*)temp_calloc(fine->N,sizeof(int)); mg->coarse_to_fine = (int*)temp_calloc(mg->coarse_count,sizeof(int)); for ( n = 0, cspot = 0 ; n < fine->N ; n++ ) if ( mg->marks[n] == coarse_mark ) { mg->fine_to_coarse[n] = cspot; mg->coarse_to_fine[cspot] = n; cspot++; } temp_free((char*)nbr_count); temp_free((char*)amaxes); temp_free((char*)stnbr_starts); temp_free((char*)stnbr_counts); temp_free((char*)stnbr_lists); temp_free((char*)wnbr_starts); temp_free((char*)wnbr_counts); temp_free((char*)wnbr_lists); } /* end choose_coarse_points() */ /*************************************************************************** * * function: get_a() * * purpose: pluck entry from fine matrix A. */ REAL get_a(S,row,col) struct linsys *S; int row,col; { int kk; /* get into upper diagonal */ if ( row > col ) { int temp = row; row = col; col = temp; } for ( kk = S->IA[row]-A_OFF+1 ; kk < S->IA[row+1]-A_OFF ; kk++ ) if ( S->JA[kk] == col ) return S->A[kk]; return 0.0; } /* end get_a() */ /************************************************************************* * * function: interpolation_weights() * * purpose: Calculate interpolation weights for multigrid coarsening * */ void interpolation_weights(mg) struct multigrid_s *mg; { struct linsys *fine = mg->fine; int c_spot; /* current place in interpolation long lists */ int n; /* index of fine variables */ int interp_alloc; /* how many entries allocated */ /* Interpolation weights (algorithm A3, p. 102) */ interp_alloc = 2*fine->IA[fine->N]; mg->interp_JA = (int*)temp_calloc(interp_alloc,sizeof(int)); mg->interp_IA = (int*)temp_calloc(fine->N+1,sizeof(int)); mg->interp_A = (REAL*)temp_calloc(interp_alloc,sizeof(int)); c_spot = 0; for ( n = 0 ; n < fine->N ; n++ ) { int strong_fnbrs[1000]; int sfcount; /* of strong_fnbrs */ int k,kk,j; REAL d_i; /* diagonal entry for this variable */ REAL d[1000]; /* for nbrs */ if ( c_spot + fine->N >= interp_alloc ) { /* need more room */ interp_alloc *= 2; mg->interp_JA = (int*)temp_realloc((char*)(mg->interp_JA), interp_alloc*sizeof(int)); mg->interp_A = (REAL*)temp_realloc((char*)(mg->interp_A), interp_alloc*sizeof(REAL)); } mg->interp_IA[n] = c_spot; if ( mg->marks[n] == coarse_mark ) { /* interpolate just on self */ mg->interp_A[c_spot] = 1.0; mg->interp_JA[c_spot] = n; continue; } /* A3.3: set C_i = S_i intersect C */ sfcount = 0; for ( k = 0 ; k < mg->snbr_counts[n] ; k++ ) { int snbr = mg->snbr_lists[mg->snbr_starts[n] + k]; if ( mg->marks[snbr] == coarse_mark ) mg->interp_JA[c_spot++] = snbr; /* add to coarse nbr list */ else strong_fnbrs[sfcount++] = snbr; /* Ds_i */ } /* A3.4, d_i = a_ii + sum weak */ d_i = fine->A[fine->IA[n]-A_OFF] + mg->wnbr_asums[n]; for ( k = mg->interp_IA[n], kk=0 ; k < c_spot ; k++,kk++ ) d[kk] = get_a(fine,n,mg->interp_JA[k]); /* A3.5 */ for ( j = 0 ; j < sfcount ; j++ ) { REAL denom; int jnbr = strong_fnbrs[j]; /* step 8 */ /* denominator */ for ( k = mg->interp_IA[n], denom = 0 ; k < c_spot ; k++ ) denom += get_a(fine,jnbr,mg->interp_JA[k]); /* dk's */ for ( k = mg->interp_IA[n] ; k < c_spot ; k++ ) d[k] += get_a(fine,n,jnbr)*get_a(fine,jnbr,mg->interp_JA[k])/denom; } /* end step A3.5 */ /* step A3.9 */ for ( k = mg->interp_IA[n], kk=0 ; k < c_spot ; k++,kk++ ) mg->interp_A[k] = -d[kk]/d_i; } /* end step A3.2 */ mg->interp_IA[n] = c_spot; /* free unneeded space */ interp_alloc = c_spot; mg->interp_JA = (int*)temp_realloc((char*)(mg->interp_JA), interp_alloc*sizeof(int)); mg->interp_A = (REAL*)temp_realloc((char*)(mg->interp_A), interp_alloc*sizeof(REAL)); } /* end interpolation_weights() */ /************************************************************************ * * function: interpolation_transpose() * */ void interpolation_transpose(mg) struct multigrid_s *mg; { int k,j; /* transpose of interpolation matrix, i.e. store fine row adjacent, and index starts by coarse number */ int *interp_tr_counts = (int*)temp_calloc(mg->coarse_count,sizeof(int)); for ( k = 0 ; k < mg->fine->N ; k++ ) for ( j = mg->interp_IA[k] ; j < mg->interp_IA[k+1] ; j++ ) { int cnum = mg->fine_to_coarse[mg->interp_JA[j]]; interp_tr_counts[cnum]++; } mg->interp_tr_IA = (int*)temp_calloc(mg->coarse_count+1,sizeof(int)); for ( k = 0 ; k < mg->coarse_count ; k++ ) { mg->interp_tr_IA[k+1] = mg->interp_tr_IA[k] + interp_tr_counts[k]; interp_tr_counts[k] = 0; } mg->interp_tr_A = (REAL*)temp_calloc(mg->interp_tr_IA[mg->coarse_count], sizeof(REAL)); mg->interp_tr_JA = (int*)temp_calloc(mg->interp_tr_IA[mg->coarse_count], sizeof(int)); for ( k = 0 ; k < mg->fine->N ; k++ ) for ( j = mg->interp_IA[k] ; j < mg->interp_IA[k+1] ; j++ ) { int cnum = mg->fine_to_coarse[mg->interp_JA[j]]; int spot = mg->interp_tr_IA[cnum] + interp_tr_counts[cnum] ; mg->interp_tr_A[spot] = mg->interp_A[j]; mg->interp_tr_JA[spot] = k; interp_tr_counts[cnum]++; } } /* end interpolation_transpose() */ /************************************************************************** * * function: coarse_matrix() * * purpose: calculate matrix for coarse system. * */ void coarse_matrix(mg) struct multigrid_s *mg; { int n,nk,kk,k,j; int temp_top; int *temp_IA,*temp_JA; REAL *temp_A; int *temp_counts; int temp_A_alloc; int c_base,c_top; int *new_upper; int spot; int coarse_alloc; /* Now assemble the coarse system */ /* project the A matrix, A_c = Interp^tr A_f Interp */ /* Doing half of diagonal, to take advantage of symmetry */ /* First, A_f Interp */ /* using row-wise interp to avoid quadratic time */ temp_IA = (int*)temp_calloc(mg->fine->N+1,sizeof(int)); temp_counts = (int*)temp_calloc(mg->fine->N,sizeof(int)); temp_A_alloc = mg->fine->IA[mg->fine->N]; temp_A = (REAL*)temp_calloc(temp_A_alloc,sizeof(REAL)); temp_JA = (int*)temp_calloc(temp_A_alloc,sizeof(int)); mg->coarse->N = mg->coarse_count; for ( n = 0 , temp_top = 0; n < mg->fine->N ; n++ ) { int temp_base = temp_top; temp_IA[n] = temp_base; for ( nk = mg->fine->IA[n]-A_OFF ; nk < mg->fine->IA[n+1]-A_OFF ; nk++ ) { int m,mm; int m_spot; int jk = mg->fine->JA[nk]-A_OFF; REAL aval = mg->fine->A[nk]; if ( nk == mg->fine->IA[n]-A_OFF ) /* diagonal */ aval /= 2; m_spot = temp_base; if ( temp_top + mg->coarse->N > temp_A_alloc ) { temp_A_alloc *= 2; temp_A = (REAL*)temp_realloc((char*)temp_A,temp_A_alloc*sizeof(REAL)); temp_JA = (int*)temp_realloc((char*)temp_JA,temp_A_alloc*sizeof(int)); } for ( kk = mg->interp_IA[jk] ; kk < mg->interp_IA[jk+1] ; kk++ ) { int ck = mg->interp_JA[kk]; REAL prod = aval*mg->interp_A[kk]; /* linear search up, since we expect relatively few entries */ for ( m = m_spot ; m < temp_top ; m++ ) { if ( ck == temp_JA[m] ) { temp_A[m] += prod; break; } if ( ck > temp_JA[m] ) continue; /* have to make room */ for ( mm = temp_top-1 ; mm >= m ; mm-- ) { temp_JA[mm+1] = temp_JA[mm]; temp_A[mm+1] = temp_A[mm]; } temp_A[m] = prod; temp_JA[m] = ck; temp_top++; break; } if ( m == temp_top ) { /* new one on top */ temp_JA[temp_top] = ck; temp_A[temp_top] = prod; temp_top++; } } } } temp_IA[mg->fine->N] = temp_top; /* next, Interp^Tr * temp */ /* using transposed version of interp */ c_base = 0; c_top = 0; mg->coarse->IA = (int*)temp_calloc(mg->coarse_count+1,sizeof(int)); coarse_alloc = temp_IA[mg->fine->N]; mg->coarse->JA = (int*)temp_calloc(coarse_alloc,sizeof(int)); mg->coarse->A = (REAL*)temp_calloc(coarse_alloc,sizeof(REAL)); for ( k = 0 ; k < mg->coarse_count ; k++ ) { mg->coarse->IA[k] = c_base = c_top; for ( n = mg->interp_tr_IA[k] ; n < mg->interp_tr_IA[k+1] ; n++ ) { int fn = mg->interp_tr_JA[n]; int j_spot = c_base; int m,mm; if ( c_top + mg->coarse->N >= coarse_alloc ) { /* need more room */ coarse_alloc *= 2; mg->coarse->JA = (int*)temp_realloc((char*)(mg->coarse->JA), coarse_alloc*sizeof(int)); mg->coarse->A = (REAL*)temp_realloc((char*)(mg->coarse->A), coarse_alloc*sizeof(REAL)); } for ( m = temp_IA[fn] ; m < temp_IA[fn+1] ; m++ ) { REAL prod = mg->interp_tr_A[n]*temp_A[m]; int ck = temp_JA[m]; /* linear search up */ for ( ; j_spot < c_top ; j_spot++ ) { if ( ck == mg->coarse->JA[j_spot] ) { mg->coarse->A[j_spot] += prod; break; } if ( ck > mg->coarse->JA[j_spot] ) continue; /* have to make room */ for ( mm = c_top-1 ; mm >= j_spot ; mm-- ) { mg->coarse->JA[mm+1] = mg->coarse->JA[mm]; mg->coarse->A[mm+1] = mg->coarse->A[mm]; } mg->coarse->JA[j_spot] = ck; mg->coarse->A[j_spot] = prod; c_top++; break; } if ( j_spot == c_top ) { /* new one on top */ mg->coarse->JA[c_top] = ck; mg->coarse->A[c_top] = prod; c_top++; } } } } mg->coarse->IA[mg->coarse_count] = c_top; mg->coarse->N = mg->coarse_count; /* now sum with transpose and keep just upper triangle */ /* count how many new upper spots might be needed */ new_upper = (int*)temp_calloc(mg->coarse->N,sizeof(int)); for ( n = 0 ; n < mg->coarse->N ; n++ ) { for ( j = mg->coarse->IA[n] ; j < mg->coarse->IA[n+1] ; j++ ) if ( mg->coarse->JA[j] < n ) new_upper[mg->coarse->JA[j]]++; else new_upper[n]++; } for ( n = 0 ; n < mg->coarse->N ; n++ ) temp_IA[n+1] = temp_IA[n] + new_upper[n]; for ( n = 0 ; n < mg->coarse->N ; n++ ) for ( j = mg->coarse->IA[n] ; j < mg->coarse->IA[n+1] ; j++ ) { REAL val = mg->coarse->A[j]; int row,col; int i,m; if ( mg->coarse->JA[j] < n ) { /* add to transpose spot */ row = mg->coarse->JA[j]; col = n; } else { row = n; col = mg->coarse->JA[j]; if ( row == col ) val *= 2; } /* now bubble up */ for ( i = 0 ; i < temp_counts[row] ; i++ ) { int ispot = temp_IA[row] + i; if ( col == temp_JA[ispot] ) { temp_A[ispot] += val; break; } if ( col > temp_JA[ispot] ) continue; /* have to insert new one */ for ( m = temp_IA[row] + temp_counts[row]-1 ; m > ispot ; m-- ) { temp_JA[m+1] = temp_JA[m]; temp_A[m+1] = temp_A[m]; } temp_counts[row]++; temp_JA[ispot] = col; temp_A[ispot] = val; break; } } /* now compact back for final system */ spot = 0; for ( n = 0 ; n < mg->coarse->N ; n++ ) { mg->coarse->IA[n] = spot; for ( k = 0 ; k < temp_counts[n] ; k++ ) { mg->coarse->JA[spot] = temp_JA[temp_IA[n]+k]; mg->coarse->A[spot] = temp_A[temp_IA[n]+k]; spot++; } } mg->coarse->IA[mg->coarse->N] = spot; /* Fortran offset */ if ( A_OFF ) { int jtop = mg->coarse->IA[mg->coarse->N]; for ( n = 0 ; n < mg->coarse->N ; n++ ) mg->coarse->IA[n] += A_OFF; for ( n = 0 ; n < jtop ; n++ ) mg->coarse->JA[n] += A_OFF; } /* free unneeded memory */ mg->coarse->JA = (int*)temp_realloc((char*)(mg->coarse->JA), mg->coarse->IA[mg->coarse->N]*sizeof(int)); mg->coarse->A = (REAL*)temp_realloc((char*)(mg->coarse->A), mg->coarse->IA[mg->coarse->N]*sizeof(REAL)); temp_free((char*)temp_IA); temp_free((char*)temp_JA); temp_free((char*)temp_A); temp_free((char*)temp_counts); temp_free((char*)new_upper); } /* end coarse_matrix() */ /************************************************************************* * * function: multigrid_cleanup() * * purpose: deallocate memory in multigrid structure * */ void multigrid_cleanup(mgp) struct multigrid_s **mgp; { struct multigrid_s *mg = *mgp; free_system(mg->coarse); temp_free((char*)mg->marks); temp_free((char*)mg->fine_to_coarse); temp_free((char*)mg->coarse_to_fine); temp_free((char*)mg->interp_IA); temp_free((char*)mg->interp_JA); temp_free((char*)mg->interp_A); temp_free((char*)mg->interp_tr_IA); temp_free((char*)mg->interp_tr_JA); temp_free((char*)mg->interp_tr_A); temp_free((char*)mg->snbr_starts); temp_free((char*)mg->snbr_counts); temp_free((char*)mg->snbr_lists); temp_free((char*)mg->wnbr_asums); /* sums of weak neighbor coefficients */ temp_free((char*)mg); *mgp = NULL; } /****************************************************************************** End MULTIGRID stuff. *****************************************************************************/ evolver-2.30c.dfsg/src/userio.c0000644000175300017530000007704611410765113016705 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************** * * File: userio.c * * Purpose: Handles interaction with user. All interaction goes * through this file for easy porting. */ #ifdef WIN32 #undef FIXED #undef DOUBLE #endif #include "include.h" /*********************************************************************** * * Function: outstring() * * Purpose: Prints string for user. Does not wait for response. * Does not add newline. */ void outstring ARGS1((outmsg), CONST char *outmsg) { if ( quiet_flag || !outmsg ) return; if ( broken_pipe_flag ) { broken_pipe_flag = 0; kb_error(1349,"Broken pipe.\n",RECOVERABLE); } if ( outfd == NULL ) outfd = stdout; /* maybe after error */ if ( logfile_flag && (outfd==stdout) ) fprintf(logfilefd,outmsg); #if defined(MAC_APP) || defined(WIN32S) || defined(MAC_CW) if ( outfd == stdout ) write_to_console(outmsg); else fputs(outmsg,outfd); #else fputs(outmsg,outfd); fflush(outfd); #endif } /*********************************************************************** * * Function: erroutstring() * * Purpose: Prints error string for user. Does not wait for response. * Does not add newline. */ void erroutstring ARGS1((outmsg), char *outmsg) { if ( !outmsg ) return; #if defined(MAC_APP) || defined(WIN32S) || defined(MAC_CW) write_to_console(outmsg); #else #ifdef MPI_EVOLVER fprintf(stderr,"Task %d: ",this_task); #endif fputs(outmsg,stderr); fflush(stderr); #endif if ( logfile_flag ) fprintf(logfilefd,outmsg); } /*********************************************************************** * * function: my_fgets() * * purpose: replacement for fgets() that recognizes various line end * formats: CR, NL, CR NL, and converts all to NL. Returned string * ends with null, included in max size. */ char *my_fgets ARGS3((s,n,stream), char *s, int n, /* max size of string */ FILE *stream) { char *p; int k; for ( k = 0, p = s ; k < n-1 ; k++,p++ ) { int c; c = fgetc(stream); if ( (c == EOF) && ( k == 0 ) ) return NULL; if ( c == EOF ) { *p = 0; return s; } *p = (char)c; if ( c == '\n' ) { *(++p) = 0; return s; } if ( c == '\r' ) { *p = '\n'; *(++p) = 0; c = fgetc(stream); if ( (c != '\n') && (c != EOF) ) ungetc(c, stream); return s; } } *p = 0; /* null terminator */ return s; } /*********************************************************************** * * Function: getstring() * * Purpose: Gets string from user. */ void getstring ARGS2((inmsg,max), char *inmsg, int max) { char *c; max -= 2; /* ensure room */ while ( commandfd && (commandfd != stdin) ) { /* from command input file */ if (my_fgets(inmsg,max,commandfd) == NULL) { pop_commandfd(); continue; /* will fall thru to stdin eventually */ } else { if ( !topflag && !quiet_load_flag ) outstring(inmsg); /* echo */ if ( (int)strlen(inmsg) == max-1 ) {inmsg[max-1] = MOREIN; inmsg[max] = 0; } return; } } /* from stdin */ broken_pipe_flag = 0; /* in case left over */ #if defined(MAC_APP) || defined(WIN32S) || defined(MAC_CW) read_line_from_console(inmsg); #else if ( my_fgets(inmsg,max,stdin) == NULL ) my_exit(0); c = inmsg + strlen(inmsg) - 1; if ( *c == '\n' ) *c = 0; if ( echo_flag ) { outstring(inmsg); outstring("\n"); } #endif if ( keylogfile_flag ) fprintf(keylogfilefd,"%s\n",inmsg); } #ifdef OOGL /* Geomview picking stuff */ /*************************************************************************** * * function: read_pick() * * purpose: Read pick message from geomview. * * return: 0 for no pick, 1 for pick */ int read_pick() { char pickbuf[500],*ptr; int len; int vnum=-1,ednum=-1,fnum=-1; int prim; len = read(gv_pipe[0],pickbuf,sizeof(pickbuf)); if ( len > 0 ) { pickbuf[len] = 0; ptr = strchr(pickbuf+1,'('); /* coord of picked pt */ if ( !ptr ) goto pick_fail_exit; ptr = strchr(ptr+1,'('); /* coord of picked vertex */ if ( !ptr ) goto pick_fail_exit; ptr = strchr(ptr+1,'('); /* coord of picked edge */ if ( !ptr ) goto pick_fail_exit; ptr = strchr(ptr+1,'('); /* coord of picked face */ if ( !ptr ) goto pick_fail_exit; ptr = strchr(ptr+1,'('); /* path to picked primitive */ if ( !ptr ) goto pick_fail_exit; prim = atoi(ptr+1); if ( setting_backcull ) prim--; ptr = strchr(ptr+1,')')+1; /* index of picked primitive */ if ( !ptr ) goto pick_fail_exit; vnum = atoi(ptr); ptr = strchr(ptr+1,'('); /* endpoints of picked edge */ if ( !ptr ) goto pick_fail_exit; if ( ptr[1] != ')' && (web.representation != SIMPLEX) ) { /* figure edge from endpoints */ int v1,v2; vertex_id v_id1,v_id2; edge_id e_id,e1; sscanf(ptr+1,"%d %d",&v1,&v2); if ( prim > 0 ) { v_id1 = vpicklist[gv_vect_start+v1]; v_id2 = vpicklist[gv_vect_start+v2]; } else { v_id1 = vpicklist[v1]; v_id2 = vpicklist[v2]; } if ( valid_id(v_id1) && valid_id(v_id2) && !(get_vattr(v_id1) & Q_MIDFACET) && !(get_vattr(v_id2) & Q_MIDFACET) ) { if ( web.modeltype != LINEAR ) { edge_id e2,e_id2; e1 = e_id = get_vertex_edge(v_id1); do { e2 = e_id2 = get_vertex_edge(v_id2); do { if ( equal_element(e_id,e_id2) ) { ednum = loc_ordinal(e_id) + 1; break; } if ( get_vattr(v_id2) & (Q_MIDPOINT | Q_MIDEDGE) ) break; e_id2 = get_next_tail_edge(e_id2); } while ( !equal_id(e2,e_id2) ); if ( get_vattr(v_id1) & (Q_MIDPOINT | Q_MIDEDGE) ) break; e_id = get_next_tail_edge(e_id); } while ( (ednum < 0) && !equal_id(e1,e_id) ); } else /* LINEAR */ { e1 = e_id = get_vertex_edge(v_id1); do { if ( equal_id(v_id2,get_edge_headv(e_id)) ) { ednum = loc_ordinal(e_id) + 1; break; } e_id = get_next_tail_edge(e_id); } while ( !equal_id(e1,e_id) ); } } } ptr = strchr(ptr+1,')')+1; /* index of picked face */ if ( !ptr ) goto pick_fail_exit; fnum = atoi(ptr); /* puts(pickbuf); */ strcpy(msg,"Picked "); if ( vnum >= 0 ) { if ( prim > 0 ) vnum += gv_vect_start; pickvnum = loc_ordinal(vpicklist[vnum]) + 1; sprintf(msg+strlen(msg),"vertex %d ",pickvnum); if ( geomview_bug_flag == 1 ) { strcat(msg, "\nGeomview version 1.6.1 before 1.6.1p7 does not do picking correctly.\n"); strcat(msg, "Get fixed version of gvx from ftp://www.susqu.edu /priv/slevy.\n"); geomview_bug_flag = 2; } if ( geomview_bug_flag == 2 ) strcat(msg," (probably wrong)"); } if ( ednum > 0 ) { pickenum = ednum; sprintf(msg+strlen(msg),"edge %d ",pickenum); if ( geomview_bug_flag == 1 ) { strcat(msg, "\nGeomview version 1.6.1 before 1.6.1p7 does not do picking correctly.\n"); strcat(msg, "Get fixed version of gvx from ftp://www.susqu.edu /priv/slevy.\n"); geomview_bug_flag = 2; } if ( geomview_bug_flag == 2 ) strcat(msg," (probably wrong)"); } if ( fnum >= 0 ) { pickfnum = loc_ordinal(fpicklist[fnum]) + 1; sprintf(msg+strlen(msg),"facet %d ",pickfnum); } strcat(msg,"\n"); outstring(msg); } return (len > 0) ? 1 : 0; pick_fail_exit: outstring("\nMessage from geomview:\n"); outstring(pickbuf); if ( current_prompt ) outstring(current_prompt); return 0; } /*************************************************************************** * * function: check_pick() * * purpose: Check for pick message from geomview. Waits for input on * either stdin or gv_pipe. * * return: 0 for stdin, 1 for pick */ #ifdef POLLIN extern int poll ARGS((struct pollfd *, unsigned long, int)); #endif int check_pick() { #ifdef POLLIN struct pollfd pp[2]; /* see if anything ready to read */ pp[0].fd = 0; /* stdin */ pp[0].events = POLLIN; pp[1].fd = gv_pipe[0]; /* geomview pick */ pp[1].events = POLLIN; poll(pp,2,-1); if ( pp[1].revents ) return read_pick(); #elif !defined(WIN32) /* try using select() from time.h */ fd_set fdset; struct timeval timeout; do { timeout.tv_sec = 5; timeout.tv_usec = 0; /* linux select() modifies */ FD_ZERO(&fdset); FD_SET(0,&fdset); FD_SET(gv_pipe[0],&fdset); } while ( select(FD_SETSIZE,&fdset,NULL,NULL,&timeout) == 0 ); if ( FD_ISSET(gv_pipe[0],&fdset) ) return read_pick(); else return 0; #endif return 0; } #endif /*********************************************************************** * * Function: prompt() * * Purpose: Displays prompt and gets response from user. */ #ifdef USE_READLINE //CSL #include "readline.c" #endif int prompt ARGS3((promptmsg,inmsg,max), char *promptmsg, /* to user */ char *inmsg, /* from user, space already allocated */ int max) /* max char, including terminal null */ { char *c,*ptr; int oldquiet; inmsg[0] = 0; /* in case no message */ #ifdef PTHREAD_LOG /* print thread event log */ if ( threadflag ) { int i; int k[MAXPROCS], kmain = 0; for ( i = 0 ; i < nprocs ; i++ ) k[i] = 0; /* print in time order */ for(;;) { unsigned int lowtime = 0,hightime = 0xFFFFFFFF; int type, lowwhich = -1; struct thread_event *em; for ( i = 0 ; i < nprocs ; i++ ) { struct thread_event *e = thread_data_ptrs[i]->events + k[i]; if ( k[i] >= thread_data_ptrs[i]->eventcount ) continue; if ( (e->time_high < hightime) || ((e->time_high == hightime) && ( e->time_low < lowtime )) ) { lowwhich = i; hightime = e->time_high; lowtime = e->time_low; type = e->type; } } em = main_events + kmain; if ( (kmain < main_eventcount) && ((em->time_high < hightime) || ((em->time_high == hightime) && ( em->time_low < lowtime ))) ) { printf("m %08X%08X %d\n",em->time_high,em->time_low,em->type); kmain++; } else if ( lowwhich >= 0 ) { printf("%d %08X%08X %d\n",lowwhich,hightime,lowtime,type); k[lowwhich]++; } else break; } for ( i = 0 ; i < nprocs ; i++ ) thread_data_ptrs[i]->eventcount = 0; main_eventcount = 0; } #endif if ( commandfd && (commandfd != stdin) ) { /* from command input file */ if (my_fgets(inmsg,max,commandfd) == NULL) { return EOF; } else { oldquiet = quiet_flag; /* quiet_flag = 0; */ #ifdef USE_READLINE //CSL if(promptmsg == MOREPROMPT || promptmsg == CONTPROMPT) { promptmsg="more> "; } #endif if ( !quiet_load_flag ) { outstring(promptmsg); /* so appears on screen also */ outstring(inmsg); /* echo */ } if ( (c = strchr(inmsg,'\n')) != NULL ) { if ( strcmp(promptmsg,"Enter command: ") != 0 ) *c = 0; } else if ( (int)strlen(inmsg) == max-1 ) inmsg[max-1] = MOREIN; quiet_flag = oldquiet; return 1; /* not EOF */ } } breakflag = 0; /* back to user input */ #ifdef USE_READLINE //CSL current_prompt = promptmsg==MOREPROMPT || promptmsg==CONTPROMPT ? "more> " : promptmsg; #else current_prompt = promptmsg; /* for those who want to redisplay prompt */ #endif #if defined(MAC_APP) || defined(WIN32S) || defined(MAC_CW) write_to_console(promptmsg); read_line_from_console(inmsg); #else #if defined(_READLINE_H_) if(use_readline(promptmsg,inmsg,max) == EOF ) return EOF; else #else { /* from stdin */ oldquiet = quiet_flag; quiet_flag = 0; outstring(promptmsg); quiet_flag = oldquiet; #ifdef WIN32 signal(SIGINT,SIG_IGN); /* no interrupt during input (esp. WIN32) */ #endif #ifdef OOGL /* check for geomview pick */ if ( geomview_flag && !geompipe_flag ) while ( check_pick() == 1 ) outstring(promptmsg); #endif if ( my_fgets(inmsg,max,stdin) == NULL ) return EOF; if ( echo_flag ) { outstring(inmsg); outstring("\n"); } signal(SIGINT,catcher); } #endif #endif current_prompt = NULL; /* strip whitespace from start of inmsg */ ptr = inmsg; while ( (*ptr==' ') || (*ptr=='\t') ) ptr++; if ( ptr != inmsg ) { for ( c = inmsg ; *ptr ; ) *(c++) = *(ptr++); *c = 0; } /* strip nl from end */ c = inmsg + strlen(inmsg) - 1; if ( *c == '\n' ) *c = 0; if ( logfile_flag ) fprintf(logfilefd,"%s\n",inmsg); if ( keylogfile_flag ) fprintf(keylogfilefd,"%s\n",inmsg); return 1; /* not EOF */ } /*********************************************************************** * * function: add_path_to_name() * * purpose: form a new file name */ void add_path_to_name ARGS3((path,name,result), const char *path,const char *name, char *result) /* the result */ /* the result has length PATHSIZE */ { char *slash; strncpy(result,path,PATHSIZE-1); result[PATHSIZE-1]=0; slash=strrchr(result,'/'); if(!slash) slash = strrchr(result,'\\'); if(slash) slash++; else slash=result; strncpy(slash,name,PATHSIZE-1-(slash-result)); } /*********************************************************************** * * function: push_datafd() * * purpose: push new datafile include file on stack. */ void push_datafd ARGS2((cfd,name), FILE *cfd, /* NULL if not opened yet */ char *name) /* name of file */ { if ( include_depth >= NESTDEPTH-1 ) kb_error(1352,"INCLUDEs nested too deeply.\n",DATAFILE_ERROR); if ( cfd == NULL ) cfd = path_open(name,NOTDATAFILENAME); /* CSL: try the directory of the previous entry */ if( cfd==NULL && include_depth>0 && datafile_stack[include_depth-1].fd != stdin ) { add_path_to_name(datafile_stack[include_depth-1].filename,name,filename); cfd=path_open(filename,NOTDATAFILENAME); } if ( cfd == NULL ) { sprintf(errmsg,"Cannot open %s.",name); kb_error(1353,errmsg,DATAFILE_ERROR); return; } if ( include_depth > 0 ) datafile_stack[include_depth-1].line = line_no; line_no = 1; strncpy(datafile_stack[include_depth].filename,name,PATHSIZE-1); datafile_stack[include_depth].line = 0; datafile_stack[include_depth++].fd = data_fd = cfd; yylex_init(); /* reset lex */ } /*********************************************************************** * * function: pop_datafd() * * purpose: Exit out of one level of datafile include nest. */ void pop_datafd() { if ( include_depth <= 1 ) { include_depth = 0; data_fd = NULL;} else fclose(data_fd); if ( include_depth > 0 ) { data_fd = datafile_stack[--include_depth-1].fd; line_no = datafile_stack[include_depth-1].line; } else data_fd = NULL; } /*********************************************************************** * * function: push_commandfd() * * purpose: push new command file on stack. */ void push_commandfd ARGS2((cfd,name), FILE *cfd, /* NULL if not opened yet */ char *name) /* name of file */ { if ( (cfd==NULL) && (name==NULL) ) return; if ( read_depth >= NESTDEPTH-1 ) kb_error(1354,"READs nested too deeply.\n",RECOVERABLE); if ( cfd == NULL ) cfd = path_open(name,NOTDATAFILENAME); /* CSL: try the directory of the previous entry */ if( cfd==NULL && read_depth>0 && cmdfile_stack[read_depth-1].fd != stdin ) { add_path_to_name(cmdfile_stack[read_depth-1].filename,name,filename); cfd=path_open(filename,NOTDATAFILENAME); } if ( cfd == NULL ) { sprintf(errmsg,"Cannot open %s.",name); kb_error(1355,errmsg,RECOVERABLE); } if ( (cfd == stdin) && (read_depth == 1) ) read_depth = 0; strncpy(cmdfile_stack[read_depth].filename,name,PATHSIZE-1); cmdfile_stack[read_depth].datafile_flag = datafile_flag; if ( read_depth > 0 ) cmdfile_stack[read_depth-1].line = line_no; /* save previous line number */ line_no = 1; cmdfile_stack[read_depth].fd = commandfd = cfd; if ( file_no_used >= file_no_max ) { file_names = (char**)kb_realloc((char*)file_names, (2*file_no_max+10)*sizeof(char*)); file_no_max = 2*file_no_max+10; } file_names[file_no_used] = mycalloc(strlen(name)+4,1); strcpy(file_names[file_no_used],name); if ( read_depth > 0 ) { file_no = file_no_used; cmdfile_stack[read_depth].file_no = file_no; } file_no_used++; read_depth++; yylex_init(); /* reset lex */ } /*********************************************************************** * * function: pop_commandfd() * * purpose: Exit out of one level of command file nest. */ void pop_commandfd() { if ( in_comment ) { kb_error(2205,"End of file in comment\n",WARNING ); } #ifdef WIN32 if ( commandfd == stdin ) { int tty = _isatty(_fileno(stdin)); if ( !tty ) my_exit(0); /* exit in case of redirected input */ else return; /* Ctrl-C gives spurious EOF */ /* Note: SIGINT is not supported for any Win32 application, including Windows 98/Me and Windows NT/2000/XP. When a CTRL+C interrupt occurs, Win32 operating systems generate a new thread to specifically handle that interrupt. This can cause a single-thread application such as UNIX, to become multithreaded, resulting in unexpected behavior. (MSDN Library on SIGINT ) */ } #endif if ( datafile_input_flag && !cmdfile_stack[read_depth-1].datafile_flag /*&& !addload_flag */) { datafile_input_flag = 0; datafile_flag = 0; /* safe since doing one-char read-ahead */ return; /* lex needs to keep reading EOF for datafile */ } if ( read_depth <= 1 ) { read_depth = 0; commandfd = NULL; file_no = 0;} else fclose(commandfd); if ( read_depth > 0 ) { commandfd = cmdfile_stack[--read_depth-1].fd; line_no = cmdfile_stack[read_depth-1].line; file_no = cmdfile_stack[read_depth-1].file_no; } if ( read_depth <= 1 ) { cmdfilename = NULL; if ( warning_messages_new ) outstring("\nNOTE: There were warning messages. To review, do \"print warning_messages\"\n"); warning_messages_new = 0; } } /************************************************************************** * * function: add_warning_messages * * purpose: appends message to warning message list. */ #define WARNING_MESSAGES_MAX 100000 void add_warning_message ARGS1((message), char *message) { size_t needed = (warning_messages ? strlen(warning_messages):0) + strlen(message) + 10; if ( warning_messages_max < needed ) { if ( warning_messages_max < WARNING_MESSAGES_MAX ) { warning_messages = (char*)kb_realloc(warning_messages,needed+1000); warning_messages_max = needed+1000; } else { /* delete early messages */ char *c; for ( c = warning_messages + 1000 ; *c != '\n' ; c++ ) ; memcpy(warning_messages,c,warning_messages_max-(c-warning_messages)); } } strcat(warning_messages,message); warning_messages_new++; } #ifdef __cplusplus void do_throw ( int errnum ) { throw errnum; } void do_cmd_throw ( ) { struct cmd_excep c; throw c; } void do_graph_throw ( ) { struct graph_excep c; throw c; } #endif /*********************************************************************** * * Purpose: Error handling. Prints error message. If error is * recoverable, longjmps to pre-set spot. If not, exits. * * May be system dependent for message display. */ void kb_error ARGS3((errnum,emsg,mode), int errnum, /* error identifier */ char *emsg, /* might be msg or errmsg */ int mode) { extern int line_no; char *fullmsg; int size; int prev_read_depth; int i; if ( emsg == NULL ) emsg = ""; /* just in case */ if ( emsg == errmsg ) { fullmsg = msg; size = msgmax; } else { fullmsg = errmsg; size = sizeof(errmsg); } last_error = errnum; if ( read_depth > 1 ) { sprintf(fullmsg,"\n%s Line %d:\n", cmdfile_stack[read_depth-1].filename,line_no); } else fullmsg[0] = 0; reading_comp_quant_flag = 0; /* just in case */ switch ( mode ) { case UNRECOVERABLE: sprintf(fullmsg+strlen(fullmsg),"FATAL ERROR %d: ",errnum); strncat(fullmsg,emsg,size); if ( datafile_flag ) dump_buff(fullmsg+strlen(fullmsg),size-strlen(fullmsg)); erroutstring(fullmsg); my_exit(errnum); case RECOVERABLE_ABORT: case RECOVERABLE: sprintf(fullmsg+strlen(fullmsg),"ERROR %d: ",errnum); strncat(fullmsg,emsg,size); /* pop stack of command files */ if ( read_depth > 0 ) cmdfile_stack[read_depth-1].line = line_no; while ( commandfd && (commandfd != stdin) ) { /* sprintf(fullmsg+strlen(fullmsg),"file %s at line %d\n", cmdfile_stack[read_depth-1].filename, cmdfile_stack[read_depth-1].line); */ pop_commandfd(); } strcat(fullmsg,"\n"); erroutstring(fullmsg); goto bailout; case RECOVERABLE_QUIET: /* pop stack of command files */ while ( commandfd && (commandfd != stdin) ) pop_commandfd(); goto bailout; case Q_ERROR: sprintf(fullmsg+strlen(fullmsg),"ERROR %d: ",errnum); strcat(fullmsg,emsg); dump_buff(fullmsg+strlen(fullmsg),size-strlen(fullmsg)); /* pop stack of command files */ if ( read_depth > 0 ) cmdfile_stack[read_depth-1].line = line_no; prev_read_depth = read_depth+1; while ( commandfd && (commandfd != stdin) ) { if ( read_depth < prev_read_depth ) sprintf(fullmsg+strlen(fullmsg),"file %s at line %d\n", cmdfile_stack[read_depth-1].filename, cmdfile_stack[read_depth-1].line); prev_read_depth = read_depth; /* pop_commandfd() doesn't always pop */ if ( datafile_flag ) { parse_error_flag = 1; recovery_flag = 1; erroutstring(fullmsg); if ( exit_after_error ) my_exit(errnum); return; } else pop_commandfd(); } erroutstring(fullmsg); erroutstring("\n"); goto bailout; case WARNING: for ( i = 0 ; i < warnings_suppressed_count ; i++ ) if ( errnum == warnings_suppressed[i] ) return; sprintf(fullmsg+strlen(fullmsg),"WARNING %d: ",errnum); strncat(fullmsg,emsg,size); strcat(fullmsg,"\n"); erroutstring(fullmsg); add_warning_message(fullmsg); if ( exit_after_warning ) my_exit(errnum); if ( break_after_warning ) breakflag = BREAKAFTERWARNING; return; case EXPRESSION_ERROR: sprintf(fullmsg+strlen(fullmsg),"SYNTAX ERROR %d: ",errnum); strncat(fullmsg,emsg,size); dump_buff(fullmsg+strlen(fullmsg),size-strlen(fullmsg)); erroutstring(fullmsg); add_warning_message(fullmsg); if ( ++parse_errors >= 5 ) { erroutstring("Too many errors. Aborting.\n"); /* pop stack of command files */ while ( commandfd && (commandfd != stdin) ) pop_commandfd(); goto bailout; } parse_error_flag = 1; recovery_flag = 1; if ( exit_after_error ) my_exit(errnum); break; case SYNTAX_ERROR: strncat(fullmsg,emsg,size); /* dump_buff(fullmsg+strlen(fullmsg),size-strlen(fullmsg)); */ rewind_globals(old_global_count); perm_rewind_globals(old_perm_global_count); local_nest_depth = 0; /* pop stack of command files */ if ( !function_kludge_flag ) { FILE *oldcommandfd = commandfd; if ( commandfd && (commandfd != stdin) ) pop_commandfd(); while ( commandfd && (commandfd != stdin) ) { char c[100]; if ( commandfd != oldcommandfd ) { sprintf(c,"called from file %s at line %d\n", cmdfile_stack[read_depth-1].filename, cmdfile_stack[read_depth-1].line); strncat(fullmsg,c,size-strlen(fullmsg)); } pop_commandfd(); } } strcat(fullmsg,"\n"); erroutstring(fullmsg); const_expr_flag = 0; if ( exit_after_error ) my_exit(errnum); break; /* return to parser for its message */ case COMMAND_ERROR: if ( datafile_flag ) { /* kludge since cmdjmpbuf empty in datafile */ kb_error(errnum,emsg,DATAFILE_ERROR); return; } sprintf(fullmsg+strlen(fullmsg),"ERROR %d: ",errnum); strncat(fullmsg,emsg,size); dump_buff(fullmsg+strlen(fullmsg),size-strlen(fullmsg)); parse_error_flag = 1; rewind_globals(old_global_count); perm_rewind_globals(old_perm_global_count); /* pop stack of command files */ if ( read_depth > 0 ) cmdfile_stack[read_depth-1].line = line_no; while ( commandfd && (commandfd != stdin) ) { /* char stuff[200]; sprintf(stuff,"file %s at line %d\n", cmdfile_stack[read_depth-1].filename, cmdfile_stack[read_depth-1].line); strncat(fullmsg,stuff,size); */ pop_commandfd(); } strcat(fullmsg,"\n"); erroutstring(fullmsg); #ifdef PTHREADS if ( pthread_equal(locking_thread,pthread_self()) ) { ABORT_GRAPH_MUTEX } #endif #ifdef WINTHREADS if ( GetCurrentThreadId() == locking_thread ) { ABORT_GRAPH_MUTEX } #endif if ( subshell_depth == 0 ) temp_free_all(); if ( exit_after_error ) my_exit(errnum); FPRESET; quiet_flag = 0; #ifdef __cplusplus do_cmd_throw(); #else longjmp(cmdbuf,1); #endif break; case PARSE_ERROR: sprintf(fullmsg+strlen(fullmsg),"SYNTAX ERROR %d: ",errnum); strncat(fullmsg,emsg,size); dump_buff(fullmsg+strlen(fullmsg),size-strlen(fullmsg)); strcat(fullmsg,"\n"); erroutstring(fullmsg); add_warning_message(fullmsg); if ( ++parse_errors >= 5 ) { erroutstring("Too many errors in datafile. Aborting.\n"); /* pop stack of command files */ while ( commandfd && (commandfd != stdin) ) pop_commandfd(); goto bailout; } parse_error_flag = 1; const_expr_flag = 0; recovery_flag = 1; if ( exit_after_error ) my_exit(errnum); break; case DATAFILE_ERROR: sprintf(fullmsg+strlen(fullmsg),"DATAFILE ERROR %d: ",errnum); strncat(fullmsg,emsg,size); dump_buff(fullmsg+strlen(fullmsg),size-strlen(fullmsg)); strcat(fullmsg,"\n"); erroutstring(fullmsg); add_warning_message(fullmsg); if ( ++parse_errors >= 5 ) { erroutstring("Too many errors in datafile. Aborting.\n"); /* pop stack of command files */ while ( commandfd && (commandfd != stdin) ) pop_commandfd(); update_display(); /* so can see what we have so far */ goto bailout; } parse_error_flag = 1; recovery_flag = 1; if ( exit_after_error ) my_exit(errnum); local_nest_depth = 0; return; } quiet_flag = 0; return; bailout: #ifdef MPI_EVOLVER if ( (this_task != 0) && !mpi_local_error_bailout ) { erroutstring("ALL SLAVE TASK ERRORS FATAL! KILLING ALL PROCESSES!\n"); MPI_Abort(MPI_COMM_WORLD,1); } #endif if ( outfd != stdout ) { if ( outfd != NULL ) { fclose(outfd); } outfd = stdout; /* in case of previous piping */ } breakflag = 0; iterate_flag = 0; if ( mode == RECOVERABLE_ABORT ) { subshell_depth = 0; hessian_subshell_flag = 0; } if ( saved.coord ) { restore_coords(&saved,SAVE_IN_ATTR); unsave_coords(&saved,SAVE_IN_ATTR); ABORT_GRAPH_MUTEX; recalc(); } clear_symtable(); /* clear symbol table */ vgrad_end(); /* clear up volume gradients */ memset((char*)(&Met),0,sizeof(Met)); #ifdef PTHREADS if ( pthread_equal(locking_thread,pthread_self()) ) { ABORT_GRAPH_MUTEX } #endif #ifdef WINTHREADS if ( GetCurrentThreadId() == locking_thread ) { ABORT_GRAPH_MUTEX } #endif if ( subshell_depth == 0 ) temp_free_all(); if ( list && (list != permlist) ) { myfree((char*)list); list = NULL; } /* plug memory leak */ quiet_flag = 0; gocount = 1; local_nest_depth = 0; if ( exit_after_error ) my_exit(errnum); FPRESET; ABORT_GRAPH_MUTEX; #ifdef WIN32 if ( draw_thread_id == GetCurrentThreadId() ) #elif defined(PTHREADS) if ( draw_thread_id == pthread_self() ) #endif #if defined(WIN32) || defined(PTHREADS) #ifdef __cplusplus do_graph_throw(); #else if ( *(int*)&graphjumpbuf ) longjmp(graphjumpbuf,1); #endif #endif { struct thread_data *td = GET_THREAD_DATA; td->stack_top = td->eval_stack; td->frame_spot = 0; } #ifdef __cplusplus do_throw(errnum); #else longjmp(jumpbuf[subshell_depth],1); #endif } /************************************************************************** * * function: start_logfile() * * purpose: open logfile and start logging user input and screen output * * arguments: char * filename : Name of file to open, append mode. * If null, use logfilename[]. * */ void start_logfile ARGS1((filename), char *filename) { char *name = filename ? filename : logfilename; if ( name[0] == 0 ) kb_error(2206,"Missing log file name.\n",RECOVERABLE); stop_logfile(); logfilefd = fopen(name,"a"); if ( !logfilefd ) { perror(name); return; } logfile_flag = 1; if ( filename ) strncpy(logfilename,filename,sizeof(logfilename)-1); } /*************************************************************************** * * function: stop_logfile() * * purpose: end logging input and output. */ void stop_logfile() { if ( logfilefd ) fclose(logfilefd); logfilefd = NULL; logfile_flag = 0; } /************************************************************************** * * function: start_keylogfile() * * purpose: open keylogfile and start logging user input * * arguments: char * filename : Name of file to open, append mode. * If null, use keylogfilename[]. * */ void start_keylogfile ARGS1((filename), char *filename) { char *name = filename ? filename : keylogfilename; if ( name[0] == 0 ) kb_error(2420,"Missing key log file name.\n",RECOVERABLE); stop_keylogfile(); keylogfilefd = fopen(name,"a"); if ( !keylogfilefd ) { perror(name); return; } keylogfile_flag = 1; if ( filename ) strncpy(keylogfilename,filename,sizeof(keylogfilename)-1); } /*************************************************************************** * * function: stop_keylogfile() * * purpose: end key logging */ void stop_keylogfile() { if ( keylogfilefd ) fclose(keylogfilefd); keylogfilefd = NULL; keylogfile_flag = 0; } /**************************************************************************** * * function: set_scroll_size() * * purpose: set output console scroll buffer size in Windows */ void set_scroll_size ARGS1((rows), int rows) { #ifdef MSC COORD size; if ( rows < 1) kb_error(2428,"Minimum scroll buffer size is 1. Command ignored.\n", WARNING); size.X = 80; size.Y = (short)rows; SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),size); #else kb_error(2429,"ScrollBufferSize not implemented on this system.\n", WARNING); #endif } evolver-2.30c.dfsg/src/f2c.h0000644000175300017530000001371011410765113016042 0ustar hazelscthazelsct/* f2c.h -- Standard Fortran to C header file */ /** barf [ba:rf] 2. "He suggested using FORTRAN, and everybody barfed." - From The Shogakukan DICTIONARY OF NEW ENGLISH (Second edition) */ #ifndef F2C_INCLUDE #define F2C_INCLUDE typedef int integer; typedef char *address; typedef short int shortint; typedef float real; typedef REAL doublereal; #ifndef KSR typedef struct { real r, i; } complex; #endif typedef struct { doublereal r, i; } doublecomplex; typedef long int logical; typedef short int shortlogical; #define TRUE_ (1) #define FALSE_ (0) #ifdef NOPROTO int ssf_(); int snfmod_( ); int sns_( ); int md_( ); int mdi_( ); int mdm_(); int mdp_( ); int mdu_(); int sro_( ); #else int ssf_(integer*,integer*,integer*,integer*,integer*,integer*,integer*, integer*,integer*,integer*,integer*,integer*,integer*); int snfmod_(integer *,integer *,integer *,integer *,integer *, doublereal *,doublereal *, integer *,integer *,integer *, doublereal *, integer *,integer *,integer *,integer *, doublereal * ); int sns_(integer *,integer *,doublereal *, integer *,integer *,integer *, doublereal *,doublereal *,doublereal *,doublereal *); int md_(integer *,integer *,integer *,integer *,integer *,integer *,integer *, integer *,integer *,integer *,integer *); int mdi_(integer *,integer *,integer *,integer *,integer *,integer *,integer *, integer *,integer *,integer *,integer *,integer *); int mdm_(integer *,integer *,integer *,integer *,integer *,integer *,integer *); int mdp_(integer *,integer *,integer *,integer *,integer *,integer *,integer *, integer *, integer *); int mdu_(integer *,integer *,integer *,integer *,integer *,integer *,integer *, integer *); int sro_(integer *,integer *,integer *,integer *,doublereal *,integer *, integer *,logical *); #endif /* Extern is for use with -E */ #ifndef Extern #define Extern extern #endif /* I/O stuff */ #ifdef f2c_i2 /* for -i2 */ typedef short flag; typedef short ftnlen; typedef short ftnint; #else typedef long flag; typedef long ftnlen; typedef long ftnint; #endif /*external read, write*/ typedef struct { flag cierr; ftnint ciunit; flag ciend; char *cifmt; ftnint cirec; } cilist; /*internal read, write*/ typedef struct { flag icierr; char *iciunit; flag iciend; char *icifmt; ftnint icirlen; ftnint icirnum; } icilist; /*open*/ typedef struct { flag oerr; ftnint ounit; char *ofnm; ftnlen ofnmlen; char *osta; char *oacc; char *ofm; ftnint orl; char *oblnk; } olist; /*close*/ typedef struct { flag cerr; ftnint cunit; char *csta; } cllist; /*rewind, backspace, endfile*/ typedef struct { flag aerr; ftnint aunit; } alist; /* inquire */ typedef struct { flag inerr; ftnint inunit; char *infile; ftnlen infilen; ftnint *inex; /*parameters in standard's order*/ ftnint *inopen; ftnint *innum; ftnint *innamed; char *inname; ftnlen innamlen; char *inacc; ftnlen inacclen; char *inseq; ftnlen inseqlen; char *indir; ftnlen indirlen; char *infmt; ftnlen infmtlen; char *inform; ftnint informlen; char *inunf; ftnlen inunflen; ftnint *inrecl; ftnint *innrec; char *inblank; ftnlen inblanklen; } inlist; #define VOID void union Multitype { /* for multiple entry points */ shortint h; integer i; real r; doublereal d; complex c; doublecomplex z; }; typedef union Multitype Multitype; typedef long Long; struct Vardesc { /* for Namelist */ char *name; char *addr; Long *dims; int type; }; typedef struct Vardesc Vardesc; struct Namelist { char *name; Vardesc **vars; int nvars; }; typedef struct Namelist Namelist; #ifndef abs #define abs(x) ((x) >= 0 ? (x) : -(x)) #endif #ifndef dabs #define dabs(x) (doublereal)abs(x) #endif #ifndef min #define min(a,b) ((a) <= (b) ? (a) : (b)) #define max(a,b) ((a) >= (b) ? (a) : (b)) #endif #define dmin(a,b) (doublereal)min(a,b) #define dmax(a,b) (doublereal)max(a,b) /* procedure parameter types for -A and -C++ */ #define F2C_proc_par_types 1 #ifdef __cplusplus typedef int /* Unknown procedure type */ (*U_fp)(...); typedef shortint (*J_fp)(...); typedef integer (*I_fp)(...); typedef real (*R_fp)(...); typedef doublereal (*D_fp)(...), (*E_fp)(...); typedef /* Complex */ VOID (*C_fp)(...); typedef /* Double Complex */ VOID (*Z_fp)(...); typedef logical (*L_fp)(...); typedef shortlogical (*K_fp)(...); typedef /* Character */ VOID (*H_fp)(...); typedef /* Subroutine */ int (*S_fp)(...); #else typedef int /* Unknown procedure type */ (*U_fp)(); typedef shortint (*J_fp)(); typedef integer (*I_fp)(); typedef real (*R_fp)(); typedef doublereal (*D_fp)(), (*E_fp)(); typedef /* Complex */ VOID (*C_fp)(); typedef /* Double Complex */ VOID (*Z_fp)(); typedef logical (*L_fp)(); typedef shortlogical (*K_fp)(); typedef /* Character */ VOID (*H_fp)(); typedef /* Subroutine */ int (*S_fp)(); #endif /* E_fp is for real functions when -R is not specified */ #ifdef XXX /* confuses some compilers */ typedef VOID C_f; /* complex function */ typedef VOID H_f; /* character function */ typedef VOID Z_f; /* REAL complex function */ #endif typedef doublereal E_f; /* real function with -R not specified */ /* undef any lower-case symbols that your C compiler predefines, e.g.: */ #ifndef Skip_f2c_Undefs #undef cray #undef gcos #undef mc68010 #undef mc68020 #undef mips #undef pdp11 #undef sgi #undef sparc #undef sun #undef sun2 #undef sun3 #undef sun4 #undef u370 #undef u3b #undef u3b2 #undef u3b5 #undef unix #undef vax #endif #endif evolver-2.30c.dfsg/src/skeleton.h0000644000175300017530000006642411410765113017226 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************** * * File: skeleton.h * * Header file for evolver. Defines skeleton structures. * */ /* depth of binary tree addition */ #define MAXADDENDS 30 /********************************************************************** * * structure for expression; goes here instead of express.h since needed */ #define EXPNAMESIZE 64 struct expnode { struct treenode *start; /* start of node list */ struct treenode *root; /* root node, end of list */ struct locallist_t *locals; /* local variable symbol table */ int flag; /* USERCOPY if user must free; HAS_STRING */ int stack_max; /* maximum stack usage (excluding locals)*/ char name[EXPNAMESIZE]; /* for error messages */ REAL elapsed_time; }; /* flags, also used for treenode? */ #define USERCOPY 1 #define NOUSERCOPY 0 #define USING_PARAM_FLAG 0x200 /************************************************************************ * base structure for a skeleton of a particular dimension */ struct skeleton { int type; /* type of element, see defines above */ int dimension; /* dimension of element */ int ctrlpts; /* number of control points */ INDIRECT_TYPE *ibase; /* to indirect list */ int ialloc; /* number elements allocated to ibase */ long maxcount; /* elements allocated */ int alloc; /* number actually in use */ element_id free; /* start of free list */ element_id freelast; /* end of free list */ /* following used if hash storage in effect */ struct element *freehead; /* start of freelist */ int free_spot; /* for scanning for empties */ element_id used; /* start of in-use elements */ element_id last; /* end of in-use chain, for adding on */ element_id discard; /* start of discard list */ int discard_count; /* how many marked for discard */ long count; /* number active */ ORDTYPE max_ord; /* highest ordinal */ int extreme[MAXCOORD+1]; /* indices of corners in Lagrange */ int maxextra; /* extra attr struct allocated */ DY_OFFSET dy_extras; /* extra attributes definitions */ int extra_count; /* number of extra attributes */ } ; /************************************************************************ * * Union skeleton element structure for common operations. * */ typedef int MAP; /* constraint, etc, bitmap type */ typedef long int ATTR; /* attribute bitmap type */ typedef short int tagtype; /* element tag type */ typedef short ETYPE; /* element type type */ typedef long int WRAPTYPE; /* symmetry group element */ typedef int NTYPE; /* for node indexes, types, etc. */ /* common fields; added to each element struct type */ #ifdef MPI_EVOLVER #define BASIC_STUFF \ element_id forechain; /* for element and free list forechain */\ element_id backchain; /* for element and free list backchain */\ element_id local_id; /* for freelisting; uses ibase index */\ ATTR attr; /* attribute bits */\ element_id self_id; /* for identifying self */ #else #define BASIC_STUFF \ element_id forechain; /* for element and free list forechain */\ element_id backchain; /* for element and free list backchain */\ ATTR attr; /* attribute bits */\ element_id self_id; /* for identifying self */ #endif #define COMMON_STUFF \ BASIC_STUFF \ element_id original; /* datafile original number */ \ unsigned short method_count; /* number of method instances */ struct element { COMMON_STUFF }; /***************************************************************** * * Structures peculiar to each dimension of skeleton. */ /**************************************************** * * facetedge structure */ struct facetedge { BASIC_STUFF edge_id fe_edge_id; /* oriented edge of base pair */ facet_id fe_facet_id; /* oriented face of base pair */ facetedge_id nextedge[2]; /* 0 previous, 1 next */ facetedge_id nextfacet[2]; /* 0 previous, 1 next */ }; /**************************************************** * * vertex structure - will be extended by other attributes. */ struct vertex { COMMON_STUFF REAL star; /* area of surrounding facets */ edge_id e_id; /* link to global structure */ /* may really be facet in Lagrange model */ facet_id f_id; /* link to global structure in simplex model */ int valence; /* number of edges incident */ }; /* attribute numbers for standard attributes */ /* be sure these numbers are in same order as allocation in reset_skeleton() */ #define V_COORD_ATTR 0 #define V_OLDCOORD_ATTR 1 #define V_PARAM_ATTR 2 #define V_FORCE_ATTR 3 #define V_VELOCITY_ATTR 4 #define V_CONSTR_LIST_ATTR 5 #define V_METHOD_LIST_ATTR 6 #define V_NORMAL_ATTR 7 extern int V_BOUNDARY_ATTR; /**************************************************** * * edge structure - will be extended by other attributes. */ struct edge { COMMON_STUFF facetedge_id fe_id; /* link to global structure */ edge_id next_vedge[2]; /* link to next edge around vertex */ REAL density; /* energy per unit length */ REAL length; /* edge length; be careful of validity */ REAL star; /* total area of adjacent facets */ short color; /* for display */ }; /* attribute numbers for standard attributes */ /* be sure these numbers are in same order as allocation in reset_skeleton() */ #define E_DENSITY_ATTR 0 #define E_VERTICES_ATTR 1 #define E_VECTOR_ATTR 2 #define E_WRAP_ATTR 3 #define E_CONSTR_LIST_ATTR 4 extern int E_BOUNDARY_ATTR; /****************************************************** * * facet structure - will be extended by other attributes. */ struct facet { COMMON_STUFF facetedge_id fe_id; /* link to global structure */ REAL density; /* for doing real currents and varifolds */ REAL area; /* for diffusion or whatever */ short color; /* for display */ short backcolor; /* for different color backside */ }; /* attribute numbers for standard attributes */ #define F_CONSTR_LIST_ATTR 0 #define F_VERTICES_ATTR 1 #define F_NORMAL_ATTR 2 #define F_BODY_LIST_ATTR 3 #define F_NEXT_VFACET_ATTR 4 #define F_NEXT_BFACET_ATTR 5 extern int F_BOUNDARY_ATTR; extern int F_TAG_ATTR; extern int F_PHASE_ATTR; /********************************************************* * * body structure */ struct body { COMMON_STUFF REAL fixvol; /* volume constraint */ REAL volume; /* actual volume */ REAL volume_addends[MAXADDENDS]; /* for binary tree addition */ REAL oldvolume; /* volume for restore_coords() */ REAL actualvolume; /* set by actual_volume in datafile */ REAL abstotal; /* for judging constraint tolerance */ REAL pressure; /* internal pressure */ REAL oldpressure; /* for restore_coords() */ REAL volconst; /* for body volume constant componenet */ REAL oldvolconst; /* for restore_coords() */ REAL density; /* for gravitational potential energy */ REAL cm[MAXCOORD]; /* center of mass, unit cell coords */ facet_id f_id; /* link to global structure */ int volquant; /* number of named quantity used for volume */ int ambquant; /* number of named quantity used for ambient pressure */ int volmeth; /* number of pos volume method instance */ int voltimestamp; /* of last volume calculation */ int fixnum; /* order in fixvol computations */ }; extern int B_PHASE_ATTR; /********************************************************************* * * Boundaries and constraints */ /* for specifying free boundaries in parametric form */ #define BDRYNAMESIZE 32 struct boundary { char name[BDRYNAMESIZE]; /* name, if any */ ATTR attr; /* attribute flags, see below for #defines */ int pcount; /* number of parameters */ int num; /* boundary number */ struct expnode *coordf[MAXCOORD]; /* coordinate functions for eval() */ struct expnode *envect[MAXCOORD]; /* functions for boundary energy */ struct expnode *convect[MAXCOORD]; /* functions for boundary interior content */ int compcount; /* number of components for integrands */ int energy_method; /* if using named quantity version */ int content_rank; /* for multiple content integrals on vertex or edge */ }; /* for specifying constraints */ #define MAXCONCOMP (MAXCOORD*(MAXCOORD-1)/2) #define CONNAMESIZE 32 struct constraint { char name[CONNAMESIZE]; /* constraint name */ ATTR attr; /* attribute flags, see below for #defines */ struct expnode *formula; /* function expression for eval() */ int compcount; /* number of components for integrands */ struct expnode *envect[MAXCONCOMP]; /* functions for energy integral */ struct expnode *convect[MAXCONCOMP]; /* functions for content integral */ int energy_method; /* if using named quantity version */ int content_rank; /* for multiple content integrals on vertex or edge */ }; /* boundary and constraint attributes */ #define NONPOSITIVE 0x0001 #define NONNEGATIVE 0x0002 #define GLOBAL 0x0004 #define B_CONVEX 0x0008 #define QFIXED 0x0010 #define IN_USE 0x0020 #define CON_ENERGY 0x0040 #define CON_CONTENT 0x0080 #define USURPED_BY_QUANTITY 0x0100 #define NAMED_THING 0x0200 #define NONWALL 0x0400 #define PARTNER_HITTING 0x0800 #define CON_FORWARD_DEF 0x1000 #define BDRY_FORWARD_DEF 0x1000 extern element_id NULLVERTEX, NULLEDGE, NULLFACET, NULLBODY, NULLFACETEDGE; /* vertex volume gradient storage */ /* structures chained from each vertex */ typedef struct volgrad { int fixnum; /* order in fixvol calculations */ int qnum; /* quantity number, or one-sided con num */ body_id bb_id; /* body for real, or which quantity represents or vertex for one-sided con */ struct volgrad *chain; /* next body for this vertex */ REAL *grad; /* volume gradient of body for this vertex */ REAL *velocity; /* form converted to vector */ REAL *raw_velocity; /* for one-sided constraints */ // REAL *perp; /* velocity component perpendicular to pointwise const */ } volgrad; struct vgradblock /* block header */ { struct volgrad *base; /* structure space */ REAL *values; /* for variable dimension data */ int max; /* number allocated */ int top; /* number used */ struct vgradblock *next; /* chaining */ }; extern struct vgradblock *vgradbase; /* allocated list header block start */ extern int vgradtop; /* number of first free structure */ extern long vgradmax; /* number allocated */ extern long vgradlastused; /* number actually used last time round */ /* loop macros, for use when lists are not modified in loop */ #ifdef MPI_EVOLVER /* MFOR includes imported elements; FOR doesn't */ /* except FOR_ALL_BODIES includes all, for now */ #define FOR_ALL_ELEMENTS(type,id) \ for (id = web.skel[type].used ; valid_id(id) ; id = elptr(id)->forechain) \ if ( !valid_element(id) || (id_task(id)!=this_task) ) continue; else #define FOR_ALL_VERTICES(v_id) \ for (v_id = web.skel[VERTEX].used ; valid_id(v_id) ; \ v_id = vptr(v_id)->forechain) \ if ( !valid_element(v_id) || (id_task(v_id)!=this_task) ) continue; else #define FOR_ALL_EDGES(e_id) \ for (e_id = web.skel[EDGE].used ; valid_id(e_id) ;\ e_id = eptr(e_id)->forechain ) \ if ( !valid_element(e_id) || (id_task(e_id)!=this_task) )\ continue; else #define FOR_ALL_FACETS(f_id) \ for (f_id = web.skel[FACET].used ; valid_id(f_id) ; \ f_id = fptr(f_id)->forechain) \ if ( !valid_element(f_id) || (id_task(f_id)!=this_task) ) continue; else #define FOR_ALL_BODIES(b_id) \ for (b_id = web.skel[BODY].used ; valid_id(b_id) ; \ b_id = bptr(b_id)->forechain) \ if ( !valid_element(b_id) /* || (id_task(b_id)!=this_task)*/ ) continue; else #define FOR_ALL_FACETEDGES(fe_id) \ for (fe_id = web.skel[FACETEDGE].used ; valid_id(fe_id) ; \ fe_id = feptr(fe_id)->forechain) \ if ( !valid_element(fe_id) || (id_task(fe_id)!=this_task) ) continue; else #define MFOR_ALL_ELEMENTS(type,id) \ for (id = web.skel[type].used ; valid_id(id) ; id = elptr(id)->forechain) \ if ( !valid_element(id) ) continue; else #define MFOR_ALL_VERTICES(v_id) \ for (v_id = web.skel[VERTEX].used ; valid_id(v_id) ; \ v_id = vptr(v_id)->forechain) \ if ( !valid_element(v_id) ) continue; else #define MFOR_ALL_EDGES(e_id) \ for (e_id = web.skel[EDGE].used ; valid_id(e_id) ; \ e_id = eptr(e_id)->forechain) \ if ( !valid_element(e_id) ) continue; else #define MFOR_ALL_FACETS(f_id) \ for (f_id = web.skel[FACET].used ; valid_id(f_id) ; \ f_id = fptr(f_id)->forechain) \ if ( !valid_element(f_id) ) continue; else #define MFOR_ALL_BODIES(b_id) \ for (b_id = web.skel[BODY].used ; valid_id(b_id) ; \ b_id = bptr(b_id)->forechain) \ if ( !valid_element(b_id) ) continue; else #define MFOR_ALL_FACETEDGES(fe_id) \ for (fe_id = web.skel[FACETEDGE].used ; valid_id(fe_id) ; \ fe_id = feptr(fe_id)->forechain) \ if ( !valid_element(fe_id) ) continue; else #else /* non-MPI */ #define FOR_ALL_ELEMENTS(type,id) \ for (id = web.skel[type].used ; valid_id(id) ; id = elptr(id)->forechain) \ if ( !(elptr(id)->attr & ALLOCATED) ) continue; else #define FOR_ALL_VERTICES(v_id) \ for (v_id = web.skel[VERTEX].used ; valid_id(v_id) ; \ v_id = vptr(v_id)->forechain) \ if ( !(vptr(v_id)->attr & ALLOCATED) ) continue; else #define FOR_ALL_EDGES(e_id) \ for (e_id = web.skel[EDGE].used ; valid_id(e_id) ; \ e_id = eptr(e_id)->forechain) \ if ( !(eptr(e_id)->attr & ALLOCATED) ) continue; else #define FOR_ALL_FACETS(f_id) \ for (f_id = web.skel[FACET].used ; valid_id(f_id) ; \ f_id = fptr(f_id)->forechain) \ if ( !(fptr(f_id)->attr & ALLOCATED) ) continue; else #define FOR_ALL_BODIES(b_id) \ for (b_id = web.skel[BODY].used ; valid_id(b_id) ; \ b_id = bptr(b_id)->forechain) \ if ( !(bptr(b_id)->attr & ALLOCATED) ) continue; else #define FOR_ALL_FACETEDGES(fe_id) \ for (fe_id = web.skel[FACETEDGE].used ; valid_id(fe_id) ; \ fe_id = feptr(fe_id)->forechain) \ if ( !(feptr(fe_id)->attr & ALLOCATED) ) continue; else #define MFOR_ALL_VERTICES(v_id) FOR_ALL_VERTICES(v_id) #define MFOR_ALL_EDGES(e_id) FOR_ALL_EDGES(e_id) #define MFOR_ALL_FACETS(f_id) FOR_ALL_FACETS(f_id) #define MFOR_ALL_BODIES(b_id) FOR_ALL_BODIES(b_id) #define MFOR_ALL_FACETEDGES(fe_id) FOR_ALL_FACETEDGES(fe_id) #endif #ifdef THREADS /* Thread version of these loops depend on initialization of the iteration variable in thread_launch. */ #define THR_ALL(id) for ( ; valid_id(id = thread_next_element()) ; ) #define THREAD_FOR_ALL_ELEMENTS(type,id) \ for ( threadflag ? 0 : (global_id = web.skel[type].used) ; \ valid_id(id = thread_next_element()) ; ) #define THREAD_FOR_ALL_VERTICES(v_id) \ for ( threadflag ? 0 : (global_id = web.skel[VERTEX].used) ; \ valid_id(v_id = thread_next_element()) ; ) #define THREAD_FOR_ALL_EDGES(e_id) \ for ( threadflag ? 0 : (global_id = web.skel[EDGE].used) ; \ valid_id(e_id = thread_next_element()) ; ) #define THREAD_FOR_ALL_FACETS(f_id) \ for ( threadflag ? 0 : (global_id = web.skel[FACET].used) ; \ valid_id(f_id = thread_next_element()) ; ) #define THREAD_FOR_ALL_BODIES(b_id) \ for ( threadflag ? 0 : (global_id = web.skel[BODY].used) ; \ valid_id(b_id = thread_next_element()) ; ) #define THREAD_FOR_ALL_FACETEDGES(fe_id) \ for ( threadflag ? 0 : (global_id = web.skel[FACETEDGE].used) ; \ valid_id(fe_id = thread_next_element()) ; ) #else #define THREAD_FOR_ALL_ELEMENTS(type,id) FOR_ALL_ELEMENTS(type,id) #define THREAD_FOR_ALL_VERTICES(v_id) FOR_ALL_VERTICES(v_id) #define THREAD_FOR_ALL_EDGES(e_id) FOR_ALL_EDGES(e_id) #define THREAD_FOR_ALL_FACETS(f_id) FOR_ALL_FACETS(f_id) #define THREAD_FOR_ALL_BODIES(b_id) FOR_ALL_BODIES(b_id) #define THREAD_FOR_ALL_FACETEDGES(fe_id) FOR_ALL_FACETEDGES(fe_id) #endif /* These access functions are defined as macros, since they involve only one evaluation of the arguments */ #define get_edge_midv(e_id) (get_edge_vertices(e_id)[2]) #define get_fe_side(fe_id,x) get_edge_side(get_fe_edge(fe_id),x) /* attribute macros */ #define EXTRAS(type) ((struct extra *)(dymem + web.skel[type].dy_extras)) #define V_EXTRA(v,attr) ((char*)(vptr(v))+EXTRAS(VERTEX)[attr].offset) #define E_EXTRA(e,attr) ((char*)(eptr(e))+EXTRAS(EDGE)[attr].offset) #define F_EXTRA(f,attr) ((char*)(fptr(f))+EXTRAS(FACET)[attr].offset) #define VREAL(v,attr) ((REAL*)((char*)(vptr(v))+EXTRAS(VERTEX)[attr].offset)) #define VREALP(vp,attr) ((REAL*)((char*)vp+EXTRAS(VERTEX)[attr].offset)) #define VINT(v,attr) ((int*)((char*)(vptr(v))+EXTRAS(VERTEX)[attr].offset)) #define VPTR(v,attr) ((char**)((char*)(vptr(v))+EXTRAS(VERTEX)[attr].offset)) #define V_ELID(v,attr) ((element_id*)((char*)(eptr(e))+EXTRAS(VERTEX)[attr].offset)) #define EREAL(e,attr) ((REAL*)((char*)(eptr(e))+EXTRAS(EDGE)[attr].offset)) #define EINT(e,attr) ((int*)((char*)(eptr(e))+EXTRAS(EDGE)[attr].offset)) #define EULONG(e,attr) ((unsigned long*)((char*)(eptr(e))+EXTRAS(EDGE)[attr].offset)) #define E_ELID(e,attr) ((element_id*)((char*)(eptr(e))+EXTRAS(EDGE)[attr].offset)) #define FREAL(f,attr) ((REAL*)((char*)(fptr(f))+EXTRAS(FACET)[attr].offset)) #define FINT(f,attr) ((int*)((char*)(fptr(f))+EXTRAS(FACET)[attr].offset)) #define FULONG(f,attr) ((unsigned long*)((char*)(fptr(f))+EXTRAS(FACET)[attr].offset)) #define F_ELID(f,attr) ((element_id*)((char*)(fptr(f))+EXTRAS(FACET)[attr].offset)) #define BREAL(b,attr) ((REAL*)((char*)(bptr(b))+EXTRAS(BODY)[attr].offset)) #define BINT(b,attr) ((int*)((char*)(bptr(b))+EXTRAS(BODY)[attr].offset)) #define BULONG(b,attr) ((unsinged long*)((char*)(bptr(b))+EXTRA(BODY)[attr].offset)) #define B_ELID(b,attr) ((element_id*)((char*)(bptr(b))+EXTRA(BODY)[attr].offset)) #define FEREAL(fe,attr) ((REAL*)((char*)(feptr(fe))+EXTRAS(FACETEDGE)[attr].offset)) #define FEINT(fe,attr) ((int*)((char*)(feptr(fe))+EXTRAS(FACETEDGE)[attr].offset)) #define get_coord(v_id) VREAL((v_id),V_COORD_ATTR) #define get_oldcoord(v_id) VREAL((v_id),V_OLDCOORD_ATTR) #define get_param(v_id) VREAL((v_id),V_PARAM_ATTR) #define get_boundary(v_id) (V_BOUNDARY_ATTR ?\ web.boundaries+*VINT(v_id,V_BOUNDARY_ATTR) : NULL) #define get_vertex_boundary(v_id) (V_BOUNDARY_ATTR ?\ web.boundaries+*VINT(v_id,V_BOUNDARY_ATTR) : NULL) #define get_vertex_boundary_num(v_id) (V_BOUNDARY_ATTR ?\ *VINT(v_id,V_BOUNDARY_ATTR) : 0) #define get_edge_boundary_num(e_id) (E_BOUNDARY_ATTR ?\ *EINT(e_id,E_BOUNDARY_ATTR) : 0) #define get_facet_boundary_num(f_id) (F_BOUNDARY_ATTR ?\ *FINT(f_id,F_BOUNDARY_ATTR) : 0) #define set_boundary_num(v_id,bnum) (V_BOUNDARY_ATTR ?\ (*VINT(v_id,V_BOUNDARY_ATTR) = (bnum)) : 0) #define set_vertex_boundary_num(v_id,bnum) (V_BOUNDARY_ATTR ?\ (*VINT(v_id,V_BOUNDARY_ATTR) = (bnum)) : 0) #define get_edge_boundary(e_id) (E_BOUNDARY_ATTR ?\ web.boundaries+*EINT(e_id,E_BOUNDARY_ATTR) : NULL) #define set_edge_boundary_num(e_id,bnum) (E_BOUNDARY_ATTR ?\ (*EINT(e_id,E_BOUNDARY_ATTR) = (bnum)) : 0) #define get_facet_boundary(f_id) (F_BOUNDARY_ATTR ? \ web.boundaries+*FINT(f_id,F_BOUNDARY_ATTR) : 0) #define set_facet_boundary_num(f_id,bnum) (F_BOUNDARY_ATTR ?\ (*FINT(f_id,F_BOUNDARY_ATTR) = (bnum)) : 0) #define get_vertex_vhead(v_id) (vhead + (*VINT(v_id,vhead_attr))) #define get_vertex_v_normal(v_id) (v_normal[*VINT(v_id,vhead_attr)]) #define set_vertex_vhead(v_id,k) (*VINT(v_id,vhead_attr)=(k)) #define get_body_vhead(b_id) (vhead + (*BINT(b_id,bhead_attr))) #define set_body_vhead(b_id,k) (*BINT(b_id,bhead_attr)=(k)) #define get_body_cm(b_id) (bptr(b_id)->cm) /* Constraints */ typedef unsigned int conmap_t; /* in element lists */ /* indicates vertex hit constraint */ #define CON_HIT_BIT (((conmap_t)1)<<(8*sizeof(conmap_t)-1)) #define TEMP_CON_HIT_BIT (((conmap_t)1)<<(8*sizeof(conmap_t)-2)) /* maximum possible constraints given size of conmap_t */ #define MAXMAXCON ((((unsigned int)1)<<(8*sizeof(conmap_t)-2))-1) /* mask to strip hit bit */ #define CONMASK (MAXMAXCON) /* maximum size of per-element global constraint list */ #define MAXCONPER 23 extern conmap_t nullcon[2]; /* default empty constraint list */ #define get_constraint(n) (GETCONSTR((n)&CONMASK)) #define get_v_constraint_map(v_id) \ ( EXTRAS(VERTEX)[V_CONSTR_LIST_ATTR].array_spec.datacount ? \ (conmap_t*)(V_EXTRA(v_id,V_CONSTR_LIST_ATTR)) : nullcon) #define get_e_constraint_map(e_id) \ ( EXTRAS(EDGE)[E_CONSTR_LIST_ATTR].array_spec.datacount ? \ (conmap_t*)(E_EXTRA(e_id,E_CONSTR_LIST_ATTR)) : nullcon) #define get_f_constraint_map(f_id) \ ( EXTRAS(FACET)[F_CONSTR_LIST_ATTR].array_spec.datacount ? \ (conmap_t*)(F_EXTRA(f_id,F_CONSTR_LIST_ATTR)) : nullcon) #define get_force(v_id) VREAL((v_id),V_FORCE_ATTR) #define get_force_p(vp) VREALP((vp),V_FORCE_ATTR) #define get_velocity(v_id) VREAL((v_id),V_VELOCITY_ATTR) #define get_restore(v_id) VREAL((v_id),V_RESTORE_ATTR) #define set_vertex_edge(v_id,e) (vptr(v_id)->e_id = (e)) #define get_vertex_edge(v_id) (vptr(v_id)->e_id) #define get_vertex_facet(v_id) (vptr(v_id)->f_id) #define set_vertex_facet(v_id,ff_id) (vptr(v_id)->f_id=ff_id) #define get_vertex_star(v_id) (vptr(v_id)->star) #define set_vertex_star(v_id,a) (vptr(v_id)->star = (a)) #define add_vertex_star(v_id,a) (vptr(v_id)->star += (a)) #define set_vertex_valence(v_id,n) (vptr(v_id)->valence = (n)) #define add_vertex_valence(v_id,n) (vptr(v_id)->valence += (n)) #define get_vertex_valence(v_id) (vptr(v_id)->valence) #define get_fe_wrap(fe_id) get_edge_wrap(get_fe_edge(fe_id)) #define get_fe_tailv(fe_id) get_edge_tailv(get_fe_edge(fe_id)) #define get_fe_headv(fe_id) get_edge_headv(get_fe_edge(fe_id)) #define get_fe_midv(fe_id) get_edge_midv(get_fe_edge(fe_id)) #define set_edge_length(e_id,x) (eptr(e_id)->length = (x)) #define get_edge_density(e_id) (eptr(e_id)->density) #define set_edge_density(e_id,den) (eptr(e_id)->density = (den)) #define get_edge_star(e_id) (eptr(e_id)->star) #define set_edge_star(e_id,a) (eptr(e_id)->star = (a)) #define add_edge_star(e_id,a) (eptr(e_id)->star += (a)) #define get_facet_vertices(f_id) F_ELID(f_id,F_VERTICES_ATTR) #define get_edge_vertices(e_id) E_ELID(e_id,E_VERTICES_ATTR) #define get_facet_density(f_id) (fptr(f_id)->density) #define set_facet_density(f_id,den) (fptr(f_id)->density = (den)) #define get_facet_area(f_id) (fptr(f_id)->area) #define set_facet_area(f_id,a) (fptr(f_id)->area = (a)) #define add_facet_area(f_id,a) (fptr(f_id)->area += inverted(f_id)?-(a):(a)) #define get_facet_flux(f_id) (fptr(f_id)->flux) #define set_facet_flux(f_id,a) (fptr(f_id)->flux = (a)) #define get_body_voltimestamp(b_id) (bptr(b_id)->voltimestamp) #define get_body_volquant(b_id) (bptr(b_id)->volquant) #define set_body_volquant(b_id,n) (bptr(b_id)->volquant = (n)) #define get_body_fixnum(b_id) (bptr(b_id)->fixnum) #define set_body_fixnum(b_id,n) (bptr(b_id)->fixnum = (n)) #define get_body_ambquant(b_id) (bptr(b_id)->ambquant) #define set_body_ambquant(b_id,n) (bptr(b_id)->ambquant = (n)) #define get_body_volmeth(b_id) (bptr(b_id)->volmeth) #define set_body_volmeth(b_id,n) (bptr(b_id)->volmeth = (n)) #define save_body_volume(b_id) (bptr(b_id)->oldvolume = bptr(b_id)->volume) #define restore_body_volume(b_id) (bptr(b_id)->volume = bptr(b_id)->oldvolume) #define save_body_pressure(b_id) (bptr(b_id)->oldpressure = bptr(b_id)->pressure) #define restore_body_pressure(b_id) (bptr(b_id)->pressure = bptr(b_id)->oldpressure) #define save_body_volconst(b_id) (bptr(b_id)->oldvolconst = bptr(b_id)->volconst) #define restore_body_volconst(b_id) (bptr(b_id)->volconst = bptr(b_id)->oldvolconst) #define get_body_oldvolume(b_id) (bptr(b_id)->oldvolume) #define set_body_oldvolume(b_id,vol) (bptr(b_id)->oldvolume = vol) #define get_body_actualvolume(b_id) (bptr(b_id)->actualvolume) #define set_body_actualvolume(b_id,vol) (bptr(b_id)->actualvolume = vol) #define get_v_extra(v_id,n) ((char*)vptr(v_id) \ + EXTRAS(VERTEX)[n].offset) #define get_e_extra(e_id,n) ((char*)eptr(e_id) \ + EXTRAS(EDGE)[n].offset) #define get_f_extra(f_id,n) ((char*)fptr(f_id) \ + EXTRAS(FACET)[n].offset) #define get_b_extra(b_id,n) ((char*)bptr(b_id) \ + EXTRAS(BODY)[n].offset) #define get_attr(id) (elptr(id)->attr) #define get_vattr(id) (vptr(id)->attr) #define get_eattr(id) (eptr(id)->attr) #define get_fattr(id) (fptr(id)->attr) #define get_battr(id) (bptr(id)->attr) #define get_feattr(id) (feptr(id)->attr) #define set_f_phase(f_id,p) (EXTRAS(FACET)[F_PHASE_ATTR].array_spec.datacount ? (*FINT(f_id,F_PHASE_ATTR) = (p)):0) #define set_b_phase(b_id,p) (EXTRAS(BODY)[B_PHASE_ATTR].array_spec.datacount ? (*BINT(b_id,B_PHASE_ATTR) = (p)):0) #define get_f_phase(f_id) ((EXTRAS(FACET)[F_PHASE_ATTR].array_spec.datacount && valid_id(f_id)) ? (*FINT(f_id,F_PHASE_ATTR)):0) #define get_b_phase(b_id) ((EXTRAS(BODY)[B_PHASE_ATTR].array_spec.datacount && valid_id(b_id)) ? (*BINT(b_id,B_PHASE_ATTR)):0) #define set_original(id,oid) (elptr(id)->original = (oid) ) #define get_original(id) (elptr(id)->original) #define set_edge_color(e_id,col) (eptr(e_id)->color = (short)(col)) #define get_edge_color(e_id) (eptr(e_id)->color) #define set_facet_color(f_id,col) (fptr(f_id)->color = fptr(f_id)->backcolor = (short)(col)) #define get_facet_color(f_id) (fptr(f_id)->color) #define set_facet_frontcolor(f_id,col) \ (inverted(f_id) ? (fptr(f_id)->backcolor = (short)(col)) : (fptr(f_id)->color = (short)(col))) #define get_facet_frontcolor(f_id) \ (inverted(f_id) ? fptr(f_id)->backcolor : fptr(f_id)->color ) #define set_facet_backcolor(f_id,col) \ (inverted(f_id) ? (fptr(f_id)->color = (short)(col)) : (fptr(f_id)->backcolor = (short)(col))) #define get_facet_backcolor(f_id) \ (inverted(f_id) ? fptr(f_id)->color : fptr(f_id)->backcolor ) evolver-2.30c.dfsg/src/sqcurve.c0000644000175300017530000012054511410765113017060 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************** * * File: sqcurve.c * * Purpose: Does calculations needed for including square curvature * in energy. Linear model only. */ #include "include.h" REAL total_sqcurve; /* total square curvature integral */ REAL total_length; int h0_flag; /* set to use (H - H_0)^2 */ REAL h0_value; /* value of H_0 */ REAL selfsim_coeff; /* self similarity */ /*********************************************************************** * * Function: sqcurve_energy_init() * * Purpose: Initializes data structures for square curvature. * Call before doing facet loop in calc_energy or calc_force. */ void sqcurve_energy_init() { int k; int eltype; total_sqcurve = 0.0; total_length = 0.0; if ( web.representation != SOAPFILM ) return; /* not needed for string */ v_curve = (struct v_curve_t *)temp_calloc(web.skel[VERTEX].max_ord+1, sizeof(struct v_curve_t)); /* see if using (H - H_0)^2 adjustment */ h0_flag = 0; h0_attr = find_extra("h_zero",&eltype); if ( h0_attr >= 0 ) h0_flag = H0_IN_ATTR; else { k = lookup_global("h_zero"); if ( k >= 0 ) { h0_flag = H0_IN_GLOBAL; h0_value = globals(k)->value.real; } } if ( self_similar_flag ) { int param = lookup_global(SELFSIM_NAME); if ( param < 0 ) /* missing, so add */ { param = add_global(SELFSIM_NAME); globals(param)->value.real = 1.0; /* default */ globals(param)->flags |= ORDINARY_PARAM | RECALC_PARAMETER | ALWAYS_RECALC; } selfsim_coeff = globals(param)->value.real/6; /* area and volume factor */ if ( h0_flag == 0 ) { h0_flag = H0_IN_GLOBAL; h0_value = 0.0; } } } /*********************************************************************** * * Function: sqcurve_force_init() * * Purpose: Initializes data structures for square curvature. * Call before doing facet loop in calc_energy or calc_force. */ void sqcurve_force_init() { int k; int eltype; if ( web.representation != SOAPFILM ) return; /* not needed for string */ v_curve = (struct v_curve_t *)temp_calloc(web.skel[VERTEX].max_ord+1, sizeof(struct v_curve_t)); e_curve = (struct e_curve_t *)temp_calloc(web.skel[EDGE].max_ord+1, sizeof(struct e_curve_t)); /* see if using (H - H_0)^2 adjustment */ h0_flag = 0; h0_attr = find_extra("h_zero",&eltype); if ( h0_attr >= 0 ) h0_flag = H0_IN_ATTR; else { k = lookup_global("h_zero"); if ( k >= 0 ) { h0_flag = H0_IN_GLOBAL; h0_value = globals(k)->value.real; } } if ( self_similar_flag ) { int param = lookup_global(SELFSIM_NAME); if ( param < 0 ) /* missing, so add */ { param = add_global(SELFSIM_NAME); globals(param)->value.real = 1.0; /* default */ globals(param)->flags |= ORDINARY_PARAM | RECALC_PARAMETER; } selfsim_coeff = globals(param)->value.real/6; /* area and volume factor */ if ( h0_flag == 0 ) { h0_flag = H0_IN_GLOBAL; h0_value = 0.0; } } } /******************************************************************** * * Function: sqcurve_energy() * * Purpose: Does square curvature energy calculation for a facet. * */ void sqcurve_energy(v_id,side) vertex_id *v_id; /* vertex list for facet */ REAL (*side)[MAXCOORD]; /* side vectors */ { REAL t1t1,t1t2,t2t2; REAL det; struct v_curve_t *vc[FACET_VERTS]; int i,j; REAL area; t1t1 = SDIM_dot(side[0],side[0]); t1t2 = SDIM_dot(side[0],side[1]); t2t2 = SDIM_dot(side[1],side[1]); det = t1t1*t2t2 - t1t2*t1t2; area = sqrt(det)/2; for ( i = 0 ; i < FACET_VERTS ; i++ ) { vc[i] = v_curve + loc_ordinal(v_id[i]); vc[i]->area += area; } if ( boundary_curvature_flag ) /* apportion area differently */ { int fixcount = 0; for ( i = 0 ; i < FACET_VERTS ; i++ ) if ( get_vattr(v_id[i]) & (BOUNDARY|FIXED) ) fixcount++; for ( i = 0 ; i < FACET_VERTS ; i++ ) { vc[i] = v_curve+loc_ordinal(v_id[i]); if ( !(get_vattr(v_id[i]) & (BOUNDARY|FIXED)) ) vc[i]->a += 3*area/(3-fixcount); } } if ( area > 0.0 ) for ( i = 0 ; i < SDIM ; i++ ) { vc[0]->force[i] -= (t2t2*side[0][i]-t1t2*side[1][i])/4/area; vc[1]->force[i] += (t2t2*side[0][i]-t1t2*side[1][i])/4/area; vc[2]->force[i] += (t1t1*side[1][i]-t1t2*side[0][i])/4/area; vc[1]->force[i] -= (t1t1*side[1][i]-t1t2*side[0][i])/4/area; } if ( effective_area_flag || normal_curvature_flag || div_normal_curvature_flag || self_similar_flag || h0_flag ) /* accumulate normal vector at each vertex */ { REAL normal[MAXCOORD]; cross_prod(side[0],side[1],normal); for ( i = 0 ; i < 3 ; i ++ ) for ( j = 0 ; j < SDIM ; j++ ) vc[i]->normal[j] += normal[j]; } } /************************************************************************ * * Function: sqcurve_force() * * Purpose: Does square curvature force calculation for a facet. * */ void sqcurve_force(v_id,e_id,side) vertex_id *v_id; /* vertex list of facet */ edge_id *e_id; /* edge list */ REAL (*side)[MAXCOORD]; /* side vectors */ { REAL det; struct v_curve_t *vc[FACET_VERTS]; int i,j,k; REAL force[FACET_VERTS][MAXCOORD]; REAL tt[FACET_VERTS][FACET_VERTS]; /* side dot products */ REAL area; struct e_curve_t *ec[FACET_EDGES]; int fixcount=0; for ( j = 0 ; j < FACET_VERTS ; j++ ) for ( k = 0 ; k <= j ; k++ ) tt[j][k] = tt[k][j] = SDIM_dot(side[j],side[k]); det = tt[0][0]*tt[1][1] - tt[0][1]*tt[0][1]; area = sqrt(det)/2; for ( i = 0 ; i < FACET_VERTS ; i++ ) { vc[i] = v_curve + loc_ordinal(v_id[i]); vc[i]->area += area; } if ( boundary_curvature_flag ) /* apportion area differently */ { fixcount = 0; for ( i = 0 ; i < FACET_VERTS ; i++ ) if ( get_vattr(v_id[i]) & (BOUNDARY|FIXED) ) fixcount++; for ( i = 0 ; i < FACET_VERTS ; i++ ) if ( !(get_vattr(v_id[i]) & (BOUNDARY|FIXED)) ) vc[i]->a += 3*area/(3-fixcount); } for ( i = 0 ; i < FACET_EDGES ; i++ ) ec[i] = e_curve + loc_ordinal(e_id[i]); memset((char*)force,0,sizeof(force)); for ( j = 0 ; j < FACET_VERTS ; j++ ) { int i1 = (j+1)%FACET_VERTS; int i2 = (j+2)%FACET_VERTS; for ( i = 0 ; i < SDIM ; i++ ) force[j][i] = (tt[i1][i1]*side[i2][i] - tt[i1][i2]*side[i1][i])/4/area; } /* first and second derivatives at vertices */ for ( i = 0 ; i < FACET_VERTS ; i++ ) { int ii = (i+1)%FACET_VERTS; /* opposite side from vertex i */ for ( j = 0 ; j < SDIM ; j++ ) vc[i]->force[j] += force[i][j]; if ( boundary_curvature_flag && (fixcount != 3) ) for ( j = 0 ; j < SDIM ; j++ ) vc[i]->star_force[j] += 3*force[i][j]/(3-fixcount); for ( j = 0 ; j < SDIM ; j++ ) { vc[i]->deriv2[j][j] += tt[ii][ii]/4/area; for ( k = 0 ; k < SDIM ; k++ ) vc[i]->deriv2[j][k] -= (force[i][j]*force[i][k]+0.25*side[ii][j] *side[ii][k])/area; } } /* now first and second derivatives on edges */ for ( i = 0 ; i < FACET_EDGES ; i++ ) { int i1 = (i+1)%FACET_EDGES; int i2 = (i+2)%FACET_EDGES; for ( j = 0 ; j < SDIM ; j++ ) { ec[i]->deriv2[j][j] += tt[i1][i2]/4/area; for ( k = 0 ; k < SDIM ; k++ ) if ( inverted(e_id[i]) ) ec[i]->deriv2[k][j] += (-side[i1][j]*side[i2][k]/2 + side[i2][j]*side[i1][k]/4 -force[i1][j]*force[i][k])/area; else ec[i]->deriv2[j][k] += (-side[i1][j]*side[i2][k]/2 + side[i2][j]*side[i1][k]/4 -force[i1][j]*force[i][k])/area; if ( boundary_curvature_flag && (get_vattr(v_id[i2]) & (FIXED|BOUNDARY)) ) { if ( inverted(e_id[i]) ) { ec[i]->aderiv[0][j] += 0.5*force[i1][j]; ec[i]->aderiv[1][j] += 0.5*force[i][j]; } else { ec[i]->aderiv[0][j] += 0.5*force[i][j]; ec[i]->aderiv[1][j] += 0.5*force[i1][j]; } } if ( inverted(e_id[i]) ) { ec[i]->deriv[0][j] += force[i1][j]; ec[i]->deriv[1][j] += force[i][j]; } else { ec[i]->deriv[0][j] += force[i][j]; ec[i]->deriv[1][j] += force[i1][j]; } } if ( self_similar_flag ) { REAL cp[MAXCOORD]; int ii; cross_prod(get_coord(get_edge_tailv(e_id[i])), get_coord(get_edge_headv(e_id[i])), cp); ii = inverted(e_id[i1]) ? 0 : 1; for ( j = 0 ; j < SDIM ; j++ ) ec[i1]->volderiv[ii][j] += cp[j]; ii = inverted(e_id[i2]) ? 1 : 0; for ( j = 0 ; j < SDIM ; j++ ) ec[i2]->volderiv[ii][j] += cp[j]; } } if ( effective_area_flag || normal_curvature_flag || div_normal_curvature_flag || self_similar_flag || h0_flag ) /* accumulate normal vector at each vertex */ { REAL normal[MAXCOORD]; cross_prod(side[0],side[1],normal); for ( i = 0 ; i < 3 ; i ++ ) for ( j = 0 ; j < SDIM ; j++ ) vc[i]->normal[j] += normal[j]; } } /************************************************************************* * * function: sqcurve_energy_end() * * purpose: Convert square curvature data into energy. * Call at end of facet loop in calc_energy. * */ void sqcurve_energy_end() { vertex_id v_id; REAL modulus = globals(square_curvature_param)->value.real; REAL energy = 0.0; REAL denom; facet_id f_id; if ( web.representation == STRING ) { binary_tree_add(web.total_energy_addends,total_sqcurve); return; /* rest not needed for string */ } if ( div_normal_curvature_flag ) { FOR_ALL_FACETS(f_id) { REAL *norm[3]; /* vertex normals */ REAL side[3][3]; /* facet sides */ REAL normal[3]; /* facet normal */ int i; REAL h; facetedge_id fe; for ( i = 0, fe = get_facet_fe(f_id) ; i < 3 ; i++ ) { v_id = get_fe_tailv(fe); get_fe_side(fe,side[i]); norm[i] = v_curve[loc_ordinal(v_id)].normal; fe = get_next_edge(fe); } cross_prod(side[0],side[1],normal); denom = dot(normal,normal,3); for ( i = 0, h = 0.0 ; i < 3 ; i++ ) h += triple_prod(normal,side[(i+1)%3],norm[i])/ sqrt(dot(norm[i],norm[i],3))/denom; energy += h*h*sqrt(denom)/2; } binary_tree_add(web.total_energy_addends,energy/4);/* mean factor */ return; } FOR_ALL_VERTICES(v_id) { REAL venergy = vertex_sq_mean_curvature(v_id); int ordv = loc_ordinal(v_id); struct v_curve_t *vc = v_curve + ordv; energy += venergy*vc->a/3; } binary_tree_add(web.total_energy_addends,modulus*energy); temp_free((char*)v_curve); v_curve = NULL; } /************************************************************************* * * function: vertex_sq_mean_curvature() * * purpose: calculate squared mean curvature of given vertex * from data generated by sqcurve_energy. * */ REAL vertex_sq_mean_curvature(v_id) vertex_id v_id; { REAL h,venergy; ATTR attr = get_vattr(v_id); int ordv = loc_ordinal(v_id); struct v_curve_t *vc; REAL denom,f; REAL area; /* curvature normalization area */ REAL h0_val=0.0; if ( !v_curve ) sqcurve_method_init(METHOD_VALUE,NULL); vc = v_curve + ordv; if ( (attr & (FIXED|BOUNDARY)) ) return 0.0; if ( vc->area == 0.0 ) return 0.0; switch ( h0_flag ) { case H0_IN_GLOBAL: h0_val = h0_value; break; case H0_IN_ATTR: h0_val = *VREAL(v_id,h0_attr); break; } if ( !boundary_curvature_flag ) { vc->a = vc->area; area = vc->area/3; } else { area = vc->area/3; } if ( attr & CONSTRAINT ) { conmap_t* conmap = get_v_constraint_map(v_id); int j,oncount = 0; struct constraint *con[MAXCONPER]; REAL perp[MAXCOORD]; for ( j = 1 ; j <= (int)conmap[0] ; j++ ) if ( conmap[j] & CON_HIT_BIT ) con[oncount++] = get_constraint(conmap[j]); constr_proj(TANGPROJ,oncount,con,get_coord(v_id), vc->force,perp,NULL,NO_DETECT,v_id); for ( j = 0 ; j < SDIM ; j++ ) vc->force[j] -= perp[j]; if ( effective_area_flag || normal_curvature_flag ) { constr_proj(TANGPROJ,oncount,con,get_coord(v_id), vc->normal,perp,NULL,NO_DETECT,v_id); for ( j = 0 ; j < SDIM ; j++ ) vc->normal[j] -= perp[j]; } } if ( normal_curvature_flag ) { f = SDIM_dot(vc->force,vc->force); denom = SDIM_dot(vc->force,vc->normal); if ( denom == 0.0 ) h = 0.0; else h = 3*f/denom; /* mean, and normal was twice area */ if ( h0_flag ) h -= h0_val; if ( mean_curv_int_flag ) venergy = h; else /* squared curvature */ venergy = h*h; } else if ( h0_flag ) { REAL term,sim; vc->h = h = SDIM_dot(vc->force,vc->normal)/ SDIM_dot(vc->normal,vc->normal)*3; /* since vc->normal = 6*volgrad */ term = h - h0_val; if ( self_similar_flag ) { vc->vol = SDIM_dot(get_coord(v_id),vc->normal); sim = selfsim_coeff*vc->vol; term -= sim; } venergy = term*term; } else if ( mean_curv_int_flag ) /* unsquared curvature */ venergy = sqrt(SDIM_dot(vc->force,vc->force))/2/area; else if ( effective_area_flag ) { f = SDIM_dot(vc->force,vc->force); denom = SDIM_dot(vc->normal,vc->normal); if ( denom == 0.0 ) venergy = 0.0; else venergy = 9*f/denom; /* 9 = 36/4 */ } else /* plain squared curvature */ venergy = SDIM_dot(vc->force,vc->force)/area/area/4; return venergy; } /************************************************************************* * * function: sqcurve_force_end() * * purpose: Convert square curvature data into forces. * */ void sqcurve_force_end() { vertex_id v_id; edge_id e_id; int i,j; REAL modulus = globals(square_curvature_param)->value.real; REAL e,e1,e2,denom; REAL f[MAXCOORD]; REAL fudge1,fudge2,fudge3; /* combinations of values */ REAL fudge11=0.0,fudge12=0.0,fudge13=0.0; /* combinations of values */ REAL fudge21=0.0,fudge22=0.0,fudge23=0.0; /* combinations of values */ REAL h; /* curvature */ REAL area; /* curvature normalization area */ REAL a; /* integral area allocation */ if ( div_normal_curvature_flag ) kb_error(1646,"Force not implemented yet for div_normal_curvature.\n", RECOVERABLE ); if ( web.representation != SOAPFILM ) { sqcurve_force_string_end(); return; } FOR_ALL_VERTICES(v_id) { REAL *force = get_force(v_id); struct v_curve_t *vc = v_curve + loc_ordinal(v_id); ATTR attr = get_vattr(v_id); REAL ad[MAXCOORD]; REAL h0_val=0.0; switch ( h0_flag ) { case H0_IN_GLOBAL: h0_val = h0_value; break; case H0_IN_ATTR: h0_val = *VREAL(v_id,h0_attr); break; } if ( (attr & (FIXED|BOUNDARY)) ) continue; if ( vc->area == 0.0 ) continue; if ( !boundary_curvature_flag ) { vc->a = vc->area; area = a = vc->a/3; } else { a = vc->a/3; area = vc->area/3; } for ( i = 0 ; i < SDIM ;i++ ) /* alloc area deriv */ if ( boundary_curvature_flag ) ad[i] = vc->star_force[i]/3; else ad[i] = vc->force[i]/3; if ( attr & CONSTRAINT ) { conmap_t *conmap = get_v_constraint_map(v_id); int oncount = 0; struct constraint *con[MAXCONPER]; REAL perp[MAXCOORD]; for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { if (conmap[j] & CON_HIT_BIT) con[oncount++] = get_constraint(conmap[j]); } constr_proj(TANGPROJ,oncount,con,get_coord(v_id), vc->force,perp,NULL,NO_DETECT,v_id); for ( j = 0 ; j < SDIM ; j++ ) vc->force[j] -= perp[j]; if ( effective_area_flag || normal_curvature_flag ) { constr_proj(TANGPROJ,oncount,con,get_coord(v_id), vc->normal,perp,NULL,NO_DETECT,v_id); for ( j = 0 ; j < SDIM ; j++ ) vc->normal[j] -= perp[j]; } } /* vertex self-second derivatives */ if ( mean_curv_int_flag ) /* unsquared curvature */ { e = sqrt(SDIM_dot(vc->force,vc->force)); if ( e == 0.0 ) continue; /* no curvature */ for ( i = 0 ; i < SDIM ; i++ ) f[i] = SDIM_dot(vc->force,vc->deriv2[i])/e/2; } else if ( normal_curvature_flag ) { e = SDIM_dot(vc->force,vc->force); denom = SDIM_dot(vc->force,vc->normal); if ( denom != 0.0 ) { h = 3*e/denom; if ( h0_flag ) h -= h0_val; if ( mean_curv_int_flag ) { fudge1 = 2/denom*vc->area; fudge2 = fudge1*e/denom/2; fudge3 = h/6; for ( i = 0 ; i < SDIM ; i++ ) f[i] = fudge1*dot(vc->force,vc->deriv2[i],SDIM) - fudge2*dot(vc->normal,vc->deriv2[i],SDIM) + fudge3*(boundary_curvature_flag ? vc->star_force[i] : vc->force[i]); /* Note: Convex Exemplar cc 6.2.1 chokes on SDIM_dot here */ } else /* squared curvature */ { fudge1 = 4*h/denom*vc->area; fudge2 = fudge1*e/denom/2; fudge3 = h*h/3; for ( i = 0 ; i < SDIM ; i++ ) f[i] = fudge1*dot(vc->force,vc->deriv2[i],SDIM) - fudge2*dot(vc->normal,vc->deriv2[i],SDIM) + fudge3*(boundary_curvature_flag ? vc->star_force[i] : vc->force[i]); } } else for ( i = 0 ; i < SDIM ; i++ ) f[i] =0.0; } else if ( effective_area_flag ) { e = SDIM_dot(vc->force,vc->force); denom = SDIM_dot(vc->normal,vc->normal); if ( denom != 0.0 ) for ( i = 0 ; i < SDIM ; i++ ) f[i] = 18*SDIM_dot(vc->force,vc->deriv2[i])/denom*a + 9*e/denom*ad[i]; else for ( i = 0 ; i < SDIM ; i++ ) f[i] =0.0; } else /* squared curvature */ { e = SDIM_dot(vc->force,vc->force)/vc->area*3.0/4; for ( i = 0 ; i < SDIM ; i++ ) f[i] = (2*SDIM_dot(vc->force,vc->deriv2[i]) - 4/3.0*e*vc->force[i])/vc->area*3.0/4; } if ( h0_flag && !normal_curvature_flag) { REAL net,sim; REAL fd[MAXCOORD],simd[MAXCOORD]; REAL aread[MAXCOORD]; vc->norm = SDIM_dot(vc->normal,vc->normal); vc->f = SDIM_dot(vc->force,vc->normal); vc->h = h = vc->f/vc->norm*3; for ( i = 0 ; i < SDIM ;i++ ) fd[i] = SDIM_dot(vc->normal,vc->deriv2[i])/vc->norm*3; for ( i = 0 ; i < SDIM ;i++ ) { aread[i] = vc->force[i]/3; if ( boundary_curvature_flag ) ad[i] = vc->star_force[i]/3; else ad[i] = aread[i]; } vc->term = h - h0_val; if ( self_similar_flag ) { vc->vol = SDIM_dot(get_coord(v_id),vc->normal); sim = selfsim_coeff*vc->vol/area; vc->term -= sim/area; for ( i = 0 ; i < SDIM ;i++ ) { simd[i] = selfsim_coeff*vc->normal[i]; force[i] -= modulus*2*vc->term* (- simd[i])*a; } } for ( i = 0 ; i < SDIM ; i++ ) { net = 2*vc->term*fd[i]*a + vc->term*vc->term*ad[i]; force[i] -= modulus*net; } } else for ( i = 0 ; i < SDIM ; i++ ) force[i] -= modulus*f[i]; } FOR_ALL_EDGES(e_id) { vertex_id headv = get_edge_headv(e_id); vertex_id tailv = get_edge_tailv(e_id); struct e_curve_t *ec = e_curve + loc_ordinal(e_id); struct v_curve_t *vc1 = v_curve + loc_ordinal(tailv); struct v_curve_t *vc2 = v_curve + loc_ordinal(headv); REAL *force1 = get_force(tailv); REAL *force2 = get_force(headv); REAL s[MAXCOORD],cross1[MAXCOORD],cross2[MAXCOORD]; REAL denom1=0.0,denom2=0.0; REAL s0[MAXCOORD]; REAL wa[MAXCOORD],wb[MAXCOORD],w[MAXCOORD],aw[MAXCOORD],vw[MAXCOORD]; facetedge_id fe; facet_id f_id; if ( (vc1->area == 0.0) || (vc2->area == 0.0) ) continue; if ( mean_curv_int_flag ) /* unsquared curvature */ { e1 = sqrt(SDIM_dot(vc1->force,vc1->force)); e2 = sqrt(SDIM_dot(vc2->force,vc2->force)); } else if ( normal_curvature_flag ) { facetedge_id fe_a; facetedge_id fe_b; REAL sa[MAXCOORD],sb[MAXCOORD]; fe = get_edge_fe(e_id); fe_a = get_prev_edge(fe); fe_b = get_prev_edge(get_next_facet(fe)); get_edge_side(e_id,s0); get_edge_side(get_fe_edge(fe_a),sa); cross_prod(vc1->force,sa,cross1); if ( (assume_oriented_flag && inverted(get_fe_facet(fe_a))) || (!assume_oriented_flag && triple_prod(vc1->normal,sa,s0) < 0.0 ) ) { for ( i = 0 ; i < SDIM ; i++ ) cross1[i] = -cross1[i]; } if ( fe_b != fe_a ) /* check for single edge on constraint */ { get_edge_side(get_fe_edge(fe_b),sb); cross_prod(vc1->force,sb,s); if ( (assume_oriented_flag && inverted(get_fe_facet(fe_b))) || (!assume_oriented_flag && triple_prod(vc1->normal,sb,s0) < 0.0 ) ) { for ( i = 0 ; i < SDIM ; i++ ) cross1[i] -= s[i]; } else for ( i = 0 ; i < SDIM ; i++ ) cross1[i] += s[i]; } e1 = SDIM_dot(vc1->force,vc1->force); denom1 = SDIM_dot(vc1->force,vc1->normal); if ( denom1 != 0.0 ) { h = 3*e1/denom1; switch ( h0_flag ) { case H0_IN_GLOBAL: h -= h0_value; break; case H0_IN_ATTR: h -= *VREAL(tailv,h0_attr); break; } if ( mean_curv_int_flag ) { fudge11 = 2/denom1*vc1->area; fudge12 = fudge11*e1/denom1/2; fudge13 = h/6; } else { fudge11 = 4*h/denom1*vc1->area; fudge12 = fudge11*e1/denom1/2; fudge13 = h*h/3; } } else fudge11 = fudge12 = fudge13 = 0.0; e2 = SDIM_dot(vc2->force,vc2->force); denom2 = SDIM_dot(vc2->force,vc2->normal); if ( denom2 != 0.0 ) { h = 3*e2/denom2; switch ( h0_flag ) { case H0_IN_GLOBAL: h -= h0_value; break; case H0_IN_ATTR: h -= *VREAL(headv,h0_attr); break; } if ( mean_curv_int_flag ) { fudge21 = 2/denom2*vc2->area; fudge22 = fudge21*e2/denom2/2; fudge23 = h/6; } else { fudge21 = 4*h/denom2*vc2->area; fudge22 = fudge21*e2/denom2/2; fudge23 = h*h/3; } } else fudge21 = fudge22 = fudge23 = 0.0; cross_prod(vc2->force,sa,cross2); if ( (assume_oriented_flag && !inverted(get_fe_facet(fe_a))) || (!assume_oriented_flag && triple_prod(vc2->normal,sa,s0) > 0.0 ) ) { for ( i = 0 ; i < SDIM ; i++ ) cross2[i] = -cross2[i]; } if ( fe_b != fe_a ) /* check for single edge on constraint */ { cross_prod(vc2->force,sb,s); if ( (assume_oriented_flag && !inverted(get_fe_facet(fe_b))) || (!assume_oriented_flag && triple_prod(vc2->normal,sb,s0) > 0.0 ) ) { for ( i = 0 ; i < SDIM ; i++ ) cross2[i] -= s[i]; } else for ( i = 0 ; i < SDIM ; i++ ) cross2[i] += s[i]; } } else if ( effective_area_flag ) { facetedge_id fe_a; facetedge_id fe_b; REAL sa[MAXCOORD],sb[MAXCOORD]; fe = get_edge_fe(e_id); fe_a = get_prev_edge(fe); fe_b = get_prev_edge(get_next_facet(fe)); get_edge_side(e_id,s0); get_edge_side(get_fe_edge(fe_a),sa); cross_prod(vc1->normal,sa,cross1); if ( (assume_oriented_flag && inverted(get_fe_facet(fe_a))) || (!assume_oriented_flag && SDIM_dot(cross1,s0) < 0.0 ) ) for ( i = 0 ; i < SDIM ; i++ ) cross1[i] = -cross1[i]; if ( fe_b != fe_a ) /* check for single edge on constraint */ { get_edge_side(get_fe_edge(fe_b),sb); cross_prod(vc1->normal,sb,s); if ( (assume_oriented_flag && inverted(get_fe_facet(fe_b))) || (!assume_oriented_flag && SDIM_dot(s,s0) < 0.0 ) ) for ( i = 0 ; i < SDIM ; i++ ) cross1[i] -= s[i]; else for ( i = 0 ; i < SDIM ; i++ ) cross1[i] += s[i]; } e1 = SDIM_dot(vc1->force,vc1->force); denom1 = SDIM_dot(vc1->normal,vc1->normal); e2 = SDIM_dot(vc2->force,vc2->force); denom2 = SDIM_dot(vc2->normal,vc2->normal); cross_prod(vc2->normal,sa,cross2); if ( (assume_oriented_flag && !inverted(get_fe_facet(fe_a))) || (!assume_oriented_flag && SDIM_dot(cross2,s0) > 0.0 ) ) for ( i = 0 ; i < SDIM ; i++ ) cross2[i] = -cross2[i]; if ( fe_b != fe_a ) /* check for single edge on constraint */ { cross_prod(vc2->normal,sb,s); if ( (assume_oriented_flag && !inverted(get_fe_facet(fe_b))) || (!assume_oriented_flag && SDIM_dot(s,s0) > 0.0 ) ) for ( i = 0 ; i < SDIM ; i++ ) cross2[i] -= s[i]; else for ( i = 0 ; i < SDIM ; i++ ) cross2[i] += s[i]; } } else /* squared curvature */ { e1 = SDIM_dot(vc1->force,vc1->force)/vc1->area*3.0/4; e2 = SDIM_dot(vc2->force,vc2->force)/vc2->area*3.0/4; } if ( !(get_vattr(tailv) & (FIXED|BOUNDARY)) ) { /* force on head due to curvature at tail */ if ( mean_curv_int_flag ) /* unsquared curvature */ { if ( e1 == 0.0 ) break; for ( i = 0 ; i < SDIM ; i++ ) f[i] = SDIM_dot(vc1->force,ec->deriv2[i])/e1/2; } else if ( normal_curvature_flag ) for ( i = 0 ; i < SDIM ; i++ ) { f[i] = fudge11*SDIM_dot(vc1->force,ec->deriv2[i]) - fudge12*(cross1[i]+SDIM_dot(vc1->normal,ec->deriv2[i])) + fudge13*ec->deriv[1][i] ; } else if ( effective_area_flag ) for ( i = 0 ; i < SDIM ; i++ ) { if ( denom1 != 0.0 ) f[i] = 6*SDIM_dot(vc1->force,ec->deriv2[i])/denom1*vc1->area + 3*e1/denom1*ec->deriv[1][i] - 6*e1/denom1/denom1*cross1[i]*vc1->area; else f[i] = 0.0; } else /* squared curvature */ for ( i = 0 ; i < SDIM ; i++ ) f[i] = (- 4/3.0*e1*ec->deriv[1][i] + 2*SDIM_dot(vc1->force,ec->deriv2[i]))/vc1->area*3.0/4; if ( h0_flag && !normal_curvature_flag ) {REAL fd[MAXCOORD],ad[MAXCOORD],net; fe = get_edge_fe(e_id); /* Gotta pay attention to wrapping wa = get_coord(get_fe_headv(get_next_edge(fe))); wb = get_coord(get_fe_headv(get_next_edge(get_next_facet(fe)))); */ get_edge_side(get_fe_edge(get_next_edge(fe)),wa); get_edge_side(get_fe_edge(get_next_edge(get_next_facet(fe))),wb); f_id = get_fe_facet(fe); if ( inverted(f_id) ) for ( i = 0 ; i < SDIM ; i++ ) w[i] = wb[i] - wa[i]; else for ( i = 0 ; i < SDIM ; i++ ) w[i] = wa[i] - wb[i]; cross_prod(w,vc1->force,aw); cross_prod(w,vc1->normal,vw); for ( i = 0 ; i < SDIM ; i++ ) { fd[i] = (SDIM_dot(vc1->normal,ec->deriv2[i])/vc1->norm + aw[i]/vc1->norm - vc1->f*2*vw[i]/vc1->norm/vc1->norm)*3; ad[i] = ec->deriv[1][i]/3; } area = vc1->area/3; a = vc1->a/3; for ( i = 0 ; i < SDIM ; i++ ) { net = 2*vc1->term*fd[i]*a + vc1->term*vc1->term*(ad[i]+ec->aderiv[1][i]/3); if ( self_similar_flag ) net += 2*vc1->term*(-selfsim_coeff*ec->volderiv[1][i])*a; force2[i] -= modulus*net; } } else for ( i = 0 ; i < SDIM ; i++ ) force2[i] -= modulus*f[i]; } if ( !(get_vattr(headv) & (FIXED|BOUNDARY)) ) { /* force on tail due to curvature at head */ if ( mean_curv_int_flag ) /* unsquared curvature */ { if ( e2 == 0.0 ) break; for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0,f [i] = 0.0 ; j < SDIM ; j++ ) f[i] += vc2->force[j]*ec->deriv2[j][i]/e2/2; } else if ( normal_curvature_flag ) for ( i = 0 ; i < SDIM ; i++ ) { f[i] = fudge23*ec->deriv[0][i] - fudge22*cross2[i]; for ( j = 0 ; j < SDIM ; j++ ) f[i] += fudge21*vc2->force[j]*ec->deriv2[j][i] - fudge22*vc2->normal[j]*ec->deriv2[j][i]; } else if ( effective_area_flag ) for ( i = 0 ; i < SDIM ; i++ ) { if ( denom2 != 0.0 ) { f[i] = 3*e2/denom2*ec->deriv[0][i] - 6*e2/denom2/denom2*cross2[i]*vc2->area; for ( j = 0 ; j < SDIM ; j++ ) f[i] += 6*vc2->force[j]*ec->deriv2[j][i]/denom2*vc2->area; } else f[i] = 0.0; } else /* squared curvature */ for ( i = 0 ; i < SDIM ; i++ ) { f[i] = -4/3.0*e2*ec->deriv[0][i]/vc2->area*3.0/4; for ( j = 0 ; j < SDIM ; j++ ) f[i] += 2*vc2->force[j]*ec->deriv2[j][i]/vc2->area*3.0/4; } if ( h0_flag && !normal_curvature_flag ) {REAL fd[MAXCOORD],ad[MAXCOORD],net; fe = get_edge_fe(e_id); get_edge_side(get_fe_edge(get_next_edge(fe)),wa); get_edge_side(get_fe_edge(get_next_edge(get_next_facet(fe))),wb); f_id = get_fe_facet(fe); if ( inverted(f_id) ) for ( i = 0 ; i < SDIM ; i++ ) w[i] = wb[i] - wa[i]; else for ( i = 0 ; i < SDIM ; i++ ) w[i] = wa[i] - wb[i]; cross_prod(vc2->force,w,aw); cross_prod(vc2->normal,w,vw); for ( i = 0 ; i < SDIM ; i++ ) { for ( j = 0, fd[i] = 0.0 ; j < SDIM ; j++ ) fd[i] += vc2->normal[j]*ec->deriv2[j][i]; fd[i] = (fd[i]/vc2->norm + aw[i]/vc2->norm - vc2->f*2*vw[i]/vc2->norm/vc2->norm)*3; ad[i] = ec->deriv[0][i]/3; } area = vc2->area/3; a = vc2->a/3; for ( i = 0 ; i < SDIM ; i++ ) { net = 2*vc2->term*fd[i]*a + vc2->term*vc2->term*(ad[i]+ec->aderiv[0][i]/3); if ( self_similar_flag ) net += 2*vc2->term*(-selfsim_coeff*ec->volderiv[0][i])*a; force1[i] -= modulus*net; } } else for ( i = 0 ; i < SDIM ; i++ ) force1[i] -= modulus*f[i]; } } temp_free((char*)e_curve); e_curve = NULL; temp_free((char*)v_curve); v_curve = NULL; } REAL curve_power; int curve_power_param; /************************************************************************ * * function: sqcurve_energy_string_init() * */ void sqcurve_energy_string_init() { curve_power_param = lookup_global(CURVE_POWER_NAME); if ( curve_power_param < 0 ) /* missing, so add */ { curve_power_param = add_global(CURVE_POWER_NAME); globals(curve_power_param)->value.real = 2.0; /* default */ globals(curve_power_param)->flags |= ORDINARY_PARAM; } curve_power = globals(curve_power_param)->value.real; } /************************************************************************ * * function: sqcurve_energy_string() * * purpose: Calculate square curvature energy for string model * Works locally vertex by vertex. Requires edges to be * on a facet, assumes two edges per vertex. * */ void sqcurve_energy_string(v_id) vertex_id v_id; { REAL s1,s2,s1s2; /* edge lengths */ REAL side1[MAXCOORD],side2[MAXCOORD]; edge_id e1; edge_id e2; REAL energy; REAL a1,a2; e2 = get_vertex_edge(v_id); e1 = get_next_tail_edge(e2);; e1 = inverse_id(e1); get_edge_side(e1,side1); get_edge_side(e2,side2); s1 = sqrt(SDIM_dot(side1,side1)); s2 = sqrt(SDIM_dot(side2,side2)); s1s2 = SDIM_dot(side1,side2); a1 = 1 - s1s2/s1/s2; a2 = (s1 + s2)/2; total_length += a2; if ( a1 <= 0.0 ) return; energy = pow(a1,curve_power/2)/pow(a2,curve_power-1); energy *= globals(square_curvature_param)->value.real; /* fudge factor to agree with def */ total_sqcurve += 2*energy; } /************************************************************************ * * function: sqcurve_force_string() * * purpose: Calculate square curvature force for string model * Works locally vertex by vertex. Requires edges to be * on a facet, assumes two edges per vertex. * */ void sqcurve_force_string(v_id) vertex_id v_id; { REAL s1,s2,s1s2; /* edge lengths */ REAL side1[MAXCOORD],side2[MAXCOORD]; edge_id e1; edge_id e2; REAL *tforce,*vforce,*hforce; int i; REAL c1,c2; REAL modulus = globals(square_curvature_param)->value.real; REAL a1,a2,energy; e2 = get_vertex_edge(v_id); e1 = get_next_tail_edge(e2); e1 = inverse_id(e1); vforce = get_force(v_id); tforce = get_force(get_edge_tailv(e1)); hforce = get_force(get_edge_headv(e2)); get_edge_side(e1,side1); get_edge_side(e2,side2); s1 = sqrt(SDIM_dot(side1,side1)); s2 = sqrt(SDIM_dot(side2,side2)); s1s2 = SDIM_dot(side1,side2); a1 = 1 - s1s2/s1/s2; if ( a1 <= 0.0 ) return; a2 = (s1 + s2)/2; energy = pow(a1,curve_power/2)/pow(a2,curve_power-1); c1 = -modulus*2*( curve_power/2*energy/a1*(s1s2/s1/s1/s1/s2) - (curve_power - 1)*energy/a2/s1/2); c2 = -modulus*2*( curve_power/2*energy/a1*(-1/s1/s2)); for ( i = 0 ; i < SDIM ; i++ ) { tforce[i] -= c1*side1[i] + c2*side2[i]; vforce[i] += c1*side1[i] + c2*side2[i]; } c1 = -modulus*2*( curve_power/2*energy/a1*(s1s2/s1/s2/s2/s2) - (curve_power - 1)*energy/a2/s2/2); c2 = -modulus*2*( curve_power/2*energy/a1*(-1/s1/s2)); for ( i = 0 ; i < SDIM ; i++ ) { hforce[i] += c2*side1[i] + c1*side2[i]; vforce[i] -= c2*side1[i] + c1*side2[i]; } } /************************************************************* * * function: sqcurve_force_string_end() * * purpose: nothing needed * */ void sqcurve_force_string_end() { return; } /***************************************************************************/ /***************************************************************************/ /* Stokes2D - method for calculating flat Willmore energy, which is * minimized for solutions of the 2D Stokes equation for * steady state slow viscous flow. * Flat willmore energy for the graph of a function is the area integral * of the square of the trace of the Hessian of the function. * * Boundary treatment: for use without boundary plateaus. Multiplies * Laplacian by two at fixed boundary points to simulate mirrored * stream function. */ #define STOKES_TYPE_NAME "stokes_type" #define STOKES_NONWALL 0 #define STOKES_SLIPWALL 1 #define STOKES_NONSLIPWALL 2 #define STOKES_VELOCITY_NAME "stokes_velocity" REAL stokes2d_all ARGS(( struct qinfo *, int)); int stokes_type_attr; int stokes_velocity_attr; /* special mode value for Laplacian only */ #define STOKES_LAPLACIAN 0x4757737 void stokes2d_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { /* find parameters */ stokes_type_attr = find_attribute(VERTEX,STOKES_TYPE_NAME); stokes_velocity_attr = find_attribute(VERTEX,STOKES_VELOCITY_NAME); if ( stokes_velocity_attr >= 0 ) { if ( EXTRAS(VERTEX)[stokes_velocity_attr].array_spec.sizes[0] < 2 ) kb_error(3674,"Dimension of stokes_velocity must be 2.\n",RECOVERABLE); } else /* create */ { int two = SDIM; stokes_velocity_attr = add_attribute(VERTEX,STOKES_VELOCITY_NAME, REAL_TYPE,1,&two,0,NULL); } } REAL stokes2d_all(v_info,mode) struct qinfo *v_info; int mode; /* METHOD_VALUE, METHOD_GRADIENT, or METHOD_HESSIAN */ { int n,nn,maxn; REAL h,H,x1,y1,x2,y2,u1,u2,det,sumarea,value; REAL wedge; /* angle around vertex, to detect boundary or corner point for kludge */ int stokes_type = (stokes_type_attr >= 0) ? *VINT(v_info->id,stokes_type_attr) : STOKES_NONWALL ; REAL * stokes_velocity = (stokes_velocity_attr >= 0) ? VREAL(v_info->id,stokes_velocity_attr) : NULL ; REAL vgx,vgy,hinc; if ( stokes_type == STOKES_SLIPWALL ) return 0; /* Calculate H as gradient of integral of square of gradient */ maxn = v_info->flags & INCOMPLETE_STAR ? v_info->vcount-2 : v_info->vcount-1; h = 0; sumarea = 0; wedge = 0; for ( n = 0 ; n < maxn ; n++ ) /* nth facet contribution */ { int nn = (n == maxn-1) ? (v_info->flags & INCOMPLETE_STAR ? n+1 : 0) : n+1; x1 = v_info->sides[0][n][0]; y1 = v_info->sides[0][n][1]; u1 = v_info->sides[0][n][2]; x2 = v_info->sides[0][nn][0]; y2 = v_info->sides[0][nn][1]; u2 = v_info->sides[0][nn][2]; det = x1*y2 - x2*y1; sumarea += fabs(det)/2; wedge += acos((x1*x2+y1*y2)/sqrt((x1*x1+y1*y1)*(x2*x2+y2*y2))); /* Not going to use the value, but the formula is here for easy reference. sqgradint = 1./2./fabs(det)*((y2*u1-y1*u2)*(y2*u1-y1*u2)+(x1*u2-x2*u1)*(x1*u2-x2*u1)); */ /* h is derivative wrt u0 only, with extra half factor since Laplacian is half of gradient of Dirichlet energy */ hinc = 1./4./fabs(det)*(2*(y2*u1-y1*u2)*(-y2+y1)+2*(x1*u2-x2*u1)*(-x1+x2)); h += hinc; if ( stokes_type == STOKES_NONSLIPWALL ) { if ( stokes_velocity == NULL ) kb_error(3833,"stokes_nonslipwall needs stokes_velocity attribute.\n", RECOVERABLE); /* contribution of virtual continuation across boundary, assuming boundary is a streamline. Extended slope taken to be 2*speed - slope */ vgx = -2*stokes_velocity[1]*det - (y2*u1-y1*u2); vgy = 2*stokes_velocity[0]*det - (x1*u2-x2*u1); /* sqgradint just for reference here sqgradint = 1./2./fabs(det)*(vgx*vgx + vgy*vgy); */ hinc = 1./4./fabs(det)*(2*vgx*(y2-y1) + 2*vgy*(x1-x2)); h += hinc; } if ( (mode == METHOD_GRADIENT) || (mode == METHOD_HESSIAN) ) { /* using grads to store grads of h */ v_info->grad[0][2] += 1./4./fabs(det)*(2*(-y2+y1)*(-y2+y1)+2*(-x1+x2)*(-x1+x2)); v_info->grad[n+1][2] += 1./4./fabs(det)*(2*(y2)*(-y2+y1)+2*(-x2)*(-x1+x2)); v_info->grad[nn+1][2] += 1./4./fabs(det)*(2*(-y1)*(-y2+y1)+2*(x1)*(-x1+x2)); if ( stokes_type == STOKES_NONSLIPWALL ) { v_info->grad[0][2] += 1./4./fabs(det)*(2*(-y2+y1)*(-y2+y1)+2*(-x1+x2)*(-x1+x2)); v_info->grad[n+1][2] += 1./4./fabs(det)*(2*(y2)*(-y2+y1)+2*(-x2)*(-x1+x2)); v_info->grad[nn+1][2] += 1./4./fabs(det)*(2*(-y1)*(-y2+y1)+2*(x1)*(-x1+x2)); } } /* note that hessian of h is identically 0 */ /* NOTE: for slipping boundary, h = 0 (not implemented yet) */ } if ( sumarea == 0.0 ) return 0.0; /* nothing there */ if ( stokes_type == STOKES_NONSLIPWALL ) { h /= 2; if ( (mode == METHOD_GRADIENT) || (mode == METHOD_HESSIAN) ) for ( n = 0 ; n < v_info->vcount ; n++ ) v_info->grad[n][2] /=2; } /* Kludge to try to get the Laplacian to come out more even on the bdry */ if ( get_vattr(v_info->id) & FIXED ) { if ( wedge < 3.3 ) /* boundary point */ { sumarea *= 3./maxn*wedge/M_PI; } } /* normalize over area */ H = h/(sumarea/3); if ( mode == STOKES_LAPLACIAN ) return H; /* integral of H^2 */ value = H*H*sumarea/3; /* or value = h*h*3/sumarea */ if ( mode == METHOD_VALUE ) return value; if ( mode == METHOD_HESSIAN ) { /* use h gradients while we still have them */ for ( n = 0 ; n < v_info->vcount ; n++ ) for ( nn = 0 ; nn < v_info->vcount ; nn++ ) v_info->hess[n][nn][2][2] = 6/sumarea*v_info->grad[n][2]*v_info->grad[nn][2]; } for ( n = 0 ; n < v_info->vcount ; n++ ) v_info->grad[n][2] *= 6*h/sumarea; if ( mode == METHOD_GRADIENT ) return value; /* nothing more to do for hessian, so ... */ return value; } REAL stokes2d_value(v_info) struct qinfo *v_info; { return stokes2d_all(v_info,METHOD_VALUE); } REAL stokes2d_grad(v_info) struct qinfo *v_info; { return stokes2d_all(v_info,METHOD_GRADIENT); } REAL stokes2d_hess(v_info) struct qinfo *v_info; { return stokes2d_all(v_info,METHOD_HESSIAN); } REAL stokes2d_laplacian(v_info) struct qinfo *v_info; { return stokes2d_all(v_info,STOKES_LAPLACIAN); } evolver-2.30c.dfsg/src/cnstrnt.c0000644000175300017530000012061311410765113017057 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /**************************************************************** * * File: cnstrnt.c * * Contents: Functions dealing with boundary constraints. */ #include "include.h" /******************************************************************* * * function: constraint_init() * * purpose: allocate expnode structures for a constraint * */ void constraint_init(con) struct constraint *con; { int i; con->formula = (struct expnode *)mycalloc(1+2*MAXCONCOMP, sizeof(struct expnode)); for ( i = 0 ; i < MAXCONCOMP ; i++ ) { con->envect[i] = con->formula + 1 + i; con->convect[i] = con->formula + 1 + MAXCONCOMP + i; } } /******************************************************************* * * function: constraint_free() * * purpose: free expnode structures and expressions for a constraint * */ void constraint_free(con) struct constraint *con; { int j; if ( con->formula ) { for ( j = 0 ; j < MAXCONCOMP ; j++ ) { free_expr(con->envect[j]); free_expr(con->convect[j]); } free_expr(con->formula); myfree((char*)con->formula); } } /****************************************************************** * * Function: constr_proj() * * Purpose: projection on level-set constraints. * * Explanation: Projecting a point or vector onto constraints * requires finding an appropriate linear combination of * constraint gradients c[i]*grad(f[i]) where c[i] = (inv A)*B, * where B is an appropriate vector (input) and * A[i][j] = . * The linear combination is returned. * Function return value is number of hit constraints. */ int constr_proj(mode,concount,constr,coord,b,combo,conlist,detect_flag,v_id) int mode; /* TANGPROJ for vector projection of b to tangent plane, (note return is normal component of b!) PLAINPROJ for using input b as right side (note return is offset from constraints!)*/ int concount; /* how many constraints */ struct constraint **constr; /* array of pointers to constraints */ REAL *coord; /* coordinates of point for evaluating functions */ REAL *b; /* vector to project */ REAL *combo; /* desired linear combination */ int *conlist; /* list of constraint numbers; for returning hits */ int detect_flag; /* set if want to detect one-sided constraints */ vertex_id v_id; /* vertex for DETECT mode, or passing on to constraint eval */ { /* maximum number of hit constraints */ #define MAXCONSTR MAXCOORD int i,j; REAL grad[MAXCONSTR][MAXCOORD]; /* gradients of constraints */ REAL c[MAXCONSTR]; /* combination coefficients */ REAL r[MAXCONSTR]; /* constructed right side B */ REAL fval; /* value of constraint */ REAL *bb; /* pointer to right side */ MAT2D(a,MAXCONSTR,MAXCONSTR); /* matrix for projection */ if ( concount > MAXCONSTR ) { sprintf(errmsg, "Trying to project vertex %s on more constraints, %d, than allowed, %d.", ELNAME(v_id),concount,MAXCONSTR); if ( conlist ) { sprintf(errmsg+strlen(errmsg)," Projecting on constraints "); for ( i = 0 ; i < concount ; i++ ) sprintf(errmsg+strlen(errmsg)," %s",get_constraint(conlist[i])->name); strcat(errmsg,". Maybe redundant one-sided constraints?"); } strcat(errmsg,"\n"); kb_error(1791,errmsg,RECOVERABLE); } if ( concount <= 0 ) { for ( j = 0 ; j < SDIM ; j++ ) combo[j] = 0.0; return concount; } /* calculate gradients */ for ( i = 0 ; i < concount ; i++ ) eval_all(constr[i]->formula,coord,SDIM,&fval,grad[i],v_id); /* maybe construct right side for vector projection */ if ( mode == TANGPROJ ) { for ( i = 0 ; i < concount ; i++ ) r[i] = SDIM_dot(b,grad[i]); bb = r; } else bb = b; /* construct matrix A */ for ( i = 0 ; i < concount ; i++ ) for ( j = 0 ; j < concount ; j++ ) a[i][j] = SDIM_dot(grad[i],grad[j]); #define BBOLDWAY #ifdef OLDWAY /* invert */ mat_inv(a,concount); /* combination coefficients */ matvec_mul(a,bb,c,concount>SDIM?SDIM:concount,concount>SDIM?SDIM:concount); #else for ( i = 0 ; i < concount ; i++ ) c[i] = bb[i]; mat_approx_solve(a,concount,c); #endif /* form combination */ for ( i = 0 ; i < SDIM ; i++ ) { combo[i] = 0.0; for ( j = 0 ; j < concount ; j++ ) combo[i] += c[j]*grad[j][i]; } return concount; } /* end constr_proj() */ /****************************************************************** * * Function: constr_proj_matrix() * * Purpose: Find matrix for projection onto constraint tangents. * * Explanation: Projecting a point or vector onto constraints * requires finding an appropriate linear combination of * constraint gradients c[i]*grad(f[i]) where c[i] = (inv A)*B, * where B is an appropriate vector (input) and * A[i][j] = . */ int constr_proj_matrix(v_id,mat) vertex_id v_id; /* vertex for DETECT mode, or passing on to constraint eval */ REAL **mat; /* projection matrix */ { /* maximum number of hit constraints */ #define MAXCONSTR MAXCOORD int i,j; MAT2D(grad,MAXCONSTR,MAXCOORD); /* gradients of constraints */ REAL fval; /* value of constraint */ int concount=0; conmap_t * conmap = get_v_constraint_map(v_id); struct constraint *constr[MAXCONSTR]; for ( j = 1 ; j <= (int)conmap[0] ; j++ ) if ( conmap[j] & CON_HIT_BIT ) constr[concount++] = get_constraint(conmap[j]); if ( concount > MAXCONSTR ) { sprintf(errmsg, "Vertex %s has more constraints, %d, than allowed, %d.", ELNAME(v_id),concount,SDIM); sprintf(errmsg+strlen(errmsg)," Projecting on constraints "); for ( i = 0 ; i < concount ; i++ ) sprintf(errmsg+strlen(errmsg)," %s",constr[i]->name); strcat(errmsg,". Maybe redundant one-sided constraints?"); strcat(errmsg,"\n"); kb_error(3515,errmsg,RECOVERABLE); } if ( concount > 0 ) { REAL *coord = get_coord(v_id); /* calculate gradients */ for ( i = 0 ; i < concount ; i++ ) eval_all(constr[i]->formula,coord,SDIM,&fval,grad[i],v_id); /* orthonormalize */ concount = gram_schmidt(grad,concount,SDIM); /* form projection matrix */ tr_mat_mul(grad,grad,mat,concount,SDIM,SDIM); for ( i = 0 ; i < SDIM ; i++ ) { for ( j = 0 ; j < SDIM ; j++ ) mat[i][j] *= -1; mat[i][i] += 1.0; } } /* else identity */ else { for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) mat[i][j] = (i==j) ? 1.0 : 0.0; } return concount; } /* end constr_proj_matrix */ /****************************************************************** * * Function: constr_proj_matrix_wall() * * Purpose: Find matrix for projection onto constraint tangents, * but ignoring nonwall constraints. * * Explanation: Projecting a point or vector onto constraints * requires finding an appropriate linear combination of * constraint gradients c[i]*grad(f[i]) where c[i] = (inv A)*B, * where B is an appropriate vector (input) and * A[i][j] = . */ int constr_proj_matrix_wall(v_id,mat) vertex_id v_id; /* vertex for DETECT mode, or passing on to constraint eval */ REAL **mat; /* projection matrix */ { /* maximum number of hit constraints */ #define MAXCONSTR MAXCOORD int i,j; MAT2D(grad,MAXCONSTR,MAXCOORD); /* gradients of constraints */ REAL fval; /* value of constraint */ int concount=0; conmap_t * conmap = get_v_constraint_map(v_id); struct constraint *constr[MAXCONSTR]; for ( j = 1 ; j <= (int)conmap[0] ; j++ ) if ( conmap[j] & CON_HIT_BIT ) { struct constraint *con = get_constraint(conmap[j]); if ( !(con->attr & NONWALL) ) constr[concount++] = con; } if ( concount > SDIM ) { sprintf(errmsg, "Vertex %s has more constraints, %d, than space dimensions, %d.", ELNAME(v_id),concount,SDIM); sprintf(errmsg+strlen(errmsg)," Projecting on constraints "); for ( i = 0 ; i < concount ; i++ ) sprintf(errmsg+strlen(errmsg)," %s",constr[i]->name); strcat(errmsg,". Maybe redundant one-sided constraints?"); strcat(errmsg,"\n"); kb_error(3028,errmsg,RECOVERABLE); } if ( concount > 0 ) { REAL *coord = get_coord(v_id); /* calculate gradients */ for ( i = 0 ; i < concount ; i++ ) eval_all(constr[i]->formula,coord,SDIM,&fval,grad[i],v_id); /* orthonormalize */ concount = gram_schmidt(grad,concount,SDIM); /* form projection matrix */ tr_mat_mul(grad,grad,mat,concount,SDIM,SDIM); for ( i = 0 ; i < SDIM ; i++ ) { for ( j = 0 ; j < SDIM ; j++ ) mat[i][j] *= -1; mat[i][i] += 1.0; } } /* else identity */ else { for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) mat[i][j] = (i==j) ? 1.0 : 0.0; } return concount; } /* end constr_proj_matrix */ /****************************************************************** * * Function: project_v_constr() * * Purpose: project a vertex onto its constraints. * Return: whether hitting a one-sided constraint * for the first time. */ int project_v_constr(v_id,mode,one_sided_reset) vertex_id v_id; int mode; /* TEST_MOVE or ACTUAL_MOVE */ int one_sided_reset; /* whether to reset one-sidedness */ { REAL *x; conmap_t *conmap; struct constraint *con[MAXCONPER],*thiscon; int oncount = 0; REAL f[MAXCONPER],delta[MAXCOORD]; int j,itercount = 0; REAL diff,totaldiff; int walls = 0; /* total number of constraints vertex is on */ REAL maxdiff; int dcon=0; /* which constraint has largest difference */ /*int hit_one_side;*/ /* if hits a one-sided constraint */ int new_hits = 0; /* total new hits */ // if ( one_sided_reset == RESET_ONESIDEDNESS ) // clear_v_constraint_status(v_id); // moved from inside loop, so 1-sided con is sticky x = get_coord(v_id); conmap = get_v_constraint_map(v_id); do { walls = 0; /*hit_one_side = 0; */ new_hits = 0; totaldiff = maxdiff = 0.0; for ( j = 1, oncount = 0 ; j <= (int)conmap[0] ; j++ ) { thiscon = get_constraint(conmap[j]); diff = eval(thiscon->formula,x,v_id,NULL); if ( ((thiscon->attr & NONNEGATIVE) && !(conmap[j] & CON_HIT_BIT) && ( diff > web.tolerance )) || ((thiscon->attr & NONPOSITIVE) && !(conmap[j] & CON_HIT_BIT) && ( diff < -web.tolerance )) ) { continue; } if ( thiscon->attr & (NONNEGATIVE|NONPOSITIVE) ) { /* hit_one_side = 1; */ if ( !(conmap[j] & CON_HIT_BIT) ) new_hits++; set_attr(v_id,HIT_ONE_SIDED); } f[oncount] = -diff; con[oncount++] = thiscon; if ( fabs(diff) > maxdiff ) { maxdiff = fabs(diff); dcon = conmap[j]&CONMASK; } totaldiff += fabs(diff); if ( one_sided_reset == RESET_ONESIDEDNESS ) set_v_constraint_status(v_id,conmap[j]); walls++; } if ( totaldiff < web.tolerance ) break; constr_proj(PLAINPROJ,oncount,con,x,f,delta,NULL,NO_DETECT,v_id); for ( j = 0 ; j < SDIM ; j++ ) x[j] += delta[j]; itercount++; } while ( itercount < MAXCONITER ); if ( (itercount >= MAXCONITER) && (mode == ACTUAL_MOVE) ) { struct constraint *con = get_constraint(dcon); sprintf(msg, "Vertex %s doesn't converge to constraint %s after %d iterations. \n maxdiff = %g, constraint_tolerance %g\n", ELNAME(v_id),con->name,MAXCONITER,(DOUBLE)totaldiff,web.tolerance); kb_error(1792,msg,WARNING); } /* if ( mode == ACTUAL_MOVE ) */ { if ( walls ) set_attr(v_id,HIT_WALL); else unset_attr(v_id,HIT_WALL); /* if ( hit_one_side ) set_attr(v_id,HIT_ONE_SIDED); else unset_attr(v_id,HIT_ONE_SIDED); */ } return new_hits; } /***************************************************************** * * Function: calc_constr_force_v() * * Purpose: calculate force on vertex due to constraint energy. (string model) */ void calc_constr_force_v(v_id) vertex_id v_id; { REAL *f,*coord; struct constraint *constr; int i,j; conmap_t * conmap; int sign; REAL fval,deriv[MAXCOORD]; int_val = ordinal(get_original(v_id))+1; /* for eval of file parameters */ f = get_force(v_id); coord = get_coord(v_id); if ( get_vattr(v_id) & NEGBOUNDARY ) sign = -1; else sign = 1; conmap = get_v_constraint_map(v_id); for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { constr = get_constraint(conmap[j]); if ( !(constr->attr & CON_ENERGY) || (constr->compcount != 1) ) continue; eval_all(constr->envect[0],coord,SDIM,&fval,deriv,v_id); for ( i = 0 ; i < SDIM ; i++ ) f[i] += -sign*deriv[i]; /* force by constraint */ } return; } /************************************************************************** * * Function: calc_constr_force_e() * * Purpose: calculate force on endpoints of edge due to constraint energy. */ void calc_constr_force_e(e_id) edge_id e_id; { REAL *tcoord,*hcoord; REAL *tforce,*hforce; struct constraint *constr; int i,k,m; REAL side[MAXCOORD]; REAL green[MAXCOORD]; REAL green_deriv[MAXCOORD][MAXCOORD]; int j,sign; REAL midpt[MAXCOORD]; REAL grad; vertex_id headv,tailv; conmap_t *conmap; int_val = ordinal(get_original(e_id))+1; /* for eval of file parameters */ if ( web.modeltype == QUADRATIC ) { constr_edge_force_q(e_id); return; } else if ( web.modeltype == LAGRANGE ) kb_error(1793,"calc_constr_force_e(): Cannot do LAGRANGE model.\n",RECOVERABLE); conmap = get_e_constraint_map(e_id); if ( get_eattr(e_id) & NEGBOUNDARY ) sign = -1; else sign = 1; if ( inverted(e_id) ) sign = -sign; headv = get_edge_headv(e_id); tailv = get_edge_tailv(e_id); tcoord = get_coord(tailv); hcoord = get_coord(headv); for ( j = 0 ; j < SDIM ; j++ ) side[j] = hcoord[j] - tcoord[j]; tforce = get_force(get_edge_tailv(e_id)); hforce = get_force(get_edge_headv(e_id)); for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { constr = get_constraint(conmap[j]); if ( !(constr->attr & CON_ENERGY) || (constr->compcount != SDIM) ) continue; for ( m = 0 ; m < gauss1D_num ; m++ ) { for ( i = 0 ; i < SDIM ; i++ ) midpt[i] = gauss1Dpt[m]*hcoord[i] + (1 - gauss1Dpt[m])*tcoord[i]; for ( i = 0 ; i < SDIM ; i++ ) eval_all(constr->envect[i],midpt,SDIM,&green[i],green_deriv[i],e_id); for ( i = 0 ; i < SDIM ; i++ ) { for ( grad = 0.0, k = 0 ; k < SDIM ; k++ ) grad += side[k]*green_deriv[k][i]; tforce[i] -= sign*gauss1Dwt[m]*((1-gauss1Dpt[m])*grad - green[i]); hforce[i] -= sign*gauss1Dwt[m]*(gauss1Dpt[m]*grad + green[i]); } } } return; } /***************************************************************** * * Function: calc_constr_energy_v() * * Purpose: calculate constraint energy due to vertex. (string model) */ void calc_constr_energy_v(v_id) vertex_id v_id; { REAL e; int j; conmap_t *conmap = get_v_constraint_map(v_id); struct constraint *constr; int_val = ordinal(get_original(v_id))+1; /* for eval of file parameters */ for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { constr = get_constraint(conmap[j]); if ( !(constr->attr & CON_ENERGY) || (constr->compcount != 1) ) continue; e = eval(constr->envect[0],get_coord(v_id),v_id,NULL); if ( get_vattr(v_id) & NEGBOUNDARY ) binary_tree_add(web.total_energy_addends,-e); else binary_tree_add(web.total_energy_addends,e); } } /***************************************************************** * * Function: calc_constr_energy_e() * * Purpose: calculate energy due to edge on constraint. * Also contributions to quantities from constraints. */ void calc_constr_energy_e(e_id) edge_id e_id; { REAL *tcoord,*hcoord; struct constraint *constr; int i,j,k; REAL energy = 0.0; REAL side[MAXCOORD]; REAL green[MAXCOORD]; int sign; REAL midpt[MAXCOORD]; vertex_id headv,tailv; conmap_t *conmap; int_val = ordinal(get_original(e_id))+1; /* for eval of file parameters */ if ( web.modeltype == QUADRATIC ) { constr_edge_energy_q(e_id); return; } else if ( web.modeltype == LAGRANGE ) kb_error(1794,"calc_constr_energy_e(): Cannot do LAGRANGE model.\n",RECOVERABLE); conmap = get_e_constraint_map(e_id); if ( get_eattr(e_id) & NEGBOUNDARY ) sign = -1; else sign = 1; if ( inverted(e_id) ) sign = -sign; headv = get_edge_headv(e_id); tailv = get_edge_tailv(e_id); tcoord = get_coord(tailv); hcoord = get_coord(headv); for ( j = 0 ; j < SDIM ; j++ ) side[j] = hcoord[j] - tcoord[j]; for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { constr = get_constraint(conmap[j]); if ( !(constr->attr & CON_ENERGY) ) continue; if ( constr->compcount != SDIM ) continue; for ( k = 0 ; k < gauss1D_num ; k++ ) { for ( i = 0 ; i < SDIM ; i++ ) midpt[i] = gauss1Dpt[k]*hcoord[i] + (1 - gauss1Dpt[k])*tcoord[i]; if ( constr->attr & CON_ENERGY ) { for ( i = 0 ; i < SDIM ; i++ ) green[i] = eval(constr->envect[i],midpt,e_id,NULL); energy += sign*gauss1Dwt[k]*SDIM_dot(side,green); } } } binary_tree_add(web.total_energy_addends,energy); } /***************************************************************** * * Function: calc_constr_content_v() * * Purpose: calculate interior content due to vertex. (string model) */ void calc_constr_content_v(v_id) vertex_id v_id; { REAL e=0.0; int j; conmap_t * conmap; struct constraint *constr; body_id b_id; facetedge_id fe_id; facet_id f_id; edge_id e_id = get_vertex_edge(v_id); edge_id start_e = e_id; edge_id next_e; int min_rank,max_rank; if ( !valid_id(e_id) ) return; /* not on a cell */ int_val = ordinal(get_original(v_id))+1; /* for eval of file parameters */ conmap = get_v_constraint_map(v_id); /* only hit constraints */ min_rank = MAXINT; max_rank = 0; for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { if ( !(conmap[j] & CON_HIT_BIT) ) continue; constr = get_constraint(conmap[j]); if ( constr->content_rank < min_rank ) min_rank = constr->content_rank; if ( constr->content_rank > max_rank ) max_rank = constr->content_rank; } for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { if ( !(conmap[j] & CON_HIT_BIT) ) continue; constr = get_constraint(conmap[j]); if ( !(constr->attr & CON_CONTENT) ) continue; if ( constr->compcount != 1 ) continue; if ( get_vattr(v_id) & NEGBOUNDARY ) e = -eval(constr->convect[0],get_coord(v_id),v_id,NULL); else e = eval(constr->convect[0],get_coord(v_id),v_id,NULL); next_e = e_id; do { facetedge_id start_fe; e_id = next_e; next_e = get_next_tail_edge(e_id); if ( get_eattr(e_id) & NONCONTENT ) continue; /* cell on one side of edge */ start_fe = fe_id = get_edge_fe(e_id); if ( !valid_id(fe_id) ) continue; do { if ( !valid_id(get_prev_edge(fe_id)) ) { f_id = get_fe_facet(fe_id); if ( valid_id(f_id) && ( (!inverted(f_id) && constr->content_rank >= max_rank) || (inverted(f_id) && constr->content_rank <= min_rank)) ) { add_facet_area(f_id,-e); b_id = get_facet_body(f_id); if ( valid_id(b_id) ) add_body_volume(b_id,-e); b_id = get_facet_body(inverse_id(f_id)); if ( valid_id(b_id) ) add_body_volume(b_id,e); } } fe_id = get_next_facet(fe_id); } while (!equal_id(fe_id,start_fe)); } while ( !equal_id(next_e,start_e) ); } } /***************************************************************** * * Function: calc_constr_content_e() * * Purpose: calculate interior content due to edge. (film model) */ void calc_constr_content_e(e_id) edge_id e_id; { REAL *tcoord,*hcoord; struct constraint *constr; int i,k; REAL content = 0.0; REAL midpt[MAXCOORD]; REAL side[MAXCOORD]; REAL green[MAXCOORD]; body_id b_id; facetedge_id fe_id; facet_id f_id; int j; conmap_t *conmap; int sign; vertex_id headv,tailv; facetedge_id first_fe; int min_rank, max_rank; if ( web.modeltype == QUADRATIC ) { constr_edge_content_q(e_id); return; } else if ( web.modeltype == LAGRANGE ) kb_error(1795,"calc_constr_content_e(): Cannot do LAGRANGE model.\n", RECOVERABLE); int_val = ordinal(get_original(e_id))+1; /* for eval of file parameters */ conmap = get_e_constraint_map(e_id); if ( get_eattr(e_id) & NEGBOUNDARY ) sign = -1; else sign = 1; if ( inverted(e_id) ) sign = -sign; headv = get_edge_headv(e_id); tailv = get_edge_tailv(e_id); tcoord = get_coord(tailv); hcoord = get_coord(headv); for ( j = 0 ; j < SDIM ; j++ ) side[j] = hcoord[j] - tcoord[j]; min_rank = MAXINT; max_rank = 0; for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { constr = get_constraint(conmap[j]); if ( constr->content_rank < min_rank ) min_rank = constr->content_rank; if ( constr->content_rank > max_rank ) max_rank = constr->content_rank; } for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { constr = get_constraint(conmap[j]); if ( !(constr->attr & CON_CONTENT) ) continue; if ( constr->compcount != SDIM ) continue; content = 0.0; for ( k = 0 ; k < gauss1D_num ; k++ ) { for ( i = 0 ; i < SDIM ; i++ ) midpt[i] = gauss1Dpt[k]*hcoord[i] + (1 - gauss1Dpt[k])*tcoord[i]; for ( i = 0 ; i < SDIM ; i++ ) green[i] = eval(constr->convect[i],midpt,e_id,NULL); content += sign*gauss1Dwt[k]*SDIM_dot(side,green); } fe_id = first_fe = get_edge_fe(e_id); if ( valid_id(fe_id) ) do { /* cell on plus side of edge */ f_id = get_fe_facet(fe_id); if ( valid_id(f_id) && !(get_fattr(f_id) & NONCONTENT) && (constr->content_rank >= max_rank) ) { b_id = get_facet_body(f_id); if ( valid_id(b_id) ) add_body_volume(b_id,content); } /* cell on other side of edge */ f_id = get_fe_facet(inverse_id(fe_id)); if ( valid_id(f_id) && !(get_fattr(f_id) & NONCONTENT) && (constr->content_rank <= min_rank) ) { b_id = get_facet_body(f_id); if ( valid_id(b_id) ) add_body_volume(b_id,-content); } fe_id = get_next_facet(fe_id); } while ( valid_id(fe_id) && !equal_id(fe_id,first_fe) ); } return; } /***************************************************************** * * Function: constr_spring_energy() * * Purpose: Calculate energy of kludge constraint force. * Constant factor included to make it best approx * of true area. */ void constr_spring_energy(e_id) edge_id e_id; { REAL sprenergy = 0.0; REAL s[MAXCOORD]; REAL ss; struct constraint *constr[MAXCONPER]; int concount; conmap_t * conmap; vertex_id tail,head; int i,j; if ( get_eattr(e_id) & FIXED ) return; if ( !(get_eattr(e_id) & CONSTRAINT) ) return; tail = get_edge_tailv(e_id); head = get_edge_headv(e_id); /* find which constraints have CONVEX attribute */ conmap = get_e_constraint_map(e_id); for ( j = 1,i = 0 ; j <= (int)conmap[0] ; j++ ) { constr[i] = get_constraint(conmap[j]); if ( constr[i]->attr & B_CONVEX ) i++; /* keep this one */ } if ( i == 0 ) return; concount = i; /* now the calculation */ get_edge_side(e_id,s); ss = SDIM_dot(s,s); #ifndef OLDWAY for ( i = 0 ; i < concount ; i++ ) { REAL *coord,ff,fs; REAL fval,grad[MAXCOORD]; coord = get_coord(tail); eval_all(constr[i]->formula,coord,SDIM,&fval,grad,e_id); ff = SDIM_dot(grad,grad); if ( ff <= 0.0 ) { sprintf(errmsg, "Gap energy error: Vertex %s is on a convex constraint at zero gradient.\n", ELNAME(tail)); kb_error(2001,errmsg,WARNING); ff = 1.0; } fs = SDIM_dot(s,grad); sprenergy += fabs(fs)*sqrt(ss/ff)/12; coord = get_coord(head); eval_all(constr[i]->formula,coord,SDIM,&fval,grad,e_id); ff = SDIM_dot(grad,grad); if ( ff <= 0.0 ) { sprintf(errmsg, "Gap energy error: Vertex %s is on a convex constraint at zero gradient.\n", ELNAME(head)); kb_error(2002,errmsg,WARNING); ff = 1.0; } fs = SDIM_dot(s,grad); sprenergy += fabs(fs)*sqrt(ss/ff)/12; } #endif #ifdef OLDWAYX constr_proj(TANGPROJ,concount,constr,get_coord(tail),s,q,NULL, NO_DETECT,tail); qq = SDIM_dot(q,q); sprenergy = ss*sqrt(qq/(ss - qq))/12; constr_proj(TANGPROJ,concount,constr,get_coord(head),s,q,NULL, NO_DETECT,head); qq = SDIM_dot(q,q); sprenergy += ss*sqrt(qq/(ss - qq))/12; #endif sprenergy *= web.spring_constant; binary_tree_add(web.total_energy_addends,sprenergy); web.spring_energy += sprenergy; } /**************************************************************** * * Function: constr_springs() * * Purpose: Since only vertices are actually confined to constraints, * edges and faces supposedly on constraints can pull * away from convex constraints, and in fact do, since * a long edge short-cuts the constraints. To prevent * this and encourage equal-length constraint edges, an * energy penalty is inflicted for an edge angling away * from its constraint. * The energy penalty is 2/3 of the area of the right * triangle whose base is half the side and whose hypoteneuse * lies on the constraint tangent. This is done for both * ends of the side. */ void constr_springs(e_id) edge_id e_id; { REAL s[MAXCOORD],*fh,*ft; REAL ss; /* square lengths */ struct constraint *constr[MAXCONPER]; int concount; conmap_t * conmap; vertex_id head,tail; int i,j; MAT2D(second,MAXCOORD,MAXCOORD); /* for second partials */ if ( get_eattr(e_id) & FIXED ) return; if ( !(get_eattr(e_id) & CONSTRAINT) ) return; tail = get_edge_tailv(e_id); head = get_edge_headv(e_id); /* find which constraints have CONVEX attribute */ conmap = get_e_constraint_map(e_id); for ( j = 1,i=0 ; j <= (int)conmap[0] ; j++ ) { constr[i] = get_constraint(conmap[j]); if ( constr[i]->attr & B_CONVEX ) i++; /* keep this one */ } if ( i == 0 ) return; concount = i; /* now the calculation */ get_edge_side(e_id,s); ss = SDIM_dot(s,s); ft = get_force(tail); fh = get_force(head); #ifndef OLDWAYX for ( i = 0 ; i < concount ; i++ ) { REAL *coord; REAL fval,grad[MAXCOORD]; REAL ff,fs,t; coord = get_coord(tail); eval_second(constr[i]->formula,coord,SDIM,&fval,grad,second,tail); ff = SDIM_dot(grad,grad); if ( ff <= 0.0 ) ff = 1; fs = SDIM_dot(s,grad); t = sqrt(ss/ff); if ( fs < 0.0 ) t = -t; /* to take care of fabs() */ for ( j = 0 ; j < SDIM ; j++ ) { REAL g; g = -t*grad[j] + t*SDIM_dot(s,second[j]) + fs/t*(-s[j]/ff - ss/ff/ff*SDIM_dot(grad,second[j])); ft[j] -= web.spring_constant*g/12; g = t*grad[j] + fs/t/ff*s[j]; fh[j] -= web.spring_constant*g/12; } coord = get_coord(head); eval_second(constr[i]->formula,coord,SDIM,&fval,grad,second,head); ff = SDIM_dot(grad,grad); if ( ff <= 0.0 ) ff = 1; fs = SDIM_dot(s,grad); t = sqrt(ss/ff); if ( fs < 0.0 ) t = -t; /* to take care of fabs() */ for ( j = 0 ; j < SDIM ; j++ ) { REAL g; g = t*grad[j] + t*SDIM_dot(s,second[j]) + fs/t*(s[j]/ff - ss/ff/ff*SDIM_dot(grad,second[j])); fh[j] -= web.spring_constant*g/12; g = -t*grad[j] - fs/t/ff*s[j]; ft[j] -= web.spring_constant*g/12; } } #endif #ifdef OLDWAYX /*tail*/ constr_proj(TANGPROJ,concount,constr,get_coord(tail),s,q,NULL, NO_DETECT,tail); for ( i = 0 ; i < SDIM ; i++ ) q[i] = s[i] - q[i]; /* get tangent side */ qq = SDIM_dot(q,q); norm = (1 + (ss-qq)/3/qq)*sqrt(fabs(ss-qq)/qq)/2; /* fabs due to machine inaccuracy */ for ( i = 0 ; i < SDIM ; i++ ) ft[i] += web.spring_constant*q[i]*norm; /* head */ constr_proj(TANGPROJ,concount,constr,get_coord(head),s,q,NULL, NO_DETECT,head); for ( i = 0 ; i < SDIM ; i++ ) q[i] = s[i] - q[i]; /* get tangent side */ qq = SDIM_dot(q,q); norm = (1 + (ss-qq)/3/qq)*sqrt(fabs(ss-qq)/qq)/2; for ( i = 0 ; i < SDIM ; i++ ) fh[i] -= web.spring_constant*q[i]*norm; #endif } /************************************************************************* Following are quadratic model version of constraint integral routines. *************************************************************************/ /************************************************************************ * * Function: constr_edge_energy_q() * * Purpose: Returns energy due to one edge on constraint. * * Quadratic version. */ void constr_edge_energy_q(e_id) edge_id e_id; { REAL x[EDGE_CTRL][MAXCOORD]; REAL *pt[EDGE_CTRL]; REAL etang[MAXCOORD]; vertex_id v[EDGE_CTRL]; int i,j,k; struct constraint *constr; REAL energy = 0.0; REAL side[MAXCOORD]; REAL green[MAXCOORD]; conmap_t *conmap; int sign; REAL gpt[MAXCOORD]; int_val = ordinal(get_original(e_id))+1; /* for eval of file parameters */ conmap = get_e_constraint_map(e_id); get_edge_side(e_id,side); if ( get_eattr(e_id) & NEGBOUNDARY ) sign = -1; else sign = 1; v[0] = get_edge_tailv(e_id); v[1] = get_edge_midv(e_id); v[2] = get_edge_headv(e_id); for ( i = 0 ; i < EDGE_CTRL ; i++ ) pt[i] = get_coord(v[i]); for ( i = 0 ; i < EDGE_CTRL ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) x[i][j] = pt[i][j]; /* calculate tangents at integration points and accumulate */ for ( i = 0 ; i < EDGE_INTERP ; i++ ) { for ( j = 0 ; j < SDIM ; j ++ ) { etang[j] = 0.0; gpt[j] = 0.0; for ( k = 0 ; k < EDGE_CTRL ; k++ ) { etang[j] += sdip[k][i]*x[k][j]; gpt[j] += gcombo[k][i]*x[k][j]; } } for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { constr = get_constraint(conmap[j]); if ( !(constr->attr & CON_ENERGY) ) continue; if ( constr->compcount != SDIM ) continue; for ( k = 0 ; k < SDIM ; k++ ) green[k] = eval(constr->envect[k],gpt,e_id,NULL); energy += sign*gauss2wt[i]*SDIM_dot(etang,green); } } binary_tree_add(web.total_energy_addends,energy); } /************************************************************************ * * Function: constr_edge_force_q() * * Purpose: Calculates force due to one edge on constraint. * * Quadratic version. */ void constr_edge_force_q(e_id) edge_id e_id; { REAL x[EDGE_CTRL][MAXCOORD]; REAL *pt[EDGE_CTRL]; REAL etang[MAXCOORD]; vertex_id v[EDGE_CTRL]; int i,j,k,m,n; struct constraint *constr; REAL side[MAXCOORD]; REAL green[MAXCOORD]; REAL green_deriv[MAXCOORD][MAXCOORD]; conmap_t *conmap; int sign; REAL gpt[MAXCOORD]; REAL *force[EDGE_CTRL]; int_val = ordinal(get_original(e_id))+1; /* for eval of file parameters */ conmap = get_e_constraint_map(e_id); get_edge_side(e_id,side); if ( get_eattr(e_id) & NEGBOUNDARY ) sign = -1; else sign = 1; v[0] = get_edge_tailv(e_id); v[1] = get_edge_midv(e_id); v[2] = get_edge_headv(e_id); for ( i = 0 ; i < EDGE_CTRL ; i++ ) { pt[i] = get_coord(v[i]); force[i] = get_force(v[i]); } for ( i = 0 ; i < EDGE_CTRL ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) x[i][j] = pt[i][j]; /* calculate tangents at integration points and accumulate */ for ( i = 0 ; i < EDGE_INTERP ; i++ ) { for ( j = 0 ; j < SDIM ; j ++ ) { etang[j] = 0.0; gpt[j] = 0.0; for ( k = 0 ; k < EDGE_CTRL ; k++ ) { etang[j] += sdip[k][i]*x[k][j]; gpt[j] += gcombo[k][i]*x[k][j]; } } for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { REAL tangdot[MAXCOORD]; constr = get_constraint(conmap[j]); if ( !(constr->attr & CON_ENERGY) ) continue; if ( constr->compcount != SDIM ) continue; for ( m = 0 ; m < SDIM ; m++ ) eval_all(constr->envect[m],gpt,SDIM,&green[m],green_deriv[m],e_id); for ( n = 0 ; n < SDIM ; n++ ) for ( m = 0, tangdot[n] = 0.0 ; m < SDIM ; m++ ) tangdot[n] += etang[m]*green_deriv[m][n]; for ( k = 0 ; k < EDGE_CTRL ; k++ ) { for ( m = 0 ; m < SDIM ; m++ ) force[k][m] -= sign*gauss2wt[i]*sdip[k][i]*green[m]; for ( n = 0 ; n < SDIM ; n++ ) force[k][n] -= sign*gauss2wt[i]*gcombo[k][i]*tangdot[n]; } } } } /************************************************************************ * * Function: constr_edge_content_q() * * Purpose: Finds volume due to one edge on a constraint. * * Quadratic version. */ void constr_edge_content_q(e_id) edge_id e_id; { REAL x[EDGE_CTRL][MAXCOORD]; REAL *pt[EDGE_CTRL]; REAL etang[MAXCOORD]; vertex_id v[EDGE_CTRL]; int i,j,k; struct constraint *constr; REAL content = 0.0; REAL side[MAXCOORD]; REAL green[MAXCOORD]; conmap_t * conmap; int sign; REAL gpt[MAXCOORD]; body_id b_id; facet_id f_id; facetedge_id fe_id = get_edge_fe(e_id); facetedge_id first_fe; int_val = ordinal(get_original(e_id))+1; /* for eval of file parameters */ conmap = get_e_constraint_map(e_id); get_edge_side(e_id,side); if ( get_eattr(e_id) & NEGBOUNDARY ) sign = -1; else sign = 1; v[0] = get_edge_tailv(e_id); v[1] = get_edge_midv(e_id); v[2] = get_edge_headv(e_id); for ( i = 0 ; i < EDGE_CTRL ; i++ ) pt[i] = get_coord(v[i]); for ( i = 0 ; i < EDGE_CTRL ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) x[i][j] = pt[i][j]; /* calculate tangents at integration points and accumulate */ for ( i = 0 ; i < EDGE_INTERP ; i++ ) { for ( j = 0 ; j < SDIM ; j ++ ) { etang[j] = 0.0; gpt[j] = 0.0; for ( k = 0 ; k < EDGE_CTRL ; k++ ) { etang[j] += sdip[k][i]*x[k][j]; gpt[j] += gcombo[k][i]*x[k][j]; } } for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { constr = get_constraint(conmap[j]); if ( !(constr->attr & CON_CONTENT) ) continue; if ( constr->compcount != SDIM ) continue; for ( k = 0 ; k < SDIM ; k++ ) green[k] = eval(constr->convect[k],gpt,e_id,NULL); content += sign*gauss2wt[i]*SDIM_dot(etang,green); } } fe_id = first_fe = get_edge_fe(e_id); if ( valid_id(fe_id) ) do { /* cell on plus side of edge */ f_id = get_fe_facet(fe_id); if ( valid_id(f_id) && !(get_fattr(f_id) & NONCONTENT) ) { b_id = get_facet_body(f_id); if ( valid_id(b_id) ) add_body_volume(b_id,content); } /* cell on other side of edge */ f_id = get_fe_facet(inverse_id(fe_id)); if ( valid_id(f_id) && !(get_fattr(f_id) & NONCONTENT) ) { b_id = get_facet_body(f_id); if ( valid_id(b_id) ) add_body_volume(b_id,-content); } fe_id = get_next_facet(fe_id); } while ( valid_id(fe_id) && !equal_id(fe_id,first_fe) ); return; } /************************************************************************ * * Function: constr_vol_grad_q() * * Purpose: Calculates volume gradients due to one edge on constraint. * * Quadratic version. */ void constr_vol_grad_q(e_id) edge_id e_id; { REAL x[EDGE_CTRL][MAXCOORD]; REAL *pt[EDGE_CTRL]; REAL etang[MAXCOORD]; vertex_id v[EDGE_CTRL]; int i,j,k; REAL green[MAXCOORD]; REAL green_deriv[MAXCOORD][MAXCOORD]; struct constraint *constr; int m,n; REAL grad[EDGE_CTRL][MAXCOORD]; conmap_t * conmap; int sign,bodysign=1; REAL gpt[MAXCOORD]; struct volgrad *vgptri; facet_id f_id; facetedge_id fe,start_fe; int_val = ordinal(get_original(e_id))+1; /* for eval of file parameters */ /* get basic edge data */ conmap = get_e_constraint_map(e_id); if ( get_eattr(e_id) & NEGBOUNDARY ) sign = -1; else sign = 1; v[0] = get_edge_tailv(e_id); v[1] = get_edge_midv(e_id); v[2] = get_edge_headv(e_id); for ( i = 0 ; i < EDGE_CTRL ; i++ ) pt[i] = get_coord(v[i]); for ( i = 0 ; i < EDGE_CTRL ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) x[i][j] = pt[i][j]; memset((char *)grad,0,sizeof(grad)); /* find content integral gradients */ for ( i = 0 ; i < EDGE_INTERP ; i++ ) { /* calculate coords and tangents at integration point */ for ( j = 0 ; j < SDIM ; j ++ ) { etang[j] = 0.0; gpt[j] = 0.0; for ( k = 0 ; k < EDGE_CTRL ; k++ ) { etang[j] += sdip[k][i]*x[k][j]; gpt[j] += gcombo[k][i]*x[k][j]; } } /* accumulate gradients due to this integration point */ for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { REAL tangdot[MAXCOORD]; constr = get_constraint(conmap[j]); if ( !(constr->attr & CON_CONTENT) ) continue; if ( constr->compcount != SDIM ) continue; for ( m = 0 ; m < SDIM ; m++ ) eval_all(constr->convect[m],gpt,SDIM,&green[m],green_deriv[m],e_id); for ( n = 0 ; n < SDIM ; n++ ) for ( m = 0, tangdot[n] = 0.0 ; m < SDIM ; m++ ) tangdot[n] += etang[m]*green_deriv[m][n]; for ( k = 0 ; k < EDGE_CTRL ; k++ ) { for ( m = 0 ; m < SDIM ; m++ ) grad[k][m] += sign*gauss2wt[i]*sdip[k][i]*green[m]; for ( n = 0 ; n < SDIM ; n++ ) grad[k][n] += sign*gauss2wt[i]*gcombo[k][i]*tangdot[n]; } } } /* now add gradients to proper bodies */ fe = start_fe = get_edge_fe(e_id); if ( valid_id(start_fe) ) do { f_id = get_fe_facet(fe); fe = get_next_facet(fe); if ( get_fattr(f_id) & NONCONTENT ) continue; for ( m = 0 ; m < EDGE_CTRL ; m++ ) { vgptri = get_vertex_vgrad(v[m]); for ( ; vgptri ; vgptri = vgptri->chain ) { if ( !valid_id(vgptri->bb_id) ) continue; /* skip quantities */ if ( !equal_id(get_facet_body(f_id),vgptri->bb_id) ) { if ( !equal_id(get_facet_body(inverse_id(f_id)),vgptri->bb_id) ) continue; else bodysign = -sign; } else bodysign = sign; for ( k = 0 ; k < SDIM ; k++ ) vgptri->grad[k] += bodysign*grad[m][k]; } } } while ( !equal_id(fe,start_fe)); } /***************************************************************** * * Function: constr_basis() * * Purpose: calculate basis of constraint tangent. */ int constr_basis(v_id,basis) vertex_id v_id; REAL **basis; /* for return */ { conmap_t *conmap; struct constraint *con[MAXCONPER]; int oncount = 0; REAL ggrad[MAXCOORD][MAXCOORD]; REAL *grad[MAXCOORD]; /* for proper matrix form */ REAL fval; int i,j; MAT2D(bas,MAXCOORD,MAXCOORD); int nullity; conmap = get_v_constraint_map(v_id); for ( j = 1 , oncount = 0; j <= (int)conmap[0] ; j++ ) con[oncount++] = get_constraint(conmap[j]); /* first calc constraint gradients */ for ( i = 0 ; i < oncount ; i++ ) { grad[i] = ggrad[i]; eval_all(con[i]->formula,get_coord(v_id),SDIM,&fval,grad[i],v_id); } /* now get basis */ nullity = kernel_basis(grad,bas,oncount,SDIM); /* transpose */ for ( i = 0 ; i < nullity ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) basis[i][j] = bas[j][i]; return nullity; } /*************************************************************************** * * function: force_project() * * purpose: project vector to constraint plane at vertex. * */ void force_project(fin,v_id,fout) REAL *fin; vertex_id v_id; REAL *fout; { int attr = get_vattr(v_id); int i,j; if ( attr & CONSTRAINT ) { conmap_t * conmap = get_v_constraint_map(v_id); int oncount = 0; struct constraint *con[MAXCONPER]; int conlist[MAXCONPER]; REAL perp[MAXCOORD]; for ( j = 1 ; j <= (int)conmap[0] ; j++ ) { if ( conmap[j] & CON_HIT_BIT ) { conlist[oncount] = conmap[j] & CONMASK; con[oncount] = get_constraint(conmap[j]); oncount++; } if ( oncount ) { constr_proj(TANGPROJ,oncount,con,get_coord(v_id), fin,perp,conlist,0,v_id); for ( j = 0 ; j < SDIM ; j++ ) fout[j] = fin[j] - perp[j]; return; } } } else if ( attr & BOUNDARY ) { MAT2D(a,MAXCOORD,MAXCOORD); b_proj(get_boundary(v_id),get_param(v_id),a,TANGPROJ,v_id); matvec_mul(a,fin,fout,SDIM,SDIM); return; } /* no projecting to do */ for ( i = 0 ; i < SDIM ; i++ ) fout[i] = fin[i]; } evolver-2.30c.dfsg/src/dodecGroup.c0000644000175300017530000002311311410765113017454 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /**************************************************************************** * This file generates the elements of the group of translations of * hyperbolic 3 space tiled with right-angled dodecahedra. The elements of the * group are represented as integers. There are 32 generators of the group * so each generator is represented by five bits. Under this scheme any * element that is the composition of up to five generators can be * represented. */ #include "include.h" #include #include #define PDIM 4 #define BITS1 1 #define BITS2 3 #define BITS3 7 #define BITS5 31 #define NUMGEN(g) ((g)&BITS3) #define FINDGEN(g) ((g)&BITS5) #define GENINV(g) FINDGEN(((g)+16)) #define CHOP(g) ((g)>>5) static REAL ident_mat[PDIM][PDIM] = {{1.0,.0,.0,.0},{.0,1.0,.0,.0}, {.0,.0,1.0,.0},{.0,.0,.0,1.0}}; static REAL centerRotate[PDIM][PDIM] = {{1.000000, 0.000000, 0.000000, 0.000000}, {0.000000, 0.000000, 1.000000, 0.000000}, {0.000000, -1.000000, 0.000000, 0.000000}, {0.000000, 0.000000, 0.000000, 1.000000}}; static REAL rotates[PDIM][PDIM][PDIM] = {{{1.,1.253445216142309e-16, -1.573266467599309e-16,-1.902396940417021e-16}, {1.824701509283392e-16,-10.47216984175156, 12.46875301145284,16.25226577604197}, {-8.46614841566922e-17,3.996614189627698, -6.472114212317669,-7.54063573964542}, {1.902396940417009e-16,-11.16419572451243, 14.01278216621864,17.94428405406906}}, {{-1.618048902555269,-4.116373394860049, 3.236086078076509,5.388400994886382}, {-0.00001548628343482931,-2.236098819547112, 2.544065459407748,3.236109854867625}, {3.202383078490913e-6,2.544051352436071, -1.000010980411733,-2.544055668570577}, {-1.272038620211825,-5.236111643244772, 4.116375119861672,6.854158702514121}}, {{-1.61804018386751,4.116370344397128, -3.236064438109621,-5.388383050270264}, {5.581793090786349e-6,-2.236104800200569, 2.544047980565907,3.2361002464282}, {-4.388099372461566e-6,2.544047980565911, -0.999990892452044,-2.544044400640008}, {1.272027529835902,-5.236110160905107, 4.11634242501839,6.854135876520134}}, {{-5.23603242311647e-12,2.272015247697663, 0.272030671522342,-2.058167624859446}, {-2.272005895364247,-4.162032037143264, -0.6180552894206566,4.676164433331837}, {-0.2720295517580531,-0.6180552894206578, 0.925999618361096,0.5598818723761604}, {-2.058157152794606,-4.676168977352982, -0.5598824164363831,5.236032418787402}}}; static int q_init_flag = 0; REAL gen[32][PDIM][PDIM]; typedef REAL pmat[PDIM]; typedef REAL ppmat[PDIM][PDIM]; static void copy ARGS(( pmat, pmat )); static void copyMat ARGS(( ppmat, ppmat)); static void matMult ARGS(( ppmat, ppmat, ppmat)); static void matVecMult ARGS(( ppmat , pmat , pmat )); static void convertToHyp ARGS(( pmat , pmat )); static void calcGen ARGS(( WRAPTYPE , ppmat )); static void calcElem ARGS(( WRAPTYPE , ppmat )); static WRAPTYPE check_inverse ARGS(( WRAPTYPE , WRAPTYPE )); void init_gen ARGS((void)); WRAPTYPE dodec_inverse ARGS((WRAPTYPE)); void dodec_wrap ARGS(( REAL x[PDIM], REAL y[PDIM],WRAPTYPE)); WRAPTYPE dodec_compose ARGS((WRAPTYPE, WRAPTYPE)); void dodec_form_pullback ARGS((REAL *,REAL *,REAL *,WRAPTYPE )); static void copy(z,w) REAL z[PDIM],w[PDIM]; { int j; for (j=0; j>4)==0) { for(i=0;i<(ggen&3);i++) matMult(mat,centerRotate,mat); matMult(mat, rotates[((ggen&12)>>2)], mat); for(i=0;i<((5-(ggen&3))%4);i++) matMult(mat, centerRotate, mat); } else { for(i=0;i<(((ggen&3)+3)%4);i++) matMult(mat, centerRotate, mat); for(i=0;i<3;i++) matMult(mat, rotates[((ggen&12)>>2)], mat); for(i=0;i<((4-(ggen&3))%4);i++) matMult(mat, centerRotate, mat); } } /* This function calculates the matrix associated with a given group genrator represented by an int */ static void calcElem(element, mat) WRAPTYPE element; REAL mat[PDIM][PDIM]; { int i, numGens; REAL genMat[PDIM][PDIM]; numGens = NUMGEN(element); element >>= 3; copyMat(ident_mat, mat); for(i=0;i>= 3; numgen2=NUMGEN(elem2); elem2 >>= 3; gens = (WRAPTYPE*) malloc(sizeof(WRAPTYPE)*(numgen1+numgen2)); for(i=0;i>(3+5*(NUMGEN(endelem)-1))); endelem = (endelem&(~(pres<<(3+5*(NUMGEN(endelem)-1))))) - 1; } } else { endelem += 1 + (pres<<(3+NUMGEN(endelem)*5)); pres = next; } } endelem += 1 + (pres<<(3+NUMGEN(endelem)*5)); free((char*)gens); return(endelem); } /* This function returns the inverse of a group element in its integer representation. */ WRAPTYPE dodec_inverse(element) WRAPTYPE element; { int i, numGens; WRAPTYPE inverse; numGens = NUMGEN(element); element >>= 3; inverse = 0; for(i=0;i5)) compp = (check_inverse(elem1,elem2)); else { elem1 >>= 3; elem1 <<= 3+(NUMGEN(elem2)*5); compp = (elem1 + elem2 + numGens); } if(NUMGEN(compp)>5) printf("too many generators \n"); return(compp); } /******************************************************************* * * function: group_form_pullback * * purpose: Pull back differential forms at vertices that get * wrapped. * */ void dodec_form_pullback(x,xform,yform,wrap) REAL *x; /* original coordinates */ REAL *xform; /* result pullback */ REAL *yform; /* original form input */ WRAPTYPE wrap; /* encoded symmetry group element */ { int i,j; REAL trans[PDIM][PDIM]; REAL jac[PDIM][PDIM]; /* Jacobian matrix */ REAL y[PDIM]; REAL w[PDIM]; /* Minkowski coord of x */ if ( wrap == 0 ) /* just copy */ { memcpy((char *)xform,(char*)yform,SDIM*sizeof(REAL)); return; } calcElem(wrap, trans); /* get transformed point in Minkowski */ memcpy((char *)(w),(char*)x,SDIM*sizeof(REAL)); w[3] = 1.0; matVecMult(trans,w,y); /* set up Jacobian */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) jac[i][j] = (trans[j][i] - y[i]*trans[j][3]/y[3])/y[3]; /* pull back form with transpose of jacobian */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0, xform[i] = 0. ; j < SDIM ; j++ ) xform[i] += jac[j][i]*yform[j]; } evolver-2.30c.dfsg/src/symtable.c0000644000175300017530000007742011410765113017213 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************** * * File: symtable.c * * Purpose: Handles symbol table functions for queries. */ #include "include.h" #include "ytab.h" /* symbol table variables */ #define SYMMAX 200 struct sym symtable[SYMMAX]; /* symbol table */ #define MAXSYMDEPTH 10 int symsave[MAXSYMDEPTH]; /* start of scope pointers */ int symdepth; /* depth of symbol table */ int symtop; /* current stack top, index of first empty spot */ /********************************************************************** * * function: begin_scope() * * purpose: enter scope, save symbol stack position */ int begin_scope() { if ( symdepth >= MAXSYMDEPTH - 1 ) kb_error(1666,"Scopes nested too deep.\n",RECOVERABLE); symsave[symdepth++] = symtop; return symdepth; } /********************************************************************** * * function: end_scope() * * purpose: leave scope, pop symbol stack */ void end_scope() { int oldsymtop = symtop; if ( symdepth >= 1 ) /* clear stack */ symtop = symsave[--symdepth]; if ( oldsymtop > symtop ) memset((char*)(symtable+symtop),0,(oldsymtop-symtop)*sizeof(struct sym)); } /********************************************************************** * * function: set_scope() * * purpose: chop symbol stack after error */ void set_scope(depth) int depth; { int oldsymtop = symtop; symdepth = depth-1; if ( symdepth > 0 ) symtop = symsave[symdepth-1]; else symtop = 0; if ( oldsymtop > symtop ) memset((char*)(symtable+symtop),0,(oldsymtop-symtop)*sizeof(struct sym)); } /********************************************************************** * * function: clear_symtable() * * purpose: resets symbol table after command error */ void clear_symtable() { symdepth = 0; symtop = 0; memset((char*)symtable,0,SYMMAX*sizeof(struct sym)); } /********************************************************************** * * function: symbol_add() * * purpose: add symbol to symbol table * */ #ifdef __WIN32__ struct sym * symbol_add(char * name,int type) #else struct sym * symbol_add(name,type) char *name; int type; #endif { int i; /* check for duplication in scope */ for ( i = symsave[symdepth-1] ; i < symtop ; i++ ) if ( strncmp(symtable[i].name,name,SYMNAMESIZE) == 0 ) { sprintf(errmsg,"Duplicate name '%s' in scope.\n",name); kb_error(1667,errmsg,RECOVERABLE); } if ( symtop >= SYMMAX ) kb_error(1668,"Internal error: Symbol table overflow.\n",RECOVERABLE); strncpy(symtable[symtop].name,name,SYMNAMESIZE); symtable[symtop].type = type; return symtable + symtop++; } /********************************************************************** * * function: symbol_lookup() * * purpose: find symbol in symbol table. Returns pointer to * symbol structure if found, NULL if not. */ #ifdef __WIN32__ struct sym *symbol_lookup(char *name) #else struct sym *symbol_lookup(name) char *name; #endif { int i; for ( i = symtop-1 ; i >= 0 ; i-- ) if ( strncmp(symtable[i].name,name,SYMNAMESIZE) == 0 ) return symtable + i; /* success */ return NULL; /* failure */ } /********************************************************************** Global variable runtime table. Implemented as a hash table. Hash table itself only has indices into true global table, so parsed expressions can have permanent indices into global table, but hash table can expand. Hash table is twice the size of global table to keep sparsity and simplicity. Hash entry -1 denotes unused. For distributed processing convenience, hash table and global table kept in dymem, so dy_globals and dy_globalshash are actually offsets in dymem, and globals and globalshash are macros for pointers to start of the tables. Table hashes global variable names, quantity names, and method instance names. Hash table entry is integer, with high two bits denoting type of name, and lower bits being index into appropriate table. **********************************************************************/ int global_hash ARGS((char*)); void expand_global_hash ARGS((void)); /********************************************************************** * * function: global_hash() * * purpose: Hash function for global variable names. Case insensitive. */ #define HASHSTRIDE (2*3*5*7*11*13*17*19 + 1) int global_hash(name) char *name; { int hashval = 0; size_t len = strlen(name); size_t i; for ( i = 0; i < len ; i++ ) hashval = (hashval + tolower(name[i])) * HASHSTRIDE; return abs(hashval); } /********************************************************************** * * function: expand_global_hash() * * purpose: double size of global name hash table, rehashing * existing names. */ void expand_global_hash() { int n; if ( dy_globalshash ) dy_free(dy_globalshash); if ( dy_global_hash_max == 0 ) /* start up */ dy_global_hash_max = (web.perm_global_count < 50) ? 200 : 4*web.perm_global_count; else dy_global_hash_max *= 2; dy_globalshash = dy_calloc(dy_global_hash_max,sizeof(int)); dy_global_hash_maxfill = dy_global_hash_max/2; dy_global_hash_used = 0; /* rehash */ for ( n = 0 ; n < web.perm_global_count ; n++ ) { int spot = global_hash(perm_globals(n)->name) % dy_global_hash_max; while ( globalshash[spot] ) { spot++; if ( spot == dy_global_hash_max ) spot = 0; } globalshash[spot] = PERM_NAME | n; dy_global_hash_used++; } for ( n = 0 ; n < web.global_count ; n++ ) { int spot = global_hash(globals(n)->name) % dy_global_hash_max; while ( globalshash[spot] ) { spot++; if ( spot == dy_global_hash_max ) spot = 0; } globalshash[spot] = VARIABLENAME | n; dy_global_hash_used++; } for ( n = 0 ; n < gen_quant_count ; n++ ) { int spot = global_hash(GEN_QUANT(n)->name) % dy_global_hash_max; while ( globalshash[spot] ) { spot++; if ( spot == dy_global_hash_max ) spot = 0; } globalshash[spot] = QUANTITYNAME | n; dy_global_hash_used++; } for ( n = LOW_INST ; n < meth_inst_count ; n++ ) { int spot = global_hash(METH_INSTANCE(n)->name) % dy_global_hash_max; while ( globalshash[spot] ) { spot++; if ( spot == dy_global_hash_max ) spot = 0; } globalshash[spot] = METHODNAME | n; dy_global_hash_used++; } } /********************************************************************** * * function: lookup_global_hash() * * purpose: add new name to global name hash list * * return: 0 if not already there. * name type and index if already there (see defines above) * */ int lookup_global_hash(name,inx,type,addflag) char *name; int inx; /* index in appropriate table */ int type; /* type of table */ int addflag; /* whether to add if not found, or delete */ { int hashval; int spot; /* in hash table */ int entry; /* index in global table */ int *htable; /* globalshash too buried in macros for debug */ if ( dy_global_hash_used >= dy_global_hash_maxfill ) expand_global_hash(); htable = globalshash; hashval = global_hash(name); spot = hashval % dy_global_hash_max; for (;;) { entry = htable[spot]; switch ( entry & NAMETYPEMASK ) { case VARIABLENAME: if ( stricmp(name,globals((entry & INDEXMASK)|EPHGLOBAL)->name) == 0 ) { if ( addflag == HASH_DELETE ) { htable[spot] = 0; dy_global_hash_used--;} return entry; } break; case PERM_NAME: if ( stricmp(name,perm_globals((entry & INDEXMASK)|PERMGLOBAL)->name) == 0 ) { if ( addflag == HASH_DELETE ) { htable[spot] = 0; dy_global_hash_used--;} return entry; } break; case QUANTITYNAME: if ( stricmp(name,GEN_QUANT(entry & INDEXMASK)->name) == 0 ) { if ( addflag == HASH_DELETE ) { htable[spot] = 0; dy_global_hash_used--;} return entry; } break; case METHODNAME: if ( stricmp(name,METH_INSTANCE(entry & INDEXMASK)->name) == 0 ) { if ( addflag == HASH_DELETE ) { htable[spot] = 0; dy_global_hash_used--;} return entry; } break; default: if ( addflag == HASH_ADD ) { htable[spot] = type | inx; dy_global_hash_used++; } return 0; } /* have to keep searching */ spot++; if ( spot == dy_global_hash_max ) spot = 0; } /* can't get here */ } /********************************************************************** * * function: add_global() * * purpose: add new global variable * * return: index number in global list */ int add_global(name) char *name; { int slot; int inx; /* index in global table */ struct global *g; ident_t iid; if ( web.maxglobals == 0 ) /* very first initialization */ { web.maxglobals = 100; dy_globals = dy_calloc(web.maxglobals,sizeof(struct global)); Globals = globals(0); /* handy for debugging */ } slot = web.global_count; iid = slot | EPHGLOBAL; g = globals(iid); memset((char*)g,0,sizeof(struct global)); strncpy(g->name,name,GLOBAL_NAME_SIZE); g->flags |= GLOB_USED; if ( strlen(name) > GLOBAL_NAME_SIZE ) { name[GLOBAL_NAME_SIZE] = 0; sprintf(errmsg,"Name too long. Truncated to %s.\n",name); kb_error(1669,errmsg,WARNING); } /* see if already existed */ inx = lookup_global_hash(name,slot,VARIABLENAME,HASH_ADD); if ( inx ) /* already there */ { sprintf(errmsg,"The name \"%s\" is already in use%s.\n",name, (inx&NAMETYPEMASK)==QUANTITYNAME ? " as a quantity name" : (inx&NAMETYPEMASK)==PERM_NAME ? " as a permanent name" : (inx&NAMETYPEMASK)==METHODNAME ? " as a method instance name" : ""); kb_error(2554,errmsg,RECOVERABLE); } else web.global_count++; /* see if we want to expand */ if ( web.global_count == web.maxglobals ) { web.maxglobals *= 2; dy_globals = dy_realloc(dy_globals,web.maxglobals,sizeof(struct global)); Globals = globals(0); /* handy for debugging */ } return iid; } /********************************************************************** * * function: lookup_global() * * purpose: searches for global variable * * return: index number in global list, -1 if not found */ int lookup_global(name) char *name; { int inx; if ( web.global_count == 0 ) return -1; /* search for name */ inx = lookup_global_hash(name,0,VARIABLENAME,HASH_LOOK); if ( inx == 0 ) return -1; if ( (inx & NAMETYPEMASK) == VARIABLENAME ) return (inx & INDEXMASK) | EPHGLOBAL; return -1; } /************************************************************************** * * function: rewind_globals() * * purpose: Delete globals added during failed parse. * */ void rewind_globals(rewind_spot) int rewind_spot; /* where to rewind to */ { int k; /* unhash in reverse order of adding */ for ( k = web.global_count-1 ; k >= rewind_spot ; k-- ) lookup_global_hash(globals(k)->name,0,0,HASH_DELETE); web.global_count = rewind_spot; } /***************************************************************** * * function: clear_globals() * * purpose: Reset global name hash table to empty. */ void clear_globals() { dy_globalshash = 0; dy_global_hash_max = 0; dy_global_hash_maxfill = 0; dy_global_hash_used = 0; } /********************************************************************** Corresponding functions for permanent globals. **********************************************************************/ /********************************************************************** * * function: add_perm_global() * * purpose: add new perm_global variable * * return: index number in perm_global list * -index - 1 if already there */ int add_perm_global(name) char *name; { int slot; int inx; /* index in global table */ if ( web.max_perm_globals == 0 ) /* very first initialization */ { web.max_perm_globals = 100; dy_perm_globals = calloc(web.max_perm_globals,sizeof(struct global)); perm_Globals = perm_globals(0); /* handy for debugging */ } slot = web.perm_global_count; strncpy(perm_globals(slot)->name,name,GLOBAL_NAME_SIZE); perm_globals(slot)->flags |= GLOB_USED; if ( strlen(name) > GLOBAL_NAME_SIZE ) { name[GLOBAL_NAME_SIZE] = 0; sprintf(errmsg,"Name too long. Truncated to %s.\n",name); kb_error(2584,errmsg,WARNING); } /* see if already existed */ inx = lookup_global_hash(name,slot,PERM_NAME,HASH_ADD); if ( inx ) /* already there */ { sprintf(errmsg,"The name \"%s\" is already in use%s.\n",name, (inx&NAMETYPEMASK)==QUANTITYNAME ? " as a quantity name" : (inx&NAMETYPEMASK)==VARIABLENAME ? " as a variable name" : (inx&NAMETYPEMASK)==METHODNAME ? " as a method instance name" : ""); kb_error(2576,errmsg,RECOVERABLE); } else web.perm_global_count++; /* see if we want to expand */ if ( web.perm_global_count == web.max_perm_globals ) { web.max_perm_globals *= 2; dy_perm_globals = realloc(dy_perm_globals,web.max_perm_globals*sizeof(struct global)); perm_Globals = perm_globals(0); /* handy for debugging */ } return slot | PERMGLOBAL; } /********************************************************************** * * function: lookup_perm_global() * * purpose: searches for global variable * * return: index number in global list, -1 if not found */ int lookup_perm_global(name) char *name; { int inx; if ( web.perm_global_count == 0 ) return -1; /* search for name */ inx = lookup_global_hash(name,0,PERM_NAME,HASH_LOOK); if ( inx == 0 ) return -1; if ( (inx & NAMETYPEMASK) == PERM_NAME ) return (inx & INDEXMASK) | PERMGLOBAL; return -1; } /************************************************************************** * * function: rewind_perm_globals() * * purpose: Delete permanent globals added during failed parse. * */ void perm_rewind_globals(perm_rewind_spot) int perm_rewind_spot; /* where to rewind to */ { int k; /* unhash in reverse order of adding */ for ( k = web.perm_global_count-1 ; k >= perm_rewind_spot ; k-- ) lookup_global_hash(perm_globals(k)->name,0,0,HASH_DELETE); web.perm_global_count = perm_rewind_spot; } /************************************************************************** Local variable functions. Local variables are stored in procedure-specific name lists, so re-defining procedure doesn't lead to memory leak. Each list is tree-linked for block scoping. **************************************************************************/ struct locallist_t *local_scope_bases[LOCALSCOPEMAX]; /* for reference during parsing */ int local_nest_depth; struct locallist_t *localbase; /************************************************************************** * * function: init_local_scope() * * purpose: Set up local scope structure for new command definition. */ void init_local_scope(iid) ident_t iid; /* identifier of procedure to begin new scope */ { struct locallist_t *locals; locals = (iid & PERMGLOBAL ) ? (struct locallist_t *)calloc(1,sizeof(struct locallist_t)) : (struct locallist_t *)mycalloc(1,sizeof(struct locallist_t)) ; if ( iid & PERMGLOBAL ) locals->flags |= LL_PERMANENT; if ( local_nest_depth < 0 ) local_nest_depth = 0; local_scope_bases[local_nest_depth] = locals; local_nest_depth++; locals->iid = iid; locals->maxlist = 8; locals->list = (locals->flags & LL_PERMANENT ) ? (struct localvar_t*)calloc(locals->maxlist, sizeof(struct localvar_t)) : (struct localvar_t*)mycalloc(locals->maxlist, sizeof(struct localvar_t)) ; localbase = locals; locals->list[0].prev = -1; } /************************************************************************** * * function: exit_local_scope() * * purpose: finish procedure list local definitions. * return: sets global variable localbase to the completed * locals structure, for parser to attach * to code block. */ void exit_local_scope() { struct locallist_t *locals; local_nest_depth--; locals = local_scope_bases[local_nest_depth]; if ( locals && !(locals->flags & LL_IN_USE) ) { myfree((char*)(locals->list)); myfree((char*)locals); } local_scope_bases[local_nest_depth] = NULL; if ( local_nest_depth > 0 ) localbase = local_scope_bases[local_nest_depth-1]; else localbase = NULL; } /************************************************************************** * * function: begin_local_scope() * * purpose: start block scope */ void begin_local_scope() { struct locallist_t *locals; if ( local_nest_depth == 0 ) { /* coming in from unnamed command */ /*init_local_scope(0); */ kb_error(3501,"INTERNAL ERROR: local_scope not initialized.\n", RECOVERABLE); } locals = local_scope_bases[local_nest_depth-1]; locals->list[locals->count].scope_depth++; locals->scope_depth++; } /************************************************************************** * * function: end_local_scope() * * purpose: end block scope */ void end_local_scope() { struct locallist_t *locals; int n; locals = local_scope_bases[local_nest_depth-1]; if ( !locals ) return; if ( locals->list ) { n = locals->list[locals->count].prev; while ( (n >= 0) && (locals->list[n].scope_depth == locals->scope_depth) ) n = locals->list[n].prev; locals->list[locals->count].prev = n; locals->list[locals->count].scope_depth--; } locals->scope_depth--; } /************************************************************************** * * function: add_local_var() * * purpose: add a local variable to procedure local variable list */ ident_t add_local_var(name,size) char *name; /* NULL if just reserving space for anonymous value */ int size; /* in stack entries */ { int n; struct localvar_t *v; int depth; struct locallist_t *locals; int retval; locals = local_scope_bases[local_nest_depth-1]; depth = locals->scope_depth; v = locals->list + locals->count; if ( name ) { /* search for duplicate in current scope */ for ( n = v->prev ; (n >= 0) && (locals->list[n].scope_depth == depth) ; n = locals->list[n].prev ) if ( strcmp(name,locals->list[n].g.name) == 0 ) { sprintf(errmsg,"Local variable %s already declared in current block.\n", name); kb_error(2613,errmsg,COMMAND_ERROR); } /* add to list */ strncpy(v->g.name,name,GLOBAL_NAME_SIZE); v->g.flags |= GLOB_USED | GLOB_LOCALVAR; v->offset = v->g.value.offset = locals->totalsize; } /* extend list, if necessary */ if ( locals->count >= locals->maxlist-1 ) { locals->list = (locals->flags & LL_PERMANENT) ? (struct localvar_t*)realloc((char*)locals->list, locals->maxlist*2*sizeof(struct localvar_t)) : (struct localvar_t*)kb_realloc((char*)locals->list, locals->maxlist*2*sizeof(struct localvar_t)); locals->maxlist*=2; v = locals->list + locals->count; /* reset dangling pointer */ } v->size = size; v->offset = locals->totalsize; /* move counter up */ v[1].prev = locals->count; v[1].scope_depth = v->scope_depth; locals->totalsize = v->offset + size; retval = locals->count | LOCALVAR; locals->count++; return retval; } /* end add_local_var */ /************************************************************************** * * function: lookup_local_var() * * purpose: lookup a local variable to procedure local variable list */ ident_t lookup_local_var(name) char *name; { int n; struct localvar_t *v; struct locallist_t *locals; if ( local_nest_depth <= 0 ) return 0; locals = local_scope_bases[local_nest_depth-1]; if ( locals->list == NULL ) return 0; v = locals->list + locals->count; /* search for duplicate in current scope */ for ( n = v->prev ; n >= 0 ; n = ( n ? locals->list[n].prev : -1) ) if ( strcmp(name,locals->list[n].g.name) == 0 ) { return LOCALVAR | n; } return 0; /* not found */ } /************************************************************************** * * function: locals_copy() * * purpose: make a copy of local variable structure when assigning * a code block in eval(). */ void locals_copy(dest,src) struct locallist_t **dest; struct locallist_t *src; { if ( *dest ) { /* have old structure */ myfree((char*)(*dest)->list); } else *dest = (struct locallist_t *)mycalloc(1,sizeof(struct locallist_t)); if ( src ) { **dest = *src; (*dest)->list = (struct localvar_t *)mycalloc(src->count, sizeof(struct localvar_t)); memcpy((*dest)->list,src->list,src->count*sizeof(struct localvar_t)); } else memset((char*)(*dest),0,sizeof(struct locallist_t)); } /************************************************************************** * * function: locals_copy_perm() * * purpose: make a copy of local variable structure when assigning * a code block in eval(). Permanent procedure version. */ void locals_copy_perm(dest,src) struct locallist_t **dest; struct locallist_t *src; { if ( *dest ) { /* have old structure */ free((char*)(*dest)->list); } else *dest = (struct locallist_t *)calloc(1,sizeof(struct locallist_t)); if ( src ) { **dest = *src; (*dest)->list = (struct localvar_t *)calloc(src->count, sizeof(struct localvar_t)); memcpy((*dest)->list,src->list,src->count*sizeof(struct localvar_t)); } else memset((char*)(*dest),0,sizeof(struct locallist_t)); } /************************************************************************** * * function: initialize_perm_globals() * * purpose: set up Evolver internal variables in permanent symbol table. */ void initialize_perm_globals() { int k; struct global *g; k = add_perm_global("view_transform_swap_colors"); view_transform_swap_colors_global = k; g = globals(k); g->attr.arrayptr = (struct array*)calloc(sizeof(struct array)+1*sizeof(int),1); g->attr.arrayptr->dim = 1; g->attr.arrayptr->itemsize = sizeof(int); g->attr.arrayptr->datatype = INTEGER_TYPE; g->attr.arrayptr->datacount = 0; g->attr.arrayptr->sizes[0] = 0; g->attr.arrayptr->datastart = 0; g->flags = INTERNAL_NAME|PERMANENT|ARRAY_PARAM|READONLY; k = add_perm_global("view_transforms"); view_transforms_global = k; g = globals(k); g->attr.arrayptr = (struct array*)calloc(sizeof(struct array)+3*sizeof(int),1); g->attr.arrayptr->dim = 3; g->attr.arrayptr->itemsize = sizeof(REAL); g->attr.arrayptr->datatype = REAL_TYPE; g->attr.arrayptr->datacount = 0; g->attr.arrayptr->sizes[0] = 0; g->attr.arrayptr->sizes[1] = 0; g->attr.arrayptr->sizes[2] = 0; g->attr.arrayptr->datastart = 0; g->flags = INTERNAL_NAME|PERMANENT|ARRAY_PARAM|READONLY; k = add_perm_global("view_matrix"); view_matrix_global = k; g = globals(k); g->attr.arrayptr = (struct array*)calloc(sizeof(struct array)+2*sizeof(int),1); g->attr.arrayptr->dim = 2; g->attr.arrayptr->itemsize = sizeof(REAL); g->attr.arrayptr->datatype = REAL_TYPE; g->attr.arrayptr->datacount = 0; g->attr.arrayptr->sizes[0] = 0; g->attr.arrayptr->sizes[1] = 0; g->attr.arrayptr->sizes[2] = 0; g->attr.arrayptr->datastart = 0; g->flags = INTERNAL_NAME|PERMANENT|ARRAY_PARAM; k = add_perm_global("torus_periods"); torus_periods_global = k; g = globals(torus_periods_global); g->attr.arrayptr = (struct array*)calloc(sizeof(struct array)+2*sizeof(int),1); g->attr.arrayptr->dim = 2; g->attr.arrayptr->itemsize = sizeof(REAL); g->attr.arrayptr->datatype = REAL_TYPE; g->attr.arrayptr->sizes[0] = 0; g->attr.arrayptr->sizes[1] = 0; g->attr.arrayptr->datacount = 0; g->flags = INTERNAL_NAME|PERMANENT|ARRAY_PARAM|READONLY; k = add_perm_global("inverse_periods"); inverse_periods_global = k; g = globals(k); g->attr.arrayptr = (struct array*)calloc(sizeof(struct array)+2*sizeof(int),1); g->attr.arrayptr->dim = 2; g->attr.arrayptr->itemsize = sizeof(REAL); g->attr.arrayptr->datatype = REAL_TYPE; g->attr.arrayptr->datacount = 0; g->attr.arrayptr->sizes[0] = 0; g->attr.arrayptr->sizes[1] = 0; g->attr.arrayptr->sizes[2] = 0; g->attr.arrayptr->datastart = 0; g->flags = INTERNAL_NAME|PERMANENT|ARRAY_PARAM|READONLY; k = add_perm_global("estimated_change"); g = globals(k); g->value.dataptr = &estimated_change; g->flags = INTERNAL_NAME|PERMANENT|REALVAL|READONLY; k = add_perm_global("eigenvalues"); eigenvalues_list_global = k; g = globals(eigenvalues_list_global); g->attr.arrayptr = (struct array*)calloc(sizeof(struct array)+1*sizeof(int),1); g->attr.arrayptr->dim = 1; g->attr.arrayptr->itemsize = sizeof(REAL); g->attr.arrayptr->datatype = REAL_TYPE; g->attr.arrayptr->sizes[0] = 0; g->attr.arrayptr->datacount = 0; g->flags = INTERNAL_NAME|PERMANENT|ARRAY_PARAM|READONLY; k = add_perm_global("slice_coeff"); slice_coeff_global = k; g = globals(k); g->attr.arrayptr = (struct array*)calloc(sizeof(struct array)+1*sizeof(int),1); g->attr.arrayptr->dim = 1; g->attr.arrayptr->itemsize = sizeof(REAL); g->attr.arrayptr->datatype = REAL_TYPE; g->attr.arrayptr->datacount = MAXCOORD+2; g->attr.arrayptr->sizes[0] = MAXCOORD+2; g->attr.arrayptr->datastart = (char*)slice_coeff-(char*)(g->attr.arrayptr); g->flags = PERMANENT|ARRAY_PARAM|RECALC_PARAMETER|ALWAYS_RECALC; k = add_perm_global("clip_coeff"); clip_coeff_global = k; g = globals(k); g->attr.arrayptr = (struct array*)calloc(sizeof(struct array)+1*sizeof(int),1); g->attr.arrayptr->dim = 2; g->attr.arrayptr->itemsize = sizeof(REAL); g->attr.arrayptr->datatype = REAL_TYPE; g->attr.arrayptr->datacount = MAXCLIPS*(MAXCOORD+2); g->attr.arrayptr->sizes[0] = MAXCLIPS; g->attr.arrayptr->sizes[1] = MAXCOORD+2; g->attr.arrayptr->datastart = (char*)clip_coeff-(char*)(g->attr.arrayptr); g->flags = PERMANENT|ARRAY_PARAM|RECALC_PARAMETER|ALWAYS_RECALC; } /************************************************************************* * * function: allocate_transform_colors() * * purpose: allocate transform_colors array and update global variable info. */ void allocate_transform_colors(count) int count; { struct global *g; if ( transform_colors ) myfree((char*)transform_colors); if ( count ) transform_colors = (int*)mycalloc(count,sizeof(int)); g = globals(view_transform_swap_colors_global); g->attr.arrayptr->sizes[0] = count; g->attr.arrayptr->datacount = count; g->attr.arrayptr->datastart = count ? ((char*)transform_colors - (char*)(g->attr.arrayptr)) : 0; } /************************************************************************* * * function: set_view_transforms_global() * * purpose: update view_transforms[][][] global variable info. */ void set_view_transforms_global() { struct global *g; /* need to reorder, since graphgen can leave it in a mess */ view_transforms = matrix3_reorder(view_transforms,transform_count,SDIM+1,SDIM+1); g = globals(view_transforms_global); g->attr.arrayptr->sizes[0] = transform_count; g->attr.arrayptr->sizes[1] = SDIM+1; g->attr.arrayptr->sizes[2] = SDIM+1; g->attr.arrayptr->datacount = transform_count*(SDIM+1)*(SDIM+1); g->attr.arrayptr->datastart = transform_count ? (char*)view_transforms[0][0] - (char*)(g->attr.arrayptr) : 0; } /************************************************************************* * * function: set_torus_periods_global() * * purpose: update torus_periods[][] global variable info. */ void set_torus_periods_global() { struct global *g; g = globals(torus_periods_global); g->attr.arrayptr->sizes[0] = SDIM; g->attr.arrayptr->sizes[1] = SDIM; g->attr.arrayptr->datacount = SDIM*SDIM; g->attr.arrayptr->datastart = (char*)&web.torus_period[0][0] - (char*)(g->attr.arrayptr); } /************************************************************************* * * function: set_inverse_periods_global() * * purpose: update inverse_periods[][] global variable info. */ void set_inverse_periods_global() { struct global *g; g = globals(inverse_periods_global); g->attr.arrayptr->sizes[0] = SDIM; g->attr.arrayptr->sizes[1] = SDIM; g->attr.arrayptr->datacount = SDIM*SDIM; g->attr.arrayptr->datastart = (char*)&web.inverse_periods[0][0] - (char*)(g->attr.arrayptr); } /************************************************************************* * * function: set_view_matrix_global() * * purpose: update view_matrix[][] global variable info. */ void set_view_matrix_global() { struct global *g; g = globals(view_matrix_global); g->attr.arrayptr->sizes[0] = SDIM+1; g->attr.arrayptr->sizes[1] = SDIM+1; g->attr.arrayptr->datacount = (SDIM+1)*(SDIM+1); g->attr.arrayptr->datastart = (char*)&view[0][0] - (char*)(g->attr.arrayptr); } /************************************************************************* * * function: set_eigenvalue_list_global() * * purpose: update eigenvalue[] global variable info. */ void set_eigenvalue_list_global(evalues,count) REAL *evalues; int count; { struct global *g; g = globals(eigenvalues_list_global); g->attr.arrayptr->sizes[0] = count; g->attr.arrayptr->datacount = count; g->attr.arrayptr->datastart = (char*)evalues - (char*)(g->attr.arrayptr); } /******************************************************************************** * * Function: get_name_arrayptr() * * Purpose: return a pointer to the array info structure associated with a * global variable or extra attribute. Called from eval*(). */ struct array *get_name_arrayptr(int name_id, REAL *newstack, struct locallist_t *localbase) { if ( (name_id & GTYPEMASK) == ATTRIBNAME ) { struct extra *ex = EXTRAS(name_eltype(name_id)) + (name_id & GLOBMASK); return &(ex->array_spec); } else /* regular global */ { struct global *g; g = globals(name_id); if ( newstack && ((g->flags & (GLOB_LOCALVAR|FIXED_SIZE_ARRAY))==GLOB_LOCALVAR) ) return *(struct array **)(newstack+g->value.offset); else return g->attr.arrayptr; } return NULL; /* just to keep some compilers happy */ } /******************************************************************************** * * Function: get_name_name() * * Purpose: return a pointer to the string name associated with a * global variable or extra attribute id number. Called from eval*(). */ char *get_name_name(int name_id, struct locallist_t *localbase) { if ( (name_id & GTYPEMASK) == ATTRIBNAME ) { struct extra *ex = EXTRAS(name_eltype(name_id)) + (name_id & GLOBMASK); return ex->name; } else /* regular global */ { struct global *g; g = globals(name_id); return g->name; } return NULL; /* just to keep some compilers happy */ } /******************************************************************************** * * Function: get_name_dim() * * Purpose: return dimension of a * global variable or extra attribute id number. */ int get_name_dim(int name_id, struct locallist_t *localbase) { if ( (name_id & GTYPEMASK) == ATTRIBNAME ) { struct extra *ex = EXTRAS(name_eltype(name_id)) + (name_id & GLOBMASK); return ex->array_spec.dim; } else /* regular global */ { struct global *g; g = globals(name_id); return g->attr.arrayptr->dim; } return 0; /* just to keep some compilers happy */ } /******************************************************************************** * * Function: get_name_datatype() * * Purpose: return datattype of a * global variable or extra attribute id number. */ int get_name_datatype(int name_id, struct locallist_t *localbase) { if ( (name_id & GTYPEMASK) == ATTRIBNAME ) { struct extra *ex = EXTRAS(name_eltype(name_id)) + (name_id & GLOBMASK); return ex->array_spec.datatype; } else /* regular global */ { struct global *g; g = globals(name_id); return g->type; } return 0; /* just to keep some compilers happy */ } evolver-2.30c.dfsg/src/method1.c0000644000175300017530000031160111410765113016724 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************* * * file: method1.c * * contents: quantities for vertices and edges */ #include "include.h" /*********************************************************************** null_length - just sets edge length and returns 0 for everything. ***********************************************************************/ REAL null_length_value(e_info) struct qinfo *e_info; { q_edge_tension_value(e_info); return 0.0; } REAL null_length_grad(e_info) struct qinfo *e_info; { return 0.0; } REAL null_length_hess(e_info) struct qinfo *e_info; { return 0.0; } /*********************************************************************** null_area - just sets facet length and returns 0 for everything. ***********************************************************************/ REAL null_area_value(f_info) struct qinfo *f_info; { q_facet_tension_value(f_info); return 0.0; } REAL null_area_grad(f_info) struct qinfo *f_info; { return 0.0; } REAL null_area_hess(f_info) struct qinfo *f_info; { return 0.0; } /********************************************************************* vertex_scalar_integral method *********************************************************************/ /********************************************************************* * * function: vertex_scalar_integral_init() * * purpose: check things * */ void vertex_scalar_integral_init(mode,mi) int mode; struct method_instance *mi; { } /********************************************************************* * * function: vertex_scalar_integral() * * purpose: method value * */ REAL vertex_scalar_integral(v_info) struct qinfo *v_info; { REAL area; struct method_instance *mi = METH_INSTANCE(v_info->method); area = eval(mi->expr[0],v_info->x[0],v_info->id, NULL); if ( mi->flags & DEFAULT_INSTANCE ) { /* add to facet area */ body_id b_id,bb_id; edge_id e_id = get_vertex_edge(v_info->id); facetedge_id fe_id = get_edge_fe(e_id); facetedge_id fe; facet_id f_id; b_id = GEN_QUANT(mi->quant)->b_id; fe = fe_id; do { f_id = get_fe_facet(fe); bb_id = get_facet_body(f_id); if ( equal_id(b_id,bb_id) ) add_facet_area(f_id,-area); bb_id = get_facet_body(inverse_id(f_id)); if ( equal_id(b_id,bb_id) ) add_facet_area(f_id,-area); fe = get_next_facet(fe); } while ( !equal_id(fe,fe_id) ); } return area; } /********************************************************************* * * function: vertex_scalar_integral_grad() * * purpose: method gradient * */ REAL vertex_scalar_integral_grad(v_info) struct qinfo *v_info; { REAL value = 0.0; eval_all(METH_INSTANCE(v_info->method)->expr[0],v_info->x[0],SDIM,&value, v_info->grad[0],v_info->id); return value; } /********************************************************************* * * function: vertex_scalar_integral_hess() * * purpose: method gradient and hessian * */ REAL vertex_scalar_integral_hess(v_info) struct qinfo *v_info; { REAL value = 0.0; eval_second(METH_INSTANCE(v_info->method)->expr[0],v_info->x[0],SDIM,&value, v_info->grad[0], v_info->hess[0][0],v_info->id); return value; } /********************************************************************* Edge length quantity *********************************************************************/ /********************************************************************* * * function: q_edge_tension_init() * */ void q_edge_tension_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { } /********************************************************************* * * function: q_edge_tension_value() * * purpose: General quantity value of edge tension. */ REAL q_edge_tension_value(e_info) struct qinfo *e_info; { REAL energy; if ( web.modeltype == QUADRATIC ) return edge_length_q_value(e_info); if ( web.modeltype == LAGRANGE ) return lagrange_edge_tension_value(e_info); energy = sqrt(SDIM_dot(e_info->sides[0][0],e_info->sides[0][0])); if ( METH_INSTANCE(e_info->method)->flags & DEFAULT_INSTANCE ) { #ifdef SHARED_MEMORY if ( nprocs > 1 ) proc_total_area[GET_THREAD_ID] += energy; else #endif binary_tree_add(web.total_area_addends,energy); set_edge_length(e_info->id,energy); } if ( METH_INSTANCE(e_info->method)->flags & USE_DENSITY ) energy *= get_edge_density(e_info->id); return energy; } /********************************************************************* * * function: q_edge_tension_gradient() * * purpose: General quantity value and gradient of edge tension. */ REAL q_edge_tension_gradient(e_info) struct qinfo *e_info; { REAL energy; REAL fudge; int j; if ( web.modeltype == QUADRATIC ) return edge_length_q_grad(e_info); if ( web.modeltype == LAGRANGE ) return lagrange_edge_tension_grad(e_info); energy = sqrt(SDIM_dot(e_info->sides[0][0],e_info->sides[0][0])); fudge = 1/energy; if ( METH_INSTANCE(e_info->method)->flags & USE_DENSITY ) { REAL density = get_edge_density(e_info->id); energy *= density; fudge *= density; } for ( j = 0 ; j < SDIM ; j++ ) { e_info->grad[0][j] = -e_info->sides[0][0][j]*fudge; e_info->grad[1][j] = e_info->sides[0][0][j]*fudge; } return energy; } /********************************************************************* * * function: q_edge_tension_hessian() * * purpose: General quantity value, gradient, and hessian of edge length. * * Remark to programmers: e_info->hess[m][n][i][j] is the entry for * coordinate i of vertex m of the edge and coordinate j of vertex n. */ REAL q_edge_tension_hessian(e_info) struct qinfo *e_info; { REAL energy; int i,j; REAL e1,e3,ss; REAL fudge,len; if ( web.modeltype == QUADRATIC ) return edge_length_q_hess(e_info); if ( web.modeltype == LAGRANGE ) return lagrange_edge_tension_hess(e_info); energy = len = sqrt(SDIM_dot(e_info->sides[0][0],e_info->sides[0][0])); fudge = 1/len; if ( METH_INSTANCE(e_info->method)->flags & USE_DENSITY ) { REAL density = get_edge_density(e_info->id); energy *= density; fudge *= density; } for ( j = 0 ; j < SDIM ; j++ ) { e_info->grad[0][j] = -e_info->sides[0][0][j]*fudge; e_info->grad[1][j] = e_info->sides[0][0][j]*fudge; } e3 = fudge/(len*len); e1 = fudge; for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { ss = e_info->sides[0][0][i]*e_info->sides[0][0][j]*e3; e_info->hess[0][0][i][j] = -ss; e_info->hess[1][1][i][j] = -ss; e_info->hess[0][1][i][j] = ss; e_info->hess[1][0][i][j] = ss; } for ( i = 0 ; i < SDIM ; i++ ) { e_info->hess[0][0][i][i] += e1; e_info->hess[1][1][i][i] += e1; e_info->hess[0][1][i][i] -= e1; e_info->hess[1][0][i][i] -= e1; } return energy; } /********************************************************************* quadratic edge_length method *********************************************************************/ /********************************************************************* * * function: edge_length_q_value() * * purpose: method value * */ REAL edge_length_q_value(e_info) struct qinfo *e_info; { int j,k,m; REAL value = 0.0; REAL tang[MAXCOORD]; for ( m = 0 ; m < gauss1D_num ; m++ ) { for ( j = 0 ; j < SDIM ; j ++ ) { tang[j] = 0.0; for ( k = 0 ; k < edge_ctrl ; k++ ) tang[j] += gauss1polyd[k][m]*e_info->x[k][j]; } value += gauss1Dwt[m]*sqrt(SDIM_dot(tang,tang)); } if ( METH_INSTANCE(e_info->method)->flags & DEFAULT_INSTANCE ) { #ifdef SHARED_MEMORY if ( nprocs > 1 ) proc_total_area[GET_THREAD_ID] += value; else #endif binary_tree_add(web.total_area_addends,value); set_edge_length(e_info->id,value); } if ( METH_INSTANCE(e_info->method)->flags & USE_DENSITY ) value *= get_edge_density(e_info->id); return value; } /********************************************************************* * * function: edge_length_integral_q_grad() * * purpose: method gradient * */ REAL edge_length_q_grad(e_info) struct qinfo *e_info; { int m,k,j; REAL value = 0.0; REAL len,fudge; REAL tang[MAXCOORD]; REAL density; if ( METH_INSTANCE(e_info->method)->flags & USE_DENSITY ) density = get_edge_density(e_info->id); else density = 1.0; for ( j = 0 ; j < SDIM ; j++ ) for ( m = 0 ; m < edge_ctrl ; m++ ) e_info->grad[m][j] = 0.0; for ( m = 0 ; m < gauss1D_num ; m++ ) { for ( j = 0 ; j < SDIM ; j ++ ) { tang[j] = 0.0; for ( k = 0 ; k < edge_ctrl ; k++ ) tang[j] += gauss1polyd[k][m]*e_info->x[k][j]; } len = sqrt(SDIM_dot(tang,tang)); if ( len == 0.0 ) continue; value += gauss1Dwt[m]*len; fudge = density*gauss1Dwt[m]/len; for ( k = 0 ; k < edge_ctrl ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) e_info->grad[k][j] += fudge*tang[j]*gauss1polyd[k][m]; } return density*value; } /********************************************************************* * * function: edge_length_q_hess() * * purpose: method gradient and hessian * */ REAL edge_length_q_hess(e_info) struct qinfo *e_info; { int m,j,jj,k,kk; REAL value = 0.0; REAL len,density,fudge; REAL sumgrad[2][MAXCOORD]; REAL sumhess[2][2][MAXCOORD][MAXCOORD]; REAL tang[MAXCOORD]; if ( METH_INSTANCE(e_info->method)->flags & USE_DENSITY ) density = get_edge_density(e_info->id); else density = 1.0; /* derivatives of gaussian sum part */ memset((char*)sumgrad,0,sizeof(sumgrad)); memset((char*)sumhess,0,sizeof(sumhess)); for ( m = 0 ; m < gauss1D_num ; m++ ) { for ( j = 0 ; j < SDIM ; j ++ ) { tang[j] = 0.0; for ( k = 0 ; k < edge_ctrl ; k++ ) tang[j] += gauss1polyd[k][m]*e_info->x[k][j]; } len = sqrt(SDIM_dot(tang,tang)); if ( len == 0.0 ) continue; value += gauss1Dwt[m]*len; fudge = density*gauss1Dwt[m]/len; for ( k = 0 ; k < edge_ctrl ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) e_info->grad[k][j] += fudge*tang[j]*gauss1polyd[k][m]; for ( k = 0 ; k < edge_ctrl ; k++ ) for ( kk = 0 ; kk < edge_ctrl ; kk++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) e_info->hess[k][kk][j][jj] += fudge* ( - tang[j]*tang[jj]*gauss1polyd[k][m]*gauss1polyd[kk][m]/len/len + ((j==jj)? gauss1polyd[k][m]*gauss1polyd[kk][m] : 0.0)); } return density*value; } /********************************************************************* edge_scalar_integral method *********************************************************************/ /********************************************************************* * * function: edge_scalar_integral() * * purpose: method value * */ REAL edge_scalar_integral(e_info) struct qinfo *e_info; { int m; REAL value = 0.0; if ( web.modeltype == QUADRATIC ) return edge_scalar_integral_q(e_info); if ( web.modeltype == LAGRANGE ) return edge_scalar_integral_lagr(e_info); for ( m = 0 ; m < gauss1D_num ; m++ ) { e_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ value += gauss1Dwt[m]*eval(METH_INSTANCE(e_info->method)->expr[0], e_info->gauss_pt[m], e_info->id,NULL); } value *= sqrt(SDIM_dot(e_info->sides[0][0],e_info->sides[0][0])); return value; } /********************************************************************* * * function: edge_scalar_integral_grad() * * purpose: method gradient * */ REAL edge_scalar_integral_grad(e_info) struct qinfo *e_info; { int m,j; REAL value = 0.0; REAL len,val; REAL derivs[MAXCOORD]; if ( web.modeltype == QUADRATIC ) return edge_scalar_integral_q_grad(e_info); if ( web.modeltype == LAGRANGE ) return edge_scalar_integral_lagr_grad(e_info); for ( j = 0 ; j < SDIM ; j++ ) for ( m = 0 ; m < 2 ; m++ ) e_info->grad[m][j] = 0.0; len = sqrt(SDIM_dot(e_info->sides[0][0],e_info->sides[0][0])); for ( m = 0 ; m < gauss1D_num ; m++ ) { e_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ eval_all(METH_INSTANCE(e_info->method)->expr[0],e_info->gauss_pt[m],SDIM,&val, derivs,e_info->id); value += gauss1Dwt[m]*val; for ( j = 0 ; j < SDIM ; j++ ) { e_info->grad[0][j] += gauss1Dwt[m]*gauss1poly[0][m]*derivs[j]*len; e_info->grad[1][j] += gauss1Dwt[m]*gauss1poly[1][m]*derivs[j]*len; } } for ( j = 0 ; j < SDIM ; j++ ) { e_info->grad[0][j] -= value*e_info->sides[0][0][j]/len; e_info->grad[1][j] += value*e_info->sides[0][0][j]/len; } return len*value; } /********************************************************************* * * function: edge_scalar_integral_hess() * * purpose: method gradient and hessian * */ REAL edge_scalar_integral_hess(e_info) struct qinfo *e_info; { int m,j,k,i; REAL value = 0.0; REAL len,sum,val; REAL derivs[MAXCOORD]; REAL lengrad[2][MAXCOORD],sumgrad[2][MAXCOORD]; REAL lenhess[2][2][MAXCOORD][MAXCOORD],sumhess[2][2][MAXCOORD][MAXCOORD]; MAT2D(second,MAXCOORD,MAXCOORD); if ( web.modeltype == QUADRATIC ) return edge_scalar_integral_q_hess(e_info); if ( web.modeltype == LAGRANGE ) return edge_scalar_integral_lagr_hess(e_info); len = SDIM_dot(e_info->sides[0][0],e_info->sides[0][0]); if ( len <= 0.0 ) { return 0.0; } len = sqrt(len); /* derivatives of gaussian sum part */ sum = 0.0; memset((char*)sumgrad,0,sizeof(sumgrad)); memset((char*)sumhess,0,sizeof(sumhess)); for ( m = 0 ; m < gauss1D_num ; m++ ) { e_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ eval_second(METH_INSTANCE(e_info->method)->expr[0],e_info->gauss_pt[m],SDIM,&val, derivs,second,e_info->id); sum += gauss1Dwt[m]*val; for ( j = 0 ; j < SDIM ; j++ ) { sumgrad[0][j] += gauss1Dwt[m]*gauss1poly[0][m]*derivs[j]; sumgrad[1][j] += gauss1Dwt[m]*gauss1poly[1][m]*derivs[j]; } for ( j = 0 ; j < SDIM ; j++ ) for ( k = 0 ; k < SDIM ; k++ ) { sumhess[0][0][j][k] += gauss1Dwt[m]*gauss1poly[0][m]*gauss1poly[0][m] *second[j][k]; sumhess[0][1][j][k] += gauss1Dwt[m]*gauss1poly[0][m]*gauss1poly[1][m] *second[j][k]; sumhess[1][0][j][k] += gauss1Dwt[m]*gauss1poly[1][m]*gauss1poly[0][m] *second[j][k]; sumhess[1][1][j][k] += gauss1Dwt[m]*gauss1poly[1][m]*gauss1poly[1][m] *second[j][k]; } } /* derivatives of length part */ for ( j = 0 ; j < SDIM ; j++ ) { lengrad[0][j] = -e_info->sides[0][0][j]/len; lengrad[1][j] = e_info->sides[0][0][j]/len; } for ( j = 0 ; j < SDIM ; j++ ) for ( k = 0 ; k < SDIM ; k++ ) { val = -e_info->sides[0][0][j]*e_info->sides[0][0][k]/len/len/len; if ( j == k ) val += 1/len; lenhess[0][0][j][k] = lenhess[1][1][j][k] = val; lenhess[0][1][j][k] = lenhess[1][0][j][k] = -val; } /* final values */ value = len*sum; for ( j = 0 ; j < SDIM ; j++ ) for ( m = 0 ; m < 2 ; m++ ) e_info->grad[m][j] = lengrad[m][j]*sum + len*sumgrad[m][j]; for ( j = 0 ; j < SDIM ; j++ ) for ( k = 0 ; k < SDIM ; k++ ) for ( m = 0 ; m < 2 ; m++ ) for ( i = 0 ; i < 2 ; i++ ) e_info->hess[m][i][j][k] = lenhess[m][i][j][k]*sum + lengrad[m][j]*sumgrad[i][k] + sumgrad[m][j]*lengrad[i][k] + len*sumhess[m][i][j][k]; return value; } /********************************************************************* quadratic edge_scalar_integral method *********************************************************************/ /********************************************************************* * * function: edge_scalar_integral_q() * * purpose: method value * */ REAL edge_scalar_integral_q(e_info) struct qinfo *e_info; { int j,k,m; REAL value = 0.0; REAL tang[MAXCOORD]; for ( m = 0 ; m < gauss1D_num ; m++ ) { for ( j = 0 ; j < SDIM ; j ++ ) { tang[j] = 0.0; for ( k = 0 ; k < edge_ctrl ; k++ ) tang[j] += gauss1polyd[k][m]*e_info->x[k][j]; } e_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ value += gauss1Dwt[m]*eval(METH_INSTANCE(e_info->method)->expr[0], e_info->gauss_pt[m],e_info->id,NULL)*sqrt(SDIM_dot(tang,tang)); } return value; } /********************************************************************* * * function: edge_scalar_integral_q_grad() * * purpose: method gradient * */ REAL edge_scalar_integral_q_grad(e_info) struct qinfo *e_info; { int m,k,j; REAL value = 0.0; REAL len,val; REAL derivs[MAXCOORD]; REAL tang[MAXCOORD]; for ( j = 0 ; j < SDIM ; j++ ) for ( m = 0 ; m < edge_ctrl ; m++ ) e_info->grad[m][j] = 0.0; for ( m = 0 ; m < gauss1D_num ; m++ ) { e_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ eval_all(METH_INSTANCE(e_info->method)->expr[0],e_info->gauss_pt[m],SDIM,&val, derivs,e_info->id); for ( j = 0 ; j < SDIM ; j ++ ) { tang[j] = 0.0; for ( k = 0 ; k < edge_ctrl ; k++ ) tang[j] += gauss1polyd[k][m]*e_info->x[k][j]; } len = sqrt(SDIM_dot(tang,tang)); if ( len == 0.0 ) continue; value += gauss1Dwt[m]*val*len; for ( k = 0 ; k < edge_ctrl ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) e_info->grad[k][j] += gauss1Dwt[m]*(gauss1poly[k][m]*derivs[j]*len + val*tang[j]/len*gauss1polyd[k][m]); } return value; } /********************************************************************* * * function: edge_scalar_integral_q_hess() * * purpose: method gradient and hessian * */ REAL edge_scalar_integral_q_hess(e_info) struct qinfo *e_info; { int m,j,jj,k,kk; REAL value = 0.0; REAL len,val; REAL derivs[MAXCOORD]; REAL sumgrad[2][MAXCOORD]; REAL sumhess[2][2][MAXCOORD][MAXCOORD]; MAT2D(second,MAXCOORD,MAXCOORD); REAL tang[MAXCOORD]; /* derivatives of gaussian sum part */ memset((char*)sumgrad,0,sizeof(sumgrad)); memset((char*)sumhess,0,sizeof(sumhess)); for ( m = 0 ; m < gauss1D_num ; m++ ) { e_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ eval_second(METH_INSTANCE(e_info->method)->expr[0],e_info->gauss_pt[m],SDIM,&val, derivs,second,e_info->id); for ( j = 0 ; j < SDIM ; j ++ ) { tang[j] = 0.0; for ( k = 0 ; k < edge_ctrl ; k++ ) tang[j] += gauss1polyd[k][m]*e_info->x[k][j]; } len = sqrt(SDIM_dot(tang,tang)); if ( len == 0.0 ) continue; value += gauss1Dwt[m]*val*len; for ( k = 0 ; k < edge_ctrl ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) e_info->grad[k][j] += gauss1Dwt[m]*(gauss1poly[k][m]*derivs[j]*len + val*tang[j]/len*gauss1polyd[k][m]); for ( k = 0 ; k < edge_ctrl ; k++ ) for ( kk = 0 ; kk < edge_ctrl ; kk++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) e_info->hess[k][kk][j][jj] += gauss1Dwt[m]* ( second[j][jj]*gauss1poly[k][m]*gauss1poly[kk][m]*len + derivs[j]*gauss1poly[k][m]*tang[jj]*gauss1polyd[kk][m]/len + derivs[jj]*gauss1poly[kk][m]*tang[j]*gauss1polyd[k][m]/len - val*tang[j]*tang[jj]*gauss1polyd[k][m]*gauss1polyd[kk][m]/len/len/len + ((j==jj)? val*gauss1polyd[k][m]*gauss1polyd[kk][m]/len : 0.0)); } return value; } /********************************************************************* Lagrange edge_scalar_integral method *********************************************************************/ /********************************************************************* * * function: edge_scalar_integral_lagr() * * purpose: method value * */ REAL edge_scalar_integral_lagr(e_info) struct qinfo *e_info; { int m; REAL value = 0.0; struct gauss_lag *gl = &gauss_lagrange[1][web.gauss1D_order]; for ( m = 0 ; m < gl->gnumpts ; m++ ) { e_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ value += gl->gausswt[m] *eval(METH_INSTANCE(e_info->method)->expr[0],e_info->gauss_pt[m],e_info->id,NULL) *sqrt(SDIM_dot(e_info->sides[m][0],e_info->sides[m][0])); } return value; } /********************************************************************* * * function: edge_scalar_integral_lagr_grad() * * purpose: method gradient * */ REAL edge_scalar_integral_lagr_grad(e_info) struct qinfo *e_info; { int m,k,j; REAL value = 0.0; REAL len,val; REAL derivs[MAXCOORD]; REAL *tang; struct gauss_lag *gl = &gauss_lagrange[1][web.gauss1D_order]; for ( m = 0 ; m < gl->gnumpts ; m++ ) { e_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ eval_all(METH_INSTANCE(e_info->method)->expr[0],e_info->gauss_pt[m],SDIM,&val, derivs,e_info->id); tang = e_info->sides[m][0]; len = sqrt(SDIM_dot(tang,tang)); if ( len == 0.0 ) continue; value += gl->gausswt[m]*val*len; for ( k = 0 ; k < gl->lagpts ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) e_info->grad[k][j] += gl->gausswt[m]*(gl->gpoly[m][k]*derivs[j]*len + val*tang[j]/len*gl->gpolypart[m][0][k]); } return value; } /********************************************************************* * * function: edge_scalar_integral_lagr_hess() * * purpose: method gradient and hessian * */ REAL edge_scalar_integral_lagr_hess(e_info) struct qinfo *e_info; { int m,j,jj,k,kk; REAL value = 0.0; REAL len,val; REAL derivs[MAXCOORD]; REAL sumgrad[2][MAXCOORD]; REAL sumhess[2][2][MAXCOORD][MAXCOORD]; MAT2D(second,MAXCOORD,MAXCOORD); REAL *tang; struct gauss_lag *gl = &gauss_lagrange[1][web.gauss1D_order]; /* derivatives of gaussian sum part */ memset((char*)sumgrad,0,sizeof(sumgrad)); memset((char*)sumhess,0,sizeof(sumhess)); for ( m = 0 ; m < gl->gnumpts ; m++ ) { e_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ eval_second(METH_INSTANCE(e_info->method)->expr[0],e_info->gauss_pt[m],SDIM,&val, derivs,second,e_info->id); tang = e_info->sides[m][0]; len = sqrt(SDIM_dot(tang,tang)); if ( len == 0.0 ) continue; value += gl->gausswt[m]*val*len; for ( k = 0 ; k < gl->lagpts ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) e_info->grad[k][j] += gl->gausswt[m]*(gl->gpoly[m][k]*derivs[j]*len + val*tang[j]/len*gl->gpolypart[m][0][k]); for ( k = 0 ; k < gl->lagpts ; k++ ) for ( kk = 0 ; kk < gl->lagpts ; kk++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) e_info->hess[k][kk][j][jj] += gl->gausswt[m]* ( second[j][jj]*gl->gpoly[m][k]*gl->gpoly[m][kk]*len + derivs[j]*gl->gpoly[m][k]*tang[jj]*gl->gpolypart[m][0][kk]/len + derivs[jj]*gl->gpoly[m][kk]*tang[j]*gl->gpolypart[m][0][k]/len - val*tang[j]*tang[jj]*gl->gpolypart[m][0][k]*gl->gpolypart[m][0][kk]/len/len/len + ((j==jj)? val*gl->gpolypart[m][0][k]*gl->gpolypart[m][0][kk]/len : 0.0)); } return value; } /********************************************************************* edge_vector_integral method *********************************************************************/ /********************************************************************* * * function: edge_vector_integral() * * purpose: method value * */ REAL edge_vector_integral(e_info) struct qinfo *e_info; { int m,j; REAL value=0.0; if ( web.modeltype == QUADRATIC ) return edge_vector_integral_q(e_info); if ( web.modeltype == LAGRANGE ) return edge_vector_integral_lagrange(e_info); for ( m = 0 ; m < gauss1D_num ; m++ ) { e_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( j = 0 ; j < SDIM ; j++ ) { REAL green; green = gauss1Dwt[m]*eval(METH_INSTANCE(e_info->method)->expr[j], e_info->gauss_pt[m], e_info->id,NULL); value += e_info->sides[0][0][j]*green; } } return (get_eattr(e_info->id) & NEGBOUNDARY) ? -value : value; } /********************************************************************* * * function: edge_vector_integral_grad() * * purpose: method gradient * */ REAL edge_vector_integral_grad(e_info) struct qinfo *e_info; { int m,j,k; REAL value = 0.0; REAL val[MAXCOORD]; REAL derivs[MAXCOORD][MAXCOORD]; REAL sum; REAL sign = (get_eattr(e_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; if ( web.modeltype == QUADRATIC ) return edge_vector_integral_q_grad(e_info); if ( web.modeltype == LAGRANGE ) return edge_vector_integral_lagrange_grad(e_info); for ( k = 0 ; k < 2 ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) e_info->grad[k][j] = 0.0; for ( m = 0 ; m < gauss1D_num ; m++ ) { REAL weight = sign*gauss1Dwt[m]; e_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( j = 0 ; j < SDIM ; j++ ) eval_all(METH_INSTANCE(e_info->method)->expr[j],e_info->gauss_pt[m],SDIM, val+j, derivs[j],e_info->id); value += gauss1Dwt[m]*SDIM_dot(val,e_info->sides[0][0]); for ( k = 0 ; k < SDIM ; k++ ) { for ( sum = 0.0, j = 0 ; j < SDIM ; j++ ) sum += derivs[j][k]*e_info->sides[0][0][j]; e_info->grad[0][k] += weight*(-val[k] + gauss1poly[0][m]*sum); e_info->grad[1][k] += weight*(val[k] + gauss1poly[1][m]*sum); } } return sign*value; } /********************************************************************* * * function: edge_vector_integral_hess() * * purpose: method gradient and hessian * */ REAL edge_vector_integral_hess(e_info) struct qinfo *e_info; { int m,i,j,k; REAL value = 0.0; REAL val[MAXCOORD]; REAL derivs[MAXCOORD][MAXCOORD]; REAL sum; MAT3D(second,MAXCOORD,MAXCOORD,MAXCOORD); REAL sign = (get_eattr(e_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; if ( web.modeltype == QUADRATIC ) return edge_vector_integral_q_hess(e_info); if ( web.modeltype == LAGRANGE ) return edge_vector_integral_lagrange_hess(e_info); for ( m = 0 ; m < gauss1D_num ; m++ ) { REAL weight = sign*gauss1Dwt[m]; e_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( j = 0 ; j < SDIM ; j++ ) eval_second(METH_INSTANCE(e_info->method)->expr[j],e_info->gauss_pt[m],SDIM,val+j, derivs[j],second[j],e_info->id); value += weight*SDIM_dot(val,e_info->sides[0][0]); for ( k = 0 ; k < SDIM ; k++ ) { for ( sum = 0.0, j = 0 ; j < SDIM ; j++ ) sum += derivs[j][k]*e_info->sides[0][0][j]; e_info->grad[0][k] += weight*(-val[k] + gauss1poly[0][m]*sum); e_info->grad[1][k] += weight*(val[k] + gauss1poly[1][m]*sum); } for ( k = 0 ; k < SDIM ; k++ ) for ( i = 0 ; i < SDIM ; i++ ) { for ( sum = 0.0, j = 0 ; j < SDIM ; j++ ) sum += second[j][k][i]*e_info->sides[0][0][j]; e_info->hess[0][0][k][i] += weight*gauss1poly[0][m] *(gauss1poly[0][m]*sum - derivs[k][i] - derivs[i][k]); e_info->hess[0][1][k][i] += weight *(gauss1poly[0][m]*gauss1poly[1][m]*sum - gauss1poly[1][m]*derivs[k][i] + gauss1poly[0][m]*derivs[i][k]); e_info->hess[1][0][k][i] += weight *(gauss1poly[0][m]*gauss1poly[1][m]*sum + gauss1poly[0][m]*derivs[k][i] - gauss1poly[1][m]*derivs[i][k]); e_info->hess[1][1][k][i] += weight*gauss1poly[1][m] *(gauss1poly[1][m]*sum + derivs[k][i] + derivs[i][k]); } } return value; } /********************************************************************* quadratic edge_vector_integral method *********************************************************************/ /********************************************************************* * * function: edge_vector_integral_q() * * purpose: method value * */ REAL edge_vector_integral_q(e_info) struct qinfo *e_info; { int m,j,k; REAL value=0.0; REAL tang[MAXCOORD]; REAL sign = (get_eattr(e_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; for ( m = 0 ; m < gauss1D_num ; m++ ) { REAL weight = sign*gauss1Dwt[m]; e_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( j = 0 ; j < SDIM ; j++ ) { tang[j] = 0.0; for ( k = 0 ; k < edge_ctrl ; k++ ) tang[j] += gauss1polyd[k][m]*e_info->x[k][j]; value += weight*tang[j]*eval(METH_INSTANCE(e_info->method)->expr[j], e_info->gauss_pt[m],e_info->id,NULL); } } return value; } /********************************************************************* * * function: edge_vector_integral_q_grad() * * purpose: method gradient * */ REAL edge_vector_integral_q_grad(e_info) struct qinfo *e_info; { int m,j,k,i; REAL value = 0.0; REAL val[MAXCOORD]; REAL derivs[MAXCOORD][MAXCOORD]; REAL sum; REAL tang[MAXCOORD]; REAL sign = (get_eattr(e_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; for ( m = 0 ; m < gauss1D_num ; m++ ) { REAL weight = sign*gauss1Dwt[m]; e_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( j = 0 ; j < SDIM ; j++ ) { tang[j] = 0.0; for ( k = 0 ; k < edge_ctrl ; k++ ) tang[j] += gauss1polyd[k][m]*e_info->x[k][j]; } for ( j = 0 ; j < SDIM ; j++ ) eval_all(METH_INSTANCE(e_info->method)->expr[j],e_info->gauss_pt[m],SDIM,val+j, derivs[j],e_info->id); value += weight*SDIM_dot(val,tang); for ( k = 0 ; k < SDIM ; k++ ) { for ( sum = 0.0, j = 0 ; j < SDIM ; j++ ) sum += derivs[j][k]*tang[j]; for ( i = 0 ; i < edge_ctrl ; i++ ) e_info->grad[i][k] += weight*(gauss1polyd[i][m]*val[k] + gauss1poly[i][m]*sum); } } return value; } /********************************************************************* * * function: edge_vector_integral_q_hess() * * purpose: method gradient and hessian * */ REAL edge_vector_integral_q_hess(e_info) struct qinfo *e_info; { int m,i,j,k,ii,kk; REAL value = 0.0; REAL val[MAXCOORD]; REAL derivs[MAXCOORD][MAXCOORD]; REAL sum; MAT3D(second,MAXCOORD,MAXCOORD,MAXCOORD); REAL tang[MAXCOORD]; REAL sign = (get_eattr(e_info->id) & NEGBOUNDARY) ? -1.0 : 1.0; for ( m = 0 ; m < gauss1D_num ; m++ ) { REAL weight = sign*gauss1Dwt[m]; e_info->gauss_pt[m][2*SDIM] = m; /* kludge for attr interpolation. */ for ( j = 0 ; j < SDIM ; j++ ) { tang[j] = 0.0; for ( k = 0 ; k < edge_ctrl ; k++ ) tang[j] += gauss1polyd[k][m]*e_info->x[k][j]; } for ( j = 0 ; j < SDIM ; j++ ) eval_second(METH_INSTANCE(e_info->method)->expr[j],e_info->gauss_pt[m],SDIM, val+j, derivs[j],second[j],e_info->id); value += weight*SDIM_dot(val,tang); for ( k = 0 ; k < SDIM ; k++ ) { for ( sum = 0.0, j = 0 ; j < SDIM ; j++ ) sum += derivs[j][k]*tang[j]; for ( i = 0 ; i < edge_ctrl ; i++ ) e_info->grad[i][k] += weight*(gauss1polyd[i][m]*val[k] + gauss1poly[i][m]*sum); } for ( ii = 0 ; ii < SDIM ; ii++ ) for ( i = 0 ; i < SDIM ; i++ ) { for ( sum = 0.0, j = 0 ; j < SDIM ; j++ ) sum += second[j][ii][i]*tang[j]; for ( k = 0 ; k < edge_ctrl ; k++ ) for ( kk = 0 ; kk < edge_ctrl ; kk++ ) e_info->hess[k][kk][i][ii] += weight* ( sum*gauss1poly[k][m]*gauss1poly[kk][m] + gauss1polyd[k][m]*derivs[i][ii]*gauss1poly[kk][m] + gauss1polyd[kk][m]*derivs[ii][i]*gauss1poly[k][m] ); } } return value; } /********************************************************************** Linear area quantity (STRING model) **********************************************************************/ /********************************************************************** * * function: q_edge_area() * * purpose: value of area integral on edge */ REAL q_edge_area(e_info) struct qinfo *e_info; { REAL **x; REAL area; if ( web.torus_flag ) area = q_edge_torus_area(e_info); else if ( web.modeltype == QUADRATIC ) area = q_edge_area_q(e_info); else if ( web.modeltype == LAGRANGE ) area = q_edge_area_lagrange(e_info); else { x = e_info->x; /* main integral over edge */ area = (x[0][1]+x[1][1])*(x[0][0] - x[1][0])/2; } if ( METH_INSTANCE(e_info->method)->flags & DEFAULT_INSTANCE ) { /* add to facet area */ body_id b_id,bb_id; facetedge_id fe_id = get_edge_fe(e_info->id); facetedge_id fe; facet_id f_id; b_id = GEN_QUANT(METH_INSTANCE(e_info->method)->quant)->b_id; fe = fe_id; do { f_id = get_fe_facet(fe); bb_id = get_facet_body(f_id); if ( equal_id(b_id,bb_id) ) add_facet_area(f_id,area); bb_id = get_facet_body(inverse_id(f_id)); if ( equal_id(b_id,bb_id) ) add_facet_area(f_id,area); fe = get_next_facet(fe); } while ( !equal_id(fe,fe_id) ); } return area; } /********************************************************************** * * function: q_edge_area_grad() * * purpose: value and gradient of area integral on edge */ REAL q_edge_area_grad(e_info) struct qinfo *e_info; { REAL **x,**g; REAL area; if ( web.torus_flag ) return q_edge_torus_area_grad(e_info); if ( web.modeltype == QUADRATIC ) return q_edge_area_q_grad(e_info); if ( web.modeltype == LAGRANGE ) return q_edge_area_lagrange_grad(e_info); x = e_info->x; g = e_info->grad; /* main integral over edge */ area = (x[0][1]+x[1][1])*(x[0][0] - x[1][0])/2; g[0][0] = (x[1][1] + x[0][1])/2; g[1][0] = -(x[1][1] + x[0][1])/2; g[0][1] = (x[0][0] - x[1][0])/2; g[1][1] = (x[0][0] - x[1][0])/2; return area; } /********************************************************************** * * function: q_edge_area_hess() * * purpose: value and gradient and hessian of area integral on edge */ REAL q_edge_area_hess(e_info) struct qinfo *e_info; { REAL **x,**g,****h; REAL area; if ( web.torus_flag ) return q_edge_torus_area_hess(e_info); if ( web.modeltype == QUADRATIC ) return q_edge_area_q_hess(e_info); if ( web.modeltype == LAGRANGE ) return q_edge_area_lagrange_hess(e_info); x = e_info->x; g = e_info->grad; h = e_info->hess; /* main integral over edge */ area = (x[0][1]+x[1][1])*(x[0][0] - x[1][0])/2; g[0][0] = (x[1][1] + x[0][1])/2; g[1][0] = -(x[1][1] + x[0][1])/2; g[0][1] = (x[0][0] - x[1][0])/2; g[1][1] = (x[0][0] - x[1][0])/2; h[0][1][0][1] += 0.5; h[0][0][0][1] += 0.5; h[1][1][0][1] -= 0.5; h[1][0][0][1] -= 0.5; h[0][0][1][0] += 0.5; h[0][1][1][0] -= 0.5; h[1][0][1][0] += 0.5; h[1][1][1][0] -= 0.5; return area; } /********************************************************************** Quadratic area quantity (STRING model) **********************************************************************/ /********************************************************************** * * function: q_edge_area_q() * * purpose: value of area integral on edge */ REAL q_edge_area_q(e_info) struct qinfo *e_info; { REAL **x; REAL area; int i,j; x = e_info->x; /* main integral over edge */ area = 0.0; for ( i = 0 ; i < edge_ctrl ; i++ ) for ( j = 0 ; j < edge_ctrl ; j++ ) { REAL v = scoeff[j][i]; area += v*x[i][0]*x[j][1]; } return area; } /********************************************************************** * * function: q_edge_area_q_grad() * * purpose: value and gradient of area integral on edge */ REAL q_edge_area_q_grad(e_info) struct qinfo *e_info; { REAL **x,**g; REAL area; int i,j,k; x = e_info->x; g = e_info->grad; /* gradients */ for ( k = 0 ; k < edge_ctrl ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) g[k][j] = 0.0; /* main integral over edge */ area = 0.0; for ( i = 0 ; i < edge_ctrl ; i++ ) for ( j = 0 ; j < edge_ctrl ; j++ ) { REAL v = scoeff[j][i]; area += v*x[i][0]*x[j][1]; g[i][0] += v*x[j][1]; g[j][1] += v*x[i][0]; } return area; } /********************************************************************** * * function: q_edge_area_q_hess() * * purpose: value and gradient and hessian of area integral on edge */ REAL q_edge_area_q_hess(e_info) struct qinfo *e_info; { REAL **x,**g,****h; REAL area; int i,j; x = e_info->x; g = e_info->grad; h = e_info->hess; /* main integral over edge */ area = 0.0; for ( i = 0 ; i < edge_ctrl ; i++ ) for ( j = 0 ; j < edge_ctrl ; j++ ) { REAL v = scoeff[j][i]; if ( v == 0.0 ) continue; area += v*x[i][0]*x[j][1]; g[i][0] += v*x[j][1]; g[j][1] += v*x[i][0]; h[i][j][0][1] += v; h[j][i][1][0] += v; } return area; } /********************************************************************** Lagrange area quantity (STRING model) **********************************************************************/ /********************************************************************** * * function: q_edge_area_lagrange() * * purpose: value of area integral on edge */ REAL q_edge_area_lagrange(e_info) struct qinfo *e_info; { REAL **x; REAL area; struct gauss_lag *gl = &gauss_lagrange[web.dimension][web.gauss1D_order]; int ctrl = web.skel[EDGE].ctrlpts; int m,k; x = e_info->x; /* main integral over edge */ area = 0.0; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL y,dx; for ( y = 0.0, dx = 0.0, k = 0 ; k < ctrl ; k++ ) { y += gl->gpoly[m][k]*x[k][1]; dx += gl->gpolypart[m][0][k]*x[k][0]; } area -= gl->gausswt[m]*y*dx; } return area; } /********************************************************************** * * function: q_edge_area_lagrange_grad() * * purpose: value and gradient of area integral on edge */ REAL q_edge_area_lagrange_grad(e_info) struct qinfo *e_info; { REAL **x,**g; REAL area; int m,k; struct gauss_lag *gl = &gauss_lagrange[web.dimension][web.gauss1D_order]; int ctrl = web.skel[EDGE].ctrlpts; x = e_info->x; g = e_info->grad; /* main integral over edge */ area = 0.0; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL y,dx; for ( y = 0.0, dx = 0.0, k = 0 ; k < ctrl ; k++ ) { y += gl->gpoly[m][k]*x[k][1]; dx += gl->gpolypart[m][0][k]*x[k][0]; } area -= gl->gausswt[m]*y*dx; for ( k = 0 ; k < ctrl ; k++ ) { g[k][0] -= gl->gausswt[m]*y*gl->gpolypart[m][0][k]; g[k][1] -= gl->gausswt[m]*dx*gl->gpoly[m][k]; } } return area; } /********************************************************************** * * function: q_edge_area_lagrange_hess() * * purpose: value and gradient and hessian of area integral on edge */ REAL q_edge_area_lagrange_hess(e_info) struct qinfo *e_info; { REAL **x,**g,****h; REAL area; int m,k,kk; struct gauss_lag *gl = &gauss_lagrange[web.dimension][web.gauss1D_order]; int ctrl = web.skel[EDGE].ctrlpts; x = e_info->x; g = e_info->grad; h = e_info->hess; /* main integral over edge */ area = 0.0; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL y,dx; for ( y = 0.0, dx = 0.0, k = 0 ; k < ctrl ; k++ ) { y += gl->gpoly[m][k]*x[k][1]; dx += gl->gpolypart[m][0][k]*x[k][0]; } area -= gl->gausswt[m]*y*dx; for ( k = 0 ; k < ctrl ; k++ ) { g[k][0] -= gl->gausswt[m]*y*gl->gpolypart[m][0][k]; g[k][1] -= gl->gausswt[m]*dx*gl->gpoly[m][k]; for ( kk = 0 ; kk < ctrl ; kk++ ) { h[k][kk][0][1] -= gl->gausswt[m]*gl->gpoly[m][kk]*gl->gpolypart[m][0][k]; h[k][kk][1][0] -= gl->gausswt[m]*gl->gpolypart[m][0][kk]*gl->gpoly[m][k]; } } } return area; } /********************************************************************** Linear torus area quantity (STRING model) **********************************************************************/ /********************************************************************** * * function: q_edge_torus_area() * * purpose: value of area integral on edge */ REAL q_edge_torus_area(e_info) struct qinfo *e_info; { REAL **x; REAL area; REAL **dx = web.inverse_periods; MAT2D(u,EDGE_VERTS,MAXCOORD); /* affine coordinates of vertices */ int wrap; if ( !dx ) kb_error(2142,"Need torus model to use edge_torus_area method.\n", RECOVERABLE); if ( web.modeltype == QUADRATIC ) return q_edge_torus_area_q(e_info); if ( web.modeltype == LAGRANGE ) return q_edge_torus_area_lagrange(e_info); x = e_info->x; /* get affine coordinates of vertices */ mat_mul_tr(x,dx,u,EDGE_VERTS,SDIM,SDIM); /* main integral over edge */ area = (u[0][1]+u[1][1])*(u[0][0] - u[1][0])/2; /* wrap correction */ wrap = (get_edge_wrap(e_info->id)>>TWRAPBITS) & WRAPMASK; area += WRAPNUM(wrap)*u[1][0]; return area*web.torusv; } /********************************************************************** * * function: q_edge_torus_area_grad() * * purpose: value and gradient of area integral on edge */ REAL q_edge_torus_area_grad(e_info) struct qinfo *e_info; { REAL **x; REAL area; REAL **dx = web.inverse_periods; MAT2D(u,EDGE_VERTS,MAXCOORD); /* affine coordinates of vertices */ MAT2D(g,EDGE_VERTS,MAXCOORD); int wrap,wrapnum; if ( web.modeltype == QUADRATIC ) return q_edge_torus_area_q_grad(e_info); if ( web.modeltype == LAGRANGE ) return q_edge_torus_area_lagrange_grad(e_info); x = e_info->x; /* get affine coordinates of vertices */ mat_mul_tr(x,dx,u,EDGE_VERTS,SDIM,SDIM); /* main integral over edge */ area = (u[0][1]+u[1][1])*(u[0][0] - u[1][0])/2; g[0][0] = (u[1][1] + u[0][1])/2*web.torusv; g[1][0] = -(u[1][1] + u[0][1])/2*web.torusv; g[0][1] = (u[0][0] - u[1][0])/2*web.torusv; g[1][1] = (u[0][0] - u[1][0])/2*web.torusv; /* wrap correction */ wrap = (get_edge_wrap(e_info->id)>>TWRAPBITS) & WRAPMASK; wrapnum = WRAPNUM(wrap); area += wrapnum*u[1][0]; g[1][0] += wrapnum*web.torusv; mat_mult(g,dx,e_info->grad,EDGE_VERTS,SDIM,SDIM); return area*web.torusv; } /********************************************************************** * * function: q_edge_torus_area_hess() * * purpose: value and gradient and hessian of area integral on edge */ REAL q_edge_torus_area_hess(e_info) struct qinfo *e_info; { REAL **x; REAL area; REAL **dx = web.inverse_periods; MAT2D(u,EDGE_VERTS,MAXCOORD); /* affine coordinates of vertices */ MAT2D(g,EDGE_VERTS,MAXCOORD); MAT4D(h,EDGE_VERTS,EDGE_VERTS,MAXCOORD,MAXCOORD); MAT2D(temph,MAXCOORD,MAXCOORD); int i,ii,j,jj,wrap,wrapnum; if ( web.modeltype == QUADRATIC ) return q_edge_torus_area_q_hess(e_info); if ( web.modeltype == LAGRANGE ) return q_edge_torus_area_lagrange_hess(e_info); x = e_info->x; for ( i = 0 ; i < EDGE_VERTS ; i++ ) for ( ii = 0 ; ii < EDGE_VERTS ; ii++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) h[i][ii][j][jj] = 0.0; /* get affine coordinates of vertices */ mat_mul_tr(x,dx,u,EDGE_VERTS,SDIM,SDIM); /* main integral over edge */ area = (u[0][1]+u[1][1])*(u[0][0] - u[1][0])/2; g[0][0] = (u[1][1] + u[0][1])/2*web.torusv; g[1][0] = -(u[1][1] + u[0][1])/2*web.torusv; g[0][1] = (u[0][0] - u[1][0])/2*web.torusv; g[1][1] = (u[0][0] - u[1][0])/2*web.torusv; h[0][1][0][1] += 0.5*web.torusv; h[0][0][0][1] += 0.5*web.torusv; h[1][1][0][1] -= 0.5*web.torusv; h[1][0][0][1] -= 0.5*web.torusv; h[0][0][1][0] += 0.5*web.torusv; h[0][1][1][0] -= 0.5*web.torusv; h[1][0][1][0] += 0.5*web.torusv; h[1][1][1][0] -= 0.5*web.torusv; /* wrap correction */ wrap = (get_edge_wrap(e_info->id)>>TWRAPBITS) & WRAPMASK; wrapnum = WRAPNUM(wrap); area += wrapnum*u[1][0]; g[1][0] += wrapnum*web.torusv; /* form pullback */ mat_mult(g,dx,e_info->grad,EDGE_VERTS,SDIM,SDIM); for ( i = 0 ; i < EDGE_VERTS ; i++ ) for ( ii = 0 ; ii < EDGE_VERTS ; ii++ ) { mat_mult(h[i][ii],dx,temph,SDIM,SDIM,SDIM); tr_mat_mul(dx,temph,e_info->hess[i][ii],SDIM,SDIM,SDIM); } return area*web.torusv; } /********************************************************************** Quadratic torus area quantity (STRING model) **********************************************************************/ /********************************************************************** * * function: q_edge_torus_area_q() * * purpose: value of area integral on edge */ REAL q_edge_torus_area_q(e_info) struct qinfo *e_info; { REAL **x; REAL area; REAL **dx = web.inverse_periods; MAT2D(u,EDGE_CTRL,MAXCOORD); /* affine coordinates of vertices */ int i,j; int wrap; x = e_info->x; /* get affine coordinates of vertices */ mat_mul_tr(x,dx,u,edge_ctrl,SDIM,SDIM); /* main integral over edge */ area = 0.0; for ( i = 0 ; i < edge_ctrl ; i++ ) for ( j = 0 ; j < edge_ctrl ; j++ ) { REAL v = scoeff[i][j]; if ( v == 0.0 ) continue; area += v*u[i][1]*u[j][0]; } /* wrap correction */ wrap = (get_edge_wrap(e_info->id)>>TWRAPBITS) & WRAPMASK; area += WRAPNUM(wrap)*u[2][0]; return area*web.torusv; } /********************************************************************** * * function: q_edge_torus_area_q_grad() * * purpose: value and gradient of area integral on edge */ REAL q_edge_torus_area_q_grad(e_info) struct qinfo *e_info; { REAL **x; REAL area; REAL **dx = web.inverse_periods; MAT2D(u,EDGE_CTRL,MAXCOORD); /* affine coordinates of vertices */ MAT2D(g,EDGE_CTRL,MAXCOORD); int i,j,k; int wrap,wrapnum; x = e_info->x; for ( i = 0 ; i < edge_ctrl ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) g[i][j] = 0.0; /* get affine coordinates of vertices */ mat_mul_tr(x,dx,u,edge_ctrl,SDIM,SDIM); /* main integral over edge */ area = 0.0; for ( i = 0 ; i < edge_ctrl ; i++ ) for ( j = 0 ; j < edge_ctrl ; j++ ) { REAL v = scoeff[j][i]; if ( v == 0.0 ) continue; area += v*u[i][0]*u[j][1]; g[i][0] += v*u[j][1]; g[j][1] += v*u[i][0]; } /* wrap correction */ wrap = (get_edge_wrap(e_info->id)>>TWRAPBITS) & WRAPMASK; wrapnum = WRAPNUM(wrap); area += wrapnum*u[2][0]; g[2][0] += wrapnum; for ( k = 0 ; k < edge_ctrl ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) g[k][j] *= web.torusv; mat_mult(g,dx,e_info->grad,edge_ctrl,SDIM,SDIM); return area*web.torusv; } /********************************************************************** * * function: q_edge_torus_area_q_hess() * * purpose: value and gradient and hessian of area integral on edge */ REAL q_edge_torus_area_q_hess(e_info) struct qinfo *e_info; { REAL **x; REAL area; REAL **dx = web.inverse_periods; MAT2D(u,EDGE_CTRL,MAXCOORD); /* affine coordinates of vertices */ MAT2D(g,EDGE_CTRL+1,MAXCOORD); REAL ****h; MAT2D(temph,MAXCOORD,MAXCOORD); int i,ii,j,k; int wrap,wrapnum; x = e_info->x; h = e_info->hess; for ( i = 0 ; i < edge_ctrl ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) g[i][j] = 0.0; /* get affine coordinates of vertices */ mat_mul_tr(x,dx,u,edge_ctrl,SDIM,SDIM); /* main integral over edge */ area = 0.0; for ( i = 0 ; i < edge_ctrl ; i++ ) for ( j = 0 ; j < edge_ctrl ; j++ ) { REAL v = scoeff[j][i]; if ( v == 0.0 ) continue; area += v*u[i][0]*u[j][1]; g[i][0] += v*u[j][1]; g[j][1] += v*u[i][0]; h[i][j][0][1] += v*web.torusv; h[j][i][1][0] += v*web.torusv; } /* wrap correction */ wrap = (get_edge_wrap(e_info->id)>>TWRAPBITS) & WRAPMASK; wrapnum = WRAPNUM(wrap); area += wrapnum*u[2][0]; g[2][0] += wrapnum; for ( k = 0 ; k < edge_ctrl ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) g[k][j] *= web.torusv; /* form pullback */ mat_mult(g,dx,e_info->grad,edge_ctrl,SDIM,SDIM); for ( i = 0 ; i < edge_ctrl ; i++ ) for ( ii = 0 ; ii < edge_ctrl ; ii++ ) { mat_mult(h[i][ii],dx,temph,SDIM,SDIM,SDIM); tr_mat_mul(dx,temph,e_info->hess[i][ii],SDIM,SDIM,SDIM); } return area*web.torusv; } /********************************************************************** Lagrange torus area quantity (STRING model) **********************************************************************/ /********************************************************************** * * function: q_edge_torus_area_lagrange() * * purpose: value of area integral on edge */ REAL q_edge_torus_area_lagrange(e_info) struct qinfo *e_info; { REAL **x; REAL area; struct gauss_lag *gl = &gauss_lagrange[web.dimension][web.gauss1D_order]; int ctrl = web.skel[EDGE].ctrlpts; int m,k; REAL **u = e_info->u; int wrap; x = e_info->x; /* get affine coordinates of vertices */ mat_mul_tr(x,web.inverse_periods,u,ctrl,SDIM,SDIM); /* main integral over edge */ area = 0.0; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL y,dx; for ( y = 0.0, dx = 0.0, k = 0 ; k < ctrl ; k++ ) { y += gl->gpoly[m][k]*u[k][1]; dx += gl->gpolypart[m][0][k]*u[k][0]; } area -= gl->gausswt[m]*y*dx; } /* wrap correction */ wrap = (get_edge_wrap(e_info->id)>>TWRAPBITS) & WRAPMASK; area += WRAPNUM(wrap)*u[ctrl-1][0]; return area * web.torusv; } /********************************************************************** * * function: q_edge_torus_area_lagrange_grad() * * purpose: value and gradient of area integral on edge */ REAL q_edge_torus_area_lagrange_grad(e_info) struct qinfo *e_info; { REAL **x; REAL area; int m,j,k,i; struct gauss_lag *gl = &gauss_lagrange[web.dimension][web.gauss1D_order]; int ctrl = web.skel[EDGE].ctrlpts; REAL **u = e_info->u; int wrap,wrapnum; MAT2D(g,MAXVCOUNT,MAXCOORD); x = e_info->x; for ( i = 0 ; i < ctrl ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) g[i][j] = 0.0; /* get affine coordinates of vertices */ mat_mul_tr(x,web.inverse_periods,u,edge_ctrl,SDIM,SDIM); /* main integral over edge */ area = 0.0; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL y,dx; for ( y = 0.0, dx = 0.0, k = 0 ; k < ctrl ; k++ ) { y += gl->gpoly[m][k]*u[k][1]; dx += gl->gpolypart[m][0][k]*u[k][0]; } area -= gl->gausswt[m]*y*dx; for ( k = 0 ; k < ctrl ; k++ ) { g[k][0] -= gl->gausswt[m]*y*gl->gpolypart[m][0][k]; g[k][1] -= gl->gausswt[m]*dx*gl->gpoly[m][k]; } } /* wrap correction */ wrap = (get_edge_wrap(e_info->id)>>TWRAPBITS) & WRAPMASK; wrapnum = WRAPNUM(wrap); area += wrapnum*u[ctrl-1][0]; g[ctrl-1][0] += wrapnum; for ( k = 0 ; k < ctrl ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) g[k][j] *= web.torusv; mat_mult(g,web.inverse_periods,e_info->grad,ctrl,SDIM,SDIM); return area*web.torusv; } /********************************************************************** * * function: q_edge_torus_area_lagrange_hess() * * purpose: value and gradient and hessian of area integral on edge */ REAL q_edge_torus_area_lagrange_hess(e_info) struct qinfo *e_info; { REAL **x,****h; REAL area; int i,ii,j; int m,k,kk; struct gauss_lag *gl = &gauss_lagrange[web.dimension][web.gauss1D_order]; int ctrl = web.skel[EDGE].ctrlpts; REAL **u = e_info->u; int wrap,wrapnum; MAT2D(temph,MAXCOORD,MAXCOORD); MAT2D(g,MAXVCOUNT,MAXCOORD); x = e_info->x; h = e_info->hess; for ( i = 0 ; i < ctrl ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) g[i][j] = 0.0; /* get affine coordinates of vertices */ mat_mul_tr(x,web.inverse_periods,u,edge_ctrl,SDIM,SDIM); /* main integral over edge */ area = 0.0; for ( m = 0 ; m < gl->gnumpts ; m++ ) { REAL y,dx; for ( y = 0.0, dx = 0.0, k = 0 ; k < ctrl ; k++ ) { y += gl->gpoly[m][k]*u[k][1]; dx += gl->gpolypart[m][0][k]*u[k][0]; } area -= gl->gausswt[m]*y*dx; for ( k = 0 ; k < ctrl ; k++ ) { g[k][0] -= gl->gausswt[m]*y*gl->gpolypart[m][0][k]; g[k][1] -= gl->gausswt[m]*dx*gl->gpoly[m][k]; for ( kk = 0 ; kk < ctrl ; kk++ ) { h[k][kk][0][1] -= gl->gausswt[m]*gl->gpoly[m][kk]*gl->gpolypart[m][0][k]; h[k][kk][1][0] -= gl->gausswt[m]*gl->gpolypart[m][0][kk]*gl->gpoly[m][k]; } } } /* wrap correction */ wrap = (get_edge_wrap(e_info->id)>>TWRAPBITS) & WRAPMASK; wrapnum = WRAPNUM(wrap); area += wrapnum*u[ctrl-1][0]; g[ctrl-1][0] += wrapnum; for ( k = 0 ; k < ctrl ; k++ ) for ( j = 0 ; j < SDIM ; j++ ) g[k][j] *= web.torusv; /* form pullback */ mat_mult(g,web.inverse_periods,e_info->grad,ctrl,SDIM,SDIM); for ( i = 0 ; i < ctrl ; i++ ) for ( ii = 0 ; ii < ctrl ; ii++ ) { mat_mult(h[i][ii],web.inverse_periods,temph,SDIM,SDIM,SDIM); tr_mat_mul(web.inverse_periods,temph,e_info->hess[i][ii],SDIM,SDIM,SDIM); } return area*web.torusv; } /******************************************************************* Hooke Energy - Hooke's Law to keep edge lengths nearly uniform. ********************************************************************/ #define POWER_NAME "hooke_power" #define LENGTH_NAME "hooke_length" static int exponent_param; static int length_param; static REAL hooke_length, hooke_power; /*************************************************************** * * function: hooke_energy_init() * * purpose: initialization for hooke_energy() and * hooke_energy_gradient(). * * No special prep. */ void hooke_energy_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { if ( web.modeltype != LINEAR ) kb_error(1766,"hooke_energy only for LINEAR model.\n",RECOVERABLE); exponent_param = lookup_global(POWER_NAME); if ( exponent_param < 0 ) /* missing, so add */ { exponent_param = add_global(POWER_NAME); globals(exponent_param)->value.real = 2.0; /* default */ globals(exponent_param)->flags |= ORDINARY_PARAM; } hooke_power = globals(exponent_param)->value.real; length_param = lookup_global(LENGTH_NAME); if ( length_param < 0 ) /* missing, so add */ { length_param = add_global(LENGTH_NAME); if ( web.representation == STRING ) globals(length_param)->value.real = web.total_area/web.skel[EDGE].count; /* default */ else globals(length_param)->value.real = sqrt(2.3*web.total_area/web.skel[FACET].count); /* default */ globals(length_param)->flags |= ORDINARY_PARAM; } hooke_length = globals(length_param)->value.real; } /******************************************************************* * * function: hooke_energy * * purpose: energy of one edge, deviation from set edge length * */ REAL hooke_energy(e_info) struct qinfo *e_info; { REAL d,diff; d = sqrt(SDIM_dot(e_info->sides[0][0],e_info->sides[0][0])); diff = fabs(d - hooke_length); if ( hooke_power == 0.0 ) return -log(diff); return pow(diff,hooke_power); } /******************************************************************* * * function: hooke_energy_gradient * * purpose: energy grad of one edge, deviation from set edge length * */ REAL hooke_energy_gradient(e_info) struct qinfo *e_info; { REAL d,diff,coeff; REAL energy; int j; d = sqrt(SDIM_dot(e_info->sides[0][0],e_info->sides[0][0])); diff = d - hooke_length; if ( diff == 0.0 ) return 0.0; if ( hooke_power == 0.0 ) { energy = -log(fabs(diff)); coeff = -1/diff/d; } else { energy = pow(fabs(diff),hooke_power); coeff = hooke_power*energy/diff/d; } for ( j = 0 ; j < SDIM ; j++ ) { e_info->grad[0][j] = -coeff*e_info->sides[0][0][j]; e_info->grad[1][j] = coeff*e_info->sides[0][0][j]; } return energy; } /******************************************************************* * * function: hooke_energy_hessian * * purpose: energy hessian of one edge, deviation from set edge length * */ REAL hooke_energy_hessian(e_info) struct qinfo *e_info; { REAL d,diff,coeff,egrad,ehess; REAL energy; int j,jj; REAL ****h = e_info->hess; d = sqrt(SDIM_dot(e_info->sides[0][0],e_info->sides[0][0])); if ( d == 0.0 ) return 0.0; diff = d - hooke_length; if ( diff == 0.0 ) { energy = 0.0; egrad = hooke_power == 1 ? 1.0 : 0.0; ehess = hooke_power == 2 ? 1.0 : 0.0; } else { if ( hooke_power == 0.0 ) { energy = -log(fabs(diff)); coeff = -1/diff/d; } else { energy = pow(fabs(diff),hooke_power); coeff = hooke_power*energy/diff/d; } for ( j = 0 ; j < SDIM ; j++ ) { e_info->grad[0][j] = -coeff*e_info->sides[0][0][j]; e_info->grad[1][j] = coeff*e_info->sides[0][0][j]; } egrad = energy/diff; ehess = energy/diff/diff; } if ( hooke_power == 0.0 ) { if ( diff ) for ( j = 0 ; j < SDIM ; j++ ) { REAL f; f = -1/diff/d; h[1][1][j][j] += f; h[1][0][j][j] -= f; h[0][1][j][j] -= f; h[0][0][j][j] += f; for ( jj = 0 ; jj < SDIM ; jj++ ) { f = (1/diff + 1/d)/diff*e_info->sides[0][0][j]*e_info->sides[0][0][jj]/d/d; h[1][1][j][jj] += f; h[1][0][j][jj] -= f; h[0][1][j][jj] -= f; h[0][0][j][jj] += f; } } } else { for ( j = 0 ; j < SDIM ; j++ ) { REAL f; f = hooke_power*egrad/d; h[1][1][j][j] += f; h[1][0][j][j] -= f; h[0][1][j][j] -= f; h[0][0][j][j] += f; for ( jj = 0 ; jj < SDIM ; jj++ ) { f = hooke_power*(ehess*(hooke_power-1) - egrad/d) *e_info->sides[0][0][j]*e_info->sides[0][0][jj]/d/d; h[1][1][j][jj] += f; h[1][0][j][jj] -= f; h[0][1][j][jj] -= f; h[0][0][j][jj] += f; } } } return energy; } /******************************************************************* Hooke Energy 2 - Hooke's Law to keep edge lengths nearly uniform. Version using extra edge attribute "hooke_size" as base length. Also example of using extra attributes in quantities. ********************************************************************/ #define POWER2_NAME "hooke2_power" #define HOOKE2_ATTR_NAME "hooke_size" static REAL hooke2_power; static int hooke2_attr; /* index number of hooke_size attribute */ /*************************************************************** * * function: hooke2_energy_init() * * purpose: initialization for hooke2_energy() and * hooke2_energy_gradient(). * */ void hooke2_energy_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { edge_id e_id; if ( web.modeltype != LINEAR ) kb_error(1451,"hooke2_energy only for LINEAR model.\n",RECOVERABLE); exponent_param = lookup_global(POWER2_NAME); if ( exponent_param < 0 ) /* missing, so add */ { exponent_param = add_global(POWER2_NAME); globals(exponent_param)->value.real = 2.0; /* default */ globals(exponent_param)->flags |= ORDINARY_PARAM; } hooke2_power = globals(exponent_param)->value.real; /* extra edge atribute */ hooke2_attr = find_attribute(EDGE,HOOKE2_ATTR_NAME); if ( hooke2_attr < 0 ) /* not found */ { int one = 1; hooke2_attr = add_attribute(EDGE,HOOKE2_ATTR_NAME,REAL_TYPE,0,&one /*dim*/, DUMP_ATTR,NULL); FOR_ALL_EDGES(e_id) /* initialize to current length */ { calc_edge(e_id); *((REAL*)(get_extra(e_id,hooke2_attr))) = get_edge_length(e_id); } } } /******************************************************************* * * function: hooke2_energy * * purpose: energy of one edge, deviation from set edge length * */ REAL hooke2_energy(e_info) struct qinfo *e_info; { REAL d,diff; d = sqrt(SDIM_dot(e_info->sides[0][0],e_info->sides[0][0])); diff = fabs(d - *((REAL*)get_extra(e_info->id,hooke2_attr))); if ( hooke2_power == 0.0 ) return -log(diff); return pow(diff,hooke2_power); } /******************************************************************* * * function: hooke2_energy_gradient * * purpose: energy grad of one edge, deviation from set edge length * */ REAL hooke2_energy_gradient(e_info) struct qinfo *e_info; { REAL d,diff,coeff; REAL energy; int j; d = sqrt(SDIM_dot(e_info->sides[0][0],e_info->sides[0][0])); diff = (d - *((REAL*)get_extra(e_info->id,hooke2_attr))); if ( diff == 0.0 ) return 0.0; if ( hooke2_power == 0.0 ) { energy = -log(fabs(diff)); coeff = -1/diff/d; } else { energy = pow(fabs(diff),hooke2_power); coeff = hooke2_power*energy/diff/d; } for ( j = 0 ; j < SDIM ; j++ ) { e_info->grad[0][j] = -coeff*e_info->sides[0][0][j]; e_info->grad[1][j] = coeff*e_info->sides[0][0][j]; } return energy; } /******************************************************************* * * function: hooke2_energy_hessian * * purpose: energy hessian of one edge, deviation from set edge length * */ REAL hooke2_energy_hessian(e_info) struct qinfo *e_info; { REAL d,diff,coeff,egrad,ehess; REAL energy; int j,jj; REAL ****h = e_info->hess; d = sqrt(SDIM_dot(e_info->sides[0][0],e_info->sides[0][0])); if ( d == 0.0 ) return 0.0; diff = (d - *((REAL*)get_extra(e_info->id,hooke2_attr))); if ( diff == 0.0 ) { energy = 0.0; egrad = hooke2_power == 1 ? 1 : 0.0; ehess = hooke2_power == 2 ? 1 : 0.0; } else { if ( hooke2_power == 0.0 ) { energy = -log(fabs(diff)); coeff = -1/diff/d; } else { energy = pow(fabs(diff),hooke2_power); coeff = hooke2_power*energy/diff/d; } for ( j = 0 ; j < SDIM ; j++ ) { e_info->grad[0][j] = -coeff*e_info->sides[0][0][j]; e_info->grad[1][j] = coeff*e_info->sides[0][0][j]; } egrad = energy/diff; ehess = energy/diff/diff; } if ( hooke2_power == 0.0 ) { if ( diff ) for ( j = 0 ; j < SDIM ; j++ ) { REAL f; f = -1/diff/d; h[1][1][j][j] += f; h[1][0][j][j] -= f; h[0][1][j][j] -= f; h[0][0][j][j] += f; for ( jj = 0 ; jj < SDIM ; jj++ ) { f = (1/diff + 1/d)/diff*e_info->sides[0][0][j]*e_info->sides[0][0][jj]/d/d; h[1][1][j][jj] += f; h[1][0][j][jj] -= f; h[0][1][j][jj] -= f; h[0][0][j][jj] += f; } } } else { for ( j = 0 ; j < SDIM ; j++ ) { REAL f; f = hooke2_power*egrad/d; h[1][1][j][j] += f; h[1][0][j][j] -= f; h[0][1][j][j] -= f; h[0][0][j][j] += f; for ( jj = 0 ; jj < SDIM ; jj++ ) { f = hooke2_power*(ehess*(hooke2_power-1) - egrad/d) *e_info->sides[0][0][j]*e_info->sides[0][0][jj]/d/d; h[1][1][j][jj] += f; h[1][0][j][jj] -= f; h[0][1][j][jj] -= f; h[0][0][j][jj] += f; } } } return energy; } /******************************************************************* Hooke Energy 3 - Hooke's Law using elastic model. Uses "hooke_size" edge attribute as equilibrium length. energy = 0.5*(length-hooke_size)^2/hooke_size ********************************************************************/ #define POWER3_NAME "hooke3_power" #define HOOKE3_ATTR_NAME "hooke_size" static REAL hooke3_power; static int hooke3_attr; /* index number of hooke_size attribute */ static int frickenhaus_flag; /* special feature for S. Frickenhaus */ /*************************************************************** * * function: hooke3_energy_init() * * purpose: initialization for hooke3_energy() and * hooke3_energy_gradient(). * */ void hooke3_energy_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { edge_id e_id; int n; if ( web.modeltype != LINEAR ) kb_error(1767,"hooke3_energy only for LINEAR model.\n",RECOVERABLE); exponent_param = lookup_global(POWER3_NAME); if ( exponent_param < 0 ) /* missing, so add */ { exponent_param = add_global(POWER3_NAME); globals(exponent_param)->value.real = 2.0; /* default */ globals(exponent_param)->flags |= ORDINARY_PARAM; } hooke3_power = globals(exponent_param)->value.real; /* extra edge atribute */ hooke3_attr = find_attribute(EDGE,HOOKE3_ATTR_NAME); if ( hooke3_attr < 0 ) /* not found */ { int one = 1; hooke3_attr = add_attribute(EDGE,HOOKE3_ATTR_NAME,REAL_TYPE,0,&one /*dim*/, DUMP_ATTR,NULL); FOR_ALL_EDGES(e_id) /* initialize to current length */ { calc_edge(e_id); *((REAL*)(get_extra(e_id,hooke3_attr))) = get_edge_length(e_id); } } n = lookup_global("frickenhaus_flag"); if ( (n >= 0) && (globals(n)->value.real != 0.0) ) frickenhaus_flag = 1; else frickenhaus_flag = 0; } /******************************************************************* * * function: hooke3_energy * * purpose: energy of one edge, deviation from set edge length * */ REAL hooke3_energy(e_info) struct qinfo *e_info; { REAL d,diff,length; length = *((REAL*)get_extra(e_info->id,hooke3_attr)); if ( length == 0.0 ) { sprintf(errmsg,"Edge %s has %s zero.\n",ELNAME(e_info->id), HOOKE3_ATTR_NAME); kb_error(2143,errmsg,RECOVERABLE); } d = sqrt(SDIM_dot(e_info->sides[0][0],e_info->sides[0][0])); if ( frickenhaus_flag && (d < length) ) return 0.0; diff = fabs(d - length); if ( hooke3_power == 0.0 ) return -log(diff); return 0.5*pow(diff,hooke3_power)/length; } /******************************************************************* * * function: hooke3_energy_gradient * * purpose: energy grad of one edge, deviation from set edge length * */ REAL hooke3_energy_gradient(e_info) struct qinfo *e_info; { REAL d,diff,coeff; REAL energy; int j; REAL length; length = *((REAL*)get_extra(e_info->id,hooke3_attr)); d = sqrt(SDIM_dot(e_info->sides[0][0],e_info->sides[0][0])); if ( frickenhaus_flag && (d < length) ) return 0.0; diff = (d - length); if ( diff == 0.0 ) return 0.0; if ( hooke3_power == 0.0 ) { energy = -log(fabs(diff)); coeff = -1/diff/d; } else { energy = 0.5*pow(fabs(diff),hooke3_power)/length; coeff = hooke3_power*energy/diff/d; } for ( j = 0 ; j < SDIM ; j++ ) { e_info->grad[0][j] = -coeff*e_info->sides[0][0][j]; e_info->grad[1][j] = coeff*e_info->sides[0][0][j]; } return energy; } /******************************************************************* * * function: hooke3_energy_hessian * * purpose: energy hessian of one edge, deviation from set edge length * */ REAL hooke3_energy_hessian(e_info) struct qinfo *e_info; { REAL d,diff,coeff,egrad,ehess; REAL energy; int j,jj; REAL ****h = e_info->hess; REAL length; length = *((REAL*)get_extra(e_info->id,hooke3_attr)); d = sqrt(SDIM_dot(e_info->sides[0][0],e_info->sides[0][0])); if ( d == 0 ) return 0.0; if ( frickenhaus_flag && (d < length) ) return 0.0; diff = (d - length); if ( diff == 0.0 ) { energy = 0.0; egrad = hooke3_power == 1 ? 1 : 0; ehess = hooke3_power == 2 ? 1 : 0; } else { if ( hooke3_power == 0.0 ) { energy = -log(fabs(diff)); coeff = -1/diff/d; } else { energy = 0.5*pow(fabs(diff),hooke3_power)/length; coeff = hooke3_power*energy/diff/d; } for ( j = 0 ; j < SDIM ; j++ ) { e_info->grad[0][j] = -coeff*e_info->sides[0][0][j]; e_info->grad[1][j] = coeff*e_info->sides[0][0][j]; } egrad = energy/diff; ehess = energy/diff/diff; } if ( hooke3_power == 0.0 ) { if ( diff ) for ( j = 0 ; j < SDIM ; j++ ) { REAL f; f = -1/diff/d; h[1][1][j][j] += f; h[1][0][j][j] -= f; h[0][1][j][j] -= f; h[0][0][j][j] += f; for ( jj = 0 ; jj < SDIM ; jj++ ) { f = (1/diff + 1/d)/diff*e_info->sides[0][0][j]*e_info->sides[0][0][jj]/d/d; h[1][1][j][jj] += f; h[1][0][j][jj] -= f; h[0][1][j][jj] -= f; h[0][0][j][jj] += f; } } } else { for ( j = 0 ; j < SDIM ; j++ ) { REAL f; f = hooke3_power*egrad/d; h[1][1][j][j] += f; h[1][0][j][j] -= f; h[0][1][j][j] -= f; h[0][0][j][j] += f; for ( jj = 0 ; jj < SDIM ; jj++ ) { f = hooke3_power*(ehess*(hooke3_power-1) - egrad/d) *e_info->sides[0][0][j]*e_info->sides[0][0][jj]/d/d; h[1][1][j][jj] += f; h[1][0][j][jj] -= f; h[0][1][j][jj] -= f; h[0][0][j][jj] += f; } } } return energy; } /********************************************************************* circular_arc_length method Models an edge as a circular arc through three points. Usable with quadratic model. Uses arclength formula L = ||v1 - v0||*asin(sinth)/sinth where sinth is the sin of the exterior angle between the short chords, found with cross product. At request of Wacharin Wichiramala, 12-24-99. *********************************************************************/ void circ_debug ARGS((REAL,REAL,REAL,REAL,REAL*,REAL*,REAL*,REAL*)); void circular_arc_aux ARGS((REAL,REAL,REAL,REAL,REAL*, REAL [][2], REAL [][2][3][2], REAL *,REAL [][2],REAL [][2][3][2],REAL *,REAL [][2], REAL [][2][3][2], REAL *,REAL [][2],REAL [][2][3][2],int)); REAL cirf ARGS((REAL)); REAL cirfp ARGS((REAL)); REAL cirfpp ARGS((REAL)); REAL circular_arc_length_all ARGS(( struct qinfo *, int )); REAL caf ARGS((REAL)); REAL cafp ARGS((REAL)); REAL cafpp ARGS((REAL)); REAL circular_arc_area_all ARGS(( struct qinfo *, int )); /********************************************************************* * * function: circular_arc_length_init() * * purpose: check that we are in quadratic model. */ void circular_arc_length_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { if ( (web.modeltype == LAGRANGE) && (web.lagrange_order >= 2) ) kb_error(3371,"circular_arc_length method needs LINEAR or QUADRATIC model.\n",RECOVERABLE); circular_arc_flag = 1; } /************************************************************************ * * function circular_arc_aux() * * purpose: calculate various quantities common to circular arc functions * */ void circ_debug(dx1,dy1,dx2,dy2,chord,sinth,costh,angle) REAL dx1,dx2,dy1,dy2,*chord,*sinth,*costh,*angle; { REAL denom1,denom2; denom1 = sqrt(dx1*dx1+dy1*dy1); denom2 = sqrt(dx2*dx2+dy2*dy2); if (denom1*denom2 == 0.0) return; *chord = sqrt((dx1+dx2)*(dx1+dx2)+(dy1+dy2)*(dy1+dy2)); *sinth = (dx1*dy2 - dy1*dx2)/denom1/denom2; *costh = (dx1*dx2 + dy1*dy2)/denom1/denom2; *angle = atan2(*sinth,*costh); } void circular_arc_aux(dx1,dy1,dx2,dy2,chordptr,dchord,ddchord,sinthptr,dsinth, ddsinth,costhptr,dcosth,ddcosth,angleptr,dangle,ddangle,mode) REAL dx1,dy1,dx2,dy2; REAL *chordptr,dchord[][2],ddchord[][2][3][2]; REAL *sinthptr,dsinth[][2],ddsinth[][2][3][2]; REAL *costhptr,dcosth[][2],ddcosth[][2][3][2]; REAL *angleptr,dangle[][2],ddangle[][2][3][2]; int mode; { REAL chord,sinth,costh,angle; REAL denom1,denom2; int i,j,ii,jj; denom1 = sqrt(dx1*dx1+dy1*dy1); denom2 = sqrt(dx2*dx2+dy2*dy2); if (denom1*denom2 == 0.0) {*chordptr = 0.0; return ;} *chordptr = chord = sqrt((dx1+dx2)*(dx1+dx2)+(dy1+dy2)*(dy1+dy2)); *sinthptr = sinth = (dx1*dy2 - dy1*dx2)/denom1/denom2; *costhptr = costh = (dx1*dx2 + dy1*dy2)/denom1/denom2; angle = atan2(sinth,costh); *angleptr = angle; if ( mode == METHOD_VALUE ) return; dchord[0][0] = -(dx1+dx2)/chord; dchord[0][1] = -(dy1+dy2)/chord; dchord[1][0] = 0.0; dchord[1][1] = 0.0; dchord[2][0] = -dchord[0][0]; dchord[2][1] = -dchord[0][1]; dsinth[0][0] = -dy2/denom1/denom2 + sinth/denom1/denom1*dx1; dsinth[0][1] = dx2/denom1/denom2 + sinth/denom1/denom1*dy1; dsinth[2][0] = -dy1/denom1/denom2 - sinth/denom2/denom2*dx2; dsinth[2][1] = dx1/denom1/denom2 - sinth/denom2/denom2*dy2; dsinth[1][0] = -dsinth[0][0]-dsinth[2][0]; dsinth[1][1] = -dsinth[0][1]-dsinth[2][1]; dcosth[0][0] = -dx2/denom1/denom2 + costh/denom1/denom1*dx1; dcosth[0][1] = -dy2/denom1/denom2 + costh/denom1/denom1*dy1; dcosth[2][0] = dx1/denom1/denom2 - costh/denom2/denom2*dx2; dcosth[2][1] = dy1/denom1/denom2 - costh/denom2/denom2*dy2; dcosth[1][0] = -dcosth[0][0]-dcosth[2][0]; dcosth[1][1] = -dcosth[0][1]-dcosth[2][1]; for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < 2 ; j++ ) dangle[i][j] = dsinth[i][j]*costh - dcosth[i][j]*sinth; if ( mode == METHOD_GRADIENT ) return; for ( i = 0 ; i < 3 ; i++ ) for ( ii = 0 ; ii < 3; ii++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) ddchord[i][j][ii][jj] = 0.0; for ( ii = 0 ; ii < 3; ii++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) { ddchord[0][0][ii][jj] = (dx1+dx2)/chord/chord*dchord[ii][jj]; ddchord[0][1][ii][jj] = (dy1+dy2)/chord/chord*dchord[ii][jj]; } ddchord[0][0][0][0] += 1/chord; ddchord[0][0][2][0] += -1/chord; ddchord[0][1][0][1] += 1/chord; ddchord[0][1][2][1] += -1/chord; for ( j = 0 ; j < SDIM ; j++ ) for ( ii = 0 ; ii < 3; ii++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) ddchord[2][j][ii][jj] = -ddchord[0][j][ii][jj]; /* dsinth[0][0] = -dy2/denom1/denom2 + sinth/denom1/denom1*dx1; */ ddsinth[0][0][0][0] = -(-dy2/denom1/denom2 + 2*sinth/denom1/denom1*dx1)/denom1*(-dx1)/denom1 + dsinth[0][0]/denom1/denom1*dx1 - sinth/denom1/denom1; ddsinth[0][0][0][1] = -(-dy2/denom1/denom2 + 2*sinth/denom1/denom1*dx1)/denom1*(-dy1)/denom1 + dsinth[0][1]/denom1/denom1*dx1; ddsinth[0][0][2][0] = dy2/denom1/denom2/denom2*dx2/denom2 + dsinth[2][0]/denom1/denom1*dx1; ddsinth[0][0][2][1] = -1/denom1/denom2 + dy2/denom1/denom2/denom2*dy2/denom2 + dsinth[2][1]/denom1/denom1*dx1; ddsinth[0][0][1][0] = -ddsinth[0][0][0][0] - ddsinth[0][0][2][0]; ddsinth[0][0][1][1] = -ddsinth[0][0][0][1] - ddsinth[0][0][2][1]; /* dsinth[0][1] = dx2/denom1/denom2 + sinth/denom1/denom1*dy1; */ ddsinth[0][1][0][0] = -(dx2/denom1/denom2 + 2*sinth/denom1/denom1*dy1)/denom1*(-dx1)/denom1 + dsinth[0][0]/denom1/denom1*dy1; ddsinth[0][1][0][1] = -(dx2/denom1/denom2 + 2*sinth/denom1/denom1*dy1)/denom1*(-dy1)/denom1 + dsinth[0][1]/denom1/denom1*dy1 -sinth/denom1/denom1; ddsinth[0][1][2][0] = 1/denom1/denom2 - dx2/denom1/denom2/denom2*dx2/denom2 + dsinth[2][0]/denom1/denom1*dy1; ddsinth[0][1][2][1] = -dx2/denom1/denom2/denom2*dy2/denom2 + dsinth[2][1]/denom1/denom1*dy1; ddsinth[0][1][1][0] = -ddsinth[0][1][0][0] - ddsinth[0][1][2][0]; ddsinth[0][1][1][1] = -ddsinth[0][1][0][1] - ddsinth[0][1][2][1]; for ( j = 0 ; j < 2 ; j++ ) for ( jj = 0 ; jj < 2 ; jj++ ) ddsinth[2][j][0][jj] = ddsinth[0][jj][2][j]; /* dsinth[2][0] = -dy1/denom1/denom2 - sinth/denom2/denom2*dx2; */ ddsinth[2][0][2][0] = -(-dy1/denom1/denom2 - 2*sinth/denom2/denom2*dx2)/denom2*dx2/denom2 - dsinth[2][0]/denom2/denom2*dx2 - sinth/denom2/denom2; ddsinth[2][0][2][1] = -(-dy1/denom1/denom2 - 2*sinth/denom2/denom2*dx2)/denom2*dy2/denom2 - dsinth[2][1]/denom2/denom2*dx2; for ( jj = 0 ; jj < 2 ; jj++ ) ddsinth[2][0][1][jj] = -ddsinth[2][0][0][jj]-ddsinth[2][0][2][jj]; ddsinth[2][1][2][0] = ddsinth[2][0][2][1]; ddsinth[2][1][2][1] = -(dx1/denom1/denom2 - 2*sinth/denom2/denom2*dy2)/denom2*dy2/denom2 - dsinth[2][1]/denom2/denom2*dy2 - sinth/denom2/denom2; for ( jj = 0 ; jj < 2 ; jj++ ) ddsinth[2][1][1][jj] = -ddsinth[2][1][0][jj]-ddsinth[2][1][2][jj]; for ( j = 0 ; j < 2 ; j++ ) for ( ii = 0 ; ii < 3 ; ii++ ) for ( jj = 0 ; jj < 2 ; jj++ ) ddsinth[1][j][ii][jj] = -ddsinth[0][j][ii][jj]-ddsinth[2][j][ii][jj]; /* dcosth[0][0] = -dx2/denom1/denom2 + costh/denom1/denom1*dx1; */ ddcosth[0][0][0][0] = -(-dx2/denom1/denom2 + 2*costh/denom1/denom1*dx1)/denom1*(-dx1)/denom1 + dcosth[0][0]/denom1/denom1*dx1 - costh/denom1/denom1; ddcosth[0][0][0][1] = -(-dx2/denom1/denom2 + 2*costh/denom1/denom1*dx1)/denom1*(-dy1)/denom1 + dcosth[0][1]/denom1/denom1*dx1; ddcosth[0][0][2][0] = -1/denom1/denom2 + dx2/denom1/denom2/denom2*dx2/denom2 + dcosth[2][0]/denom1/denom1*dx1; ddcosth[0][0][2][1] = dx2/denom1/denom2/denom2*dy2/denom2 + dcosth[2][1]/denom1/denom1*dx1; for ( jj = 0 ; jj < 2 ; jj++ ) ddcosth[0][0][1][jj] = -ddcosth[0][0][0][jj]-ddcosth[0][0][2][jj]; /* dcosth[0][1] = -dy2/denom1/denom2 + costh/denom1/denom1*dy1; */ ddcosth[0][1][0][0] = -(-dy2/denom1/denom2 + 2*costh/denom1/denom1*dy1)/denom1*(-dx1)/denom1 + dcosth[0][0]/denom1/denom1*dy1; ddcosth[0][1][0][1] = -(-dy2/denom1/denom2 + 2*costh/denom1/denom1*dy1)/denom1*(-dy1)/denom1 + dcosth[0][1]/denom1/denom1*dy1 - costh/denom1/denom1; ddcosth[0][1][2][0] = dy2/denom1/denom2/denom2*dx2/denom2 + dcosth[2][0]/denom1/denom1*dy1; ddcosth[0][1][2][1] = -1/denom1/denom2+dy2/denom1/denom2/denom2*dy2/denom2 + dcosth[2][1]/denom1/denom1*dy1; for ( jj = 0 ; jj < 2 ; jj++ ) ddcosth[0][1][1][jj] = -ddcosth[0][1][0][jj]-ddcosth[0][1][2][jj]; for ( j = 0 ; j < 2 ; j++ ) for ( jj = 0 ; jj < 2 ; jj++ ) ddcosth[2][j][0][jj] = ddcosth[0][jj][2][j]; /* dcosth[2][0] = dx1/denom1/denom2 - costh/denom2/denom2*dx2; */ ddcosth[2][0][2][0] = -(dx1/denom1/denom2 - 2*costh/denom2/denom2*dx2)/denom2*dx2/denom2 - dcosth[2][0]/denom2/denom2*dx2 - costh/denom2/denom2; ddcosth[2][0][2][1] = -(dx1/denom1/denom2 - 2*costh/denom2/denom2*dx2)/denom2*dy2/denom2 - dcosth[2][1]/denom2/denom2*dx2; for ( jj = 0 ; jj < 2 ; jj++ ) ddcosth[2][0][1][jj] = -ddcosth[2][0][0][jj]-ddcosth[2][0][2][jj]; /* dcosth[2][1] = dy1/denom1/denom2 - costh/denom2/denom2*dy2; */ ddcosth[2][1][2][0] = ddcosth[2][0][2][1]; ddcosth[2][1][2][1] = -(dy1/denom1/denom2 -2*costh/denom2/denom2*dy2)/denom2*dy2/denom2 - dcosth[2][1]/denom2/denom2*dy2 - costh/denom2/denom2; for ( jj = 0 ; jj < 2 ; jj++ ) ddcosth[2][1][1][jj] = -ddcosth[2][1][0][jj]-ddcosth[2][1][2][jj]; for ( j = 0 ; j < 2 ; j++ ) for ( ii = 0 ; ii < 3 ; ii++ ) for ( jj = 0 ; jj < 2 ; jj++ ) ddcosth[1][j][ii][jj] = -ddcosth[0][j][ii][jj]-ddcosth[2][j][ii][jj]; for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < 2 ; j++ ) for ( ii = 0 ; ii < 3; ii++ ) for ( jj = 0 ; jj < 2 ; jj++ ) ddangle[i][j][ii][jj] = ddsinth[i][j][ii][jj]*costh - ddcosth[i][j][ii][jj]*sinth + dsinth[i][j]*dcosth[ii][jj] - dcosth[i][j]*dsinth[ii][jj]; #ifdef XXX /* debugging */ { REAL dx[2][2]; REAL ch[4],si[4],co[4],an[4]; REAL delta = 1e-4; dx[0][0] = dx1; dx[0][1] = dy1; dx[1][0] = dx2; dx[1][1] = dy2; /* check numerically */ for ( i = 0 ; i < 2; i++ ) for ( j = 0 ; j < 2; j++ ) for ( ii = 0 ; ii < 2 ; ii ++ ) for ( jj = 0 ; jj < 2 ; jj ++ ) { dx[i][j] += delta; dx[ii][jj] += delta; circ_debug(dx[0][0],dx[0][1],dx[1][0],dx[1][1],&ch[0],&si[0], &co[0],&an[0]); dx[i][j] -= 2*delta; circ_debug(dx[0][0],dx[0][1],dx[1][0],dx[1][1],&ch[1],&si[1], &co[1],&an[1]); dx[ii][jj] -= 2*delta; circ_debug(dx[0][0],dx[0][1],dx[1][0],dx[1][1],&ch[3],&si[3], &co[3],&an[3]); dx[i][j] += 2*delta; circ_debug(dx[0][0],dx[0][1],dx[1][0],dx[1][1],&ch[2],&si[2], &co[2],&an[2]); dx[ii][jj] += 2*delta; printf("ddchord[%d][%d][%d][%d]: %18.15f %18.15f\n",2*i,j,2*ii,jj, (ch[0]-ch[1]-ch[2]+ch[3])/delta/delta/4,ddchord[2*i][j][2*ii][jj]); printf("ddsinth[%d][%d][%d][%d]: %18.15f %18.15f\n",2*i,j,2*ii,jj, (si[0]-si[1]-si[2]+si[3])/delta/delta/4,ddsinth[2*i][j][2*ii][jj]); printf("ddcosth[%d][%d][%d][%d]: %18.15f %18.15f\n",2*i,j,2*ii,jj, (co[0]-co[1]-co[2]+co[3])/delta/delta/4,ddcosth[2*i][j][2*ii][jj]); printf("ddangle[%d][%d][%d][%d]: %18.15f %18.15f\n",2*i,j,2*ii,jj, (an[0]-an[1]-an[2]+an[3])/delta/delta/4,ddangle[2*i][j][2*ii][jj]); } } #endif return; } /********************************************************************* * * function: circular_arc_length_value() * * purpose: method value * */ REAL circular_arc_length_value(e_info) struct qinfo *e_info; { REAL value = 0.0; REAL dx1,dx2,dy1,dy2,sinth,costh,chord,angle; REAL density = 1.0; if ( web.modeltype != QUADRATIC ) return q_edge_tension_value(e_info); if ( METH_INSTANCE(e_info->method)->flags & USE_DENSITY ) density = get_edge_density(e_info->id); dx1 = e_info->x[1][0] - e_info->x[0][0]; dy1 = e_info->x[1][1] - e_info->x[0][1]; dx2 = e_info->x[2][0] - e_info->x[1][0]; dy2 = e_info->x[2][1] - e_info->x[1][1]; circular_arc_aux(dx1,dy1,dx2,dy2,&chord,NULL,NULL,&sinth,NULL, NULL,&costh,NULL,NULL,&angle,NULL,NULL,METHOD_VALUE); if ( chord == 0.0 ) return 0.0; if ( sinth == 0.0 ) value = chord; else { vertex_id v_id = e_info->v[1]; value = angle*chord/sinth; /* fix up midpoint to center of arc */ e_info->x[1][0] = e_info->x[0][0]+(dx1+dx2)/2+(dy1+dy2)/2*sinth/(1+costh); e_info->x[1][1] = e_info->x[0][1]+(dy1+dy2)/2-(dx1+dx2)/2*sinth/(1+costh); if (get_vattr(v_id) & BOUNDARY ) { /* update boundary parameter to agree with coordinates */ b_extrapolate(get_boundary(v_id),get_coord(v_id),e_info->x[1], get_coord(v_id),get_param(v_id), get_param(v_id),v_id); } } if ( METH_INSTANCE(e_info->method)->flags & DEFAULT_INSTANCE ) { binary_tree_add(web.total_area_addends,value); set_edge_length(e_info->id,value); } value *= density; return value; } REAL cirf(th) REAL th; { if ( th == 0.0 ) return 1.0; return th/sin(th); } REAL cirfp(th) REAL th; { if ( fabs(th) < .1 ) return th*(1./3 + th*th*(7./90 + th*th*(31./2520 + th*th*(127./75600 + th*th*73./342144)))); return (sin(th)-th*cos(th))/sin(th)/sin(th); } REAL cirfpp(th) REAL th; { REAL s,c; if ( fabs(th) < .1 ) return (1./3 + th*th*(7./30 + th*th*(31./504 + th*th*(127./10800 + th*th*73./38016)))); s = sin(th); c = cos(th); return (-2*c/s + th*c*c/s/s + th/s/s)/s; } /********************************************************************* * * function: circular_arc_length_all() * * purpose: method value, gradient and hessian * * NOTE: hessian_diff does not work with hessian here, since * value() moves midpoints. */ REAL circular_arc_length_all(e_info,mode) struct qinfo *e_info; int mode; { int i,j,ii,jj; REAL angle,value = 0.0; REAL dx1,dx2,dy1,dy2,sinth,costh,chord; REAL dchord[3][2]; REAL dsinth[3][2]; REAL dcosth[3][2]; REAL dangle[3][2]; REAL ddchord[3][2][3][2]; REAL ddsinth[3][2][3][2]; REAL ddcosth[3][2][3][2]; REAL ddangle[3][2][3][2]; REAL density = 1.0; if ( web.modeltype != QUADRATIC ) return q_edge_tension_gradient(e_info); if ( METH_INSTANCE(e_info->method)->flags & USE_DENSITY ) density = get_edge_density(e_info->id); dx1 = e_info->x[1][0] - e_info->x[0][0]; dy1 = e_info->x[1][1] - e_info->x[0][1]; dx2 = e_info->x[2][0] - e_info->x[1][0]; dy2 = e_info->x[2][1] - e_info->x[1][1]; circular_arc_aux(dx1,dy1,dx2,dy2,&chord,dchord,ddchord,&sinth,dsinth, ddsinth,&costh,dcosth,ddcosth,&angle,dangle,ddangle,mode); if (chord == 0.0) return 0.0; value = chord*cirf(angle); if ( mode == METHOD_VALUE ) if ( METH_INSTANCE(e_info->method)->flags & DEFAULT_INSTANCE ) binary_tree_add(web.total_area_addends,value); value *= density; if ( mode == METHOD_VALUE ) return value; for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) e_info->grad[i][j] = density*(dchord[i][j]*cirf(angle) + chord*cirfp(angle)*dangle[i][j]); if ( mode == METHOD_GRADIENT ) return value; for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( ii = 0 ; ii < 3 ; ii++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) e_info->hess[i][ii][j][jj] = density*( ddchord[i][j][ii][jj]*cirf(angle) + dchord[i][j]*cirfp(angle)*dangle[ii][jj] + dchord[ii][jj]*cirfp(angle)*dangle[i][j] + chord*cirfpp(angle)*dangle[ii][jj]*dangle[i][j] + chord*cirfp(angle)*ddangle[i][j][ii][jj]); return value; } /* end circular_arc_length_all */ REAL circular_arc_length_grad(e_info) struct qinfo *e_info; { if ( web.modeltype != QUADRATIC ) return q_edge_tension_gradient(e_info); return circular_arc_length_all(e_info,METHOD_GRADIENT); } REAL circular_arc_length_hess(e_info) struct qinfo *e_info; { if ( web.modeltype != QUADRATIC ) return q_edge_tension_hessian(e_info); return circular_arc_length_all(e_info,METHOD_HESSIAN); } REAL edge_symmetric_area_all ARGS(( struct qinfo *, int)); REAL spherical_arc_length_all ARGS(( struct qinfo *, int)); REAL spherical_arc_area_all ARGS(( struct qinfo *, int,int)); /********************************************************************** * * function: edge_symmetric_area_all() * * purpose: value and gradient and hessian of area integral on edge * using symmetric area. Mostly for use of circular_arc_area * in linear case. */ REAL edge_symmetric_area_all(e_info,mode) struct qinfo *e_info; int mode; /* METHODVALUE, etc */ { REAL **x,**g,****h; REAL area; if ( web.torus_flag ) return q_edge_torus_area_hess(e_info); if ( web.modeltype == QUADRATIC ) return q_edge_area_q_hess(e_info); if ( web.modeltype == LAGRANGE ) return q_edge_area_lagrange_hess(e_info); x = e_info->x; g = e_info->grad; h = e_info->hess; /* main integral over edge */ area = (x[0][0]*x[1][1] - x[0][1]*x[1][0])/2; if ( mode == METHOD_VALUE ) return area; g[0][0] = x[1][1]/2; g[1][0] = -x[0][1]/2; g[0][1] = -x[1][0]/2; g[1][1] = x[0][0]/2; if ( mode == METHOD_GRADIENT ) return area; h[0][1][0][1] += 0.5; h[1][0][0][1] -= 0.5; h[0][1][1][0] -= 0.5; h[1][0][1][0] += 0.5; return area; } /********************************************************************* circular_arc_area method Models an edge as a circular arc through three points. Usable with quadratic model. Uses arclength formula of lune area plus trapezoid under chord: A = ||s1+s2||^2/4*(asin(sinth)/sinth^2 - (s1 dot s2)/(s1 cross s2)) + (v1.y+v0.y)*(v0.x-v1.x)/2 where sinth is the sin of the exterior angle between the short chords s1 and s2, found with cross product. Or, in a form more amenable to small and zero angles, A = ||s1+s2||^2/4*(th - sin(th)*cos(th))/sin(th)^2 + (v1.y+v0.y)*(v0.x-v1.x)/2 At request of Wacharin Wichiramala, 12-24-99. *********************************************************************/ REAL caf(th) REAL th; { if ( fabs(th) > .1 ) return (th - sin(th)*cos(th))/sin(th)/sin(th); return th*(2./3 + th*th*(4./45 + th*th*(4./315 + th*th*(8./4725 + th*th*4./18711)))); } REAL cafp(th) REAL th; { if ( fabs(th) > .1 ) return 2*(sin(th) - th*cos(th))/sin(th)/sin(th)/sin(th); return 2./3 + th*th*(4./15 + th*th*(4./63 + th*th*(8./675 + th*th*4./2079))); } REAL cafpp(th) REAL th; { if ( fabs(th) > .1 ) { REAL s = sin(th), c = cos(th); return (4*th + 2*th*(c*c-s*s) - 6*s*c)/s/s/s/s; } return th*(8./15 + th*th*(16./63 + th*th*(16./225 + th*th*32./2079))); } /********************************************************************* * * function: circular_arc_area_init() * * purpose: check that we are in quadratic model. */ void circular_arc_area_init(mode,mi) int mode; /* energy or gradient */ struct method_instance *mi; { if ( (web.modeltype == LAGRANGE) && (web.lagrange_order >= 2) ) kb_error(2860,"circular_arc_area method needs LINEAR or QUADRATIC model.\n",RECOVERABLE); } /************************************************************************** * * function: circular_arc_area_all() * * purpose: combined function for circular_arc_area method. */ REAL circular_arc_area_all(e_info,mode) struct qinfo *e_info; int mode; { int i,j,ii,jj; REAL value = 0.0; REAL dx1,dx2,dy1,dy2,sinth,chord,costh,angle; REAL dsinth[3][2],dchord[3][2],dangle[3][2],dcosth[3][2]; REAL ddsinth[3][2][3][2],ddchord[3][2][3][2],ddangle[3][2][3][2]; REAL ddcosth[3][2][3][2]; int wrap=0; MAT2D(u,EDGE_CTRL,MAXCOORD); /* affine coordinates of vertices */ if ( web.modeltype != QUADRATIC ) return q_edge_area_grad(e_info); dx1 = e_info->x[1][0] - e_info->x[0][0]; dy1 = e_info->x[1][1] - e_info->x[0][1]; dx2 = e_info->x[2][0] - e_info->x[1][0]; dy2 = e_info->x[2][1] - e_info->x[1][1]; circular_arc_aux(dx1,dy1,dx2,dy2,&chord,dchord,ddchord,&sinth,dsinth, ddsinth,&costh,dcosth,ddcosth,&angle,dangle,ddangle,mode); if (chord == 0.0) return 0.0; if ( web.torus_flag ) { wrap = (get_edge_wrap(e_info->id)>>TWRAPBITS) & WRAPMASK; /* get affine coordinates of vertices */ mat_mul_tr(e_info->x,web.inverse_periods,u,edge_ctrl,SDIM,SDIM); value = chord*chord/4*caf(angle) + web.torusv*(u[0][1]+u[2][1])*(u[0][0] - u[2][0])/2; if ( wrap ) { /* wrap correction */ value += WRAPNUM(wrap)*u[2][0]*web.torusv; } } else if ( web.symmetric_content ) { value = chord*chord/4*caf(angle) + (e_info->x[0][0]*e_info->x[2][1]-e_info->x[0][1]*e_info->x[2][0])/2; } else { value = chord*chord/4*caf(angle) - (dx1+dx2)*(e_info->x[2][1]+e_info->x[0][1])/2; } if ( mode == METHOD_VALUE ) return value; /* Gradient */ /* parallelogram gradient */ if ( web.torus_flag ) { MAT2D(g,EDGE_CTRL,MAXCOORD); g[0][0] = (u[2][1] + u[0][1])/2*web.torusv; g[2][0] = -(u[2][1] + u[0][1])/2*web.torusv; g[0][1] = (u[0][0] - u[2][0])/2*web.torusv; g[2][1] = (u[0][0] - u[2][0])/2*web.torusv; g[1][0] = g[1][1] = 0.0; if ( wrap ) g[2][0] += WRAPNUM(wrap)*web.torusv; mat_mult(g,web.inverse_periods,e_info->grad,edge_ctrl,SDIM,SDIM); } else if ( web.symmetric_content ) { e_info->grad[2][1] += e_info->x[0][0]/2; e_info->grad[0][1] -= e_info->x[2][0]/2; e_info->grad[0][0] += e_info->x[2][1]/2; e_info->grad[2][0] -= e_info->x[0][1]/2; } else { e_info->grad[2][1] -= (dx1+dx2)/2; e_info->grad[0][1] -= (dx1+dx2)/2; e_info->grad[0][0] -= -(e_info->x[2][1]+e_info->x[0][1])/2; e_info->grad[2][0] -= (e_info->x[2][1]+e_info->x[0][1])/2; } /* lune gradient */ for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < 2 ; j++ ) e_info->grad[i][j] += chord*dchord[i][j]/2*caf(angle) + chord*chord/4*cafp(angle)*dangle[i][j]; if ( mode == METHOD_GRADIENT ) return value; /* Hessian */ /* parallelogram hessian */ if ( web.torus_flag ) { MAT2D(temph,MAXCOORD,MAXCOORD); MAT4D(h,EDGE_CTRL,EDGE_CTRL,MAXCOORD,MAXCOORD); for ( i = 0 ; i < EDGE_CTRL ; i++ ) for ( ii = 0 ; ii < EDGE_CTRL ; ii++ ) for ( j = 0 ; j < SDIM ; j++ ) for ( jj = 0 ; jj < SDIM ; jj++ ) h[i][ii][j][jj] = 0.0; h[0][2][0][1] = 0.5*web.torusv; h[0][0][0][1] = 0.5*web.torusv; h[2][2][0][1] = -0.5*web.torusv; h[2][0][0][1] = -0.5*web.torusv; h[0][0][1][0] = 0.5*web.torusv; h[0][2][1][0] = -0.5*web.torusv; h[2][0][1][0] = 0.5*web.torusv; h[2][2][1][0] = -0.5*web.torusv; /* form pullback */ for ( i = 0 ; i < EDGE_CTRL ; i++ ) for ( ii = 0 ; ii < EDGE_CTRL ; ii ++ ) { mat_mult(h[i][ii],web.inverse_periods,temph,SDIM,SDIM,SDIM); tr_mat_mul(web.inverse_periods,temph,e_info->hess[i][ii],SDIM,SDIM,SDIM); } } else if ( web.symmetric_content ) { e_info->hess[2][0][1][0] += 1./2; e_info->hess[0][2][1][0] -= 1./2; e_info->hess[0][2][0][1] += 1./2; e_info->hess[2][0][0][1] -= 1./2; } else { e_info->hess[2][0][1][0] += 1./2; e_info->hess[2][2][1][0] -= 1./2; e_info->hess[0][0][1][0] += 1./2; e_info->hess[0][2][1][0] -= 1./2; e_info->hess[0][0][0][1] += 1./2; e_info->hess[0][2][0][1] += 1./2; e_info->hess[2][0][0][1] -= 1./2; e_info->hess[2][2][0][1] -= 1./2; } /* chord hessian */ for ( i = 0 ; i < 3 ; i++ ) for ( j = 0 ; j < 2 ; j++ ) for ( ii = 0 ; ii < 3 ; ii++ ) for ( jj = 0 ; jj < 2 ; jj++ ) e_info->hess[i][ii][j][jj] += dchord[ii][jj]*dchord[i][j]/2*caf(angle) + chord*ddchord[i][j][ii][jj]/2*caf(angle) + chord*dchord[i][j]/2*cafp(angle)*dangle[ii][jj] + 2*dchord[ii][jj]*chord/4*cafp(angle)*dangle[i][j] + chord*chord/4*cafpp(angle)*dangle[ii][jj]*dangle[i][j] + chord*chord/4*cafp(angle)*ddangle[i][j][ii][jj]; return value; } /* end circular_arc_area_all() */ /********************************************************************* * * function: circular_arc_area_value() * * purpose: method value * */ REAL circular_arc_area_value(e_info) struct qinfo *e_info; { REAL area; if ( web.modeltype != QUADRATIC ) { if ( web.symmetric_content ) area = edge_symmetric_area_all(e_info,METHOD_VALUE); else return q_edge_area(e_info); /* does own facet area */ } else area = circular_arc_area_all(e_info,METHOD_VALUE); if ( METH_INSTANCE(e_info->method)->flags & DEFAULT_INSTANCE ) { /* add to facet area */ body_id b_id,bb_id; facetedge_id fe_id = get_edge_fe(e_info->id); facetedge_id fe; facet_id f_id; b_id = GEN_QUANT(METH_INSTANCE(e_info->method)->quant)->b_id; fe = fe_id; do { f_id = get_fe_facet(fe); bb_id = get_facet_body(f_id); if ( equal_id(b_id,bb_id) ) add_facet_area(f_id,area); bb_id = get_facet_body(inverse_id(f_id)); if ( equal_id(b_id,bb_id) ) add_facet_area(f_id,area); fe = get_next_facet(fe); } while ( !equal_id(fe,fe_id) ); } return area; } /********************************************************************* * * function: circular_arc_area_grad() * purpose: method gradient * */ REAL circular_arc_area_grad(e_info) struct qinfo *e_info; { if ( web.modeltype != QUADRATIC ) { if ( web.symmetric_content ) return edge_symmetric_area_all(e_info,METHOD_GRADIENT); else return q_edge_area_grad(e_info); } return circular_arc_area_all(e_info,METHOD_GRADIENT); } /********************************************************************* * * function: circular_arc_area_hess() * purpose: method hessian * */ REAL circular_arc_area_hess(e_info) struct qinfo *e_info; { if ( web.modeltype != QUADRATIC ) { if ( web.symmetric_content ) return edge_symmetric_area_all(e_info,METHOD_HESSIAN); else return q_edge_area_hess(e_info); } return circular_arc_area_all(e_info,METHOD_HESSIAN); } /*************************************************************************/ /********************************************************************** SPHERICAL ARC methods Linear model only for the moment **********************************************************************/ /********************************************************************* * * function: spherical_arc_length_all() * * purpose: method value, gradient and hessian * */ REAL spherical_arc_length_all(e_info,mode) struct qinfo *e_info; int mode; { int i,j; REAL density = 1.0; REAL radius = sqrt(SDIM_dot(e_info->x[0],e_info->x[0])); REAL c,value; /* Linear mode only for now */ if ( METH_INSTANCE(e_info->method)->flags & USE_DENSITY ) density = get_edge_density(e_info->id); c = SDIM_dot(e_info->sides[0][0],e_info->sides[0][0]); value = 2*radius*asin(sqrt(c)/2/radius); if ( mode == METHOD_VALUE ) if ( METH_INSTANCE(e_info->method)->flags & DEFAULT_INSTANCE ) { binary_tree_add(web.total_area_addends,value); set_edge_length(e_info->id,value); } value *= density; if ( mode == METHOD_VALUE ) return value; /* gradient */ for ( i = 0 ; i < SDIM ; i++ ) { REAL g = density*2*radius/sqrt(4*radius*radius - c)/sqrt(c)* e_info->sides[0][0][i]; e_info->grad[0][i] = -g; e_info->grad[1][i] = g; } if ( mode == METHOD_GRADIENT ) return value; /* hessian */ for ( i = 0 ; i < SDIM ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) { REAL h,d; d = sqrt(4*radius*radius - c); h = (-.5)/d/d/d*(-2*e_info->sides[0][0][j])/sqrt(c)* e_info->sides[0][0][i] + 1/sqrt(4*radius*radius - c)*(-.5)/sqrt(c) /sqrt(c)/sqrt(c)*2*e_info->sides[0][0][j]* e_info->sides[0][0][i]; if ( i == j ) h += 1/sqrt(4*radius*radius - c)/sqrt(c); h *= density*2*radius; e_info->hess[0][0][i][j] = h; e_info->hess[1][0][i][j] = -h; e_info->hess[0][1][i][j] = -h; e_info->hess[1][1][i][j] = h; } return value; } /* end spherical_arc_length_all */ REAL spherical_arc_length_value(e_info) struct qinfo *e_info; { if ( web.modeltype != LINEAR ) return q_edge_tension_gradient(e_info); return spherical_arc_length_all(e_info,METHOD_VALUE); } REAL spherical_arc_length_grad(e_info) struct qinfo *e_info; { if ( web.modeltype != LINEAR ) return q_edge_tension_gradient(e_info); return spherical_arc_length_all(e_info,METHOD_GRADIENT); } REAL spherical_arc_length_hess(e_info) struct qinfo *e_info; { if ( web.modeltype != LINEAR ) return q_edge_tension_hessian(e_info); return spherical_arc_length_all(e_info,METHOD_HESSIAN); } /*********************************************************************** Named Method spherical_arc_area Assumes vertices of an edge are on a sphere (in any dimension). Value is the area of the spherical triangle to the south pole. ***********************************************************************/ #define NORTH 1 #define SOUTH -1 void spherical_arc_area_init(mode,mi) int mode; struct method_instance *mi; { if ( web.modeltype != LINEAR ) kb_error(3944,"spherical_arc_area only in LINEAR mode.\n",RECOVERABLE); } REAL spherical_arc_area_all(e_info,mode,pole) struct qinfo *e_info; int mode; int pole; /* NORTH or SOUTH */ { int i,j,ii,jj; REAL area; REAL *x[2]; REAL rad = pole*sqrt(SDIM_dot(e_info->x[0],e_info->x[0])); REAL normal[3],dndx[3][2][3]; /* normal component derivatives */ REAL ss1,ss2,ss12; REAL legnorm[2][3],dlegnorm[2][3][2][3]; REAL ang1,ang2,ang12; REAL denom[2],denom12; REAL ddenom[2][2][3],ddenom12[2][3]; x[0] = e_info->x[0]; x[1] = e_info->x[1]; /* area from pole */ normal[0] = x[0][1]*x[1][2] - x[0][2]*x[1][1]; normal[1] = x[0][2]*x[1][0] - x[0][0]*x[1][2]; normal[2] = x[0][0]*x[1][1] - x[0][1]*x[1][0]; /* L'Huilier's formula, nonsingular for degenerate triangles */ ss1 = (x[0][0]*x[0][0] + x[0][1]*x[0][1] + (x[0][2]-rad)*(x[0][2]-rad))/ rad/rad/4; ss2 = (x[1][0]*x[1][0] + x[1][1]*x[1][1] + (x[1][2]-rad)*(x[1][2]-rad))/ rad/rad/4; ss12 = ((x[1][0]-x[0][0])*(x[1][0]-x[0][0]) + (x[1][1]-x[0][1])*(x[1][1]-x[0][1]) + (x[1][2]-x[0][2])*(x[1][2]-x[0][2]))/rad/rad/4; ang1 = atan(sqrt(ss1/(1-ss1))); ang2 = atan(sqrt(ss2/(1-ss2))); ang12 = atan(sqrt(ss12/(1-ss12))); area = 4*atan(sqrt(fabs(tan((ang1+ang2+ang12)/2)*tan((-ang1+ang2+ang12)/2)* tan((ang1-ang2+ang12)/2)*tan((ang1+ang2-ang12)/2)))); /* need fabs here in case of slightly negative values */ area *= rad*rad; /*printf("%20.15f %20.15f %20.15f\n",ang1,ang2,ang12);*/ if ( (normal[2] > 0.0 && pole==SOUTH) || (normal[2] < 0.0 && pole==NORTH) ) area = -area; if ( mode == METHOD_VALUE ) return area; /* Gradient follows geometric derivation of strip widths, rather than blindly differentiating value expression. Get nice expression for gradient on a side from A to B: (A x B)*R/(R^2 + A.B) which is nonsingular for small edges. */ memset(legnorm,0,sizeof(legnorm)); legnorm[0][0] = pole*x[0][1]; legnorm[0][1] = -pole*x[0][0]; legnorm[1][0] = -pole*x[1][1]; legnorm[1][1] = pole*x[1][0]; denom[0] = rad*rad + rad*x[0][2]; denom[1] = rad*rad + rad*x[1][2]; denom12 = rad*rad + x[0][0]*x[1][0] + x[0][1]*x[1][1] + x[0][2]*x[1][2]; for ( i = 0 ; i < 2 ; i++ ) for ( j = 0 ; j < 3 ; j++ ) e_info->grad[i][j] = pole*rad*(legnorm[i][j]/denom[i] - normal[j]/denom12); if ( mode == METHOD_GRADIENT ) return area; /* gradient of normal */ memset(dndx,0,sizeof(dndx)); /* i = normal component, j = vertex number, k = vertex coord */ for ( i = 0 ; i < 3 ; i++ ) { int ii = i >= 2 ? i-2 : i+1; int iii = i >= 1 ? i-1 : i+2; dndx[i][0][ii] = x[1][iii]; dndx[i][0][iii] = -x[1][ii]; dndx[i][1][iii] = x[0][ii]; dndx[i][1][ii] = -x[0][iii]; } memset(ddenom,0,sizeof(ddenom)); ddenom[0][0][2] = rad; ddenom[1][1][2] = rad; for ( i = 0 ; i < 2 ; i++ ) for ( j = 0 ; j < 3 ; j++ ) ddenom12[i][j] = x[1-i][j]; memset(dlegnorm,0,sizeof(dlegnorm)); dlegnorm[0][0][0][1] = -pole; dlegnorm[0][1][0][0] = pole; dlegnorm[1][0][1][1] = pole; dlegnorm[1][1][1][0] = -pole; /* Hessian */ for ( i = 0 ; i < 2 ; i++ ) for ( ii = 0 ; ii < 2 ; ii++ ) for ( j = 0 ; j < 3 ; j++ ) for ( jj = 0 ; jj < 3; jj++ ) { e_info->hess[i][ii][j][jj] = pole*rad*( dlegnorm[i][j][ii][jj]/denom[i] - dndx[j][ii][jj]/denom12 + legnorm[i][j]/denom[i]*(-1/denom[i]*ddenom[i][ii][jj]) - normal[j]/denom12*(-1/denom12*ddenom12[ii][jj]) ); } return area; } REAL spherical_arc_area_n_value(e_info) struct qinfo *e_info; { if ( web.modeltype != LINEAR ) return 0; return spherical_arc_area_all(e_info,METHOD_VALUE,NORTH); } REAL spherical_arc_area_n_grad(e_info) struct qinfo *e_info; { if ( web.modeltype != LINEAR ) return 0; return spherical_arc_area_all(e_info,METHOD_GRADIENT,NORTH); } REAL spherical_arc_area_n_hess(e_info) struct qinfo *e_info; { if ( web.modeltype != LINEAR ) return 0; return spherical_arc_area_all(e_info,METHOD_HESSIAN,NORTH); } REAL spherical_arc_area_s_value(e_info) struct qinfo *e_info; { if ( web.modeltype != LINEAR ) return 0; return spherical_arc_area_all(e_info,METHOD_VALUE,SOUTH); } REAL spherical_arc_area_s_grad(e_info) struct qinfo *e_info; { if ( web.modeltype != LINEAR ) return 0; return spherical_arc_area_all(e_info,METHOD_GRADIENT,SOUTH); } REAL spherical_arc_area_s_hess(e_info) struct qinfo *e_info; { if ( web.modeltype != LINEAR ) return 0; return spherical_arc_area_all(e_info,METHOD_HESSIAN,SOUTH); } evolver-2.30c.dfsg/src/pixgraph.c0000644000175300017530000001367511410765113017217 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /********************************************************************** * * File: pixgraph.c * * Purpose: Creates data file for Pixar from current triangulation. * Hooks directly to graphgen(). * */ #include "include.h" /* Pixar defines */ #define QUAD 1 #define NQUAD 2 #define CQUAD 3 #define CNQUAD 4 static FILE *pfd = NULL; static REAL **verts; /* for adjusted triangle vertices */ /**************************************************************************** * * Function: pix_start() * * Purpose: Initialize Pixar output file. */ char pix_file_name[150]; /* base picture name */ void pix_start() { char quadname[160]; /* quadrilateral file */ char response[120]; char *tptr; if ( pix_file_name[0] == 0 ) { prompt("Enter name of picture file: ",pix_file_name, sizeof(pix_file_name)-10); if ( commandfd == stdin ) { for ( normflag = -1 ; normflag == -1 ; ) { prompt("Do normal interpolation? ",response,sizeof(response)); switch(toupper(response[0])) { case 'Y': normflag = 1; break; case 'N': normflag = 0; break; case '0': kb_error(3223,"Pixar file aborted.\n",RECOVERABLE); default: outstring("Unintelligible response. Try again.\n"); } } prompt("Do inner, outer, or all surfaces? (i,o,a)",response,sizeof(response)); switch ( response[0] ) { case 'i' : innerflag = 1; outerflag = 0; break; case 'o' : innerflag = 0; outerflag = 1; break; default : innerflag = 1; outerflag = 1; break; } prompt("Do body colors? ",response,sizeof(response)); if ( toupper(response[0]) == 'Y' ) colorflag = 1; else colorflag = 0; if ( colorflag && ( strlen(cmapname) == 0 ) ) { prompt("Enter name of colormap file: ",cmapname,sizeof(cmapname)); if ( cmapname[0] == 0 ) { outstring("No colormap used.\n"); colorflag = 0; } } thickness = overall_size/1000; sprintf(msg,"Thicken(n | y [thickness(%g)])? ",(DOUBLE)thickness); prompt(msg,response,sizeof(response)); if ( toupper(response[0]) == 'Y' ) { thickenflag = 1; strtok(response," \t,;:\n"); tptr = strtok(NULL," \t;:,\n"); if ( tptr ) thickness = atof(tptr); } else thickenflag = 0; } } strcpy(quadname,pix_file_name); strcat(quadname,".quad"); pix_file_name[0] = 0; pfd = fopen(quadname,"w"); if (pfd == NULL) { perror(quadname); kb_error(3373,"Bad filename.\n",RECOVERABLE); } if ( normflag ) { fprintf(pfd,"CNPOLY"); } else { fprintf(pfd,"CPOLY"); } fprintf(pfd,"\n"); verts = dmatrix(0,2,0,2); } /************************************************************************ * * Function: pix_facet() * * Purpose: Accepts facets from graphgen() and plots them. */ void pix_facet(gdata,f_id) struct graphdata *gdata; facet_id f_id; { int i,j; REAL map[4]; /* color of this vertex */ int color = get_facet_color(f_id); if ( color == CLEAR ) return; if ( colormap ) for ( i = 0 ; i < 4 ; i++ ) map[i] = colormap[gdata[0].color][i]; else { for ( j = 0 ; j < 3 ; j++ ) map[j] = rgb_colors[color][j]; map[3] = facet_alpha; } if ( thickenflag ) for ( i = 0 ; i < FACET_VERTS ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) gdata[i].x[j] += gdata[i].norm[j]*thickness; for ( i = 0 ; i < FACET_VERTS ; i++ ) { fprintf(pfd,"%17.15f %17.15f %17.15f ",(DOUBLE)gdata[i].x[0], (DOUBLE)gdata[i].x[1],(DOUBLE)gdata[i].x[2]); if ( normflag ) { fprintf(pfd,"%5.3f %5.3f %5.3f ",(DOUBLE)gdata[i].norm[0], (DOUBLE)gdata[i].norm[1],(DOUBLE)gdata[i].norm[2]); } fprintf(pfd,"%5.3f %5.3f %5.3f %5.3f ", (DOUBLE)map[0],(DOUBLE)map[1],(DOUBLE)map[2],(DOUBLE)map[3]); } i = FACET_VERTS-1; /* repeat last point */ fprintf(pfd,"%17.15f %17.15f %17.15f ",(DOUBLE)gdata[i].x[0], (DOUBLE)gdata[i].x[1],(DOUBLE)gdata[i].x[2]); if ( normflag ) { fprintf(pfd,"%5.3f %5.3f %5.3f ",(DOUBLE)gdata[i].norm[0], (DOUBLE)gdata[i].norm[1],(DOUBLE)gdata[i].norm[2]); } fprintf(pfd,"%5.3f %5.3f %5.3f %5.3f ", (DOUBLE)map[0],(DOUBLE)map[1],(DOUBLE)map[2],(DOUBLE)map[3]); fprintf(pfd,"\n"); if ( thickenflag ) { /* do other orientation */ for ( i = 0 ; i < FACET_VERTS ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) gdata[i].x[j] -= gdata[i].norm[j]*2*thickness; for ( i = FACET_VERTS-1 ; i >= 0 ; i-- ) { fprintf(pfd,"%17.15f %17.15f %17.15f ",(DOUBLE)gdata[i].x[0], (DOUBLE)gdata[i].x[1],(DOUBLE)gdata[i].x[2]); if ( normflag ) { fprintf(pfd,"%5.3f %5.3f %5.3f ",-(DOUBLE)gdata[i].norm[0], -(DOUBLE)gdata[i].norm[1],-(DOUBLE)gdata[i].norm[2]); } fprintf(pfd,"%5.3f %5.3f %5.3f %5.3f ",(DOUBLE)map[0],(DOUBLE)map[1], (DOUBLE)map[2],(DOUBLE)map[3]); } i = 0; /* repeat last point */ fprintf(pfd,"%17.15f %17.15f %17.15f ",(DOUBLE)gdata[i].x[0], (DOUBLE)gdata[i].x[1],(DOUBLE)gdata[i].x[2]); if ( normflag ) { fprintf(pfd,"%5.3f %5.3f %5.3f ",-(DOUBLE)gdata[i].norm[0], -(DOUBLE)gdata[i].norm[1],-(DOUBLE)gdata[i].norm[2]); } fprintf(pfd,"%5.3f %5.3f %5.3f %5.3f ",(DOUBLE)map[0],(DOUBLE)map[1],(DOUBLE)map[2],(DOUBLE)map[3]); } /* end thickenflag */ fprintf(pfd,"\n"); } /******************************************************************* * * Function: pix_end() * * Purpose: Finish off Pixar file. * */ void pix_end() { fclose(pfd); free_matrix(verts); } evolver-2.30c.dfsg/src/symmetry.c0000644000175300017530000000727611410765113017266 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /************************************************************* /* /* file: symmetry.c /* /* Purpose: Functions dealing with symmetries of surface. /* Has the three functions for torus. /* If user wants to define his/her own symmetry /* group, put your functions in this file, /* and change names right below here. But leave /* torus functions intact, so torus domain can /* be used. /* */ #include "include.h" /* change torus stuff in following lines to your own names */ char *symmetry_name = "torus"; /* name appearing in datafile. lowercase! */ #ifdef NOPROTO void torus_wrap(); /* declare */ WRAPTYPE torus_compose(); /* declare */ WRAPTYPE torus_inverse(); /* declare */ void (*sym_func)() = torus_wrap; /* set global pointer to function */ WRAPTYPE (*sym_compose)() = torus_compose; /* set global pointer to function */ WRAPTYPE (*sym_inverse)() = torus_inverse; /* set global pointer to function */ #else void torus_wrap(REAL*,REAL*,WRAPTYPE); /* declare */ WRAPTYPE torus_compose(WRAPTYPE,WRAPTYPE); /* declare */ WRAPTYPE torus_inverse(WRAPTYPE); /* declare */ void (*sym_func)(REAL*,REAL*,WRAPTYPE) = torus_wrap; WRAPTYPE (*sym_compose)(WRAPTYPE,WRAPTYPE) = torus_compose; WRAPTYPE (*sym_inverse)(WRAPTYPE) = torus_inverse; #endif /* Group elements are encoded in three-bit fields in "wrap", one for each dimension, with "001" for positive wrap and "011" for negative wrap. You are free to encode any way you want, but use wrap = 0 for the identity. */ #define ALLWRAPMASK 03333333333 /******************************************************************* * * function: torus_wrap * * purpose: Provide adjusted coordinates for vertices that get * wrapped around torus. Serves as example for user-written * symmetry function. * * This function uses the values of the torus periods read * in from the data file. Yours doesn't have the privilege * of defining its own datafile syntax, but you can access * values of parameters defined in the datafile as * web.params[n].value. */ void torus_wrap(x,y,wrap) REAL *x; /* original coordinates */ REAL *y; /* wrapped coordinates */ WRAPTYPE wrap; /* encoded symmetry group element, 3 bits per dimension */ { int i,j; memcpy((char*)y,(char*)x,web.space_dimension*sizeof(REAL)); for ( i = 0 ; wrap != 0 ; i++, wrap >>= TWRAPBITS ) switch ( wrap & WRAPMASK ) { case 0: break; case POSWRAP: for ( j = 0 ; j < web.space_dimension ; j++ ) y[j] += web.torus_period[i][j]; break; case NEGWRAP: for ( j = 0 ; j < web.space_dimension ; j++ ) y[j] -= web.torus_period[i][j]; break; default: error("Unexpectedly large torus wrap.\n",WARNING); } } /******************************************************************** * * function: torus_compose() * * purpose: do composition of two group elements * */ WRAPTYPE torus_compose(wrap1,wrap2) WRAPTYPE wrap1,wrap2; /* the elements to compose */ { return (wrap1 + wrap2) & ALLWRAPMASK; } /******************************************************************** * * function: torus_inverse() * * purpose: return inverse of group element. * */ WRAPTYPE torus_inverse(wrap) WRAPTYPE wrap; /* the element invert */ { return ((~ALLWRAPMASK)-wrap) & ALLWRAPMASK; } evolver-2.30c.dfsg/src/modify.c0000644000175300017530000025536411410765113016667 0ustar hazelscthazelsct/************************************************************* * This file is part of the Surface Evolver source code. * * Programmer: Ken Brakke, brakke@susqu.edu * *************************************************************/ /****************************************************************** * * File: modify.c * * Contents: low-level triangulation modification routines: * * edge_divide() - subdivide an edge * cross_cut() - subdivide a facet * */ #include "include.h" /****************************************************************** * * Function: face_triangulate() * * Purpose: Triangulate a n-sided face by putting in central vertex. */ void face_triangulate(f_id,edgecount) facet_id f_id; int edgecount; { int i,k; vertex_id center; facetedge_id fe,pre_fe; vertex_id rimv; edge_id spoke; facetedge_id fe_in,fe_out,next_fe; REAL *centerx,*x; struct boundary *bdry; WRAPTYPE wrap = 0; facetedge_id first_fe; if ( web.modeltype == LAGRANGE ) kb_error(1237,"Can't face_triangulate() in Lagrange model.\n",RECOVERABLE); f_id = positive_id(f_id); /* axial points like positive orientation here */ /* put a new vertex in the center */ center = new_vertex(NULL,f_id); if ( get_fattr(f_id) & FIXED ) set_attr(center,FIXED); /* center coordinates are average of vertices */ centerx = get_coord(center); for ( i = 0 ; i < SDIM ; i++ ) centerx[i] = 0.0; fe = first_fe = get_facet_fe(f_id); if ( valid_id(fe) ) do { REAL w[MAXCOORD]; x = get_coord(get_fe_tailv(fe)); if ( web.symmetry_flag ) { (*sym_wrap)(x,w,wrap); x = w; wrap = (*sym_compose)(wrap,get_fe_wrap(fe)); } for ( i = 0 ; i < SDIM ; i++ ) centerx[i] += x[i]; fe = get_next_edge(fe); } while ( valid_id(fe) && !equal_id(fe,first_fe) ); for ( i = 0 ; i < SDIM ; i++ ) centerx[i] /= edgecount; /* find centerpoint parameters for facet on boundary */ if ( get_fattr(f_id) & BOUNDARY ) /* not working for torus */ { REAL defaultp[MAXCOORD]; REAL *paramb,*parammid,*xb; vertex_id base_v; REAL s[MAXCOORD]; REAL midp[MAXCOORD]; int no_interp = 0; int vercount = 0; set_attr(center,BOUNDARY); bdry = get_facet_boundary(f_id); set_boundary_num(center,bdry->num); for ( i = 0 ; i < bdry->pcount ; i++ ) midp[i] = 0.; /* center parameters extrapolate from a vertex */ /* try to find a vertex on same boundary */ base_v = NULLVERTEX; fe = first_fe = get_facet_fe(f_id); if ( valid_id(fe) ) do { base_v = get_fe_tailv(fe); if ( bdry == get_boundary(get_fe_tailv(fe)) ) { REAL *p = get_param(base_v); for ( i = 0 ; i < bdry->pcount ; i++ ) midp[i] += p[i]; } else if ( extra_bdry_attr && (*(int*)get_extra(base_v,extra_bdry_attr) == bdry->num) ) { REAL *p = (REAL*)get_extra(base_v,extra_bdry_param_attr); for ( i = 0 ; i < bdry->pcount ; i++ ) midp[i] += p[i]; } else no_interp = 1; vercount++; fe = get_next_edge(fe); } while ( valid_id(fe) && !equal_id(fe,first_fe) ); if ( valid_id(base_v) && (bdry == get_boundary(base_v)) ) { paramb = get_param(base_v); xb = get_coord(base_v); for ( i = 0 ; i < SDIM ; i++ ) s[i] = xb[i]; /* displacement vector */ } else { paramb = defaultp; for ( i = 0 ; i < bdry->pcount ; i++ ) defaultp[i] = 0.0; for ( i = 0 ; i < SDIM ; i++ ) s[i] = eval(bdry->coordf[i],defaultp,NULLID,NULL); sprintf(msg,"Could not find vertex on same boundary as facet %s.\n", ELNAME(f_id)); /* not sure if we want to output this or not */ } parammid = get_param(center); if ( (bdry->pcount < 2) && (vercount == 0) ) { for ( i = 0 ; i < bdry->pcount ; i++ ) parammid[i] = 0; for ( k = 0 ; k < SDIM ; k++ ) centerx[k] = eval(bdry->coordf[k],parammid,center,NULL); } if ( (bdry->pcount < 2) || (interp_bdry_param && !no_interp) ) { for ( i = 0 ; i < bdry->pcount ; i++ ) parammid[i] = midp[i]/vercount; for ( k = 0 ; k < SDIM ; k++ ) centerx[k] = eval(bdry->coordf[k],parammid,center,NULL); } else b_extrapolate(bdry,s,centerx,centerx,paramb,parammid,center); } /* install edge from rim to center */ fe = get_facet_fe(f_id); /* canonical starting point */ pre_fe = get_prev_edge(fe); rimv = get_fe_tailv(fe); spoke = new_edge(rimv,center,f_id); if ( get_fattr(f_id) & FIXED ) set_attr(spoke,FIXED); if ( get_fattr(f_id) & NO_REFINE ) set_attr(spoke,NO_REFINE); if ( web.symmetry_flag ) set_edge_wrap(spoke,0); fe_in = new_facetedge(f_id,spoke); set_edge_fe(spoke,fe_in); set_prev_edge(fe_in,pre_fe); set_next_edge(pre_fe,fe_in); fe_out = new_facetedge(f_id,edge_inverse(spoke)); set_prev_edge(fe_out,fe_in); set_next_edge(fe_in,fe_out); set_prev_edge(fe,fe_out); set_next_edge(fe_out,fe); set_next_facet(fe_in,fe_inverse(fe_out)); set_prev_facet(fe_in,fe_inverse(fe_out)); set_next_facet(fe_out,fe_inverse(fe_in)); set_prev_facet(fe_out,fe_inverse(fe_in)); if ( get_fattr(f_id) & BOUNDARY ) { set_attr(spoke,BOUNDARY); bdry = get_facet_boundary(f_id); set_edge_boundary_num(spoke,bdry->num); } else if ( get_fattr(f_id) & CONSTRAINT ) { ATTR attr = get_fattr(f_id) & (BDRY_ENERGY | BDRY_CONTENT | CONSTRAINT ); conmap_t * conmap = get_f_constraint_map(f_id); set_attr(spoke,attr); set_attr(center,attr); set_e_conmap(spoke,conmap); set_v_conmap(center,conmap); project_v_constr(center,ACTUAL_MOVE,RESET_ONESIDEDNESS); if ( web.modeltype == QUADRATIC ) { vertex_id mid = get_edge_midv(spoke); set_attr(mid,attr); set_v_conmap(mid,conmap); project_v_constr(mid,ACTUAL_MOVE,RESET_ONESIDEDNESS); } } /* now go around cutting off triangles */ fe_in = get_edge_fe(spoke); while ( !equal_id(get_next_edge(fe),fe_in) ) { next_fe = get_next_edge(fe); cross_cut(get_prev_edge(fe),fe); fe = next_fe; } if ( web.symmetry_flag ) /* check for axial vertices */ { fe = get_facet_fe(f_id); for ( i = 0 ; i < FACET_VERTS ; i++ ) { if ( get_vattr(get_fe_tailv(fe)) & AXIAL_POINT ) { set_facet_fe(f_id,fe); break; } fe = get_next_edge(fe); } } top_timestamp = ++global_timestamp; } /* end face_triangulate */ /***************************************************************** * * function: unstar() * * purpose: Remove center vertex from known starred triangle; * necessary for edge removal of one side. Checks that all * interior edges are valence 2. Does inner edge deletion * by hand rather than call eliminate_edge(). * * return: -1 failure, 0 unremovable star, 1 success */ int unstar(fe_a) facetedge_id fe_a; /* removable vertex is at tail */ { vertex_id v_id = get_fe_tailv(fe_a); facet_id fkeep = get_fe_facet(fe_a); facetedge_id fe_b = get_next_edge(fe_a); facetedge_id fe_c = get_next_edge(fe_b); facetedge_id fe_d = get_next_facet(fe_c); facetedge_id fe_e = get_next_edge(fe_d); facetedge_id fe_f = get_next_edge(fe_e); facetedge_id fe_g = get_next_facet(fe_e); facetedge_id fe_h = get_next_edge(fe_g); facetedge_id fe_i = get_next_edge(fe_h); facet_id f_id; body_id b_id; if ( verbose_flag ) { sprintf(msg,"Unstarring vertex %s\n",ELNAME(v_id)); outstring(msg); } /* some checks on legality */ if ( get_vattr(v_id) & FIXED ) /* fixed vertex */ { if ( verbose_flag ) { sprintf(msg,"Unstarring failed, vertex %s is fixed.\n", ELNAME(v_id),get_vertex_evalence(v_id)); outstring(msg); } return -1; } if ( get_vertex_evalence(v_id) != 3 ) /* not lonesome star */ { if ( verbose_flag ) { sprintf(msg,"Unstarring failed, vertex %s has valence %d\n", ELNAME(v_id),get_vertex_evalence(v_id)); outstring(msg); } return -1; } if ( !equal_id(fe_c,get_next_facet(fe_d)) ) { if ( verbose_flag ) { sprintf(msg,"Unstarring failed, triple valence edge %s\n", ELNAME(get_fe_edge(fe_c))); outstring(msg); } return -1; } if ( !equal_id(fe_e,get_next_facet(fe_g)) ) { if ( verbose_flag ) { sprintf(msg,"Unstarring failed, triple valence edge %s\n", ELNAME(get_fe_edge(fe_e))); outstring(msg); } return -1; } if ( !equal_id(inverse_id(fe_a),get_next_facet(fe_i)) ) { if ( verbose_flag ) { sprintf(msg,"Unstarring failed, triple valence edge %s\n", ELNAME(get_fe_edge(fe_a))); outstring(msg); } return -1; } if ( !equal_id(inverse_id(fe_a),get_prev_facet(fe_i)) ) { if ( verbose_flag ) { sprintf(msg,"Unstarring failed, triple valence edge %s\n", ELNAME(get_fe_edge(fe_a))); outstring(msg); } return -1; } /* fix up body links */ f_id = get_fe_facet(fe_e); b_id = get_facet_body(f_id); if ( valid_id(b_id) ) set_body_facet(b_id,f_id); b_id = get_facet_body(inverse_id(f_id)); if ( valid_id(b_id) ) set_body_facet(b_id,inverse_id(f_id)); /* fix up fe's around edges of new big facet */ set_fe_facet(fe_f,inverse_id(fkeep)); set_fe_facet(fe_h,fkeep); set_facet_fe(fkeep,fe_b); set_next_edge(fe_b,inverse_id(fe_f)); set_next_edge(fe_f,inverse_id(fe_b)); set_prev_edge(fe_f,inverse_id(fe_h)); set_prev_edge(fe_h,inverse_id(fe_f)); set_next_edge(fe_h,fe_b); set_prev_edge(fe_b,fe_h); /* discard */ free_element(get_fe_facet(fe_e)); free_element(get_fe_facet(fe_i)); free_element(get_fe_edge(fe_a)); free_element(get_fe_edge(fe_d)); free_element(get_fe_edge(fe_e)); free_element(fe_a); free_element(fe_c); free_element(fe_d); free_element(fe_e); free_element(fe_g); free_element(fe_i); free_element(v_id); top_timestamp = ++global_timestamp; return 1; } /* end unstar() */ /************************************************************************ * * function: simple_unstar() * * Purpose: Test and unstar facet next to an edge set for deletion. * May unstar multiple times if necessary. * * Return: -1 for failure, 1 for success */ int simple_unstar(facetedge_id base_fe) { facetedge_id aa,bb; facetedge_id a=0; /* for edge to be merged */ facetedge_id b=0; /* for edge to be merged with */ facetedge_id a_next=0,b_next=0; /* facet chain links around edges a,b */ edge_id a_edge=0; /* edge of side a */ edge_id b_edge=0; /* edge of side b */ facet_id facet; /* facet to be eliminated */ int retval = 1; /* check we really have a facet */ facet = get_fe_facet(base_fe); if ( !valid_id(facet) ) return 1; /* label relevant edges and */ /* see if we have an adjacent starred triangle which will give trouble if we don't unstar it */ for(;;) /* may take multiple passes for total unstarring */ { int unstar_count = 0; a = get_next_edge(base_fe); a_edge = get_fe_edge(a); if ( get_vattr(get_edge_headv(a_edge)) & AXIAL_POINT ) return 1; a_next = get_next_facet(a); b = get_prev_edge(base_fe); b_edge = get_fe_edge(b); b_next = get_next_facet(b); if ( equal_element(a_edge,b_edge) ) return 1; aa = get_next_edge(a_next); bb = get_prev_edge(b_next); if ( !equal_id(get_next_facet(aa),inverse_id(bb)) ) return 1; /* no star */ retval = unstar(aa); if ( retval < 0 ) { sprintf(errmsg, "Edge %s not deleted due to adjacent configuration involving facet %s.\n", ELNAME(get_fe_edge(base_fe)),ELNAME1(facet)); kb_error(1903,errmsg,WARNING); return -1; } unstar_count++; } /* end for loop */ } /* end simple_unstar() */ /********************************************************************* * * function: star_finagle() * * Purpose: Clean up common-endpoint edges coming off an edge that * weren't taken care of by unstar() since the interior is * more complicated than a star. * Does this by refining common endpoint edges that aren't * on a facet adjacent to the eliminatable edge. * * return: 1 if */ struct finagle { edge_id e_id; vertex_id v_id; }; int finagle_comp(a,b) struct finagle *a,*b; { if ( a->v_id < b->v_id ) return -1; if ( a->v_id > b->v_id ) return 1; if ( a->e_id < b->e_id ) return -1; if ( a->e_id > b->e_id ) return 1; return 0; } int star_finagle(e_id) edge_id e_id; { struct finagle tail_edges[100]; /* tails into e_id tail */ int tail_valence = 0; struct finagle head_edges[100]; /* heads into e_id head */ int head_valence = 0; int i,j; edge_id ee_id; int keeps; facetedge_id start_fe,fe_id; REAL side1[MAXCOORD],side2[MAXCOORD],side3[MAXCOORD]; REAL aa,bb,cc,ab,ac,bc; /* get edge lists from the two vertices */ ee_id = e_id; do { ee_id = get_next_tail_edge(ee_id); if ( !equal_id(e_id,ee_id) && tail_valence < 99 ) { tail_edges[tail_valence].e_id = ee_id; tail_edges[tail_valence++].v_id = get_edge_headv(ee_id); } } while ( !equal_id(e_id,ee_id) ); ee_id = e_id; do { ee_id = get_next_head_edge(ee_id); if ( !equal_id(e_id,ee_id) && head_valence < 99 ) { head_edges[head_valence].e_id = ee_id; head_edges[head_valence++].v_id = get_edge_tailv(ee_id); } } while ( !equal_id(e_id,ee_id) ); /* sort by other endpoint */ qsort(tail_edges,tail_valence,sizeof(struct finagle),FCAST finagle_comp); qsort(head_edges,head_valence,sizeof(struct finagle),FCAST finagle_comp); /* keep only pairs of edges with same endpoints */ for ( i = j = keeps = 0 ; i < tail_valence && j < head_valence ; ) { if ( tail_edges[i].v_id < head_edges[j].v_id ) while ( tail_edges[i].v_id < head_edges[j].v_id ) i++; else while ( tail_edges[i].v_id > head_edges[j].v_id ) j++; if ( (i >= tail_valence) || (j >= head_valence) ) break; if ( tail_edges[i].v_id == head_edges[j].v_id ) { /* skip if nonzero wrap */ if ( web.symmetry_flag ) { WRAPTYPE w1 = get_edge_wrap(inverse_id(tail_edges[i].e_id)); WRAPTYPE w2 = get_edge_wrap(e_id); WRAPTYPE w3 = get_edge_wrap(head_edges[j].e_id); WRAPTYPE netwrap = (sym_compose)(w1,w2); netwrap = (*sym_compose)(netwrap,w3); if ( netwrap != 0 ) { i++; j++; continue; } } tail_edges[keeps] = tail_edges[i]; i++; head_edges[keeps] = head_edges[j]; j++; keeps++; } } tail_valence = head_valence = keeps; /* remove edges that are facet-adjacent to elim edge */ start_fe = fe_id = get_edge_fe(e_id); do { edge_id e_a = get_fe_edge(get_prev_edge(fe_id)); edge_id e_b = get_fe_edge(get_next_edge(fe_id)); for ( i = 0 ; i < tail_valence ; i++ ) { if ( equal_element(e_a,tail_edges[i].e_id) ) { tail_edges[i] = tail_edges[--tail_valence]; break; } } for ( i = 0 ; i < head_valence ; i++ ) { if ( equal_element(e_b,head_edges[i].e_id) ) { head_edges[i] = head_edges[--head_valence]; break; } } fe_id = get_next_facet(fe_id); } while ( !equal_id(fe_id,start_fe) ); if ( head_valence != tail_valence ) { sprintf(errmsg,"Internal error: star_finagle() edge %s: head_valence %d != tail_valence %d\n", ELNAME(e_id),head_valence,tail_valence); kb_error(3982,errmsg,RECOVERABLE); } /* Remove edges that have facets at large angle from */ /* triangle plane, indicating neck pinch */ get_edge_side(e_id,side1); aa = dot(side1,side1,SDIM); for ( i = 0 ; i < head_valence ; i++ ) { int flatflag = 0; /* calculate triangle normal */ get_edge_side(tail_edges[i].e_id,side2); ab = dot(side1,side2,SDIM); start_fe = fe_id = get_edge_fe(tail_edges[i].e_id); for ( j = 0 ; j < 2 ; j++ ) /* check both head and tail edges */ { do { REAL numer,denom; facetedge_id ffe = inverse_id(get_prev_edge(fe_id)); edge_id ee_id = get_fe_edge(ffe); get_edge_side(ee_id,side3); ac = dot(side1,side3,SDIM); bb = dot(side2,side2,SDIM); bc = dot(side2,side3,SDIM); cc = dot(side3,side3,SDIM); numer = (j?-1:1)*(ac*bb - ab*bc); denom = sqrt(aa*bb-ab*ab)*sqrt(bb*cc-bc*bc); if ( numer > .8*denom ) flatflag = 1; fe_id = get_next_facet(fe_id); } while ( !equal_id(fe_id,start_fe) ); /* set up to try the head side */ start_fe = fe_id = inverse_id(get_edge_fe(head_edges[i].e_id)); } if ( !flatflag ) { /* probably a neck, so let it be deleted */ head_edges[i] = head_edges[--head_valence]; tail_edges[i] = tail_edges[--tail_valence]; i--; } } /* refine remaining edges */ for ( i = 0 ; i < tail_valence ; i++ ) edge_refine(tail_edges[i].e_id); for ( i = 0 ; i < head_valence ; i++ ) edge_refine(head_edges[i].e_id); return tail_valence; } /* end star_finagle */ /********************************************************* * * edge_divide() * * Purpose: Subdivide an edge in the first stage of * refinement. Marks the new vertex with * the NEWVERTEX attribute, and both new edges * with the NEWEDGE attribute, so they can be * properly skipped during refinement. * Also sets FIXED attribute bit of new vertex and * edge if the old edge is FIXED. * * Input: edge_id e_id - the edge to subdivide * * Output: New legal configuration with new vertex. * New edge is head half of old edge. * * Return: ID of new edge. */ edge_id edge_divide(e_id) edge_id e_id; { REAL s[MAXCOORD],*t,*mu=NULL,*mv,*h,m[MAXCOORD],q1[MAXCOORD],q3[MAXCOORD]; edge_id new_e; vertex_id divider=NULLID,old_mid=0,new_mid=0,headv,tailv; int i,j,k,n; facetedge_id new_fe,old_fe; int wrap = 0,wrap1=0,wrap2=0; REAL w[MAXCOORD]; vertex_id *oldv=NULL,*newv; REAL *oldx[MAXLAGRANGE+1]; REAL prod1[MAXLAGRANGE+1],prod2; facetedge_id first_fe; vertex_id allv[2*MAXLAGRANGE+1]; if ( extra_bdry_attr && !extra_bdry_param_attr ) kb_error(2841, "extra_boundary attribute defined but not extra_boundary_param.\n", RECOVERABLE); if ( !extra_bdry_attr && extra_bdry_param_attr ) kb_error(3367, "extra_boundary_param attribute defined but not extra_boundary.\n", RECOVERABLE); if ( !valid_element(e_id) ) return NULLID; if ( web.representation == SIMPLEX ) kb_error(1239,"Edge divide not implemented for simplex representation.\n", COMMAND_ERROR); if ( verbose_flag ) { sprintf(msg,"Refining edge %s\n",ELNAME(e_id)); outstring(msg); } headv = get_edge_headv(e_id); tailv = get_edge_tailv(e_id); t = get_coord(tailv); h = get_coord(headv); if ( web.symmetry_flag ) { wrap = get_edge_wrap(e_id); /* always use tail as base, for predictability */ if ( wrap ) /* see which endpoint closer to origin, to use as base */ { /* use tail as base */ (*sym_wrap)(get_coord(headv),w,wrap); h = w; wrap1 = 0; wrap2 = wrap; } else wrap1 = wrap2 = 0; } if ( web.modeltype == LINEAR ) { for ( k = 0 ; k < SDIM ; k++ ) m[k] = (t[k] + h[k])/2; divider = new_vertex(m,e_id); set_attr(divider,get_eattr(e_id) & (FIXED|BARE_NAKED)); } else if ( web.modeltype == QUADRATIC ) { divider = get_edge_midv(e_id); /* use old midpoint */ unset_attr(divider,Q_MIDPOINT); set_vertex_edge(divider,NULLID); /* so set_vertex_edge works */ /* get coordinates of new midpoint(s) */ mu = get_coord(divider); if ( circular_arc_flag && (SDIM == 2)) { /* using facts that midpoint is on perpendicular bisector and inversions with tail center lie on a straight line */ REAL x1 = mu[0] - t[0]; REAL y1 = mu[1] - t[1]; REAL x2 = h[0] - t[0]; REAL y2 = h[1] - t[1]; REAL det = x2*y1 - y2*x1; REAL xp = det > 0 ? -y1 : y1; REAL yp = det > 0 ? x1 : -x1; REAL rr1 = x1*x1 + y1*y1; REAL rr2 = x2*x2 + y2*y2; REAL xx = x2/rr2 - x1/rr1; REAL yy = y2/rr2 - y1/rr1; REAL a = -x1*yy + y1*xx; /* lambda^2 coeff */ REAL b = xp*yy - yp*xx; /* lambda coeff */ REAL c = x1/4*yy - y1/4*xx; /* const coeff */ REAL lambda = (fabs(c) < fabs(b)/100000) ? ( (b > 0.0) ? (-2*c/(b + sqrt(b*b-4*a*c))) : (2*c/(-b + sqrt(b*b-4*a*c))) ) : (( c > 0 ) ? (2*c/(-b + sqrt(b*b-4*a*c))) : (-2*c/(b + sqrt(b*b-4*a*c)))) ; q1[0] = t[0] + x1/2 + lambda*xp; q1[1] = t[1] + y1/2 + lambda*yp; /* turn it all around for other midpoint */ x1 = mu[0] - h[0]; y1 = mu[1] - h[1]; x2 = t[0] - h[0]; y2 = t[1] - h[1]; det = x2*y1 - y2*x1; xp = det > 0 ? -y1 : y1; yp = det > 0 ? x1 : -x1; rr1 = x1*x1 + y1*y1; rr2 = x2*x2 + y2*y2; xx = x2/rr2 - x1/rr1; yy = y2/rr2 - y1/rr1; a = -x1*yy + y1*xx; /* lambda^2 coeff */ b = xp*yy - yp*xx; /* lambda coeff */ c = x1/4*yy - y1/4*xx; /* const coeff */ lambda = (fabs(c) < fabs(b)/100000) ? ( (b > 0.0) ? (-2*c/(b + sqrt(b*b-4*a*c))) : (2*c/(-b + sqrt(b*b-4*a*c))) ) : (( c > 0 ) ? (2*c/(-b + sqrt(b*b-4*a*c))) : (-2*c/(b + sqrt(b*b-4*a*c)))) ; q3[0] = h[0] + x1/2 + lambda*xp; q3[1] = h[1] + y1/2 + lambda*yp; } else { for ( k = 0 ; k < SDIM ; k++ ) { q1[k] = 0.375*t[k] + 0.75*mu[k] - 0.125*h[k]; m[k] = mu[k]; q3[k] = 0.375*h[k] + 0.75*mu[k] - 0.125*t[k]; } } if ( wrap1 ) { (*sym_wrap)(q1,w,wrap1); for ( k = 0 ; k < SDIM ; k++ ) q1[k] = w[k]; } } else if ( web.modeltype == LAGRANGE ) { oldv = get_edge_vertices(e_id); /* get old vertex coordinates */ for ( i = 0 ; i <= web.lagrange_order ; i++ ) { if ( i < web.lagrange_order ) oldx[i] = get_coord(oldv[i]); else oldx[i] = h; /* in case of unwrapping */ for ( j = 0, prod1[i] = 1.0 ; j <= web.lagrange_order ; j++ ) if ( i != j ) prod1[i] *= i-j; } /* figure out divider vertex */ for ( k = 0 ; k < SDIM ; k++ ) m[k] = (t[k] + h[k])/2; if ( web.lagrange_order & 1 ) /* odd order */ { divider = new_vertex(m,e_id); set_attr(divider,get_eattr(e_id) & (FIXED|BARE_NAKED)); } else /* even order */ { divider = oldv[web.lagrange_order/2]; unset_attr(divider,Q_MIDEDGE); set_vertex_edge(divider,NULLID); /* so set_vertex_edge works */ oldv[web.lagrange_order/2] = new_vertex(NULL,e_id); } } /* make refine() work properly */ set_attr(divider,NEWVERTEX); remove_vertex_edge(headv,inverse_id(e_id)); new_e = dup_edge(e_id); insert_vertex_edge(headv,inverse_id(new_e)); set_edge_tailv(new_e,divider); set_edge_headv(e_id,divider); set_attr(new_e,NEWEDGE | get_eattr(e_id)); if ( web.symmetry_flag ) { set_edge_wrap(e_id,wrap1); /* new vertex in same unit cell as tail */ set_edge_wrap(new_e,wrap2); } if ( (web.modeltype == LINEAR) && web.metric_flag ) { /* get midpoint in metric middle */ REAL front,rear; /* edge lengths */ REAL tt[MAXCOORD],hh[MAXCOORD]; REAL *xm = get_coord(divider); for(;;) /* binary search for reasonable endpoint */ { calc_edge(e_id); rear = get_edge_length(e_id); calc_edge(new_e); front = get_edge_length(new_e); if ( rear < 0.8*front ) { /* bisect high end */ t = tt; for ( i = 0 ; i < SDIM ; i++ ) { t[i] = xm[i]; xm[i] = (t[i] + h[i])/2; } } else if ( rear > 1.2*front ) { /* bisect low end */ h = hh; for ( i = 0 ; i < SDIM ; i++ ) { h[i] = xm[i]; xm[i] = (t[i] + h[i])/2; } } else break; } } if ( web.modeltype == QUADRATIC ) { /* new midpoint for old edge */ old_mid = new_vertex(q1,e_id); set_edge_midv(e_id,old_mid); set_attr(old_mid,get_eattr(e_id) & (FIXED|BARE_NAKED)); /* set up midpoint of new edge */ new_mid = get_edge_midv(new_e); set_attr(new_mid,get_eattr(e_id) & (FIXED|BARE_NAKED)); mv = get_coord(new_mid); for ( k = 0 ; k < SDIM ; k++ ) mv[k] = q3[k]; } else if ( web.modeltype == LAGRANGE ) { /* calculate new vertex coordinates */ /* get vertices in right order */ newv = get_edge_vertices(new_e); for ( n = 0 ; n < web.lagrange_order ; n++ ) { allv[2*n] = oldv[n]; allv[2*n+1] = newv[n]; } allv[2*n] = newv[n]; /* last one */ /* get divider back into order */ allv[1] = allv[web.lagrange_order]; allv[web.lagrange_order] = divider; for ( n = 1 ; n < web.lagrange_order ; n++ ) { oldv[n] = allv[n]; newv[n] = allv[web.lagrange_order + n]; set_vertex_edge(oldv[n],e_id); set_vertex_edge(newv[n],new_e); set_attr(oldv[n],Q_MIDEDGE); set_attr(newv[n],Q_MIDEDGE); } /* set coordinates of new vertices */ if ( bezier_flag ) { /* have to change all control points */ MAT2D(oldc,MAXLAGRANGE+1,MAXCOORD); REAL *newx[2*MAXLAGRANGE+1]; for ( i = 0 ; i <= web.lagrange_order ; i++ ) for ( j = 0 ; j < SDIM ; j++ ) oldc[i][j] = oldx[i][j]; for ( i = 0 ; i <= 2*web.lagrange_order ; i++ ) newx[i] = get_coord(allv[i]); mat_mult(bezier_refine_1d[web.lagrange_order],oldc,newx, 2*web.lagrange_order+1,web.lagrange_order+1,SDIM); } else for ( n = 0 ; n < web.lagrange_order ; n++ ) { REAL coeff; REAL x; REAL *newx = get_coord(allv[2*n+1]); x = n + 0.5; for ( j = 0, prod2 = 1.0 ; j <= web.lagrange_order ; j++ ) prod2 *= x - j; for ( k = 0 ; k < SDIM ; k++ ) newx[k] = 0.0; for ( i = 0 ; i <= web.lagrange_order ; i++ ) { coeff = prod2/(x-i)/prod1[i]; for ( k = 0 ; k < SDIM ; k++ ) newx[k] += coeff*oldx[i][k]; } } } /* for free boundary edges, cannot just interpolate parameters due to wrap-around of periodic parameters. So tangent extrapolate from one endpoint. */ if ( get_eattr(e_id) & BOUNDARY ) { struct boundary *bdry; REAL *parammid; REAL defaultp[MAXCOORD]; REAL *parama=NULL; /* tail */ REAL *paramb=NULL; /* head */ bdry = get_edge_boundary(e_id); set_edge_boundary_num(new_e,bdry->num); if ( get_boundary(tailv) == bdry ) parama = get_param(tailv); else if ( extra_bdry_attr && extra_bdry_param_attr) { int bnum = *(int*)get_extra(tailv,extra_bdry_attr); if ( bnum == bdry->num ) parama = (REAL*)get_extra(tailv,extra_bdry_param_attr); } if ( get_boundary(headv) == bdry ) paramb = get_param(headv); else if ( extra_bdry_attr && extra_bdry_param_attr && (*(int*)get_extra(headv,extra_bdry_attr) == bdry->num) ) paramb = (REAL*)get_extra(headv,extra_bdry_param_attr); if ( (web.modeltype == LINEAR) || ((web.modeltype==LAGRANGE) && (web.lagrange_order == 1)) ) { set_attr(divider,BOUNDARY); set_boundary_num(divider,bdry->num); /* find parameters of new midpoint */ mv = get_coord(divider); parammid = get_param(divider); if ( interp_bdry_param && parama && paramb ) { for ( i = 0 ; i < bdry->pcount ; i++ ) parammid[i] = (parama[i] + paramb[i])/2; } else if ( !parama && !paramb ) { sprintf(errmsg, "Vertices %s and %s are on different boundaries from edge %s .\n", ELNAME(headv),ELNAME1(tailv),ELNAME2(e_id)); kb_error(1240,errmsg,WARNING); paramb = defaultp; defaultp[0] = defaultp[1] = defaultp[2] = 0.1; for ( i = 0 ; i < SDIM ; i++ ) s[i] = eval(bdry->coordf[i],defaultp,NULLID,NULL); mu = s; /* projecting on tangent */ b_extrapolate(bdry,mu,mv,mv,paramb,parammid,NULLID); } else #ifdef PARAMAVG if ( (get_boundary(headv) == bdry) && (get_boundary(tailv) == bdry) ) { mu = get_coord(tailv); parama = get_param(tailv); paramb = get_param(headv); /* projecting on tangent */ b_extrapolate(bdry,mu,mv,mv,parama,parammid,tailv); /* if not wrapped, take average parameter */ for ( i = 0 ; i < bdry->pcount ; i++ ) { if ( ((parama[i] < parammid[i]) && (parammid[i] < paramb[i])) || ((parama[i] > parammid[i]) && (parammid[i] > paramb[i]))) parammid[i] = (parama[i] + paramb[i])/2; } } else #endif if ( (get_boundary(headv) == bdry) && !wrap2 ) { mu = get_coord(headv); paramb = get_param(headv); /* projecting on tangent */ b_extrapolate(bdry,mu,mv,mv,paramb,parammid,headv); } else { mu = get_coord(tailv); paramb = get_param(tailv); /* projecting on tangent */ b_extrapolate(bdry,mu,mv,mv,paramb,parammid,tailv); } } /* end linear */ else if ( web.modeltype == QUADRATIC ) { REAL *paramc; set_attr(old_mid,BOUNDARY); set_boundary_num(old_mid,bdry->num); set_attr(new_mid,BOUNDARY); set_boundary_num(new_mid,bdry->num); paramc = get_param(divider); if ( interp_bdry_param && parama ) { parammid = get_param(old_mid); for ( i = 0 ; i < bdry->pcount ; i++ ) parammid[i] = (parama[i] + paramc[i])/2; } else { mu = get_coord(divider); /* find new parameters of old edge midpoint */ /* projecting on tangent */ parammid = get_param(old_mid); mv = get_coord(old_mid); b_extrapolate(bdry,mu,mv,mv,paramc,parammid,old_mid); } if ( interp_bdry_param && paramb ) { parammid = get_param(new_mid); for ( i = 0 ; i < bdry->pcount ; i++ ) parammid[i] = (paramb[i] + paramc[i])/2; } else { /* find parameters of new edge midpoint */ /* projecting on tangent */ parammid = get_param(new_mid); mv = get_coord(new_mid); b_extrapolate(bdry,mu,mv,mv,paramc,parammid,new_mid); } } else if ( web.modeltype == LAGRANGE ) { set_attr(divider,BOUNDARY); set_boundary_num(divider,bdry->num); oldv = get_edge_vertices(e_id); newv = get_edge_vertices(new_e); /* calculate new parameter values */ if ( interp_bdry_param && parama && paramb ) { for ( i = 1; i < 2*web.lagrange_order ; i += 2 ) /* tail half */ { REAL *pa = (i==1) ? parama : get_param(allv[i-1]); REAL *pb = (i==2*web.lagrange_order-1)? get_param(allv[1]) : paramb; REAL *p = get_param(allv[i]); for ( k = 0 ; k < bdry->pcount ; k++ ) p[k] = (pa[k] + pb[k])/2; } } else /* interpolate */ { oldv = get_edge_vertices(e_id); for ( i = 1; i < 2*web.lagrange_order ; i += 2 ) { REAL *pa = get_param(allv[i-1]); REAL *p = get_param(allv[i]); mu = get_coord(allv[i-1]); mv = get_coord(allv[i]); b_extrapolate(bdry,mu,mv,mv,pa,p,allv[i]); } } } } else { ATTR attr = get_eattr(e_id) & (BDRY_ENERGY | BDRY_CONTENT | CONSTRAINT ); conmap_t * conmap = get_e_constraint_map(e_id); set_attr(new_e,attr); set_attr(divider,attr); if ( conmap || attr ) { set_e_conmap(new_e,conmap); set_v_conmap(divider,conmap); project_v_constr(divider,ACTUAL_MOVE,RESET_ONESIDEDNESS); if ( web.modeltype == QUADRATIC ) { set_attr(old_mid,attr); set_attr(new_mid,attr); clear_v_conmap(old_mid); clear_v_conmap(new_mid); set_v_conmap(old_mid,conmap); set_v_conmap(new_mid,conmap); project_v_constr(old_mid,ACTUAL_MOVE,RESET_ONESIDEDNESS); project_v_constr(new_mid,ACTUAL_MOVE,RESET_ONESIDEDNESS); } /* this should have been taken care of for Lagrange in dup_edge */ } } old_fe = first_fe = get_edge_fe(e_id); new_fe = NULLID; if ( valid_id(old_fe) ) do { /* create new facetedge and splice into edge net */ facetedge_id next; new_fe = new_facetedge(get_fe_facet(old_fe),new_e); set_next_edge(new_fe,get_next_edge(old_fe)); next = get_next_edge(old_fe); if ( valid_id(next) ) set_prev_edge(get_next_edge(old_fe),new_fe); set_next_edge(old_fe,new_fe); set_prev_edge(new_fe,old_fe); if ( web.representation == STRING ) /* keep facet fe at start of arc */ { facet_id ff_id = get_fe_facet(old_fe); if ( inverted(ff_id) && equal_id(old_fe,get_facet_fe(ff_id)) ) set_facet_fe(ff_id,new_fe); } old_fe = get_next_facet(old_fe); } while ( valid_id(old_fe) && !equal_id(old_fe,first_fe) ); set_edge_fe(new_e,new_fe); old_fe = first_fe = get_edge_fe(e_id); if ( valid_id(old_fe) ) do { /* copy over facet chain */ facetedge_id fe,nfe; fe = get_next_edge(old_fe); nfe = get_next_facet(old_fe); set_next_facet(fe,get_next_edge(nfe)); nfe = get_prev_facet(old_fe); set_prev_facet(fe,get_next_edge(nfe)); old_fe = get_next_facet(old_fe); } while ( valid_id(old_fe) && !equal_id(old_fe,first_fe) ); /* check for dangling ends with loopback fe */ new_fe = first_fe = get_edge_fe(new_e); if ( valid_id(new_fe) ) do { if ( valid_id(get_next_edge(new_fe)) ) if ( !equal_id(get_prev_edge(get_next_edge(new_fe)),new_fe ) ) set_next_edge(new_fe,get_prev_edge(get_next_edge(new_fe))); new_fe = get_next_facet(new_fe); } while ( valid_id(new_fe) &&!equal_id(new_fe,first_fe) ); #ifdef MPI_EVOLVER { unsigned short *nbr = mpi_export_eattr_ptr(e_id); for (i = 0 ; i < MPI_EXPORT_MAX ; i++ ) { if ( nbr[i] == 0 ) break; if ( nbr[i] != this_task ) mpi_refine_edge(e_id,divider,new_e,(int)nbr[i]); } } #endif top_timestamp = ++global_timestamp; return new_e; } /* end edge_divide() */ /************************************************************ * * cross_cut() * * Purpose: subdivides a facet into two facets by introducing * a new edge and a new facet. * * Inputs: facetedge_id first_fe - first in chain defining new facet * facetidge_id last_fe - last in chain; * * Output: new facet created with boundary first_fe,chain,last_fe,newedge * also both facets marked with NEWFACET attribute, so they * can be left alone during a refinement. * */ void cross_cut(first_fe,last_fe) facetedge_id first_fe,last_fe; { facet_id old_f,new_f; edge_id new_e; facetedge_id fe,new_fe_new,new_fe_old; int wrap; ATTR attr; vertex_id headv,tailv; int i,k; old_f = get_fe_facet(first_fe); attr = get_fattr(old_f); tailv = get_fe_headv(last_fe); headv = get_fe_tailv(first_fe); if ( get_vattr(headv) & AXIAL_POINT ) new_e = inverse_id(new_edge(headv,tailv,old_f)); else new_e = new_edge(tailv,headv,old_f); if ( attr & FIXED ) { set_attr(new_e,FIXED); if ( web.modeltype == QUADRATIC ) set_attr(get_edge_midv(new_e),FIXED); else if ( web.modeltype == LAGRANGE ) { vertex_id *v = get_edge_vertices(new_e); for ( i = 1 ; i < web.lagrange_order ; i++ ) set_attr(v[i],FIXED); } } if ( attr & NO_REFINE ) set_attr(new_e,NO_REFINE); /* for QUADRATIC model, midpoint of new edge is left as the linear interpolation given by new_edge() since triangular facets may not yet be established yet */ new_f = dup_facet(old_f); /* copy facet data */ /* if ( inverted(old_f) ) invert(new_f);*/ /* same orientation */ set_attr(old_f,NEWFACET); new_fe_new = new_facetedge(new_f,new_e); new_fe_old = new_facetedge(old_f,edge_inverse(new_e)); set_edge_fe(new_e,new_fe_new); set_facet_body(new_f,get_facet_body(old_f)); set_facet_body(facet_inverse(new_f),get_facet_body(facet_inverse(old_f))); if ( phase_flag ) set_f_phase_density(new_f); set_facet_fe(new_f,last_fe); /* preserves starting point of a facet */ set_facet_fe(old_f,new_fe_old); /* install new facet into its facet-edges */ /* and set torus wrap flags if needed */ fe = first_fe; wrap = 0; for(;;) { set_fe_facet(fe,new_f); if ( web.symmetry_flag ) wrap = (*sym_compose)(wrap,get_fe_wrap(fe)); if ( equal_id(fe,last_fe) ) break; fe = get_next_edge(fe); } if ( wrap ) { set_edge_wrap(new_e,(*sym_inverse)(wrap)); if ( web.modeltype == QUADRATIC ) { /* have to adjust coordinates of midpoint */ REAL temp[MAXCOORD]; REAL *mv,*tv,*hv; tv = get_coord(tailv); mv = get_coord(get_edge_midv(new_e)); hv = get_coord(headv); (*sym_wrap)(hv,temp,(*sym_inverse)(wrap)); for ( k = 0 ; k < SDIM ; k++ ) mv[k] = (tv[k] + temp[k])/2; } } /* link up facet-edges */ set_next_edge(get_prev_edge(first_fe),new_fe_old); set_prev_edge(new_fe_old,get_prev_edge(first_fe)); set_prev_edge(get_next_edge(last_fe),new_fe_old); set_next_edge(new_fe_old,get_next_edge(last_fe)); set_prev_edge(first_fe,new_fe_new); set_next_edge(new_fe_new,first_fe); set_next_edge(last_fe,new_fe_new); set_prev_edge(new_fe_new,last_fe); set_next_facet(new_fe_new,fe_inverse(new_fe_old)); set_prev_facet(new_fe_new,fe_inverse(new_fe_old)); set_next_facet(new_fe_old,fe_inverse(new_fe_new)); set_prev_facet(new_fe_old,fe_inverse(new_fe_new)); if ( attr & BOUNDARY ) { struct boundary *bdry = get_facet_boundary(old_f); REAL *paramb,*parammid; REAL *mu,*mv; set_attr(new_e,BOUNDARY); set_edge_boundary_num(new_e,bdry->num); if ( web.modeltype == QUADRATIC ) { vertex_id divider = get_edge_midv(new_e); set_attr(divider,BOUNDARY); set_boundary_num(divider,bdry->num); /* find parameters of new midpoint */ mv = get_coord(divider); parammid = get_param(divider); if ( (get_boundary(headv) != bdry) && (get_boundary(tailv) != bdry) ) { sprintf(errmsg, "Vertices %s and %s are on different boundaries from edge %s.\n", ELNAME(headv),ELNAME1(tailv),ELNAME2(new_e)); kb_error(1242,errmsg,RECOVERABLE); } else #ifdef PARAMAVG if ( (get_boundary(headv) == bdry) && (get_boundary(tailv) == bdry) ) { mu = get_coord(tailv); parama = get_param(tailv); paramb = get_param(headv); /* projecting on tangent */ b_extrapolate(bdry,mu,mv,mv,parama,parammid,tailv); /* if not wrapped, take average parameter */ for ( i = 0 ; i < bdry->pcount ; i++ ) { if ( ((parama[i] < parammid[i]) && (parammid[i] < paramb[i])) || ((parama[i] > parammid[i]) && (parammid[i] > paramb[i]))) parammid[i] = (parama[i] + paramb[i])/2; } } else #endif if ( get_boundary(headv) == bdry ) { mu = get_coord(headv); paramb = get_param(headv); /* projecting on tangent */ b_extrapolate(bdry,mu,mv,mv,paramb,parammid,headv); } else { mu = get_coord(tailv); paramb = get_param(tailv); /* projecting on tangent */ b_extrapolate(bdry,mu,mv,mv,paramb,parammid,tailv); } } } if ( attr & CONSTRAINT ) { ATTR cattr = attr & (BDRY_ENERGY | BDRY_CONTENT | CONSTRAINT ); conmap_t * conmap = get_f_constraint_map(old_f); set_attr(new_e,cattr); set_e_conmap(new_e,conmap); if ( web.modeltype == QUADRATIC ) { vertex_id mid = get_edge_midv(new_e); set_attr(mid,cattr); set_v_conmap(mid,conmap); project_v_constr(mid,ACTUAL_MOVE,RESET_ONESIDEDNESS); } } top_timestamp = ++global_timestamp; } /* end cross_cut() */ /************************************************************************** * * function: merge_bodies() * * purpose: Merge bodies, after facet dissolve, perhaps * Frees merged bodies. * * return: Number of merged bodies. */ int merge_bodies() { body_id *merge_body; /* final destination of body */ body_id b_id,bb_id; facetedge_id fe_id,next_fe; facet_id f_id,ff_id; int merge_count=0; merge_body = (body_id *)temp_calloc(web.skel[BODY].max_ord+5,sizeof(body_id)); FOR_ALL_BODIES(b_id) merge_body[loc_ordinal(b_id)] = b_id; /* Find junctions between bodies */ if ( web.representation == STRING ) { /* test for distinct facets joining at vertex */ FOR_ALL_FACETEDGES(fe_id) { next_fe = get_next_edge(fe_id); if ( equal_element(get_fe_edge(fe_id),get_fe_edge(next_fe)) ) continue; f_id = get_fe_facet(fe_id); if ( !valid_id(f_id) ) continue; ff_id = get_fe_facet(next_fe); if ( !valid_id(ff_id) ) continue; if ( equal_id(f_id,ff_id)) continue; /* now ready to merge */ while ( equal_id(ff_id,get_fe_facet(next_fe)) ) set_fe_facet(next_fe,f_id); free_element(ff_id); } } else if ( web.representation == SOAPFILM ) { FOR_ALL_FACETEDGES(fe_id) { next_fe = get_next_facet(fe_id); if ( equal_id(fe_id,next_fe) ) continue; /* ok to disagree on valence 1 */ f_id = get_fe_facet(fe_id); ff_id = get_fe_facet(next_fe); b_id = get_facet_body(inverse_id(f_id)); bb_id = get_facet_body(ff_id); if ( !equal_id(b_id,bb_id) ) { /* merge higher number body to lower */ if ( loc_ordinal(b_id) > loc_ordinal(bb_id) ) merge_body[loc_ordinal(b_id)] = bb_id; else merge_body[loc_ordinal(bb_id)] = b_id; } } } /* find net merge destinations */ FOR_ALL_BODIES(b_id) { if ( b_id != merge_body[loc_ordinal(b_id)] ) { bb_id = b_id; while ( bb_id != merge_body[loc_ordinal(bb_id)] ) bb_id = merge_body[loc_ordinal(bb_id)]; merge_body[loc_ordinal(b_id)] = bb_id; merge_count++; } } /* reset all facets appropriately */ FOR_ALL_FACETS(f_id) { b_id = get_facet_body(f_id); bb_id = merge_body[loc_ordinal(b_id)]; if ( !equal_id(b_id,bb_id) ) set_facet_body(f_id,bb_id); b_id = get_facet_body(inverse_id(f_id)); bb_id = merge_body[loc_ordinal(b_id)]; if ( !equal_id(b_id,bb_id) ) set_facet_body(inverse_id(f_id),bb_id); } /* get rid of unneeded bodies */ FOR_ALL_BODIES(b_id) { if ( b_id != merge_body[loc_ordinal(b_id)] ) free_element(b_id); } temp_free((char*)merge_body); return merge_count; } /******************************************************************** * * function: ffcomp() * * purpose: comparison for ordering facets in flist in rebody() * */ int ffcomp(a,b) facet_id *a,*b; { if ( loc_ordinal(*a) < loc_ordinal(*b) ) return -1; if ( loc_ordinal(*a) > loc_ordinal(*b) ) return 1; if ( inverted(*a) < inverted(*b) ) return -1; if ( inverted(*a) > inverted(*b) ) return 1; return 0; } /************************************************************************ * * function: rebody() * * purpose: redivide bodies into connected pieces. Useful after * neck pinches. * * method: Starting from body facet, labels all adjacent facets * for body. An unlabelled body facet is assigned new body * and propagated to neighbors. * * return: Number of new bodies. */ int rebody() { body_id b_id,old_b; int faces_left; int new_bodies = 0; int i; struct fface { facet_id f_id; int wrapflag; } *flist, *bf; facet_id *stack; /* of known faces */ int stacktop; int facetop; /* length of flist */ facet_id f_id,ff_id; facetedge_id fe; if ( web.representation == SIMPLEX ) { kb_error(3032,"\"rebody\" not implemented for the simplex model.\n", RECOVERABLE); } if ( web.representation == STRING ) return string_rebody(); /* working list of facets */ flist = (struct fface *)temp_calloc(2*web.skel[FACET].count, sizeof(struct fface)); stack = (facet_id *)temp_calloc(2*web.skel[FACET].count,sizeof(facet_id*)); faces_left = 0; FOR_ALL_FACETS(f_id) { if ( valid_id(get_facet_body(f_id))) flist[faces_left++].f_id = f_id; ff_id = facet_inverse(f_id); if ( valid_id(get_facet_body(ff_id)) ) flist[faces_left++].f_id = ff_id; } if ( faces_left == 0 ) return 0; facetop = faces_left; /* sort in facet order, so can find facets quickly */ qsort((char *)flist,faces_left,sizeof(struct fface),FCAST ffcomp); /* initialize stack with body facets */ stacktop = 0; FOR_ALL_BODIES(b_id) { f_id = get_body_facet(b_id); if ( !valid_id(f_id) ) continue; stack[stacktop++] = f_id; bf = (struct fface *)bsearch((char *)&f_id,(char *)flist, facetop, sizeof(struct fface),FCAST ffcomp); if ( bf == NULL ) { kb_error(1243, "Internal error: rebody() cannot find facet on stack.\n",WARNING); continue; } bf->wrapflag = 1; faces_left--; } /* pull stuff off stack till empty */ while (stacktop > 0 ) { f_id = stack[--stacktop]; b_id = get_facet_body(f_id); for ( i = 0, fe = get_facet_fe(f_id); i < 3 ; i++,fe = get_next_edge(fe)) { ff_id = get_fe_facet(get_prev_facet(fe)); if ( !valid_id(ff_id) ) continue; ff_id = facet_inverse(ff_id); if ( !equal_id(get_facet_body(ff_id),b_id) ) continue; bf = (struct fface *)bsearch((char *)&ff_id,(char *)flist, facetop, sizeof(struct fface),FCAST ffcomp); if ( bf == NULL ) { kb_error(1244,"Internal error: rebody() cannot find facet on stack (old body).\n",WARNING); continue; } if ( bf->wrapflag ) continue; bf->wrapflag = 1; /* mark as part of original body */ stack[stacktop++] = ff_id; faces_left--; } } /* now have to create new bodies */ while ( faces_left > 0 ) { /* find undone face */ for ( i = 0 ; i < facetop ; i++ ) if ( flist[i].wrapflag == 0 ) break; if ( i == facetop ) break; flist[i].wrapflag = 1; f_id = flist[i].f_id; old_b = get_facet_body(f_id); b_id = dup_body(b_id); new_bodies++; set_body_facet(b_id,NULLID); /* clear dup'ed value */ set_facet_body(f_id,b_id); /* takes care of set_body_facet() for first one */ stack[stacktop++] = f_id; faces_left--; /* pull stuff off stack till empty */ while (stacktop > 0 ) { f_id = stack[--stacktop]; for ( i=0, fe = get_facet_fe(f_id); i < 3 ; i++,fe = get_next_edge(fe)) { ff_id = facet_inverse(get_fe_facet(get_prev_facet(fe))); if (!valid_id(ff_id) || !valid_id(get_facet_body(ff_id))) continue; if ( !equal_id(get_facet_body(ff_id),old_b) ) continue; bf = (struct fface *)bsearch((char *)&ff_id,(char *)flist, facetop, sizeof(struct fface),FCAST ffcomp); if ( bf == NULL ) { kb_error(1245,"Internal error: rebody() cannot find facet on stack (new body).\n",WARNING); continue; } if ( bf->wrapflag ) continue; bf->wrapflag = 1; /* mark as part of original body */ set_facet_body(ff_id,b_id); stack[stacktop++] = ff_id; faces_left--; } } } temp_free((char*)flist); temp_free((char*)stack); top_timestamp = ++global_timestamp; FOR_ALL_BODIES(b_id) { if ( get_body_volconst(b_id) != 0.0 ) { sprintf(msg, "Nonzero VOLCONST on body %s. May need adjusting due to rebody.\n", ELNAME(b_id)); kb_error(2171,msg,WARNING); } } return new_bodies; } /************************************************************************ * * function: string_rebody() * * purpose: redivide bodies into connected pieces. Useful after * neck pinches. String model. * * method: Starting from body facet edge, labels all adjacent edges * for body. An ruUnlabelled body edges is assigned new body * and propagated to neighbors. * * return: Number of new bodies. */ int string_rebody() { body_id b_id,old_b; int edges_left; int new_bodies = 0; int i; struct bodyface *felist, *be; facetedge_id *stack; /* of known faces */ int stacktop; int edgetop; /* length of flist */ facet_id f_id,ff_id,new_f; edge_id e_id; facetedge_id fe,ffe; /* working list of edges */ felist = (struct bodyface *)temp_calloc(2*web.skel[EDGE].count, sizeof(struct bodyface)); stack = (facet_id *)temp_calloc(2*web.skel[EDGE].count,sizeof(edge_id*)); edges_left = 0; FOR_ALL_EDGES(e_id) { fe = get_edge_fe(e_id); if ( !valid_id(fe) ) continue; f_id = get_fe_facet(fe); if ( !valid_id(f_id) ) continue; if ( valid_id(get_facet_body(f_id))) felist[edges_left++].f_id = fe; ff_id = facet_inverse(f_id); if ( valid_id(get_facet_body(ff_id)) ) felist[edges_left++].f_id = inverse_id(fe); fe = get_next_facet(fe); ff_id = get_fe_facet(fe); if ( equal_element(f_id,ff_id) ) continue; if ( valid_id(get_facet_body(ff_id))) felist[edges_left++].f_id = fe; ff_id = facet_inverse(ff_id); if ( valid_id(get_facet_body(ff_id)) ) felist[edges_left++].f_id = inverse_id(fe); } if ( edges_left == 0 ) return 0; edgetop = edges_left; /* sort in edge order, so can find edges quickly */ qsort((char *)felist,edges_left,sizeof(struct bodyface),FCAST ffcomp); /* initialize stack with body edges */ stacktop = 0; FOR_ALL_BODIES(b_id) { fe = get_body_fe(b_id); stack[stacktop++] = fe; be = (struct bodyface *)bsearch((char *)&fe,(char *)felist, edgetop, sizeof(struct bodyface),FCAST ffcomp); if ( be == NULL ) { kb_error(1246, "Internal error: string_rebody() cannot find edge on stack.\n", WARNING); continue; } be->wrapflag = 1; edges_left--; } /* pull stuff off stack till empty */ while (stacktop > 0 ) { fe = stack[--stacktop]; f_id = get_fe_facet(fe); ffe = get_prev_edge(fe); if ( !valid_id(ffe) ) goto otherend; ff_id = get_fe_facet(ffe); if ( !equal_id(ff_id,f_id) ) goto otherend; be = (struct bodyface *)bsearch((char *)&ffe,(char *)felist, edgetop, sizeof(struct bodyface),FCAST ffcomp); if ( be == NULL ) { kb_error(1247,"Internal error: string_rebody() cannot find edge on stack (old body).\n",WARNING); goto otherend; } if ( !be->wrapflag ) { be->wrapflag = 1; /* mark as part of original body */ stack[stacktop++] = ffe; edges_left--; } otherend: ffe = get_next_edge(fe); if ( !valid_id(ffe) ) continue; ff_id = get_fe_facet(ffe); if ( !equal_id(ff_id,f_id) ) continue; be = (struct bodyface *)bsearch((char *)&ffe,(char *)felist, edgetop, sizeof(struct bodyface),FCAST ffcomp); if ( be == NULL ) { kb_error(1248,"Internal error: string_rebody() cannot find edge on stack (old body).\n",WARNING); continue; } if ( !be->wrapflag ) { be->wrapflag = 1; /* mark as part of original body */ stack[stacktop++] = ffe; edges_left--; } } /* now have to create new bodies */ while ( edges_left > 0 ) { /* find undone edge */ for ( i = 0 ; i < edgetop ; i++ ) if ( felist[i].wrapflag == 0 ) break; if ( i == edgetop ) break; felist[i].wrapflag = 1; fe = felist[i].f_id; f_id = get_fe_facet(fe); old_b = get_facet_body(f_id); b_id = dup_body(old_b); new_f = dup_facet(f_id); new_bodies++; set_body_facet(b_id,f_id); set_facet_body(new_f,b_id); set_facet_fe(new_f,fe); set_fe_facet(fe,new_f); stack[stacktop++] = fe; edges_left--; /* pull stuff off stack till empty */ while (stacktop > 0 ) { fe = stack[--stacktop]; ffe = get_prev_edge(fe); if ( !valid_id(ffe) ) goto anotherend; ff_id = get_fe_facet(ffe); if ( !equal_id(ff_id,f_id) ) goto otherend; be = (struct bodyface *)bsearch((char *)&ffe,(char *)felist, edgetop, sizeof(struct bodyface),FCAST ffcomp); if ( be == NULL ) { kb_error(1249,"Internal error: string_rebody() cannot find edge on stack (old body).\n",WARNING); goto otherend; } if ( !be->wrapflag ) { be->wrapflag = 1; /* mark as part of original body */ stack[stacktop++] = ffe; edges_left--; set_fe_facet(ffe,new_f); } anotherend: ffe = get_next_edge(fe); if ( !valid_id(ffe) ) continue; ff_id = get_fe_facet(ffe); if ( !equal_id(ff_id,f_id) ) continue; be = (struct bodyface *)bsearch((char *)&ffe,(char *)felist, edgetop, sizeof(struct bodyface),FCAST ffcomp); if ( be == NULL ) { kb_error(1250,"Internal error: string_rebody() cannot find edge on stack (old body).\n",WARNING); continue; } if ( !be->wrapflag ) { be->wrapflag = 1; /* mark as part of original body */ stack[stacktop++] = ffe; edges_left--; set_fe_facet(ffe,new_f); } } } temp_free((char*)felist); temp_free((char*)stack); top_timestamp = ++global_timestamp; return new_bodies; } /**************************************************************************** * * function: dissolve_vertex() * * purpose: Remove unattached vertex from surface. * * return: 1 if vertex removed, 0 if not because attached. */ int dissolve_vertex(v_id) vertex_id v_id; { edge_id e_id; if ( !valid_element(v_id) ) return 0; if ( ( web.representation == SIMPLEX ) || ( web.modeltype == LAGRANGE ) ) { if ( valid_id(get_vertex_facet(v_id)) ) return 0; } e_id = get_vertex_edge(v_id); if ( valid_id(e_id) ) return 0; free_element(v_id); top_timestamp = ++global_timestamp; if ( verbose_flag ) { sprintf(msg,"Dissolving vertex %s\n",ELNAME(v_id)); outstring(msg); } return 1; } /**************************************************************************** * * function: dissolve_edge() * * purpose: Remove edge from surface if not on a facet. * * return: 1 if edge removed, 0 if not because attached. */ int dissolve_edge(e_id) edge_id e_id; { facetedge_id fe,fe_id,start_fe; vertex_id head,tail; int evalence; facet_id f_id=NULLID; if ( !valid_element(e_id) ) return 0; evalence = get_edge_valence(e_id); if ( web.representation == STRING ) { #ifdef CODDLING /* check for at most one facet */ if ( evalence > 1 ) { if ( verbose_flag ) { sprintf(msg,"Not dissolving edge %s since still on two facets.\n", ELNAME(e_id)); outstring(msg); } return 0; } #endif /* check for being at beginning or end of edge arc for all facets */ if ( evalence > 0 ) { start_fe = fe_id = get_edge_fe(e_id); do { fe = fe_id; if ( valid_id(get_next_edge(fe)) && valid_id(get_prev_edge(fe)) ) { /* check for full loop */ do { fe = get_next_edge(fe); } while ( valid_id(fe) && !equal_id(fe,fe_id) ); if ( !valid_id(fe) ) { if ( verbose_flag ) { sprintf(msg, "Not dissolving edge %s since would make facet into two arcs.\n", ELNAME(e_id)); outstring(msg); } return 0; } } fe_id = get_next_facet(fe_id); } while ( fe_id != start_fe ); if ( evalence > 1 ) { sprintf(msg,"Dissolving edge %s between two facets.\n",ELNAME(e_id)); kb_error(2172,msg,WARNING); } } } else if ( evalence > 0 ) { if ( verbose_flag ) { sprintf(msg,"Not dissolving edge %s since still on a facet.\n", ELNAME(e_id)); outstring(msg); } return 0; } if ( verbose_flag ) { sprintf(msg,"Dissolving edge %s\n",ELNAME(e_id)); outstring(msg); } fe = get_edge_fe(e_id); if ( !valid_id(fe) ) { free_element(e_id); return 1; } head = get_edge_headv(e_id); tail = get_edge_tailv(e_id); /* reset vertex-edge links */ remove_vertex_edge(tail,e_id); remove_vertex_edge(head,inverse_id(e_id)); /* connect facetedges of stumps */ if ( web.representation == STRING ) { facetedge_id ffe = fe; do { f_id = get_fe_facet(ffe); /* in case of STRING */ if ( valid_id(f_id) ) { if ( inverted(f_id) ) { if ( valid_id(get_prev_edge(ffe)) ) set_facet_fe(f_id,get_prev_edge(ffe)); else if ( !valid_id(get_next_edge(ffe)) ) set_facet_fe(f_id,NULLID); } else { if ( valid_id(get_next_edge(ffe)) ) set_facet_fe(f_id,get_next_edge(ffe)); else if ( !valid_id(get_prev_edge(ffe)) ) set_facet_fe(f_id,NULLID); } } ffe = get_next_facet(ffe); } while ( valid_id(ffe) && !equal_id(ffe,fe) ); } for ( fe_id = fe ; ; ) { facetedge_id prev,next; prev = get_prev_edge(fe_id); if ( valid_id(prev) ) { if ( valid_id(f_id) ) set_next_edge(prev,NULLID); else set_next_edge(prev,inverse_id(prev)); } next = get_next_edge(fe_id); if ( valid_id(next) ) { if ( valid_id(f_id) ) set_prev_edge(next,NULLID); else set_prev_edge(next,inverse_id(next)); } free_element(fe_id); fe_id = get_next_facet(fe_id); if ( equal_id(fe_id,fe) ) break; } free_element(e_id); top_timestamp = ++global_timestamp; return 1; } /**************************************************************************** * * function: dissolve_facet() * * purpose: Remove facet from surface if not on a body. * * return: 1 if facet removed, 0 if not because attached. */ int dissolve_facet(f_id) facet_id f_id; { facetedge_id fe,fe_id; edge_id e_id; body_id b1,b2; if ( !valid_element(f_id) ) return 0; b1 = get_facet_body(f_id); b2 = get_facet_body(inverse_id(f_id)); if ( !equal_id(b1,b2) && valid_id(b1) && valid_id(b2) ) { if ( verbose_flag ) { sprintf(msg,"Not dissolving facet %s since on two different bodies\n",ELNAME(f_id)); outstring(msg); } return 0; } if ( verbose_flag ) { sprintf(msg,"Dissolving facet %s\n",ELNAME(f_id)); outstring(msg); } if ( web.representation != SIMPLEX ) { /* Identify the edges, but not the facets yet. delete_facet() will take care of the second facet and patch up its facetedges. */ fe = get_facet_fe(f_id); for ( fe_id = fe ; ; ) { facetedge_id prev,next,next_fe; prev = get_prev_facet(fe_id); next = get_next_facet(fe_id); e_id = get_fe_edge(fe_id); next_fe = get_next_edge(fe_id); if ( !equal_id(prev,fe_id) ) { set_next_facet(prev,next); set_prev_facet(next,prev); set_edge_fe(e_id,next); free_element(fe_id); } else { set_attr(e_id,BARE_NAKED); set_edge_fe(e_id,NULLID); free_element(fe_id); } fe_id = next_fe; if ( !valid_id(fe_id) ) break; /* open facet in string model */ if ( equal_id(fe_id,fe) ) break; } } set_facet_body(f_id,NULLID); set_facet_body(inverse_id(f_id),NULLID); if ( (web.modeltype == LAGRANGE) && (web.lagrange_order >= 3) ) { vertex_id *v = get_facet_vertices(f_id); int i; for ( i = 0 ; i < web.skel[FACET].ctrlpts ; i++ ) if ( get_vattr(v[i]) & Q_MIDFACET ) free_element(v[i]); } free_element(f_id); top_timestamp = ++global_timestamp; return 1; } /**************************************************************************** * * function: dissolve_body() * * purpose: Remove body from surface. * * return: 1 if body removed. */ int dissolve_body(b_id) body_id b_id; { facet_id f_id,ff_id; int k; if ( !valid_element(b_id) ) return 0; if ( verbose_flag ) { sprintf(msg,"Dissolving body %s\n",ELNAME(b_id)); outstring(msg); } FOR_ALL_FACETS(f_id) { if ( equal_id(b_id,get_facet_body(f_id)) ) set_facet_body(f_id,NULLBODY); ff_id = inverse_id(f_id); if ( equal_id(b_id,get_facet_body(ff_id)) ) set_facet_body(ff_id,NULLBODY); } if ( everything_quantities_flag ) { struct method_instance *mi = METH_INSTANCE(get_body_volmeth(b_id)); struct gen_quant *q = GEN_QUANT(mi->quant); mi->flags = Q_DELETED; q->flags = Q_DELETED; for ( k = LOW_INST ; k < meth_inst_count ; k++ ) { struct method_instance *mm = METH_INSTANCE(k); if ( mm->quant == mi->quant ) mi->flags = Q_DELETED; } } free_element(b_id); top_timestamp = ++global_timestamp; return 1; } /***************************************************************************** * * function: divide_quad() * * purpose: Divide quadrilateral into two triangles. * */ void divide_quad(fe) facetedge_id fe; { facetedge_id fea,feb,fec,new_fe,new_fe2; facet_id old_f,new_f; edge_id new_e; conmap_t *old_conmap; old_f = get_fe_facet(fe); old_conmap = get_f_constraint_map(old_f); if ( inverted(old_f) ) { fe = inverse_id(fe); old_f = inverse_id(old_f); } fea = get_next_edge(fe); feb = get_next_edge(fea); fec = get_next_edge(feb); new_f = dup_facet(old_f); new_e = new_edge(get_fe_headv(fe),get_fe_tailv(fec),old_f); set_e_conmap(new_e,old_conmap); new_fe = new_facetedge(old_f,new_e); set_edge_fe(new_e,new_fe); set_facet_fe(old_f,new_fe); set_next_edge(new_fe,fec); set_prev_edge(fec,new_fe); set_next_edge(fe,new_fe); set_prev_edge(new_fe,fe); if ( web.symmetry_flag ) { WRAPTYPE wrap = get_fe_wrap(fec); wrap = (*sym_compose)(wrap,get_fe_wrap(fe)); wrap = (*sym_inverse)(wrap); set_edge_wrap(new_e,wrap); } new_fe2 = new_facetedge(new_f,inverse_id(new_e)); set_facet_fe(new_f,new_fe2); set_next_edge(new_fe2,fea); set_prev_edge(fea,new_fe2); set_next_edge(feb,new_fe2); set_prev_edge(new_fe2,feb); set_next_facet(new_fe,inverse_id(new_fe2)); set_next_facet(new_fe2,inverse_id(new_fe)); set_prev_facet(new_fe,inverse_id(new_fe2)); set_prev_facet(new_fe2,inverse_id(new_fe)); set_fe_facet(fea,new_f); set_fe_facet(feb,new_f); } /************************************************************************* * * function: t1_edgeswap() * * purpose: Does 2D string model T1 topological transition. * For edge with valence 3 at each end, forcibly switches * orientation of the edge. Only swaps if the edge and its * two endpoints are not fixed and not on constraints or boundaries. * * return value: Number of edges swapped. */ int t1_edgeswap(e_id) edge_id e_id; { facetedge_id fe_a,fe_b,fe_c,fe_d,fe_e,fe_f,fe_g,fe_h,fe_i,fe_j; vertex_id headv,tailv; facet_id f_a,f_b,f_c,f_d; REAL *hx,*tx,newhx[MAXCOORD],newtx[MAXCOORD]; int i,j; if ( web.dimension != STRING ) { kb_error(3665,"t1_edgeswap valid only for STRING model.\n",WARNING); return 0; } /* check fixedness and boundary */ headv = get_edge_headv(e_id); tailv = get_edge_tailv(e_id); if ( (get_eattr(e_id) & FIXED) || (get_vattr(headv) & FIXED) || (get_vattr(tailv) & FIXED) ) { if ( verbose_flag ) { sprintf(msg, "Edge %s not t1_swapped due to fixedness.\n", ELNAME(e_id)); outstring(msg); } return 0; } /* check constraints */ if ( compare_vertex_edge_attr(headv,e_id) != A_EQ_B || compare_vertex_edge_attr(tailv,e_id) != A_EQ_B ) { if ( verbose_flag ) { sprintf(msg, "Edge %s not t1_swapped due to differing constraints.\n", ELNAME(e_id)); outstring(msg); } return 0; } /* check boundaries */ if ( get_vertex_boundary_num(headv) != get_edge_boundary_num(e_id) || get_vertex_boundary_num(tailv) != get_edge_boundary_num(e_id) ) { if ( verbose_flag ) { sprintf(msg, "Edge %s not t1_swapped due to differing boundaries.\n", ELNAME(e_id)); outstring(msg); } return 0; } /* gather facet edges and check topology */ fe_a = get_edge_fe(e_id); if ( !valid_id(fe_a) || !valid_id(get_fe_facet(fe_a)) ) { if ( verbose_flag ) { sprintf(msg, "Edge %s not t1_swapped since it does not have adjacent facets.\n", ELNAME(e_id)); outstring(msg); } return 0; } fe_b = get_next_facet(fe_a); if ( equal_id(fe_a,fe_b) ) { if ( verbose_flag ) { sprintf(msg, "Edge %s not t1_swapped since it has only one adjacent facet.\n", ELNAME(e_id)); outstring(msg); } return 0; } fe_c = get_prev_edge(fe_a); fe_d = get_next_edge(fe_a); fe_e = get_next_facet(fe_c); fe_f = inverse_id(get_next_edge(fe_e)); fe_g = get_prev_edge(fe_b); if ( !equal_id(fe_g,get_next_facet(fe_f)) ) { if ( verbose_flag ) { sprintf(msg, "Edge %s not t1_swapped since tail vertex doesn't have valence 3.\n", ELNAME(e_id)); outstring(msg); printf("fe_f edge %08X fe_g edge %08X\n",get_fe_edge(fe_g),get_fe_edge(fe_f)); } return 0; } fe_j = get_next_facet(fe_d); fe_i = inverse_id(get_prev_edge(fe_j)); fe_h = get_next_edge(fe_b); if ( !equal_id(fe_i,get_next_facet(fe_h)) ) { if ( verbose_flag ) { sprintf(msg, "Edge %s not t1_swapped since head vertex doesn't have valence 3.\n", ELNAME(e_id)); outstring(msg); } return 0; } f_a = inverse_id(get_fe_facet(fe_a)); f_b = get_fe_facet(fe_b); f_c = get_fe_facet(fe_e); f_d = get_fe_facet(fe_j); /* adjust wrapping so swapped edge is not wrapped */ if ( web.torus_flag ) torus_unwrap_edge(e_id); else if ( web.symmetry_flag ) { edge_id pos_e = positive_id(e_id); /* in case of quadratic */ int wrap = get_edge_wrap(pos_e); if ( wrap ) wrap_vertex(get_edge_headv(pos_e),wrap); } /* Now the actual swapping */ /* kludge to get quantity body integrals attached */ set_fe_facet(fe_a,inverse_id(f_c)); set_fe_facet(fe_b,f_d); if ( equal_element(fe_a,get_facet_fe(f_a)) ) set_facet_fe(f_a,inverse_id(fe_c)); if ( equal_element(fe_b,get_facet_fe(f_b)) ) set_facet_fe(f_b,fe_g); set_next_edge(fe_a,inverse_id(fe_e)); set_prev_edge(inverse_id(fe_e),fe_a); set_prev_edge(fe_a,fe_f); set_next_edge(fe_f,fe_a); set_next_edge(fe_g,fe_h); set_prev_edge(fe_h,fe_g); set_prev_edge(fe_b,inverse_id(fe_i)); set_prev_edge(fe_i,inverse_id(fe_b)); set_next_edge(fe_b,fe_j); set_prev_edge(fe_j,fe_b); set_next_edge(fe_c,fe_d); set_prev_edge(fe_d,fe_c); remove_vertex_edge(tailv,get_fe_edge(inverse_id(fe_c))); remove_vertex_edge(headv,get_fe_edge(fe_h)); set_edge_headv(get_fe_edge(fe_c),headv); set_edge_tailv(get_fe_edge(fe_h),tailv); /* move vertices (note that edge was unwrapped earlier */ hx = get_coord(headv); tx = get_coord(tailv); if ( inverted(get_fe_facet(fe_a)) ) { newtx[0] = (hx[0]+tx[0])/2 - (hx[1]-tx[1])/2; newhx[0] = (hx[0]+tx[0])/2 + (hx[1]-tx[1])/2; newtx[1] = (hx[1]+tx[1])/2 + (hx[0]-tx[0])/2; newhx[1] = (hx[1]+tx[1])/2 - (hx[0]-tx[0])/2; } else { newtx[0] = (hx[0]+tx[0])/2 + (hx[1]-tx[1])/2; newhx[0] = (hx[0]+tx[0])/2 - (hx[1]-tx[1])/2; newtx[1] = (hx[1]+tx[1])/2 - (hx[0]-tx[0])/2; newhx[1] = (hx[1]+tx[1])/2 + (hx[0]-tx[0])/2; } hx[0] = newhx[0]; hx[1] = newhx[1]; tx[0] = newtx[0]; tx[1] = newtx[1]; if ( web.modeltype == QUADRATIC ) { REAL *midx = get_coord(get_edge_midv(e_id)); for ( i = 0 ; i < SDIM ; i++ ) midx[i] = (hx[i] + tx[i])/2; } if ( web.modeltype == LAGRANGE ) { vertex_id *v = get_edge_vertices(e_id); for ( j = 1 ; j < web.lagrange_order ; j++ ) { REAL *x = get_coord(v[j]); for ( i = 0 ; i < SDIM ; i++ ) x[i] = (j*hx[i] + (web.lagrange_order-j)*tx[i])/web.lagrange_order; } } return 1; } /************************************************************************** * * function: merge_vertex() * * purpose: merge two supposedly close vertices. Not meant for vertices * connected with an edge. Replacement for kludge of defining * edge between vertices and deleting edge. Keeps first vertex. * */ void merge_vertex(keepv,throwv) edge_id keepv,throwv; { edge_id e_id; int nn = 0; int i,wrap=0; facet_id match_f=NULLID; facetedge_id match_kfe=NULLID,match_tfe=NULLID; if ( get_vattr(keepv) & (Q_MIDPOINT|Q_MIDEDGE|Q_MIDFACET) ) { sprintf(errmsg,"vertex_merge: Cannot merge %s since not at end of edge.\n", ELNAME(keepv)); kb_error(4321,errmsg,RECOVERABLE); } if ( get_vattr(throwv) & (Q_MIDPOINT|Q_MIDEDGE|Q_MIDFACET) ) { sprintf(errmsg,"vertex_merge: Cannot merge %s since not at end of edge.\n", ELNAME(throwv)); kb_error(4322,errmsg,RECOVERABLE); } if ( (web.modeltype != LINEAR) && (web.modeltype != QUADRATIC) ) kb_error(3913,"vertex_merge only in LINEAR or QUADRATIC model so far.\n", RECOVERABLE); if ( equal_id(keepv,throwv) ) { kb_error(3882,"vertex_merge called with identical vertices.\n",WARNING); return; } if ( web.symmetry_flag && !web.torus_flag ) { kb_error(3883,"vertex_merge can't handle wraps in non-torus symmetries.\n", RECOVERABLE); } /* no checks on constraints or fixedness; let the user beware! */ /* maybe have wrapping in torus mode */ if ( web.torus_flag ) { REAL *x = get_coord(keepv); REAL *y = get_coord(throwv); REAL z[MAXCOORD]; REAL u[MAXCOORD]; for ( i = 0 ; i < SDIM ; i++ ) z[i] = y[i] - x[i]; matvec_mul(web.inverse_periods,z,u,SDIM,SDIM); for ( i = SDIM-1, wrap = 0 ; i >= 0 ; i-- ) { wrap <<= TWRAPBITS; wrap |= (int)(floor(u[i]+0.5)) & WRAPMASK; } } /* for later fixup in string model */ if ( web.representation == STRING ) { /* find common face four of the facetedges have in common */ facetedge_id keep_fe; facetedge_id throw_fe_1,throw_fe_2; edge_id keep_e, throw_e, keep_e_start, throw_e_start; facet_id kf,tf; match_kfe = match_tfe = NULLID; match_f = NULLID; keep_e = keep_e_start = get_vertex_edge(keepv); do { throw_e = throw_e_start = get_vertex_edge(throwv); do { /* see if facetedges match up */ keep_fe = get_edge_fe(keep_e); if ( valid_id(keep_fe) && valid_id(get_fe_facet(keep_fe)) ) { throw_fe_1 = get_edge_fe(throw_e); if ( valid_id(throw_fe_1) && valid_id(get_fe_facet(throw_fe_1)) ) { throw_fe_2 = get_next_facet(throw_fe_1); kf = get_fe_facet(keep_fe); tf = get_fe_facet(throw_fe_1); if ( equal_element(kf,tf) && !like_sign(kf,tf) ) { match_f = kf; match_kfe = keep_fe; match_tfe = throw_fe_1; goto found_match; } tf = get_fe_facet(throw_fe_2); if ( equal_element(kf,tf) && !like_sign(kf,tf) ) { match_f = kf; match_kfe = keep_fe; match_tfe = throw_fe_2; goto found_match; } keep_fe = get_next_facet(keep_fe); kf = get_fe_facet(keep_fe); tf = get_fe_facet(throw_fe_1); if ( equal_element(kf,tf) && !like_sign(kf,tf) ) { match_f = kf; match_kfe = keep_fe; match_tfe = throw_fe_1; goto found_match; } tf = get_fe_facet(throw_fe_2); if ( equal_element(kf,tf) && !like_sign(kf,tf) ) { match_f = kf; match_kfe = keep_fe; match_tfe = throw_fe_2; goto found_match; } } } throw_e = get_next_tail_edge(throw_e); } while ( !equal_id(throw_e,throw_e_start) ); keep_e = get_next_tail_edge(keep_e); } while ( !equal_id(keep_e,keep_e_start) ); found_match: ; } /* change all references to the eliminated vertex to the kept vertex. */ for (;;) { e_id = get_vertex_edge(throwv); if ( !valid_id(e_id) ) break; remove_vertex_edge(throwv,e_id); set_edge_tailv(e_id,keepv); if ( web.torus_flag ) { WRAPTYPE invwrap = (*sym_inverse)(wrap); set_edge_wrap(e_id,(*sym_compose)(invwrap,get_edge_wrap(e_id))); if ( !inverted(e_id) ) /* keep interior vertices with tail */ { if ( web.modeltype == QUADRATIC ) { vertex_id midv = get_edge_midv(e_id); REAL *midx = get_coord(midv); (*sym_wrap)(midx,midx,invwrap); } else if ( web.modeltype == LAGRANGE ) { vertex_id *v = get_edge_vertices(e_id); for ( i = 1 ; i < web.lagrange_order ; i++ ) { REAL *vx = get_coord(v[i]); (*sym_wrap)(vx,vx,invwrap); } } } } if ( ++nn > web.skel[EDGE].count ) { sprintf(errmsg,"Internal error: Bad edge loop on vertex %s.\n", ELNAME(throwv)); kb_error(3884,errmsg,WARNING); break; } } /* fix up facetedge links in string model */ if ( (web.representation == STRING) && valid_id(match_f) ) { /* find common face four of the facetedges have in common */ facetedge_id prev_k = get_prev_edge(match_kfe); facetedge_id prev_t = get_prev_edge(match_tfe); facetedge_id next_fe,next_ffe; int found; int fe_counter; /* to prevent infinite loop in case of error */ int fe_counter_max = 2*web.skel[FACETEDGE].count; set_next_edge(prev_k,inverse_id(prev_t)); set_next_edge(prev_t,inverse_id(prev_k)); set_prev_edge(match_kfe,inverse_id(match_tfe)); set_prev_edge(match_tfe,inverse_id(match_kfe)); /* see if we need to make a new facet */ next_fe = match_kfe; found = 0; fe_counter = 0; do { if ( equal_element(next_fe,prev_k) ) { found = 1; break; } next_ffe = get_next_edge(next_fe); if ( equal_element(next_fe,next_ffe) ) break; next_fe = next_ffe; if ( fe_counter++ > fe_counter_max ) kb_error(3616,"Internal error - unclosed facetedge loop\n", RECOVERABLE); } while ( !equal_element(next_fe,match_kfe) ); next_fe = match_tfe; /* try the other way */ fe_counter = 0; if ( !found ) do { if ( equal_element(next_fe,prev_k) ) { found = 1; break; } next_ffe = get_next_edge(next_fe); if ( equal_element(next_fe,next_ffe) ) break; next_fe = next_ffe; if ( fe_counter++ > fe_counter_max ) kb_error(3613,"Internal error - unclosed facetedge loop\n", RECOVERABLE); } while ( !equal_element(next_fe,match_kfe) ); if ( !found ) { /* have to create new facet */ facet_id newf = dup_facet(match_f); set_facet_fe(newf,prev_k); set_facet_fe(get_fe_facet(match_tfe),match_tfe); /*make sure match_f has valid fe */ next_fe = prev_k; fe_counter = 0; do { set_fe_facet(next_fe,newf); next_ffe = get_prev_edge(next_fe); if ( equal_element(next_ffe,next_fe) ) break; next_fe = next_ffe; if ( fe_counter++ > fe_counter_max ) kb_error(2873,"Internal error - unclosed facetedge loop\n", RECOVERABLE); } while ( !equal_element(next_fe,prev_k)); if ( !equal_element(next_fe,prev_k) ) next_fe = inverse_id(prev_t); fe_counter = 0; do { set_fe_facet(next_fe,newf); next_ffe = get_next_edge(next_fe); if ( equal_element(next_ffe,next_fe) ) break; next_fe = next_ffe; if ( fe_counter++ > fe_counter_max ) kb_error(3621,"Internal error - unclosed facetedge loop\n", RECOVERABLE); } while ( !equal_element(next_fe,prev_k)); } } free_element(throwv); } /************************************************************************** * * function: merge_edge() * * purpose: merge two supposedly close and parallel edges. * merges tail to tail and head to head. * In string model, first edge is on left, second on right. * Endpoints don't have to be distinct; i.e. can merge edges * with same tail. * * Doesn't eliminate any facetedges, so continuity of chain * of edges of middle facet is maintained. Edge winds up * with maybe 4 facet edges, which might confuse some things. * */ void merge_edge(e_id1,e_id2) edge_id e_id1,e_id2; { facetedge_id fe_start,fe,ffe,fep,ffep; vertex_id t1,t2,h1,h2; REAL side1[MAXCOORD],side2[MAXCOORD]; if ( web.modeltype != LINEAR ) kb_error(2894,"merge_edge only in LINEAR model so far.\n",RECOVERABLE); if ( equal_id(e_id1,e_id2) ) { kb_error(3887,"merge_edge called with identical edges.\n",WARNING); return; } if ( web.symmetry_flag && !web.torus_flag ) { kb_error(3888,"vertex_merge can't handle wraps in non-torus symmetries.\n", RECOVERABLE); } /* orient as parallel as possible */ if ( equal_id(get_edge_tailv(e_id1),get_edge_tailv(e_id2)) || equal_id(get_edge_headv(e_id1),get_edge_headv(e_id2)) ) { /* ok */ } else if ( equal_id(get_edge_tailv(e_id1),get_edge_headv(e_id2)) || equal_id(get_edge_headv(e_id1),get_edge_tailv(e_id2)) ) { invert(e_id2); } else { /* try geometric alignment */ get_edge_side(e_id1,side1); get_edge_side(e_id2,side2); if ( dot(side1,side2,SDIM) < 0 ) invert(e_id2); } t1 = get_edge_tailv(e_id1); t2 = get_edge_tailv(e_id2); if ( !equal_id(t1,t2) ) merge_vertex(t1,t2); h1 = get_edge_headv(e_id1); h2 = get_edge_headv(e_id2); if ( !equal_id(h1,h2) ) merge_vertex(h1,h2); /* now transfer facetedges */ fe = fe_start = get_edge_fe(e_id2); do { set_fe_edge(fe,e_id1); fe = get_next_facet(fe); } while ( !equal_id(fe,fe_start) ); ffe = get_edge_fe(e_id1); fep = get_next_facet(fe); ffep = get_prev_facet(ffe); set_next_facet(fe,ffe); set_prev_facet(ffe,fe); set_next_facet(ffep,fep); set_prev_facet(fep,ffep); fe_reorder(e_id1); /* for proper geometrical order */ free_element(e_id2); /* also removes from vertex lists */ } /************************************************************************** * * function: merge_facet() * * purpose: merge two supposedly close and parallel facets. * Tries to use body info to control orientation of merger. * picks best correspondence spatially of vertices to merge. * */ void merge_facet(f_id1,f_id2) edge_id f_id1,f_id2; { MAT2D(f1x,FACET_VERTS,MAXCOORD); MAT2D(f2x,FACET_VERTS,MAXCOORD); MAT2D(tempx,FACET_VERTS,MAXCOORD); int wrap,i; facetedge_id fe1,fe2; body_id b_id; REAL bestdist; int besti,edge_match; int j,k; body_id b1_front,b1_back,b2_front,b2_back; int body_restricted = 0; /* 1 if only one way to match bodies */ if ( web.modeltype != LINEAR ) kb_error(3881,"facet_merge only in LINEAR model so far.\n",RECOVERABLE); if ( equal_element(f_id1,f_id2) ) { kb_error(3889,"facet_merge called with identical facets.\n",WARNING); return; } if ( web.symmetry_flag && !web.torus_flag ) { kb_error(3915,"facet_merge can't handle wraps in non-torus symmetries.\n", RECOVERABLE); } /* figure best correspondence of vertices */ /* First, see if any identical vertices */ besti = -1; edge_match = 0; fe1 = get_facet_fe(f_id1); for ( i = 0 ; (i < FACET_VERTS) && !edge_match ; i++ ) { fe2 = get_facet_fe(f_id2); for ( j = 0 ; j < FACET_VERTS ; j++ ) { if ( equal_id(get_fe_tailv(fe1),get_fe_tailv(fe2)) ) { int newbesti = j >= i ? j - i : j - i + 3; if ( besti >= 0 ) { edge_match = 1; if ( besti != newbesti ) /* have to reverse relative facet orientation */ { invert(f_id2); j = (4 - j) % 3; besti = j >= i ? j - i : j - i + 3; } } else besti = newbesti; break; } fe2 = get_next_edge(fe2); } fe1 = get_next_edge(fe1); } /* see if body information is enough to specify direction */ b1_front = get_facet_body(f_id1); b1_back = get_facet_body(inverse_id(f_id1)); b2_front = get_facet_body(f_id2); b2_back = get_facet_body(inverse_id(f_id2)); if ( !equal_id(b1_front,b2_back) ) { if ( equal_id(b1_front,b2_front) && !edge_match ) { invert(f_id2); if ( !equal_id(b1_back,b2_back) ) body_restricted = 1; } else if ( equal_id(b1_back,b2_back) && !edge_match ) { invert(f_id1); if ( !equal_id(b1_front,b2_front) ) body_restricted = 1; } else if ( equal_id(b1_back,b2_front) ) { invert(f_id1); invert(f_id2); if ( besti >= 0 ) besti = (3 - besti) % 3; body_restricted = 1; } else { sprintf(errmsg,"Cannot get body agreement for merging facets %s and %s\n", ELNAME(f_id1),ELNAME1(f_id2)); kb_error(3896,errmsg,WARNING); return; } } else if ( !equal_id(b1_back,b2_front) ) body_restricted = 1; /* need vertex coordinates for orientation matching and vertex matching */ get_facet_verts(f_id1,f1x,NULL); /* follows facet orientation */ get_facet_verts(f_id2,f2x,NULL); if ( web.torus_flag ) { REAL z[MAXCOORD]; REAL u[MAXCOORD]; for ( i = 0 ; i < SDIM ; i++ ) z[i] = f2x[0][i] - f1x[0][i]; matvec_mul(web.inverse_periods,z,u,SDIM,SDIM); for ( i = SDIM-1, wrap = 0 ; i >= 0 ; i-- ) { wrap |= (int)(floor(u[i]+0.5)) & WRAPMASK; wrap <<= TWRAPBITS; } if ( wrap ) { for ( i = 0 ; i < FACET_VERTS ; i++ ) torus_wrap(f1x[i],tempx[i],wrap); f1x = tempx; } } if ( besti < 0 ) { /* no identical vertices, so try minimizing distance */ /* calculate sum of distances for various matchings */ for ( i = 0, bestdist = 1e30, besti = -1 ; i < FACET_VERTS ; i++ ) { REAL dist = 0.0; for ( j = 0 ; j < FACET_VERTS ; j++ ) { REAL s; int cor = (i+j)%3; for ( k = 0, s = 0.0 ; k < SDIM ; k++ ) { REAL d = (f1x[j][k] - f2x[cor][k]); s += d*d; } dist += sqrt(s); } if ( dist < bestdist ) { bestdist = dist; besti = i; } } } /* Merge vertices and edges. Doing own edge merge since * can't pass enough info to merge_edge() */ fe1 = get_facet_fe(f_id1); fe2 = get_facet_fe(f_id2); for ( i = 0 ; i < besti ; i++ ) fe2 = get_next_edge(fe2); for ( i = 0 ; i < FACET_EDGES ; i++ ) { edge_id e_id1 = get_fe_edge(fe1); edge_id e_id2 = get_fe_edge(fe2); vertex_id tail1 = get_edge_tailv(e_id1); vertex_id tail2 = get_edge_tailv(e_id2); facetedge_id fe_next; facetedge_id next_fe2,prev_fe1; if ( !equal_element(tail1,tail2) ) merge_vertex(tail1,tail2); if ( !equal_id(e_id1,e_id2) ) { fe_next = fe2; do { set_fe_edge(fe_next,e_id1); fe_next = get_next_facet(fe_next); } while ( !equal_id(fe2,fe_next) ); next_fe2 = get_next_facet(fe2); prev_fe1 = get_prev_facet(fe1); set_next_facet(fe2,fe1); set_prev_facet(fe1,fe2); set_next_facet(prev_fe1,next_fe2); set_prev_facet(next_fe2,prev_fe1); free_element(e_id2); } fe1 = get_next_edge(fe1); fe2 = get_next_edge(fe2) ; } b_id = get_facet_body(f_id2); set_facet_body(f_id1,b_id); set_facet_body(inverse_id(f_id2),b_id); /* so dissolve_facet() works */ dissolve_facet(f_id2); } /* end merge_facet90 */ /**************************************************************************** * * function: reverse_orientation_edge() * * purpose: reverse the orientation of an edge */ void reverse_orientation_edge(e_id) edge_id e_id; { vertex_id v_id[2],*v; facetedge_id start_fe; int i; struct edge *e_ptr; v_id[0] = get_edge_tailv(e_id); v_id[1] = get_edge_headv(e_id); /* swap endpoints */ switch ( web.modeltype ) { case LINEAR: remove_vertex_edge(v_id[0],e_id); remove_vertex_edge(v_id[1],inverse_id(e_id)); set_edge_headv(e_id,v_id[0]); set_edge_tailv(e_id,v_id[1]); break; case QUADRATIC: remove_vertex_edge(v_id[0],e_id); remove_vertex_edge(v_id[1],inverse_id(e_id)); set_edge_headv(e_id,v_id[0]); set_edge_tailv(e_id,v_id[1]); break; case LAGRANGE: remove_vertex_edge(v_id[0],e_id); remove_vertex_edge(v_id[1],inverse_id(e_id)); set_edge_headv(e_id,v_id[0]); set_edge_tailv(e_id,v_id[1]); v = get_edge_vertices(e_id); for ( i = 1 ; i <= web.lagrange_order/2 ; i++ ) { vertex_id vv = v[i]; v[i] = v[web.lagrange_order - i]; v[web.lagrange_order - i] = vv; } break; } set_vertex_edge(v_id[1],e_id); set_vertex_edge(v_id[0],inverse_id(e_id)); /* reverse id in facet-edges */ start_fe = get_edge_fe(e_id); if ( valid_id(start_fe) ) { facetedge_id fe = start_fe; do { facet_id feprev,fenext,fe_fore,fe_aft; facet_id f_id = get_fe_facet(fe); set_fe_facet(fe,inverse_id(f_id)); set_facet_fe(f_id,inverse_id(fe)); fenext = get_next_facet(fe); feprev = get_prev_facet(fe); set_next_facet(fe,feprev); set_prev_facet(fe,fenext); fe_fore = get_next_edge(fe); fe_aft = get_prev_edge(fe); set_next_edge(fe,inverse_id(fe_aft)); set_prev_edge(fe,inverse_id(fe_fore)); set_prev_edge(fe_fore,inverse_id(fe)); set_next_edge(fe_aft,inverse_id(fe)); fe = fenext; } while ( !equal_id(fe,start_fe) ); } /* reverse symmetry wraps */ if ( web.symmetry_flag ) set_edge_wrap(e_id,(*sym_inverse)(get_edge_wrap(e_id))); /* reverse "orientation" for methods and constraint integrals */ e_ptr = eptr(e_id); e_ptr->attr ^= NEGBOUNDARY; } /* end edge_reverse_orientation() */ /*************************************************************************** * * Function: reverse_orientation_facet() * * Purpose: reverse the orientation of one facet. */ void reverse_orientation_facet(f_id) facet_id f_id; { facetedge_id start_fe,fe,last_fe; body_id b_id,bb_id; struct facet *e_ptr; /* reverse body attachments (before reversing edges!!) */ b_id = get_facet_body(f_id); bb_id = get_facet_body(inverse_id(f_id)); set_facet_body(f_id,bb_id); set_facet_body(inverse_id(f_id),b_id); /* reverse fe */ fe = start_fe = get_facet_fe(f_id); do { set_fe_facet(fe,inverse_id(f_id)); last_fe = fe; fe = get_next_edge(fe); } while (valid_id(fe) && !equal_id(fe,start_fe)); if (!valid_id(fe)) /* in case of open string facet */ set_facet_fe(f_id,inverse_id(last_fe)); else set_facet_fe(f_id,inverse_id(start_fe)); /* invert Lagrange vertices */ if ( (web.representation == SOAPFILM) && (web.modeltype == LAGRANGE) ) { vertex_id *v = get_facet_vertices(f_id); int row,spot = web.lagrange_order+1; for ( row = 1 ; row < web.lagrange_order ; row++ ) { int rowsize = web.lagrange_order+1-row; int i,mid = rowsize/2; for ( i = 1 ; i < mid ; i++ ) { vertex_id vv = v[spot+i]; v[spot+i] = v[spot+rowsize-i-1]; v[spot+rowsize-i] = vv; } spot += rowsize; } } /* reverse method orientations */ e_ptr = fptr(f_id); e_ptr->attr ^= NEGBOUNDARY; } evolver-2.30c.dfsg/doc/0000755000175300017530000000000011410765113015173 5ustar hazelscthazelsctevolver-2.30c.dfsg/doc/ringblob.htm0000644000175300017530000002520011410765113017502 0ustar hazelscthazelsct Surface Evolver Documentation - Torus Example

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Example: Ring around rotating rod

This example consists of a ring of liquid forming a torus around a rod rotating along its long axis (z axis) in weightlessness. The liquid has controllable contact angle with the rod. The interesting question is the stability of the ring as the spin increases.

The effect of the rotation is incorporated in the energy through an integral using the Divergence Theorem:

        ///                    
  E = - ||| (1/2) p r^2 w^2 dV 
	///B                   

        //
    = - ||       (1/2) p w^2 (x^2+y^2) z k . dA
        //bdry B

where B is the region of the liquid, r is radius from the axis, p is the fluid density and w is the angular velocity. Note the energy is negative, because spin makes the liquid want to move outward. This has to be countered by surface tension forces holding the liquid on the rod. If p is negative, then one has a toroidal bubble in a rotating liquid, and high spin stabilizes the torus. The spin energy is put in the datafile using the named quantity syntax (see below). "centrip" is a user-chosen name for the quantity, "energy" declares that this quantity is part of the total energy, "global_method" says that the following method is to be applied to the whole surface, "facet_vector_integral" is the pre-defined name of the method that integrates vector fields over facets, and "vector_integrand" introduces the components of the vectorfield.

The rod surface is defined to be constraint 1 with equation x^2 + y^2 = R^2, where R is the radius of the rod. The contact energy of the liquid with the rod is taken care of with an edge integral over the edges where the liquid surface meets the rod:

      //                          /                  /
  E = || -T cos(a) dA = -T cos(a) |  z ds = T cos(a) | (z/R)(yi - xj).ds
      //S                         /bdry S            / bdry S
Here S is the rod surface not included as facets in bdry B, T is the surface tension of the free surface, and a is the internal contact angle. A more intuitive way to arrive at this integral is to think in cylindrical coordinates and imagine the replaced surface of the rod between the contact line and z = 0 to be divided into thin vertical strips of height z and width R dtheta. The energy of a strip is -T cos(a) z R dtheta. Converting back to Cartesian coordinates, dtheta = (x dy - y dx)/(x^2 + y^2), so
 
                 //                                   //
  E =  -T cos(a) || z R (x dy - y dx)/R^2 = -T cos(a) || (z/R)(x dy - y dx)
                 //                                   //

Constraint 2 is a horizontal symmetry plane. By assuming symmetry, we only have to do half the work.

Constraint 3 is a one-sided constraint that keeps the liquid outside the rod. Merely having boundary edges on the rod with constraint 1 is not enough in case the contact angle is near 180 degrees and the liquid volume is large. Constraint 3 may be put on any vertices, edges, or faces likely to try to invade the rod. However, it should be noted that if you put constraint 3 on only some vertices and edges, equiangulation will be prevented between facets having different constraints.

Constraint 4 is a device to keep the vertices on the rod surface evenly spaced. Edges on curved constraints often tend to become very uneven, since long edges short-cutting the curve can save energy. Hence the need for a way to keep the vertices evenly spread circumferentially, but free to move vertically. One way to do that is with another constraint with level sets being vertical planes through the z axis at evenly spaced angles. Constraint 4 uses the real modulus function with arctangent to create a periodic constraint. Each refinement, the parameters need to be halved to cut the period in half. This is done by redefining the "r" refinement command at the end of the datafile. Note that autorecalc is temporarily turned off to prevent projecting vertices to the constraint when it is in an invalid state. Also note the pi/6 offset to avoid the discontinuity in the modulus function. pi/6 was cleverly chosen so that all refinements would also avoid the discontinuity.

One way of detecting stability is to perturb the torus and seeing if it evolves back to equilibrium. The datafile defines a command

    perturb := set vertex y y+.01 where not on_constraint 1
This sets the y coordinate of each vertex to y+.01. This command is defined in the "read" section at the end of the datafile, where you can put whatever commands you want to execute immediately after the datafile is loaded. To detect small perturbations, and get numerical values for the size of perturbations, the y moment of the liquid is calculated in the named quantity "ymoment". It is not part of the energy, as indicated by the info_only keyword. You can see the value with the `v' command.

A better way to check stability is to examine the eigenvalues of the Hessian matrix. First, evolve normally to reasonably near an equilibrium. Then use the hessian command several times to converge exactly to the equilibrium. Then use the command "ritz(0,5)" to display the 5 eigenvalues of the Hessian nearest 0. By gradually raising the spin and tracking the lowest eigenvalue, one can detect the onset of instability, where the lowest eigenvalue becomes 0. Note that the datafile toggles on hessian_normal so that the Hessian only considers perturbations normal to the surface.
ringblob The initial ringblob skeleton, with vertices and edges numbered. Taking advantage of symmetry, just the top half is represented.

// ringblob.fe

// Toroidal liquid ring on a rotating rod in weightlessness.
// Half of full torus
// Using second periodic constraint surface intersecting rod to
// confine vertices on rod to vertical motion. 

// Important note to user: Use only the 'rr' command defined at
// the end of this file to do refinement.  This is due to the
// nature of constraint 4 below.

// This permits drawing both halves of the ring
view_transforms 1
1 0 0 0
0 1 0 0
0 0 -1 0 
0 0 0 1

// Basic parameters.  These may be adjusted at runtime with the
// 'A' command.  Only spin is being adjusted in these experiments.
parameter rodr = 1  // rod radius
parameter spin = 0.0 // angular velocity
parameter angle = 30 // internal contact angle with rod
parameter tens = 1   // surface tension of free surface
#define rode (-tens*cos(angle*pi/180))  // liquid-rod contact energy
parameter dens = 1  // density of liquid, negative for bubble

// spin centripetal energy
quantity centrip energy global_method facet_vector_integral
vector_integrand:
q1: 0
q2: 0
q3: -0.5*dens*spin*spin*(x^2+y^2)*z

// y moment, for detecting instability
quantity ymoment info_only global_method facet_vector_integral
vector_integrand:
q1: 0
q2: 0
q3: y*z

// Constraint for vertices and edges confined to rod surface,
// with integral for blob area on rod
constraint 1
formula: x^2 + y^2 = rodr^2
energy: 
e1: -rode*z*y/rodr
e2: rode*z*x/rodr
e3: 0

// Horizontal symmetry plane
constraint 2 
formula: z = 0

// Rod surface as one-sided constraint, to keep stuff from caving in
// Can be added to vertices, edges, facets that try to cave in
constraint 3 nonnegative 
formula: x^2 + y^2 = rodr^2

// Constraint to force vertices on rod to move only vertically.
// Expressed in periodic form, so one constraint fits arbitrarily
// many vertices. Note offset to pi/6 to avoid difficulties with
// modulus discontinuity at 0.
parameter pp = pi/2    /* to be halved each refinement */
parameter qq = pi/6    /* to be halved each refinement */
constraint 4 
formula:  (atan2(y,x)+pi/6) % pp = qq

//initial dimensions
#define ht 2
#define wd 3

vertices
1  0   -wd 0  constraints 2    // equatorial vertices
2  wd    0 0  constraints 2
3  0    wd 0  constraints 2
4  -wd   0 0  constraint 2
5  0   -wd ht                 // upper outer corners
6  wd    0 ht
7  0    wd ht
8  -wd   0 ht 
9  0 -rodr ht constraints 1,4   // vertices on rod
10 rodr  0 ht constraints 1,4
11 0  rodr ht constraints 1,4
12 -rodr  0 ht constraints 1,4

edges
1   1 2 constraint 2  // equatorial edges
2   2 3 constraint 2
3   3 4 constraint 2
4   4 1 constraint 2
5   5 6               // upper outer edges 
6   6 7
7   7 8
8   8 5
9   9  10 constraint 1,4   // edges on rod
10  10 11 constraint 1,4
11  11 12 constraint 1,4
12  12  9 constraint 1,4
13   1  5        // vertical outer edges
14   2  6
15   3  7
16   4  8
17   5  9        // cutting up top face
18   6 10 
19   7 11 
20   8 12 

faces  /* given by oriented edge loop */
1   1 14 -5 -13 tension tens // side faces
2   2 15 -6 -14 tension tens  // Remember you can't change facet tension
3   3 16 -7 -15 tension tens  // dynamically just by changing tens; you have
4   4 13 -8 -16 tension tens  // to do "tens := 2; set facet tension tens"
5   5 18 -9 -17 tension tens  // top faces
6   6 19 -10 -18 tension tens
7   7 20 -11 -19 tension tens
8   8 17 -12 -20 tension tens 

bodies  /* one body, defined by its oriented faces */
1   1 2 3 4 5 6 7 8  volume 25.28

read  // some initializations
transforms off     // just show fundamental region to start with

// special refinement command redefinition
r :::= { autorecalc off;  pp := pp/2; qq := qq % pp; 'r'; autorecalc on; }

// a slight perturbation, to check stability
perturb := set vertex y y+.01 where not on_constraint 1

hessian_normal // to make Hessian well-behaved
linear_metric  // to normalize eigenvalues 


Liquid column example. Back to top of tutorial.
Back to top of Evolver documentation. Index. evolver-2.30c.dfsg/doc/single.htm0000644000175300017530000007740211410765113017200 0ustar hazelscthazelsct Surface Evolver Documentation: single letter main commands

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Single letter main commands

The oldest and most commonly used Surface Evolver commands are just single letters. Case is significant for these. Single letters are always interpreted as commands, so you may not use single letters for variable names. Single letter commands may be redefined.

Single letter commands may be summarized in five groups:

  • Reporting:
    • C Run consistency checks.
    • c Report count of elements.
    • e Extrapolate.
    • i Information on status.
    • v Report volumes.
    • v List extra attributes.
    • z Do curvature test.
  • Model characteristics:
    • A Display and set variables and various parameters.
    • a Toggle area normalization
    • b Set body pressures.
    • f Set diffusion constant.
    • G Set gravity.
    • J Toggle jiggling on every move.
    • k Set boundary gap constant.
    • M Toggle linear/quadratic model.
    • m Toggle fixed motion scale.
    • p Set ambient pressure.
    • Q Report or set quantities.
    • U Toggle conjugate gradient method.
    • W Homothety toggle.
  • Surface modification
    • g Go one iteration step. Often followed by a repetition count.
    • j Jiggle once.
    • K Skinny triangle long edge divide.
    • l Subdivide long edges.
    • N Set target volumes to actual.
    • n Notch ridges and valleys.
    • O Pop non-minimal edges.
    • o Pop non-minimal vertices.
    • r Refine triangulation.
    • t Remove tiny edges.
    • u Equiangulate.
    • V Vertex averaging.
    • w Weed out small triangles.
    • y Torus duplication.
    • Z Zoom in on vertex.
  • Output:
    • D Toggle display every iteration.
    • d Dump surface to datafile.
    • P Graphics output (geomview, Postscript, etc.).
    • s Screen display (native graphics).
  • Miscellaneous:


Single letter redefinition

It is possible to reassign a single letter to your own command by the syntax
 letter :::= command
but this should only be used in special circumstances, such as redefining 'r' to do additional actions along with refinement. The standard meaning can be restored with a null assignment:
 letter :::=
Use single quotes around the letter to get the standard meaning, i.e. 'r' will do a standard refine when r has been redefined. Redefinitions are cleared when a new surface is loaded. Be careful when using redefined commands in defining other commands. Redefinition is effective on execution of the redefinition command, not on parsing. Redefinition is not retroactive to uses in previously defined commands, but restoring the standard meaning is retroactive. Examples:
   V :::= { fix vertex[5]; 'V'; unfix vertex[5] }
   r :::= { divisions := divisions * 2; 
            hooke_length := hooke_length/2; 'r' }

A

Single letter main command. Lists the current values of variables and named quantity values, moduli, and targets. Only explicitly user-defined named quantities are shown unless show_all_quantities is toggled on. You are allowed you to enter new values (except quantity values). The new value is entered as the number of the variable (from the list) and the new value. Exit by hitting RETURN on a blank line. All changes that can be made here can also be made with assignment commands.

a

Single letter main command. Toggles area normalization of vertex forces and other gradients, to model motion by mean curvature. Meant to be used with a fixed scale factor. Be sure you have a small enough scale factor or else things tend to blow up. Reduce the scale factor temporarily after refinement, since triangle areas are cut by a factor of 4 but the old creases remain. When this option is ON, there is an optional check that can be made for facets that move too much. This is done by computing the ratio of the length of the normal change to the length of the old normal. If this exceeds the user-specified value, then all vertices are restored to their previous position. The user should reduce the motion scale factor and iterate again.


b

Single letter main command. Permits user to interactively change body prescribed volumes or pressures. Prints old value for each body and prompts for new.


C

Single letter main command. Runs various internal consistency checks. Synonym: check. If no problems, just prints "Checks completed." The number of errors found is stored in the variable check_count. The checks are:
  • Element list integrity - checks that data structures are intact.
  • Facet-edge check - that if a facet adjoins an edge, then the edge adjoins the facet, and that the three edges around a facet link up.
  • Facet-body check - whether adjacent facets have the same body on the same side.
  • Collapsed elements - check if endpoints of an edge are the same, and whether neighboring facets share more than one edge and two vertices.

c

Single letter main command. Prints count of elements and memory used. The memory is just the total of the element structures. On some systems, enabling "memdebug" will print more complete statistics on total memory usage. Synonym: counts.

D

Single letter main command. Toggles updating graphics every iteration or other surface change. Default is to display. Status can also be changed or queried with the autodisplay toggle.

d

Single letter main command. Dumps data to ASCII file in same format as initial data file. You will be prompted for a filename. An empty reponse will use the default dump name, which is the datafile name with a ".dmp" extension. Same as the dump command, except the dump command requires the filename as part of the command. Useful for checking your input is being read correctly, for saving current configuration, and for debugging.


e

Single letter main command. Extrapolates total energy to infinite refinement if at least two r commands have been done. Uses last energy values at three successive levels of refinement, and uses a power law fit for the error. For best results, use only the r command to refine, and iterate to complete convergence at each level of refinement. Synonym: extrapolate.

F

Single letter main command. Toggle logging of commands in file. If starting logging, you will be prompted for the name of a log file. Any existing file of that name will be appended to. Logging stops automatically when the surface is exited. Only correctly parsed commands are logged. Output resulting from commands is not logged. Responses to interactive single-letter commands are logged, but not responses to other interactive commands.

f

Single letter main command. Sets diffusion constant. Prints old and prompts for new.


G

Single letter main command. Toggles gravity on or off. Gravity starts ON if any body has a nonzero density; otherwise OFF. If followed by a value, sets gravity to that value. Otherwise prints old value of gravitational constant and prompts for new.

g

Single letter main command. Do one iteration step. The output consists of the number of iterations left (for people who wonder how close their 1000 iterations are to ending), the area and energy, and the scale factor. g is commonly used with an iteration count, as in "g 100". The user can abort repeated iterations by sending an interrupt to the process (SIGINT, to be precise; CTRL-C or whatever on your keyboard). As a special dispensation to lazy users, the syntax "gn" is equivalent to "g n". Synonym: go

H,h,?

Single letter main command. Prints a very primitive help message listing common commands. help is much better, as it accesses the full HTML documentation. Or best, use a separate HTML browser on this documentation.


i

Single letter main command. Prints miscellaneous information:
  • Name of datafile
  • Total energy
  • Total area of facets
  • Count of elements and memory usage
  • Area normalization, if on
  • LINEAR or QUADRATIC model
  • Whether conjugate gradient on
  • Order of numerical integration
  • Scale factor value and option (fixed or optimizing)
  • Diffusion option and diffusion constant value
  • Gravity option and gravitational constant value
  • Jiggling status and temperature
  • Gap constant (for gap energy, if active)
  • Ambient pressure (if ideal gas model in effect)


J

Single letter main command. Toggles jiggling on every iteration of the g command. If jiggling gets turned on, prompts for temperature value. Default temperature is the value of the temperature internal variable.


j

Single letter main command. Jiggles all vertices once. Meant to be used for simulated annealing. Useful for shaking up surfaces that get in a rut, especially crystalline integrands. You will be prompted for a "temperature" which is used as a scaling factor, if you don't give a temperature with the command. Default temperature is the value of the jiggle_temperature internal variable, which starts as 0.05. The actual jiggle is a random displacement of each vertex independently with a Gaussian distribution with deviation being the temperature times the mean edge length. See the longj command for a user-definable perturbation.


K

Single letter main command. Finds skinny triangles whose smallest angle is less than a specified cutoff. You will be prompted for a value if you don't give a value on the command line. Such triangles will have their longest edge subdivided. Should be followed with tiny edge removal (t) and equiangulation (u).


k

Single letter main command. Sets "gap constant" for gap energy for convex constraints. Adds energy roughly proportional to area between edge and boundary. You will be prompted for a value if you don't give a value on the command line. Normal values are on the order of magnitude of unity. Value k = 1 is closest to true area. Use 0 to eliminate the energy.


l (lower case L)

Single letter main command. Subdivides long edges, creating new facets as necessary. You will be prompted for a cutoff edge length, if you don't give a value with the command. Existing edges longer than the cutoff will be divided once only. Newly created edges will not be divided. Hence there may be some long edges left afterward. If you enter h, you will get a histogram of edge lengths. If you hit RETURN with no value, nothing will be done. It is much better to use the refine command r than to subdivide all edges. A synonym for "l value" is "edge_divide value". This command does not respect the no_refine attribute.


M

Single letter main command. Sets model type to linear , quadratic, or Lagrange. Changing from LINEAR to QUADRATIC adds vertices at the midpoints of each edge. Changing from QUADRATIC to LINEAR deletes the midpoints. Optionally takes new model type ( 1 for LINEAR, 2 for QUADRATIC, > 2 for Lagrange. ) on command line. Otherwise will prompt you.


m

Single letter main command. Toggles quadratic search for optimal global motion scale factor. If search is toggled OFF, you will be prompted for a fixed scale factor. If you give a value with the command, then you are setting a fixed scale factor.

N

Single letter main command. Set body target volumes to current actual volumes.


n

Single letter main command. Notching ridges and valleys. Finds edges that have two adjacent facets, and those facets' normals make an angle greater than some cutoff angle. You will be prompted for the cutoff angle (radians) if you don't give a value with the command. Qualifying edges will have the adjacent facets subdivided by putting a new vertex in the center. Should follow with equiangulation. In the string model, it will refine edges next to vertices with angle between edges (parallel orientation) exceeding the given value. Optionally takes cutoff angle on command line.


O

Single letter main command. Pop non-minimal edges. Scans for edges with more than three facets attached. Splits such edges into triple-facet edges. Splits propagate along a multiple edge until they run into some obstacle. This command is meant for surfaces that have equal tension on all facets. Also tries to pop edges on walls properly. For finer control on which edges to try, use the pop command. Try octa.fe for an example.


o

Single letter main command. Pop non-minimal vertices. This command scans the surface for vertices that don't have the topologies of one of the three minimal tangent cones that are legal in soap films (plane, triple edge, tetrahedral point). These are "popped" to proper local topologies. The algorithm is to replace the vertex with a sphere. The facets into the original vertex are truncated at the sphere surface. The sphere is divided into cells by those facets, and the largest cell is deleted, which preserves the topology of the complement of the surface. A special case is two cones meeting at a vertex; if the cones are broad enough, they will be merged, otherwise they will be split. In case of merging cones, if both cone interiors are defined to be part of the same body, then no facet is placed across the neck created by the merger; if they are different bodies or no bodies, a facet will be placed across the neck. Only vertices in the interior of a surface, not fixed or on constraints or boundaries, are tested. Try popstr.fe and octa.fe for examples.


P

Single letter main command. Produce graphics output files. "P" is for "picture". This brings up a menu, unless you give the menu option on the command line. For the 2D graphics options, the view is the same as seen with the s command. For options that output to a file, you will be prompted for a filename. Some other possible options you may be asked:
Display raw cells, connected bodies or clipped cells? (0,1,2)
If you are doing torus model, you will be asked for a display option, unless you have already set one.
Do normal interpolation?
Some formats are capable of doing interpolation between vertex normals for smoother shading, and you will be asked if you want to do that.
Do inner, outer, or all surfaces? (i,o,a)
When bodies are present, there is an option to plot the inner surfaces(adjacent to two bodies), outer surfaces (adjacent to 0 or 1 bodies), or all surfaces of the bodies.
Do body colors?
This gives you a chance to color the bodies differently. If you do, the current colormap file will be used to color the bodies according to id number. This scheme is a relict of early days of the Evolver, and it is suggested that you use the color, frontcolor and backcolor facet attributes instead.
Enter name of colormap file:
If there is no current colormap file, you will be prompted. The colormap file has the format of RGB values, one set per line, values between 0 and 1. (This map may not be effective on all devices.)
Thicken? (n | y [thickness(0.001)])
You may also be asked if you want thickening. If you do, each facet will be recorded twice, with opposite orientations, with vertices moved from their original positions by the thickening distance (which the option lets you enter) in the normal direction. The normal used at each vertex is the same as used for normal interpolation, so all the facets around a planar vertex will have that vertex moved the same amount. Triple junctions will be separated. Thickening is good for rendering programs that insist on consistently oriented surfaces, or that have problems with show-through of the backside of a surface. Choosing 'y' or 'n' will reset the thicken toggle. If you answer 'y', you can optionally specify the thickness, which defaults to the value of the thickness internal variable.
The menu choices for types of output are:
1. Pixar file
For Pixar format. Actually same format as option 2.
2. OOGL file
This is a file in a file format used by geomview, which is Object Oriented Graphics Language. Suitable for direct input into geomview.
3. PostScript file
Generates a PostScript file.
4. Triangle file
A private format file, just listing data. Not much use any more.
5. Softimage file
Output file in Softimage format.
8. Start simultaneous geomview
If you have the geomview package installed, this command will start geomview and display the current surface. Changes to the surface are automatically displayed unless autodisplay is toggled off.
9. End simultaneous geomview
Terminates any geomview program or pipe.
A. Start OOGL pipe.
Geomview uses a pipe interface at the moment. This starts a named pipe with geomview output, but without invoking geomview. You will be told the name of the pipe, and it is up to you to start a pipe reader. Evolver blocks until a pipe reader is started. This is useful for having a second instance of Evolver feed a second surface to geomview by having geomview load the pipe. Also good for checking exactly what Evolver is sending to geomview. The geompipe command does the same thing. Terminate the pipe with "P 9". Note that only one geomview output at a time is possible, so you can't have a geomview display and separate pipe active at the same time.
B. End OOGL pipe.
Same as option 9.

p

Single letter main command. Sets ambient pressure in ideal gas model. If you don't give a value with the command, you will be prompted. A large value gives more incompressible bodies.


Q

Single letter main command. Report current values of user-defined method instances and named quantities. If the show_all_quantities toggle is on, then internal quantities and method instances are also shown. This is particularly informative if convert_to_quantities has been done (same as -q command line option), since then internal values such as constraint integrals are in the form of method instances.


q

Single letter main command. Exit program. You will be given a chance to have second thoughts. You may also load a new datafile. Automatically closes graphics if you really quit. Does not save anything.


r

Single letter main command. Refines the triangulation. Edges are divided in two, and facets are divided into four facets with inherited attributes. Edges and facets with the no_refine attribute set are not refined. Reports the number of element structures and amount of memory used by those structures.


s

Single letter main command. Shows the surface with screen graphics. Goes into the graphics command mode. Torus model surfaces have display options you will be asked for the first time. The graphics window may be closed with the close_show command.


t

Single letter main command. Eliminates tiny edges and their adjacent facets. You will be prompted for a cutoff edge length if you don't give a value with the command. If you enter h, you will get an edge length histogram. If you hit RETURN without a value, nothing will happen. Some edges may not be eliminable due to being FIXED or endpoints having different attrtibutes from the edge.


U

Single letter main command. This toggles conjugate gradient mode, which will usually give faster convergence to the minimum energy than the default gradient descent mode. The only difference is that motion is along the conjugate gradient direction. The scale factor should be in optimizing mode. The conjugate gradient history vector is reset after every surface modification, such as refinement or equiangulation. After large changes (say, to volume), run without conjugate gradient a few steps to restore sanity.


u

Single letter main command. This command, called "equiangulation", tries to polish up the triangulation. In the soapfilm model, each edge that has two neighboring facets (and hence is the diagonal of a quadrilateral) is tested to see if switching the quadrilateral diagonal would make the triangles more equiangular. For a plane triangulation, a fully equiangulated triangulation is a Delaunay triangulation, but the test makes sense for skew quadrilaterals in 3-space also. It may be necessary to repeat the command several times to get complete equiangulation. The edgeswap command can force flipping of prescribed edges.

In the simplex model, equiangulation works only for surface dimension 3. There, two types of move are available when a face of a tetrahedron violates the Delaunay void condition: replacing two tetrahedra with a common face by three, or the reverse operation of replacing three tetrahedra around a common edge by two, depending on how the condition is violated. This command is inoperative in the string model.


V

Single letter main command. Vertex averaging. For each vertex, computes new position as area-weighted average of centroids of adjacent facets. Only adjacent facets with the same constraints and boundaries are used. Preserves volumes, at least to first order. See the rawv command for vertex averaging without volume preservation, and rawestv for ignoring likeness of constraints. Vertices on triple edges are averaged only with adjacent vertices on the triple edge, and then only if there are exactly two neighboring triple edge vertices. Fixed vertices do not move. Vertices on constraints are projected back onto their constraints. The computation of new vertex positions are all done before any vertex is moved. For sequential movement applied to a subset of vertices, see the vertex_average command.


v

Single letter main command. Shows target volume, actual volume, and pressure of each body. Also shows named quantities. Pressures are really the Lagrange multipliers. Pressures are computed before an iteration, so the reported values are essentially are one iteration behind. Synonym: show_vol


W

Single letter main command. Toggles homothety. If homothety ON, then after every iteration, the surface is scaled up so that the total volume of all bodies is 1. Meant to be used on surfaces without any blowup constraints of any kind, to see the limiting shape as surface collapses to a point.


w

Single letter main command. Tries to weed out small triangles. You will be prompted for the cutoff area value if you don't give a value with the command. If you enter h, you will get a histogram of areas to guide you. If you hit RETURN with no value, nothing will be done. Some small triangles may not be eliminable due to constraints or other such obstacles. The action is to eliminate an edge on the triangle, eliminating several facets in the process. Edges will be tried for elimination in shortest to longest order. WARNING: Although checks are made to see if it is reasonable to eliminate an edge, it is predicated on facets being relatively small. If you tell it to eliminate all below area 5, Evolver may eliminate your entire surface without compunction.


X

Single letter main command. List the current extra attributes, including name, dimension, type, size in bytes, and offset within the element structure. Some internal attributes are also listed, whose names begin with a double underscore.

x

Single letter main command. Same as q. Exit Evolver, or start new surface.


y

Single letter main command. Torus duplication. In torus model, prompts for a period number (1,2, or 3) and then doubles the torus unit cell in that direction. Useful for extending simple configurations into more extensive ones.


Z

Single letter main command. Zooms in on a vertex. Asks for vertex number and radius. Number is as given in vertex list in datafile. Beware that vertex numbers change in a dump (but correct current zoom vertex number will be recorded in dump). Eliminates all elements outside radius distance from vertex 1. New edges at the radius are made FIXED. Meant to investigate tangent cones and intricate behavior, for example, where wire goes through surface in the overhand knot surface. Zooming is only implemented for surfaces without bodies.


z

Single letter main command. Do curvature test on QUADRATIC model. Supposed to be useful if you're seeking a surface with monotone mean curvature. Currently checks angle of creases along edges and samples curvature on facet interiors. Orientation is with respect the way facets were originally defined. Deprecated.


Back to top of Surface Evolver documentation. Index. evolver-2.30c.dfsg/doc/commands.htm0000644000175300017530000035061011410765113017513 0ustar hazelscthazelsct Surface Evolver Documentation: commands

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Surface Evolver command language

The Surface Evolver has an interactive command language. It has variables, expressions, subroutines, conditionals,iteration constructs, subroutines and functions with arguments, local variables, and arrays. But not structures or objects or pointers. Variables are either floating point, string, or subroutine names. The Evolver command language continues to grow by accretion, and it looks like it's headed towards a full programming language.

Command input

The Surface Evolver command interpreter reads from an input stream, which may be from the end of the datafile, from a file given on the system command line, from stdin (the terminal), or from a file given in a READ command.

The interactive command prompt is "Enter command: ".

Commands are read one at a time, parsed, and executed. By default, a line is expected to contain a complete command, so no special end-of-command token is needed.

Multi-line commands may be entered by enclosing them in braces {...}. If a line ends while nested in braces or parenthesis, Evolver will ask for more input with the prompt "more> ". It will also ask for more if the line ends with certain tokens (such as `+') that cannot legally end a command. Unclosed quotes will also ask for more, and embedded newlines will be omitted from the final string. Explicit continuation to the next line may be indicated by ending a line with a backslash (linesplicing). You may want to use the read command to read long commands from a file, since there is no command line editing.

Successfully parsed commands are saved in a history list, up to 100 commands. They may be accessed with !! for the last command or !string for the latest command with matching initial string. !n will repeat a command by history list number. The command will be echoed. The saved history list may be printed with the history command.

Some single-letter commands require interactive input. For those, there are equivalent commands that take input information as part of the command. This is so commands may be read from a script without having to put input data on additional lines after the command, although that can still be done for the single-letter versions.

General note: Some commands will prompt you for a value. A null response (just RETURN) will leave the old value unchanged and return you to the command prompt. On options where a zero value is significant, the zero must be explicitly entered. Commands that need a real value will accept an arbitrary expression.

Many commands that change the surface or change the model will cause energies and volumes to be recalculated. If you suspect a command has not done this, the recalc command will recalculate everything. It will also update any automatic display.

In the command syntax descriptions, keywords are shown in upper case, although case is irrelevant in actual commands, except for single-letter commands.


Command separator

Several commands on the same line or within a compound command must be separated by a semicolon. A semicolon is not needed after the last command, but won't hurt. Do not use a semicolon after the first command in an IF THEN ELSE command. Do use a semicolon to separate a compound command from the next. Example:
      g 10; r; g 10; u  
      myc := { print 5;
               g 10;
               if qwer < foo then print 3 else { print 2; print 4; };
               aa := 23
             }

Compound commands

Curly braces group a list of commands into one command. The commands are separated by semicolons. A semicolon is needed after a compound command within a compound command to separate it from following commands (note this is different from the C language). Do not use a semicolon after the first command in an IF THEN ELSE command. An empty compound command {} is legal. Examples:
  if ( foo > 4 ) then { g;g;u; } else print 4;
  while ( xx < 2 ) do { g; xx := xx + 1 }
  aa := { g 5; 
          foreach vertex vv do {
              printf "id: %g  coord: %f %f %f\n",id,x,y,z;
              count := count + 1;
              };  // note semicolon here!
          printf "done.\n"
        }

Command repetition

Certain types of commands can be repeated a number of times by following the command with an integer. Be sure to leave a space between a single-letter command and the expression lest your command be interpreted as one identifier. To avoid several types of confusion, only certain types of commands are repeatable: Examples:
   g 10
   U 2
   { g 20; u; V; u } 20
   myc := { g 20; V }; myc 10

Assignment commands

The assignment operator := can be used to assign values to various entities. Note that ':=' is used for assignment, not '='. The C-style arithmetic assignments +=, -=, *=, and /= work. For example, val += 2 is equivalent to val := val + 2. These also work in other assignment situations where I thought they made sense, such as attribute assignment. Possible assignments:

Permanent assignment commands

The permanent assignment operator ::= can be used to make assignments to variables and commands that are not forgotten when a new datafile is loaded. Such a command may only make reference to permanent variables, permanent commands, and internal variables. See permload command for an example of use.

User-defined commands

Users may define their own commands with the syntax
   identifier := command
The shortest complete command on the right side is used. Thus "gg := g 10; u" would give gg the same value as "gg := g 10". It is wise and strongly advised to use braces to enclose the command on the right side so the parser can tell it's a command and not an expression. Also multiline commands then don't need linesplicing. Do not try to redefine single-letter commands this way; use :::=. Example:
   gg := {g 10; u}

User-defined procedures

Users may define their own procedures with arguments with the syntax
   procedure identifier ( type arg1, type arg2, ... )
   { commands }
Right now the implemented types for arguments are real and integer. The argument list can be empty. Example:
   procedure proc1 ( real ht, real wd )
   { 
     prod := ht*wd;   // this would make prod a global variable
     return; 
   }
Note that the procedure arguments act as local variables, i.e. their scope is the procedure body, and they have stack storage so procedures may be recursive. Procedure prototypes may be used to declare procedures before their bodies are defined with the same syntax, just replacing the body of the procedure with a semicolon. Prototype syntax:
   procedure identifier ( type arg1, type arg2, ... );
Note that a procedure is used as a command, and a function is used in a numerical expression.

User-defined functions

Users may define their own functions that have arguments and return values with the syntax
   function type identifier ( type arg1, type arg2, ... )
   { commands }
Right now the implemented types for return value and arguments are real and integer. The argument list can be empty. The return value is given in a return expr statement. Example:
   function real func1 ( real ht, real wd )
   { local prod;
     prod := ht*wd;
     return prod;
   }
Note that the function arguments act as local variables, i.e. their scope is the function body, and they have stack storage so functions may be recursive. Function prototypes may be used to declare functions before their bodies are defined with the same syntax, just replacing the body of the function with a semicolon. Prototype syntax:
   function type identifier ( type arg1, type arg2, ... );
Note that a procedure is used as a command, and a function is used in a numerical expression.

Variable assignment

Values can be assigned to variables. Values can be numeric or string. The variable names must be two or more letters, in order they not be confused with single-letter commands. Syntax:
   identifier := expr
   identifier := stringexpr
If the variable does not exist, it will be created. These are the same class of variables as the adjustable parameters in the datafile, hence are all of global scope and may also be inspected and changed with the 'A' command. Examples:
   maxlen := max(edge,length)
   newname := sprintf "file%03g",counter 

Local scope

The scope of a variable name may be restricted to a compound command block by declaring the name to be local. Example:
   do_stuff := { 
     local inx;
     for ( inx := 1 ; inx < 5 ; inx += 1 )
     { local jnx;
       jnx := inx*2;
       print jnx;
     };
   }
Using local variables is good for avoiding pollution of global namespace and for writing recursive functions (storage space for locals is allocated on the runtime stack). Note that the local declaration is a scope declaration, not a type declaration. Also, it cannot be combined with initialization of the variable (yet), and there is one name per declaration. Function arguments also act as local variables.

Redirecting and piping command output

The output of a command can be redirected to a file with the unix-style append symbol '>>'. This appends output to the file; it does not erase any existing file. Syntax:
   command >> stringexpr
The output of a command can be redirected to a file with the symbol '>>>'. This overwrites an existing file. Syntax:
   command >>> stringexpr
Redirection with `>' is not available due to the use of `>' as an comparison operator. The output of a command can be piped to a system command using the unix-style pipe symbol `|'. Syntax:
   command | stringexpr
The stringexpr is interpreted as a system command.

Examples:

   list facets | "more"
   list vertices | "tee vlist" ; g 10 | "tee g.out"
   { {g 10; u } 20 } >> "logfile"
   {foreach facet do print area} | "cat > areafile"

Control structures

The following control structures are available in the Evolver commmand language:

IF ... THEN ... ELSE

Commands may be conditionally executed by the syntax
   IF expr THEN command 

   IF expr THEN command ELSE command
expr is true if nonzero. Parentheses around expr are not needed, but do not hurt. Do not use a semicolon to end the first command. Example:
   if max(edges,length) > 0.02 then {r; g 100} else g 4 

WHILE ... DO ...

Command syntax for pretest iteration loop. Syntax:
  WHILE expr DO command
expr is true if nonzero. Parentheses around expr are not needed, but do not hurt. Example:
   count := 0
   while count < 10 do { g 10; u; print total_energy; count := count + 1 }

DO ... WHILE ...

Command syntax for posttest iteration loop. Syntax:
   DO command WHILE expr
expr is true if nonzero. Parentheses around expr are not needed, but do not hurt. Example:
   do { oldenergy := total_energy; g 10 } 
   while (oldenergy-total_energy < 1e-6)

FOR

This is the Evolver's version of the C language "for" construct. Syntax:
  FOR ( command1 ; expr ; commmand2 ) command3
The first command is the initialization command; note that it is a single command, rather than an expression as in C. If you want multiple commands in the initialization, use a compound command enclosed in curly braces. The middle expression is evaluated at the start of each loop iteration; if its value is true (i.e. nonzero) then the loop is executed; otherwise the flow of control passes to the command after command3. The command2 is executed at the end of each loop iteration; again, it is a single command. The body of the loop is the single command command3, often a compound] command. Note: Command3 should end with a semicolon, unless it is the if clause of an if-then statement. Examples:
   for ( inx := 1 ; inx < 3 ; inx += 1 )
      print facet[inx].area;
   for ( {inx := 1; factorial := 1; } ; inx < 7 ; inx += 1 )
   { factorial *= inx;
     printf "factorial %d is %d\n",inx,factorial;
   };

FOREACH

Repeat a command for each element produced by an element generator. Syntax:
FOREACH generator DO command  
Examples:
 foreach vertex do print x^2 + y^2 + z^2

 foreach edge ee where ee.dihedral > .4 do {
   printf "id %g\n",id; 
   foreach ee.vertex do printf " %g %g %g\n",x,y,z; 
 }

BREAK

Command syntax for exiting loops. Syntax:
   BREAK

   BREAK n
The first form exits the innermost current loop. The second form exits n loops. Note: Commands with repetition counts do not qualify as loops. Example:
   foreach vertex do { print x; if y < 0 then break; print z }

CONTINUE

Command syntax for skipping the rest of the body of the current loop, and going to the next iteration of the loop.
   CONTINUE

   CONTINUE n
The second form exits the innermost n-1 loops, and skips to the loop control of the nth innermost loop. Note: Commands with repetition counts do not qualify as loops. Example:
   foreach vertex vv do {
       foreach vv.edge do {
         print length; if length < .4 then continue 2; 
       }
   }
 

RETURN

Command syntax for exiting the current command. This is essentially a return from a subroutine. If the current command is a user-defined command called by another command, the parent command continues. Example:
  if ( acc < 1.e-10 ) then return;

Element generators

One feature different from ordinary C is the presence of generators of sets of geometric elements. These occur wherever an element type (vertices, edges, facets, bodies, singular or plural) appears in a command. Attributes of the iteration element may be used later in the command. The general form of a generator is
  elementgen name where expr
elementgen may be
  • a multiple element generator, which can be
    • an element type, vertex, edge, facet, or body, which generates all elements of that type in the surface. But new elements created during a loop will not be generated, so "refine edges" will refine all existing edges just once.
    • a single element subelement. The implemented subelements are:
      • of a vertex: edge, facet (in no particular order)
      • of an edge: vertex (in tail, head order), facet (in geometric order)
      • of a facet: vertex, edge, body (all in order around the facet)
      • of a body: facet (in no particular order)
  • a single element, which can be
    • an element name of an active generator
    • an indexed element type, vertex, edge, facet, or body. Indexing starts at 1. The index may be negative, in which case the generated element has negative orientation.
    • an indexed subelement of an element (error if no element of that index). Indexing starts at 1. The indexing is the same as the order produced by the foreach generator. Indexed subelements of an edge or facet follow the orientation of the edge or facet.
name is an optional identifier which can be used in the body of a loop to refer to the generated element. expr is interpreted as a boolean expression, 0 for false, nonzero for true. Only elements for which expr is true are generated. The where expr clause is optional. The innermost generator generates a default element, which can have its attributes referred to just by attribute name. But be sure to remember that in a nested iteration, an unqualified element type generates all elements of that type, not just those associated with the parent element. Examples:
   list facet where color == red

   foreach edge ee where ee.length < .3 do list ee.vertex

   print facet[2].edge[1].vertex[2].id

   foreach facet ff do { printf "facet %g:\n"; list ff.edge }

   print max(edge where on_constraint 1, length)

General commands

Many commands in the Evolver command language have a sentence-like structure and start with a verb.

ABORT

Main prompt command. Causes immediate termination of the executing command and returns to the command prompt. Meant for stopping execution of a command when an error condition is found. There will be an error message output, giving the file and line number where the abort occurred, but it is still wise to have a script or procedure or function print an error message using errprintf before doing the abort command, so the user knows why.

ADDLOAD

Main prompt command. Loads a new datafile without deleting the current surface, permitting the simultaneous loading of multiple copies of the same datafile or different datafiles. Syntax:
   ADDLOAD string
where string is either a sting literal in double quotes, or a string variable name such as datafilename. Elements in the new datafile are re-numbered to not conflict with existing elements. This is actually the same as the default behavior of Evolver when loading a single datafile. Thus the -i command line option or the keep_originals keyword is not obeyed for the new datafile. The read section of the new datafile is not executed; this permits a datafile to use the addload commnand in its read section to load more copies of itself. The loading script is responsible for all initialization that would ordinarily be done in the read section of the new datafile. Declarations in the top section of the new datafile will overwrite any existing declarations. This is usually not a problem when loading multiple copies of the same datafile, but requires attention when loading different datafiles. For example, numbered constraints are a bad idea; use named constraints instead. See the sample datafile addload_example.fe for an example of how to load and distinguish between multiple copies of the same surface.

AREAWEED

Main prompt command. For deleting facets with less than a given area. Syntax:
   AREAWEED expr
Same as 'w' command, except does not need interactive response. Also same as "delete facets where area < expr". Examples:
   areaweed 0.001
   areaweed 2*min(facet,area)

BINARY_OFF_FILE

Main prompt command. Produces one frame file for my evmovie 3D movie program. Syntax:
 BINARY_OFF_FILE string
where string is the name of the output file, either a double-quoted string, a string variable, or a string-generating expression (typically using sprintf).

BINARY_PRINTF

Main prompt command. For printing formatted binary output to files. Syntax:
BINARY_PRINTF string,expr,expr,...
Prints to standard output using a binary interpretation of the standard C formats:
  • %c one byte
  • %d two byte integer
  • %ld four byte integer
  • %f four byte float
  • %lf eight byte float
  • %s string, without the trailing null
  • non-format characters are copied verbatim as single bytes.
The byte order for numbers can be set with the big_endian or little_endian toggles. NOTE: Either big_endian or little_endian must be set for binary_printf to work! The format string can be a string variable or a quoted string. There is a limit of 1000 characters on the format string, otherwise there is no limit on the number of arguments. Meant to be use with redirection to a file. In Microsoft Windows, the output file type is temporarily changed from TEXT to BINARY so newline bytes don't get converted. Example:
  binary_printf "%ld%ld%ld",vertex_count,edge_count,facet_count >>"out.bin"

BODY_METIS

Main prompt command. Partitions the set of bodies into n parts using the METIS library of Karypis and Kumar, if this library has been compiled into the Evolver. The partition number of each body is left in its extra attribute bpart (which will be created if it does not already exist). BODY_METIS works only in the soapfilm model; for the string model, partition facets using metis or kmetis. BODY_METIS uses the PMETIS algorithm. Meant for experiments in partitioning the surface for multiprocessors. Syntax:
 BODY_METIS n
Example:
  body_metis 50;  // supposing we have thousands of bodies, say
  for each body bb do set bb.facet frontcolor (bb.bpart imod 15)+1;

BREAKPOINT

Main prompt command. The user may set a breakpoint in an already loaded script with the "set breakpoint" command. The syntax is
  BREAKPOINT scriptname linenumber
where scriptname is the name of the function or procedure and linenumber is the line number in the file where the breakpoint is to be set. There must be executable code on the line, or you will get an error. linenumber may be an expression.

Breakpoints may be unset individually with

  UNSET BREAKPOINT scriptname linenumber
or as a group with
  UNSET BREAKPOINTS
When a breakpoint is reached, Evolver will enter into a subcommand prompt, at which the user may enter any Evolver commands (although some commands, such as load would be very unwise). To exit from the subcommand prompt, use q or exit or quit.

CHDIR

Main prompt command. Changes the current directory, used for searching for files before EVOLVERPATH is used. Syntax:
   CHDIR stringexpr
In MS-Windows, use a front slash '/' or a double backslash '\\' instead of a single backslash as the path character. Example:
  chdir "/usr/smith/project" 

CLOSE_SHOW

Main prompt command. Closes the native graphics window started by the `s' or SHOW commands. Does not affect geomview version. Synonym: show_off.

DEFINE

Main prompt command. For runtime defining of variables, arrays, level set constraints, boundaries, named quantities, named method instances, and extra attributes of elements. The syntax for defining single variables is
    DEFINE variable type 
where type is REAL, INTEGER, or STRING. Note that this way of declaring a variable does not take an initial value; thus it is a way of making sure a variable is defined without overwriting an existing value of the variable. The syntax for defining arrays and extra attributes is the same as in the top of the datafile; for constraints, boundaries, named quantities, and method instances, it is the same as in the top of the datafile except the word "define" comes first. Multi-line definitions should be enclosed in brackets and terminated with a semicolon. Or they can be enclosed in quotes and fed to the exec command. Of course, using exec means the parser doesn't know about the define until the exec is executed, so you cannot use the defined item in commands until then. It is legal to re-define an existing array or array extra attribute with different dimensions (but the same number of dimensions); data will be preserved as best as possible in the resized array. An array may be given the dimension 0 to free its memory allocation. Examples:
  define fudge_factor real
  define pqlist real[imax][jmax]
  define edge attribute charlie real 
  define vertex attribute oldx real[3] 
  define facet attribute knots real[5][5][5] 
  { define constraint frontcon formula z = 0
    energy:
    e1: -y/2
    e2:  x/2
    e3:  0; }
  exec "define boundary newboundary parameters 1
    x: sin(p1)
    y: cos(p1)
    z: 3"
  exec "define quantity qarea info_only method facet_area global"

DELETE

Main prompt command. For collapsing edges or facets. Syntax:
   DELETE  generator
Deletes edges by shrinking the edge to zero length (as in the tiny edge weed command t) and facets by eliminating one edge of the facet. Facet edges will be tried for elimination in shortest to longest order. Edges will not be deleted if both endpoints are fixed, or both endpoints have different constraints or boundaries from the edge. DELETE will also fail if it would create two edges with the same endpoints, unless the force_deletion toggle is on; also see star_finagling. DELETE maintains the continuity and connectedness of the surface, as opposed to DISSOLVE. Example:
      delete facets where area < 0.0001

DELETE_TEXT

Command to delete a text string from the graphics display. Syntax:
   delete_text(text_id)
where text_id is the value returned by the call to display_text that created the string.

DIRICHLET

Main prompt command. Does one iteration of minimizing the Dirichlet integral of the surface. The current surface is the domain, and the Dirichlet integral is of the map from the current surface to the next. This is according to a scheme of Konrad Polthier and Ulrich Pinkall [PP]. At minimum Dirichlet integral, the area is minimized also. Works only on area with fixed boundary; no volume constraints or anything else. Seems to converge very slowly near minimum, so not a substitute for other iteration methods. But if you have just a simple soap film far, far from the minimum, then this method can make a big first step. DIRICHLET_SEEK will do an energy-minimizing search in the direction.

DIRICHLET_SEEK

Main prompt command. Calculates a motion as in the DIRICHLET command, but uses this as a direction of motion instead of as the motion itself. DIRICHLET_SEEK then uses a line-search along this direction to find a minimum of energy.

DISPLAY_TEXT

Main prompt command. Causes the display of simple text on the graphics display. Currently implemented for OpenGL and PostScript graphics. Syntax:
  text_id := display_text(x,y,string)
The x,y coordinates of the start of the string are in window units, i.e. the window coordinates run from (0,0) in the lower left to (1,1) in the upper right. The return value should be saved in a variable in case you want to delete the text later; even if you don't want to delete it, you must have something on the left of the assignment for syntax purposes. No font size control or font type or color implemented. Meant for captioning images, for example a timer in frames of a movie.

DISSOLVE

Main prompt command. Removes elements from the surface without closing the gap left. Syntax:
  DISSOLVE  generator
The effect is the same as if the line for the element were erased from a datafile. Hence no element will be dissolved that is used by a higher dimensional element. (There are three exceptions: dissolving an edge on a facet in the string model, and dissolving a facet on one body or with both adjacent bodies the same in the soapfilm model.) Thus "dissolve edges; dissolve vertices" is safe because only unused edges and vertices will be dissolved. No error messages are generated by doing this. Good for poking holes in a surface. Example:
  dissolve facets where original == 2; 
  dissolve edges; dissolve vertices
Thus "dissolve edges; dissolve vertices" is safe because only unused edges and vertices will be dissolved. No error messages are generated by doing this.

DUMP

Main prompt command. Dumps current surface to named file in datafile format. Syntax:
  DUMP filename
The filename is a string. With no filename, dumps to the default dump file, which is the current datafile name with ".dmp" extension. Same as the 'd' command, except 'd' requires a response from the user for the filename. Examples:
   dump "foo.dmp"
   dump sprintf "%s.%g.dmp",datafilename,counter

DUMP_MEMLIST

Main prompt command. Lists the currently allocated memory blocks. For my own use in debugging memory problems.

EDGE_MERGE

Main prompt command. Merges two edges into one in a side-by-side fashion. Meant for joining together surfaces that bump into each other. Should not be used on edges already connected by a facet, but merging edges that already have a common endpoint(s) is fine. Syntax:
  edge_merge(integer,integer)
Note the arguments are signed integer ids for the elements, not element generators. The tails of the edges are merged, and so are the heads. Orientation is important. Example:
   edge_merge(3,-12)

EDGESWAP

Main prompt command. For changing the endpoints of edges. Syntax:
 EDGESWAP edgegenerator
If any of the qualifying edges are diagonals of quadrilaterals, they are flipped in the same way as in equiangulation, regardless of whether equiangularity is improved. "edgeswap edge" will try to swap all edges, and is not recommended, unless you like weird things. Various conditions will prevent an edge from being swapped:
  • The edge is fixed.
  • There are not exactly two facets adjacent to the edge.
  • The adjacent facets do not have equal density.
  • The adjacent facets are not on the same level set constraints as the edge.
  • The adjacent facets are not on the same parametric boundary as the edge.
  • Swapping would create an edge with both endpoints the same (a loop).
  • Swapping would create two edges with the same endpoints (an "ear").
All but the first two reasons print messages. This is a compromise between informing the user why edges were not switched and preventing a cascade of messages. When edge swapping is invoked through the 'u' command, none of these messages are printed. Examples:
 edgeswap edge[22] 

 edgeswap edge where color == red

EDGEWEED

Main prompt command. Deletes edges shorter than given value. Syntax:
 EDGEWEED expr
 
Same as 't' command, except does not need interactive response. Same as "delete edge where length < expr".

EIGENPROBE

Main prompt command. For finding the number of eigenvalues of the energy Hessian that are less than, equal to, and greater than a given value. Syntax:
EIGENPROBE expr
EIGENPROBE(expr,expr)
The first form prints the number of eigenvalues of the energy Hessian that are less than, equal to, and greater than expr. It is OK to use an exact eigenvalue (like 0, often) for the value, but not really recommended. Useful for probing stability. Second form will further do inverse power iteration to find an eigenvector. The second argument is the limit on the number of iterations. The eigenvalue will be stored in the last_eigenvalue internal variable, and the eigenvector can be used by the move command. The direction of the eigenvector is chosen to be downhill in energy, if the energy gradient is nonzero.

EQUIANGULATE

Main prompt command. This command tests the given edges to see if flipping them would improve equiangularity. It is the u command applied to a specified set of edges. It differs from the edgeswap command in that only edges that pass the test are flipped. Syntax:
  EQUIANGULATE edge_generator
Examples:
  equiangulate edge[3];
  equilangulate edge where color == red;

ERRPRINTF

Main prompt command. Same as printf, except it sends its output to stderr instead of stdout. Useful in reporting error messages in scripts that have their output redirected to a file.

EXEC

Main prompt command. Executes a command in string form. Good for runtime generation of commands. Syntax:
   EXEC stringexpr
Example:
   exec sprintf "define vertex attribute prop%d real",propnumber

EXPRINT

Main prompt command. Prints the original input string defining a user-defined command, including comments. Syntax:
   EXPRINT commandname
Example:
Enter command: aa := { print 5; /* this is a test */ }
Enter command: exprint aa
 { print 5; /* this is a test */ }

FACET_MERGE

Main prompt command. Merges two soapfilm-model facets into one in a side-by-side fashion. Meant for joining together surfaces that bump into each other. The pairs of vertices to be merged are selected in a way to minimize the distance between merged pairs subject to the orientations given, so there are three choices the algorithm has to choose from. It is legal to merge facets that already have some endpoints or edges merged. Syntax:
  facet_merge(integer,integer)
Note the syntax is a function taking signed integer facet id arguments, not element generators. IMPORTANT: The frontbody of the first facet should be equal to the backbody of the second (this includes having no body); this is the body that will be squeezed out when the facets are merged. If this is not true, then facet_merge will try flipping the facets orientations until it finds a legal match. Example:
   facet_merge(3,-12)

FIX

Main prompt command. For setting the FIXED attribute of elements. Syntax:
   FIX generator
Example:
   fix vertices where on_constraint 2
Can also convert a parameter from optimizing to non-optimizing. Example:
  fix radius
Can also convert a named quantity from info_only to fixed. See also unfix.

FLUSH_COUNTS

Main prompt command. Causes the printing of various internal counters that have become nonzero. The counters are: equi_count, edge_delete_count, facet_delete_count, edge_refine_count, facet_refine_count, notch_count, vertex_dissolve_count, edge_dissolve_count, facet_dissolve_count, body_dissolve_count, vertex_pop_count, edge_pop_count, facet_pop_count, pop_tri_to_edge_count, pop_edge_to_tri_count, pop_quad_to_quad_count, where_count, edgeswap_count, fix_count, unfix_count, t1_edgeswap_count, and notch_count. Normally, these counts are accumulated during the execution of a command and printed at the end of the command. Flush_counts can be used to display them at some point within a command. Flush_counts is usually followed by reset_counts, which resets all these counters to 0.

FREE_DISCARDS

Main prompt command. Frees deleted elements in internal storage. Ordinarily, deleting elements does not free their memory for re-use until the command completes, so that element iteration loops do not get disrupted. If for some reason this behavior leads to excess memory usage or some other problem, the user may use the free_discards command to free element storage of deleted elements. Just be sure not to do this inside any element iteration loop that might be affected.

GEOMPIPE

Main prompt command. Redirects Evolver's geomview output to a command in place of sending it to geomview. Syntax:
   geompipe stringexpr
The redirection can be closed with the command "P 9". geompipe is useful for debugging geomview data; but be sure to toggle gv_binary OFF to get ascii data to look at.

GEOMVIEW

Main prompt command. The plain form "geomview" toggles the geomview display on and off. The form
   geomview stringexpr
will send a command to an already started geomview. This string must be in the geomview command language, for which consult the geomview documentation.

HELP

Main prompt command. Main prompt command. Prints what Evolver knows about an identifier or keyword. User-defined variables, named quantities, named methods, named constraints, and element attributes are identified as such. Information for syntax keywords comes from a file evhelp.txtt in the doc subdirectory of your Evolver installation, so that subdirectory should be on your EVOLVERPATH environment variable. Syntax:
   help keyword
The keyword need not be in quotes, unless there are embedded blanks. After printing the help section exactly matching the keyword, a list of related terms is printed. These are just the keywords containing your keyword as a substring.

The built-in browser is in no way a complete substitute for using a full-fledged browser such as Netscape or Mosaic.


HESSIAN

Main prompt command. Does one step using Newton's method with the Hessian matrix of the energy. If the Hessian is not positive definite, a warning will be printed, but the move will be made anyway. If the check_increase toggle is on, then no move will be made if it would increase energy. Hessian_seek will use a variable step size to seek minimum energy in the direction of motion. The motion vector is stored, and may be accessed with the move command. Not all energies and constraints have Hessian calculations yet. See the Hessian tutorial for more.

HESSIAN_MENU

Main prompt command. Brings up a menu of experimental stuff involving the energy Hessian matrix. Not all of it works well, and may disappear in future versions. A one-line prompt with options appears. Use option '?' to get a fuller description of the choices. For those options that calculate an eigenvalue, the eigenvalue (or first, if several) is saved in the internal variable last_eigenvalue. A quick summary of the current options:
1. Fill in hessian matrix.
Allocation and calculation of Hessian matrix.
2. Fill in right side. (Do 1 first)
Calculates gradient and constraint values.
3. Solve. (Do 2 first)
Solves system for a motion direction.
4. Move. (Do 3, A, B, C, E, K, or L first)
Having a motion direction, this will move some stepsize in that direction. Will prompt for stepsize. The direction of motion is saved and is available in the move command.
7. Restore original coordinates.
Will undo any moves. So you can move without fear.
9. Toggle debugging. (Don't do this!)
Prints Hessian matrix and right side as they are calculated in other options. Produces copious output, and is meant for development only. Do NOT try this option.
B. Chebyshev (For Hessian solution ).
Chebyshev iteration to solve system. This option takes care of its own initialization, so you don't have to do steps 1 and 2 first. Not too useful.
C. Chebyshev (For most negative eigenvalue eigenvector).
Chebyshev iteration to find most negative eigenvalue and eigenvector. Will ask for number of iterations, and will prompt for further iterations. End by just saying 0 iterations. Prints Rayleigh quotient every 50 iterations. After finding an eigenpair, gives you the chance to find next lowest. Last eigenvector found becomes motion for step 4. Self initializing. Not too useful.
E. Lowest eigenvalue. (By factoring. Do 1 first)
Uses factoring to probe the inertia of the shifted Hessian H-cI until it has the lowest eigenvalue located within .01. Then uses inverse iteration to find eigenpair.
F. Lowest eigenvalue. (By conjugate gradient. Do 1 first)
Uses conjugate gradient to minimize the Rayleigh quotient.
L. Lanczos. (Finds eigenvalues near probe value. )
Uses Lanczos method to solve for 15 eigenvalues near the probe value left over from menu choices 'P' or 'V'. These are approximate eigenvalues, but the first one is usually very accurate. Do not trust apparent multiplicities. From the main command prompt, you can use the lanczos command.
R. Lanczos with selective reorthogonalization.
Same as 'L', but a little more elaborate to cut down on spurious multiplicities by saving some vectors to reorthogonalize the Lanczos vectors. Not quite the same as the official "selective reorthogonalization" found in textbooks.
Z. Ritz subspace iteration for eigenvalues. (Do 1 first)
Calculate a number of eigenpairs near a probe value. Will prompt for probe value and number of eigenpairs. Same as ritz main command. Can be interrupted gracefully by keyboard interrupt. Afterwards, one can use the X option to pick a particular eigenvector to look at.
X. Pick Ritz vector for motion. (Do Z first)
Selects an eigenvector calculated by the Z option for use in motion (option 4). First eigenvalue listed is number 1, etc. Particularly useful for discriminating among high multiplicity eigenvalues, which the V option does not let you do. You can enter the eigenvector by its number in the list from the Z option. As a special bonus useful when there are multiple eigenvectors for an eigenvalue, you can enter the vector as a linear combination of eigenvectors, e.g. ``0.4 v1 + 1.3 v2 - 2.13 v3''.
P. Eigenvalue probe. (By factoring. Do 1 first)
Reports the inertia of the shifted Hessian H-cI for user-supplied values of the shift c. The Hessian H includes the effects of constraints. Will prompt repeatedly for c. Null response exits. From the main command prompt, you can use the eigenprobe command.
V. Eigenvalue probe with eigenvector. (By factoring. Do 1 first)
Reports the inertia of the shifted Hessian H-cI for user-supplied values of the shift c, and calculates the eigenvector for the eigenvalue nearest c by inverse power iteration. You will be prompted for c and for the maximum number of iterations to do. From the main command prompt, you can use the eigenprobe command.
S. Seek along direction. (Do 3, A, B, E, C, K, or L first)
Can do this instead of option 4 if you want Evolver to seek to lowest energy in an already found direction of motion. Uses the same line search algorithm as the optimizing `g' command.
Y. Toggle YSMP/alternate minimal degree factoring.
Default Hessian factoring is by Yale Sparse Matrix Package. The alternate is a minimal degree factoring routine of my own devising that is a little more aware of the surface structure, and maybe more efficient. If YSMP gives problems, like running out of storage, try the alternate. This option is available at the main prompt as the ysmp toggle.
U. Toggle Bunch-Kaufman version of min deg.
YSMP is designed for positive definite matrices, since it doesn't do any pivoting or anything. The alternate minimal degree factoring method, though, has the option of handling negative diagonal elements in a special way. This option is available at the main prompt as the bunch_kaufman toggle.
M. Toggle projecting to global constraints in move.
Toggles projecting to global constraints, such as volume constraints. Default is ON. Don't mess with this. Actually, I don't remember why I put it in.
G. Toggle minimizing square gradient in seek.
For converging to unstable critical points. When this is on, option 'S' will minimize the square of the energy gradient rather than minimizing the energy. Also the regular saddle and hessian_seek commands will minimize square gradient instead of energy.
=. Subshell.
Starts a command prompt while still in hessian_menu. You can do pretty much any command, but you should not do anything that changes the surface, thus invalidating the Hessian data. This is meant, for example, for creating a graphics file of an eigenvalue perturbation and then returning to the hessian_menu prompt. You exit the subshell with the "q" command.
0. Exit hessian.
Exits the menu. `q' also works.
For example, to inspect what eigenvectors look like, one would do steps 1 and z, then repeatedly use x to pick an eigenvector, 4 to move, and 7 to restore.

HESSIAN_SEEK

Main prompt command. Seeks to minimize energy along the direction found by Newton's method using the Hessian. Otherwise same as the hessian command. Syntax:
 
  HESSIAN_SEEK maxscale
where maxscale is an optional upper bound for the distance to seek. The default maxscale is 1, which corresponds to a plain hessian step. The seek will look both ways along the direction, and will test down to 1e-6 of the maxscale before giving up and returning a scale of 0. This command is meant to be used when the surface is far enough away from equilibrium that the plain hessian command is unreliable, as hessian_seek guarantees an energy decrease, if it moves at all.

HISTOGRAM, LOGHISTOGRAM

Main prompt command. For printing histograms in text form to standard output. Syntax:
HISTOGRAM(generator, expr)
LOGHISTOGRAM(generator, expr)
Prints a histogram of the values of expr for the generated elements. It uses 20 bins evenly divided between minimum and maximum values. It finds its own maximum and minimum values, so the user does not have to specify binsize. The log version will lump all zero and negative values into one bin. Examples:
 histogram(edge,dihedral*180/pi) 
 loghistogram(facet where color == red, area)
 histogram(vertex where on_constraint 1, sqrt(x^2+y^2+z^2))

HISTORY

Main prompt command. Print the saved history list of commands.

LAGRANGE

Main prompt command. Changes to Lagrange model from quadratic or linear models. Syntax:
LAGRANGE n
where n is the lagrange_order, which is between 1 and some built-in maximum (currently 8). This command can also convert between Lagrange models of different orders. Note that lagrange 1 gives the Lagrange model of order 1, which has a different internal representation than the linear model. Likewise, lagrange 2 does not give the quadratic model.

LANCZOS

Main prompt command. For finding eigenvalues of the energy Hessian near a given value. Syntax:
   LANCZOS expr

   LANCZOS (expr,expr)
Does a little Lanczos algorithm and reports the nearest approximate eigenvalues to the given probe value. In the first form, expr is the probe value, and 15 eigenvalues are found. In the second form, the first argument is the probe value, the second is the number of eigenvalues desired. The output begins with the number of eigenvalues less than, equal to, and greater than the probe value. Then come the eigenvalues in distance order from the probe. Not real polished yet. Beware that multiplicities reported can be inaccurate. The eigenvalue nearest the probe value is usually very accurate, but others can be misleading due to incomplete convergence. Since the algorithm starts with a random vector, running it twice can give an idea of its accuracy.

LINEAR

Main prompt command. Changes to linear model from quadratic or Lagrange models.

LIST

Main prompt command. List elements on the screen in the same format as in the datafile, or lists individual constraint, boundary, quantity, or method instance definitions. Syntax:
   LIST  generator
   LIST constraintname
   LIST CONSTRAINT constraintnumber
   LIST boundaryname
   LIST BOUNDARY boundarynumber
   LIST quantityname
   LIST instancename
On unix systems, piping to more can be used for long displays. Examples:
   list edges where id <= 12  
   list vertices | "more"
   list vertices where x < 1 and y > 2 and z >= 3  | "tee vfile"
   list facet[3]
   list facet[3].edges where on_constraint 1
   list facet[3].edge[2].vertex[1]
   list constraint 1
See also LIST ATTRIBUTES, LIST BOTTOMINFO, LIST PROCEDURES, and LIST TOPINFO.

LIST ATTRIBUTES

Prints a list of the "extra attributes" of each type of element. Besides user-defined extra attributes, this list also contains the predefined attributes that make use of the extra attribute mechanism (being of variable size), such as coordinates, parameters, forces, and velocities. It does not list permanent, fixed-size attributes such as color or fixedness, or possible attributes that are not used at all.

LIST BOTTOMINFO

Main prompt command. Prints what would be dumped in the "read" section at the end of a dumpfile: command definitions and various toggle states.

LIST PROCEDURES

Main prompt command. Prints names of all current user-defined commands.

LIST TOPINFO

Main prompt command. Prints the first section of the datafile on the screen. This is everything before the vertices section.

LOAD

Main prompt command. For loading a new surface. Syntax:
LOAD filename
Terminates the current surface and loads a new datafile. The filename is the datafile name, and can be either a quoted string or a string variable. This completely re-initializes everything, including the command interpreter. In particular, the currently executing command ends. Useful only as the last command in a script. For loading a new surface and continuing with the current command, see permload. Wildcard matching is in effect on some systems (Windows, linux, maybe others), but be very careful when using wildcards since there can be unexpected matches.

LOGFILE

Main prompt command. Syntax:
LOGFILE stringexpr
LOGFILE OFF
Starts recording all input and output to the file specified by stringexpr, which must be a quoted string or a string variable or expression. Appends to an existing file. To end logging, use logfile off. To record just input keystrokes, use keylogfile.

KEYLOGFILE

Main prompt command. Syntax:
KEYLOGFILE stringexpr
KEYLOGFILE OFF
Starts recording all input keystrokes to the file specified by stringexpr, which must be a quoted string or a string variable or expression. Appends to an existing file. To end logging, use keylogfile off. To record both input and output, use logfile.

METIS, KMETIS

Main prompt command. Partitions the set of facets (edges in string model) into n parts using the METIS library of Karypis and Kumar, if this library has been compiled into the Evolver. Meant for experiments in partitioning the surface for multiprocessors. The partition number of facet is left in the facet extra attribute fpart (edge epart for string model), which will be created if it does not already exist. METIS uses the PMETIS algorithm, KMETIS uses the KMETIS algorithm. Syntax:
  METIS n
  KMETIS n
Example:
  metis 20;
  set facet color (fpart imod 15) + 1;
For partitioning bodies, see body_metis.

LONGJ

Main prompt command. For perturbing the surface. This does a "long jiggle", which provides long wavelength perturbations that can test a surface for stability. The parameters are a wavevector, a phase, and a vector amplitude. The user will be prompted for values. Numbers for vectors should be entered separated by blanks, not commas. An empty reply will accept the defaults. A reply of r will generate random values. Any other will exit the command without doing a jiggle. In the random cases, a random amplitude $\vec A$ and a random wavelength $\vec L$ are chosen from a sphere whose radius is the size of the object. The wavelength is inverted to a wavevector $\vec w$. A random phase $\psi$ is picked. Then each vertex $\vec v$ is moved by $\vec A\sin(\vec v \cdot \vec w + \psi)$. This command is archaic. More control over perturbations may be had with the "set vertex x ..." type of command.

MATRIX_INVERSE

Main prompt command. For computing the inverse of a square matrix. Currently applies only to global matrices, not element attribute matrices. Syntax:
  MATRIX_INVERSE(matrix1, matrix2)
Here matrix1 is the name of the original matrix, and matrix2 is the name of the inverse matrix. They may be the same matrix to get an in-place inverse. Examples:
  define mata real[5][5]
  define matb real[5][5]
  ...  // fill in values of mata
  matrix_inverse(mata,matb)
  matrix_inverse(mata,mata)  

MATRIX_MULTIPLY

Main prompt command. For computing the product of matrices. Currently applies only to global matrices, not element attribute matrices. Syntax:
  MATRIX_MULTIPLY(matrix1, matrix2, matrix3)
Here matrix1 and matrix2 are the names of the multiplicands, and matrix3 is the name of the product matrix. The product matrix may be the same as one (or both) of the multiplicands. The matrices can be one-dimensional or two-dimensional, so you can do vector-matrix or matrix-vector multiplication (but you can't do vector times vector). Examples:
  define mata real[5][5]
  define matb real[5][5]
  define matc real[5][5]
  ...  // fill in values of mata and matb
  matrix_multiply(mata,matb,matc)
  matrix_multiply(mata,mata,mata)  

MOVE

Main prompt command. For moving along the current direction of motion. Syntax:
  MOVE expr
Moves the surface along the previous direction of motion by the stepsize given by expr. The previous direction can be either from a gradient step (g command) or a hessian step (hessian, saddle, hessian_seek, hessian_menu option 4, etc.). The stepsize does not affect the current scale factor. A negative step is not a perfect undo, since it cannot undo projections to constraints. "Move" sometimes does not work well with optimizing parameters and hessian together.

NEW_VERTEX

Main prompt command. For creating a new vertex. The syntax is that of a function instead of a verb, since it returns the id number of the new vertex. The arguments are the coordinates of the vertex. The new vertex is not connected to anything else; use the new_edge command to connect it. Syntax:
  newid := NEW_VERTEX(expr, expr,...)
Examples:
  newid1 := new_vertex(0,0,1)
  newid2 := new_vertex(pi/2,0,max(vertex,x))

NEW_EDGE

Main prompt command. For creating a new edge. The syntax is that of a function instead of a verb, since it returns the id number of the new edge. The arguments are the id's of the tail and head vertices. Syntax:
  newid := NEW_EDGE(expr, expr)
The new edge has the same default properties as if it had been created in the datafile with no attributes, so you will need to explicitly add any attributes you want. Example to create a set of coordinate axes in 3D:
  newv1 := new_vertex(0,0,0); fix vertex[newv1]; 
  newv2 := new_vertex(1,0,0); fix vertex[newv2];
  newv3 := new_vertex(0,1,0); fix vertex[newv3];
  newv4 := new_vertex(0,0,1); fix vertex[newv4];
  newe1 := new_edge(newv1,newv2); fix edge[newe1]; 
  newe2 := new_edge(newv1,newv3); fix edge[newe2];
  newe3 := new_edge(newv1,newv4); fix edge[newe3];
  set edge[newe1] no_refine; set edge[newe1] bare;
  set edge[newe2] no_refine; set edge[newe2] bare;
  set edge[newe3] no_refine; set edge[newe3] bare;

NEW_FACET

Main prompt command. For creating a new facet. The syntax is that of a function instead of a verb, since it returns the id number of the new facet. The arguments are the oriented id's of the edges around the boundary of the facet, in the same manner that a face is defined in the datafile. The number of edges is arbitrary, and they need not form a closed loop in the string model. In the soapfilm model, if more than three edges are given, the new face will be triangulated by insertion of a central vertex. In that case, the returned value will be the original attribute of the new facets. In the simplex model, the arguments are the id's of the facet vertices. Syntax:
  newid := NEW_FACET(expr, expr,...)
The new facet has the same default properties as if it had been created in the datafile with no attributes, so you will need to explicitly add any attributes you want. Example:
  newf := new_facet(1,2,-3,-4); fix facet where original == newf;

NEW_BODY

Main prompt command. For creating a new body. The syntax is that of a function instead of a verb, since it returns the id number of the new body. There are no arguments. Syntax:
  newid := NEW_BODY
The body is created with no facets. Use the set facet frontbody and set facet backbody commands to install the body's facets. The new body has the same default properties as if it had been created in the datafile with no attributes, so you will need to explicitly add any attributes you want, such as density or target volume. Example:
  newb := new_body
  set facet frontbody newb where color == red

NOTCH

Main prompt command. For refining a surface in regions of high curvature. Syntax:
 NOTCH expr
Notches all edges with dihedral angle greater than given value. Same as 'n' command, or
   foreach edge ee where ee.dihedral > expr do refine ee.facet
Notching is done by adding a vertex in the middle of adjacent facets. Should be followed by equiangulation.

OMETIS

Main prompt command. Computes an ordering for Hessian factoring using the METIS library of Karypis and Kumar, if this library has been compiled into the Evolver (not part of the public distribution yet). Prints ordering tree. To actually use METIS ordering during factoring, use the toggle metis_factor. Note: ometis no longer works for Metis version 3 or later, since Metis does not return the tree any more. But metis_factor still works. Syntax:
  OMETIS n  // n is smallest partition size
  OMETIS           // defaults to n = 100

OOGLFILE

Main prompt command. Writes a file containing OOGL-formatted graphics data for the surface as a POLY or CPOLY quad file. This is a non-interactive version of the P 2 command. Syntax:
 ooglfile stringexpr
The string gets ".quad" appended to form the filename. This command does not ask any of the other questions the P 2 command asks; it uses the default values, or whatever the last responses were to the previous use of the interactive P 2 command. Good for use in scripts. Example:
   ooglfilename := sprintf "frame%d",framecounter;
   ooglfile ooglfilename;
   framecounter += 1;

OPTIMIZE

Main prompt command. Set gradient descent iteration to optimizing mode, with an upper bound on the scale factor. "Optimise" is a synonym. Syntax:
  OPTIMIZE expr

PAUSE

Main prompt command. Pauses execution until the user hits RETURN. Useful in scripts to give the user a chance to look at some output before proceeding.

PERMLOAD

Main prompt command. Loads a new datafile and continues with the current command after the read section of the datafile finishes. The filename is the datafile name, and can be either a quoted string or a string variable. Since the automatic re-initialization makes Evolver forget all non-permanent variables, care should be taken that the current command only uses permanently assigned variables (assigned with ::= ). Useful for writing scripts that run a sequence of evolutions based on varying parameter values. Using permload is a little tricky, since you don't want to be redefining your permanent commands and variables every time you reload the datafile, and your permanent command cannot refer directly to variables parameterizing the surface. One way to do it is to read in commands from separate files. For example, the catenoid of cat.fe has height controlled by the variable zmax. You could have a file permcat.cmd containing the overall series script command
  run_series ::= {
    for ( height ::= 0.5 ; height < 0.9 ; height ::= height + .05 )
    { permload "cat"; read "permcat.gogo"; }
  }
and a file permcat.gogo containing the evolution commands
  u; zmax := height; recalc; r; g 10; r; g 10; hessian;
  printf "height: %f  area: %18.15f\n",height,total_area >> "permcat.out";
Then at the Evolver command prompt,
  Enter command: read "permcat.cmd"
  Enter command: run_series
For loading a new surface and not continuing with the current command, see load. Wildcard matching is in effect on some systems (Windows, linux, maybe others), but be very careful when using wildcards since there can be unexpected matches.

POP

Main prompt command. Pops an individual edge or vertex or set of edges or vertices, giving finer control than the universal popping of the O and o commands. The specified vertices or edges are tested for not being minimal in the soap film sense. For vertices, this means having more than four triple edges adjacent; higher valence edges are automatically popped. For edges, this means having more than three adjacent facets when not on constraints or otherwise restricted. It tries to act properly on constrained edges also, but beware that my idea of proper behavior may be different from yours. Normally, popping puts in new edges and facets to keep originally separated regions separate, but that behavior can be changed with the pop_disjoin toggle. The style of popping a cone over a triangular prism can be controlled with the pop_to_edge and pop_to_face commands. The pop_enjoin toggle forces joining cones to be popped by widening the vertex into a neck. Examples:
   pop edge[2]
   pop edge where valence==5

POP_EDGE_TO_TRI

Main prompt command. This command does a particular topological transformation common in three-dimensional foam evolution. An edge with tetrahedral point endpoints is transformed to a single facet. A preliminary geometry check is made to be sure the edge satisfies the necessary conditions, one of which is that the triple edges radiating from the endpoints have no common farther endpoints. If run in verbose mode, messages are printed when a specified edge fails to be transformed. This command is the inverse of the pop_tri_to_edge command. Works in linear and quadratic mode. Examples:
   pop_edge_to_tri edge[2]
   pop_edge_to_tri edge where valence==3 and length < 0.001

POP_QUAD_TO_QUAD

Main prompt command. This command does a particular topological transformation common in three-dimensional foam evolution. A quadrilateral bounded by four triple edges is transformed to a quadrilateral oriented in the opposite direction. The shortest pair of opposite quadrilateral edges are shrunk to zero length, converting the quadrilateral to an edge, then the edge is expanded in the opposite direction to form the new quadrilateral. The new quadrilateral inherits attributes such as color from the first quadrilateral, although all the facet numbers are different. A preliminary geometry check is made to be sure the edge satisfies the necessary conditions, one of which is that the triple edges radiating from the quadrilateral corners have no common farther endpoints. If run in verbose mode, messages are printed when a specified quadriteral fails to be transformed. The specified facet can be any one of the facets of the quadrilateral with a triple line on its border. It doesn't hurt to apply the command to all the facets of the quadrilateral, or to facets of multilple quadrilaterals. Quadrilaterals may be arbitrarily subdivided into facets; in particular, they may have some purely interior facets. Works in linear and quadratic mode. Examples:
   pop_quad_to_quad facet[2]
   pop_quad_to_quad facet where color==red

POP_TRI_TO_EDGE

Main prompt command. This command does a particular topological transformation common in three-dimensional foam evolution. A facet with three tetrahedral point vertices is transformed to a single facet. A preliminary geometry check is made to be sure the edge satisfies the necessary conditions, one of which is that the triple edges radiating from the vertices have no common farther endpoints. If run in verbose mode, messages are printed when a specified edge fails to be transformed. This command is the inverse of the pop_edge_to_tri command. Works in linear and quadratic mode. Examples:
   pop_tri_to_edge facet[2]
   pop_tri_to_edge facet where color == red

POSTSCRIPT

Main prompt command. Creates a PostScript file of the current surface in a file. Syntax:
  POSTSCRIPT stringexpr 
The string gives the name of the file; a .ps extension will be appended if it is missing. It is the same as the P option 3 command, except that there are no interactive responses needed. Output options are controlled by the ps_colorflag, ps_gridflag, ps_crossingflag, and ps_labelflag toggles. full_bounding_box toggles.

PRINT

Main prompt command. For printing expression values, strings, commands, arrays, or accumulated warning messages. Syntax:
PRINT expr
PRINT stringexpr
PRINT commandname
PRINT arrayslice
PRINT WARNING_MESSAGES
The arrayslice option takes an array name or a partially indexed array name. If more than one element results, the slice is printed in nested curly braces. The arrayslice can also be that of an array attribute of an element. The warning_messages option is handy for reviewing warning messages that occur early in the loading of a datafile but scroll off the screen too rapidly to see. PRINT expr can also be used inside an expression, where it prints the expression and evaluates to the value of its expression. Examples:
  print datafilename;
  print max(edge,length);
  print max(vertex, print (x^2+y^2+z^2) );
  gg := {list vertex where id < 10; g 5};
  print gg;
  define parts real[3][2][3];
  print parts;
  print parts[3][2];

PRINTF

Main prompt command. For printing formatted output. Syntax:
PRINTF string,expr,expr,...
Prints to standard output using the standard C sprintf function. All string, integer, and floating point formats are valid. Integer formats force floating point arguments to be converted to integer. The format string can be a string variable or a quoted string. There is a limit of 1000 characters on the format string, otherwise there is no limit on the number of arguments. Example:
  printf "This is %s with total energy %f\n",datafilename,total_energy

QUADRATIC

Main prompt command. Changes to quadratic model from linear or Lagrange models.

QUIT, BYE, EXIT

Main prompt command. Exits Evolver or starts new datafile. Same as `q' command.

RAWESTV

Main prompt command. Does vertex averaging for all vertices without regard for conserving volume or whether averaged vertices have like constraints. But doesn't move vertices on boundaries. To do a selected group of vertices, use rawest_vertex_average.

RAWEST_VERTEX_AVERAGE

Main prompt command. Does vertex averaging on selected vertices without conserving volume on each side of surface, or attention to being on like constraints. Doesn't move vertices on boundaries. Using the verbose toggle will print messages. Syntax:
RAWEST_VERTEX_AVERAGE generator
Example:
rawest_vertex_average vertex[3]

RAWV

Main prompt command. Does vertex averaging for all vertices without conserving volume on each side of surface. Will only average vertices with those of like type of constraints. Doesn't move vertices on boundaries. To do a selected group of vertices, use raw_vertex_average.

RAW_VERTEX_AVERAGE

Main prompt command. Does vertex averaging on selected vertices without conserving volume on each side of surface. Will only average vertices with those of like type of constraints. Doesn't move vertices on boundaries. Using the verbose toggle will print messages. Syntax:
RAW_VERTEX_AVERAGE generator
Example:
raw_vertex_average vertex where valence == 6

READ

Main prompt command. For reading commands from a file. Syntax:
READ filename
The filename can be either a quoted string or a string variable. The effect is as if the file were typed in at the keyboard. Hence main commands, responses to commands, and graphics mode commands can be included. Read commands may be nested. On the occurence of an error, input reverts to the original standard input. Example:
   read "zebra.cmd"

REBODY

Main prompt command. Recalculates connected bodies. Useful after a body has been disconnected by a neck pinching off. Facets of an old body are divided into edge-connected sets, and each set defines a new body (one of which gets the old body id). The new bodies inherit the attributes of the old body. If the original body volume was fixed, then the new bodies' target volumes become the new actual volumes. If the original body had a volconst, the new bodies will inherit the same value. This will likely lead to incorrect values, so you will have to adjust the volconsts by hand. In commands, you may specify the new bodies descended from an original body by using the 'original' atttribute.

RECALC

Main prompt command. Recalculates and redisplays everything. Useful after changing some variable or something and recalculation is not automatically done. Evolver tries to automatically recalculate when some significant change is made, but doesn't always know. Also see autorecalc.

REFINE

Main prompt command. For subdividing sets of edges or facets. Syntax:
REFINE generator
Subdivides the generated edges or facets. Subdivides edges by putting a vertex in the middle of each edge, and splitting neighboring facets in two in the soapfilm model. It is the same action as the long edge subdivide command (command l). Facets will be subdivided by putting a vertex in the center and creating edges out to the old vertices. It is strongly suggested that you follow this with equiangulation to nicen up the triangulation. Edge refinement is better than facet refinement as facet refinement can leave long edges even after equiangulation. This command does not respect the no_refine attribute. Example:
       refine edges where not fixed and length > .1 

RESET_COUNTS

Main prompt command. Resets to 0 various internal counters. The counters are:
  • equi_count,
  • edge_delete_count,
  • facet_delete_count,
  • edge_refine_count,
  • facet_refine_count,
  • notch_count,
  • vertex_dissolve_count,
  • edge_dissolve_count,
  • facet_dissolve_count,
  • body_dissolve_count,
  • vertex_pop_count,
  • edge_pop_count,
  • facet_pop_count,
  • pop_tri_to_edge_count,
  • pop_edge_to_tri_count,
  • pop_quad_to_quad_count,
  • where_count,
  • edgeswap_count,
  • fix_count,
  • unfix_count,
  • t1_edgeswap_count, and
  • notch_count.
Normally, a count is set to 0 at the start of a command that potentially affects it, accumulated during the execution of the command, and printed at the end of the command. To be precise, each counter has a "reported" bit associated with it, and if the "reported" bit is set when the appropriate command (such as 'u') is encountered, the counter will be reset to 0 and the "reported" bit cleared. The "reported" bit is set by either flush_counts or the end of a command. The idea is to have the counts from previous commands available to subsequent commands as long as possible, but still have the counter reflect recent activity.

REVERSE_ORIENTATION

Main prompt command. For reversing the orientation of sets of edges or facets. Syntax:
REVERSE_ORIENTATION generator
Reverses the internal orientation of selected edges or facets, as if they had been entered in the datafile with the opposite orientation. Useful, for example, when edges come in contact with a constraint and you want to get them all oriented in the same direction. Relative orientations of constraint and quantity integrals change to compensate, so energy, volumes, etc. should be the same after the command, but it would be wise to check in your application. Examples:
   reverse_orientation edge[7]
   reverse_orientation facets where backbody != 0

RITZ

Main prompt command. For finding eigenvalues of the energy Hessian near a given value. Syntax:
RITZ(expr,expr)
Applies powers of inverse shifted Hessian to a random subspace to calculate eigenvalues near the shift value. First argument is the shift. Second argument is the dimension of the subspace. Prints out eigenvalues as they converge to machine accuracy. This may happen slowly, so you can interrupt it by hitting whatever your interrupt key is, such as CTRL-C, and the current values of the remaining eigenvalues will be printed out. Good for examining multiplicities of eigenvalues. It is legal to shift to an exact eigenvalue, but not wise, as they will not be printed. See the Hessian tutorial for more. The first eigenvalue is subsequently available in the last_eigenvalue internal variable. The full list of eigenvalues produced is subsequently available in the eigenvalues[] array. Example: To get the lowest 5 eigenvalues of a Hessian you know is positive definite:
   ritz(0,5)

RENUMBER_ALL

Reassigns element id numbers of all types of elements in accordance with order in storage, i.e. as printed with the LIST commands. Besides renumbering after massive topology changes, this can be used with the reorder_storage command to number elements as you desire. Do NOT use this command inside an element generator loop!

REORDER_STORAGE

Reorders the storage of element data structures, sorted by the extra attributes vertex_order_key, edge_order_key, facet_order_key, body_order_key, and facetedge_order_key. Originally written for testing dependence of execution speed on storage ordering, but could be useful for other purposes, particularly when renumber_all is used afterwards. Example:
   define vertex attribute vertex_order_key real
   define edge attribute edge_order_key real
   define facet attribute facet_order_key real
   define body attribute body_order_key real
   define facetedge attribute facetedge_order_key real

   reorder := {
     set vertex vertex_order_key x+y+z;
     set edge ee edge_order_key min(ee.vertex,vertex_order_key);
     set facetedge fe facetedge_order_key fe.edge[1].edge_order_key;
     set facet ff facet_order_key min(ff.vertex,vertex_order_key);
     set body bb body_order_key min(bb.facet,facet_order_key);
     reorder_storage;
     }

SADDLE

Main prompt command. Seek to minimum energy along the eigenvector of the lowest negative eigenvalue of the Hessian. If there is no negative eigenvalue, then the surface is unchanged. The alternate form
SADDLE expr
will limit the step size to expr. The motion vector is available afterwards through the move command.

SET

Main prompt command. For setting element attributes to values. Syntax:
 SET elementtype [name] attrib expr1 where expr2 
 
 SET elementtype.attrib expr1 where expr2 
 
 SET name attrib expr
 
 SET name.attrib expr

 SET quantityname attrib expr

 SET instancename attrib expr
The first two forms set the value of the attribute attrib to the value expr1 for all elements of the given type that satisfy expr2. elementtype can be vertex, edge, facet, or body, or any element generator without a where clause. The optional name refers to the element under consideration, and can be used in expr1 and expr2 to refer to attributes of that element. Even without name, attributes of the element can be referred to if the references are not nested in element generators in expr1 or expr2. The next two forms can be used inside an element generator which defines name. When name is not used, a '.' can be used, for those who like that sort of thing. SET can change the following attributes: constraint, coordinates, density, orientation, non-global named quantity or named method, user-defined extra attributes, body target volume, body volconst, fixed, frontbody, backbody, pressure, color, frontcolor, backcolor, boundary, and opacity (for the appropriate type elements). Fixed, named quantity, and named method attributes are just toggled on; they do not need the first expr. Setting the pressure on a body automatically unfixes its volume. For constraint, the expr is the constraint number. If using set to put a vertex on a parametric boundary, set the vertex's boundary parameters p1, p2, etc. first. Examples:
  set facets density 0.3 where original == 2
  set vertices x 3*x where id < 5  // multiplies x coordinate by 3
  set body target 5 where id == 1   // sets body 1 target volume to 5
  set vertices constraint 1 where id == 4
  set facet color clear where original < 5
  foreach facet ff do set ff color red
  define vertex attribute weight real; set vertex weight 3
  set vertex quantity my_quantity
  set vertex[1].facet color red
Note the first form of syntax has the attribute and new value in the middle of an element generator. Syntactically inconsistent with other commands that use element generators, but more natural English. Actually, the syntactically consistent
set facet where id < 5 color red
does work.

The last two forms set the value of a named quantity or named method instance attribute. For a named quantity, the settable attributes are target, modulus, volconst, and tolerance. For a named method instance, only modulus. There is no implicit reference to the quantity in the expression, so say

   set myquant target myquant.value 
rather than set myquant target value. Also see unset.

SHELL

Main prompt command. Invokes a system subshell for the user on systems where this is possible. No arguments. See the system command for execution of an explicit shell command.

SHOW

Main prompt command. Which edges and facets are actually shown in graphics displays can be controlled by defining boolean expressions that edges or facets must satisfy in order to be passed to the graphics display. There are two expressions internally: one for edges and one for facets. They may be set with the syntax
   show edges where expr
   
   show facets where expr
The default is to show all facets, and to show all special edges: fixed edges, constraint edges, boundary edges, and edges without exactly two adjacent facets. The defaults can be restored with "show facets" and "show edges". Some graphics modules (like geomview) can show edges of facets on their own initiative. This is separate from the edge show criterion here; to show the colors of edges, the edges must satisfy the criterion. Show causes graphics to be redrawn. If a graphics display is not active, show will start screen graphics. Show_expr is the same as show in setting the show expressions, except it does not start graphics. Show alone will just start screen graphics. Examples:
 show facets where color == red
 show edges where 1
 show edges where color != black
The string model will show facets (default is not to show them) as the facet show expression specifies, but the triangulation algorithm is fairly simple.

As an edge or facet attribute, "show" is a Boolean read-only attribute giving the current status of the edge or facet, for example, to report the number of edges being shown, do

   print sum(edge,show)

SHOW_EXPR

Main prompt command. This does the same as show, except it does not start or redraw graphics; it just sets a show expression. Good for use in the read section of the datafile for controlling which elements will be displayed without automatically starting a display.

SHOW_TRANS

Main prompt command. Applies string of graphics commands to the image transformation matrix without doing any graphic display. The string must be in double quotes or be a string variable, and is the same format as is accepted by the regular graphics command prompt. Example:
  show_trans "rrdd5z" 

SHOWQ

Main prompt command. Displays screen graphics, but returns immediately to the main prompt and does not go into graphics command mode.

SIMPLEX_TO_FE

Main prompt command. Converts a simplex model surface to a string or soapfilm model surface. Only works for dimension 1 or 2 surfaces, but works in any ambient dimension.

SOBOLEV

Main prompt command. Uses a positive definite approximation to the area Hessian to do one Newton iteration, following a scheme due to Renka and Neuberger [RN]. Works only on area with fixed boundary; no volume constraints or anything else. Seems to converge very slowly near minimum, so not a substitute for other iteration methods. But if you have just a simple soap film far, far from the minimum, then this method can make a big first step. SOBOLEV_SEEK will do an energy-minimizing search in the direction.

SPRINTF

Main prompt command. Prints to a string using the standard C sprintf function. May be used whereever a stringexpr is called for in syntax. Otherwise same as printf. Syntax:
SPRINTF stringexpr,expr,expr,...
Example:
   dumpname := SPRINTF "file%04g.dmp",counter

SUBCOMMAND

Main prompt command. Invokes a subsidiary command interpreter. Useful if you want to pause in the middle of a script to give the user the chance to enter commands. A subcommand interpreter gives the prompt Subcommand: instead of Enter command:. Subcommands may be nested several deep, in which case the prompt will display the subcommand level. To exit a subcommand prompt, use q, quit, or exit. The abort command will return to the prompt on the same subcommand level.

SYSTEM

Main prompt command. For executing a program. Syntax:
  SYSTEM stringexpr
Invokes a subshell to execute the given command, on systems where this is possible. Command must be a quoted string or a string variable. Will wait for command to finish before resuming.

TRANSFORM_DEPTH

Main prompt command. Quick way of generating all possible view transforms from view transform generators, to a given depth n. Syntax:
  TRANSFORM_DEPTH n
where n is the maximum number of generators to multiply together. This will toggle immediate showing of transforms, if they are not already being shown.

TRANSFORM_EXPR

Main prompt command. If view transform generators were included in the datafile, then a set of view transforms may be generated by an expression with syntax much like a regular expression. An expression generates a set of transform matrices, and are compounded by the following rules. Here a lower-case letter stands for one of the generators, and an upper-case letter for an expression.
a Generates set {I,a}.
!a Generates set {a}.
AB Generates all ordered products of pairs from A and B.
nA Generates all n-fold ordered products.
A|B Generates union of sets A and B.
(A) Grouping; generates same set as A.
The precedence order is that nA is higher than AB which is higher than A|B. The "!" character suppresses the identity matrix in the set of matrices generated so far. Note that the expression string must be enclosed in double quotes or be a string variable. Examples:
  transform_expr "3(a|b|c)"    //all products of 3 or fewer generators
  transform_expr "abcd"  // generates 16 transforms
  transform_expr "!a!a!a!a!"  // generates one transform
All duplicate transforms are removed, so the growth of the sets does not get out of hand. Note the identity transform is always included. The letter denoting a single generator may be upper or lower case. The order of generators is the same as in the datafile. In the torus model, transforms along the three period vectors are always added to the end of the list of generators given in the datafile. If 26 generators are not enough for somebody, let me know. The current value of the expression may be accessed as a string variable, and the number of transformations generated can be accessed as transform_count. For example,
  print transform_expr
  print transform_count

T1_EDGESWAP

Main prompt command. Does a T1 topological transition in the string model. When applied to an edge joining two triple points, it reconnects edges so that opposite faces originally adjacent are no longer adjacent, but two originally non-adjacent faces become adjacent.
      \_/   =>   \ /
      / \         |
                 / \
It will silently skip edges it is applied to that don't fulfill the two triple endpoint criteria, or whose flipping is barred due to fixedness or constraint incompatibilities. The number of edges flipped can be accessed through the t1_edgeswap_count internal variable. Running with the verbose toggle on will print details of what it is doing. Syntax:
 T1_EDGESWAP edge_generator
Examples:
   t1_edgeswap edge[23]
   t1_edgeswap edge where length < 0.1

UNFIX

Main prompt command. Removes the FIXED attribute from a set of elements. Syntax:
 UNFIX generator
Example:
  unfix vertices where on_constraint 2
Can also convert a parameter from non-optimizing to optimizing. Example:
  unfix radius
Can also convert a named quantity from fixed to info_only.

UNSET

Main prompt command. Removes an attribute from a set of elements. Syntax:
UNSET elements [name] attrib where clause
Unsettable attributes are fixed (vertices, edges, or facets) , body target volume, body pressure, body gravitational density, non-global named quantities, non-global named methods, level-set constraints, parametric boundary. frontbody, or backbody. A use for the last is to use a boundary or constraint to define an initial curve or surface, refine to get a decent triangulation, then use "unset vertices boundary 1" and "unset edges boundary 1" to free the curve or surface to evolve. The form "unset facet bodies ..." is also available to disassociate given facets from their bodies. Examples:
   unset body[1] target
   unset vertices constraint 1; unset edges constraint 1

VERTEX_AVERAGE

Main prompt command. Does vertex averaging for one vertex at a time. Syntax:
  VERTEX_AVERAGE vertex_generator
The action is the same as the V command, except that each new vertex position is calculated sequentially, instead of simultaneously, and an arbitrary subset of vertices may be specified. Fixed vertices do not move. Examples:
  vertex_average vertex[2]
  vertex_average vertex where id < 10
  vertex_average vertex vv where max(vv.facet,color==red) == 1

VERTEX_MERGE

Main prompt command. Merges two soapfilm-model vertices into one. Meant for joining together surfaces that bump into each other. Should not be used for vertices already joined by an edge. Syntax:
  vertex_merge(integer,integer)
Note the syntax is a function taking integer vertex id arguments, not element generators. Example:
   vertex_merge(3,12)

WHEREAMI

Main prompt command. If Evolver is at a debugging breakpoint, then whereami will print a stack trace of the sequence of commands invoked to get to the current breakpoint.

WRAP_VERTEX

Main prompt command. Syntax:
 wrap_vertex(vexpr,wexpr)
In a symmetry group model, transforms the coordinates of vertex number vexpr by symmetry group element wexpr and adjusts wraps of adjacent edges accordingly.

ZOOM

Main prompt command. For isolating a region of a surface. Syntax:
 ZOOM integer expr
Zooms in on vertex whose id is the given integer, with radius the given expr. Same as the 'Z' command, but not interactive.
Back to top of Surface Evolver documentation. Index. evolver-2.30c.dfsg/doc/catbody.gif0000644000175300017530000005235211410765113017316 0ustar hazelscthazelsctGIF89ad      ###''('(''(((''('(((',,,//0/0//000//00/333787788877878887<<@ @ )W$IP@F\peQTf|@aI*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTqRabTT:52E ~\r4 (P@ &<\p… .\p… .\pIbb̘1 hp… .\p… .\1$\…K$Bd„ Z (P@%TRJi!\L Å%UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*Ua(P, `A|@b+TvPh@bH.ap1BLp… .\p… .\pIbb̘1cƀ .\p… .\$11f.p… .\p .\9! 2@JsRH !\pC;0H`TRJcƼBJ*UTRJ*UTRJ*UTRJ*UTRJG*ըE\@2-\&~c.`p e 1\pʘ1gΌ! .\p… .\1fL1cƌ .\p… .\3f1fp .\p…K.\$1aL YpEː 0T !fJ* OcxĂ*UTJ*UTRJ*UTRJ*UTRJU$(UTRJ &!ő,c4cp% /\p#㇙3cƌA3 $\p… .\1f̘1cƔSf4p… .\3f3cΜ3D.\p… .\(1af 4\,p3._L`*UT,فE.qHea葉TTRJ*UTRJ*UTRJ*UTRj,>2*UTRN' `@3&̘1ahcF>2.^bǘ3c̜13͘1 hp… .\Р1̘1eƔ3f̘1cv .\L3̘1cƌ3fIp….\$1a4h@)1.cti(UTF1ą&DjLC4TRJ*UTRJ*UTRJ*UTRƠ,YH#'UNt*UT&Å.cLbƌ3fL4K.cxbL3cƜ93̘1f~… .\hcf̘1cʌ)3̘1c .I&p3f̘3cΌ3f̘1&p… .\&1q挙1hΌ1c.c@.PpST GG-@QJ*UTRJ*UTRJ*UTRJHH"\0J*UT|e̘/c˜bL5f̘1%.^pibb̘1cΘ33iƌ! .\h3f1cƌ3f̘1hƌ.W,pSfL1cƌ3̘3cƘe .\ 3&3fƜ931\piARJ*UTd"$Π$?~8J*UTRJ*UTRJ*UTR%OIyc \0a .\p1f .cbƌ3f̘1#f1\p2a3č1s̘1g̜)3+\@3f̘1cƌ3f̘1cƌCfW@p3f̘1c̜cf̘1cƌ.c03ƌ4g̜sfP1`TTu*UT!58< *UTRJ*UTRJ*UTRe@Gc~$DƨTRJ*(:P@c̘1_v0cƌ3f̘1c ,\de1eΘcf3cƘSfL@$I$II$I$ -ZhѢE h!$I$Wʌ3f̙1fƌ3f \8c̙3f̠9c3\@pARTRJTR EjQcE !TRJ*UTRJ*UTR$c2bg 9RJ5(RJei@1_8cM5f̘1cƌ0 xe̘1eƌ3L$BZaE rʕ+W\rʕ+Wԩc6v)s%˕$4hhb ZhA#I(L!3 3eƜ1s̘3c@H`)UTRJj@4>tBj)RFQ3j)RRJ*UTRJ*UTRɄQHgԨQ=R#ǟTRJ*I83f̘Qc 3jD(#I-LaWcǎ:DرN9vر3:rةS:-yN:uرSǎ:vةcʕ+40q$YҘ9cf4cL``.\` .\4QH3fE\,RJ*UTRJ*UT"%!˨QFP3jQcL а˖-\l… |1c5fԘ1s*?ZHƕ,\رc:vةSN;^PS:vԱSǎ9rԑc:!D:vرcǎ:vԱc;DL1ǎ1YhhaŎ$P@ *#\FJ*UTXΕ$Mx"5*R|iJ*UTRJ*UTRƠQH54(8=2*UTRJ*UTrhHC3g@F Y11E:uԱcǎ;tرc/& /uةc;vرSNIӧN9uԱN;vرcǎ;ELƄ;vرcǎ$ p*UT2*UT\5ҋQFI#1?t*UTRJ*UTR5ĘA:%y4ʓu])ȠTRJ*UTPJȄ ZЀbƎ;viϘvرcN;vԱcǎ6&ӧO>/ȩcGN;uȱS!IӧO1-رcǎ;vرc$Bg;vرS &J*UTR˨N25jԢ$F5$2t$UTJ*UTR* (eZc;uرSǎ9ӧO>}RN;uȱSgN.&'O>}c:vرc$Mӧ\؁b" pɂe -PL8т  5jԨQl5jԨQQѩ TRJ*UTRBH'~I2ҨQ5jԨQ|I*%RJ*U*F;vѧO>}ƴcǎ;vcǎ;-ӧO>};rةcG./O<}qŎ:vةcN$IӧO?u`LTJ*URRJO$Z$IhԨQF%Y4jԨQF`%K ƨRJ*UTR5fNE4iԨQF5(RFc(⃋-ruL$O>})ӢN:vةcRӧO>}9vԱS uO>}c;vбc$}g h*T蔊TTJ%QF%ʏAFeӨQFe}ӧODةSvӇO>}G8uةS%u1 0d*TRur*URriԨQF=i(3.ԌĨQ0 J90J*UTREA2jԨQ.%5jԨQGy#ɜ ԩSR1*UTRBՉ$b*-رSN.&ӧO>}ӧO>}L$SN jӧO>}ӧO5 Ա  X"QNPLbŇ,:JSNP~5jԨQF3ӨQFqӢ$FԉS',ѐ3A*UTRiA@0fGQF%4jԨLk 5BbDɜQFjT"9Y|@q%QStbTf T$}ӧO>}ӧO?}(RN cӧO>|3K9*0 -:J*UShJ*UTŇ a25j(RFeʨQFҏ$WӉS'NYeDUTR*I1>.Ȥ)vF 2I*$BS(@;h ?JJ*UTRH*UTRYbą`;vH… .\pb⇉;vaCG `„,Y0O(L0:u*UTXJe*UT, $It5jԨQF5*(OFjP0h)S'N,Ȕ TRԀ,42=)ӦL$KBy(II@ *TP%I-:J*U*SR*c)UTRQ[:J)Q"Frl"L0a„ Z`eiuJe*U*SR UTRY 9Ru*2Lvpai+<1:DMQF5jԏQ@5ӨN\v\d&M4)S&TRj.2qSL,0SP< ))@y *T(.-$y1(O`NR"HNT!gTT:mI*UTJKT`t*USRJ5ʄTH*U*SRY*UTRJU*թT003<}ɓ'OP,y$Ix5jԨ1dʔ$ F4u%SL_RJ.0eSN,H2'IMB)'O;B *~**UTR I*UT| UTR*CRSNR*RLQT,H U*TRYbp I\f.d)L.eʔ iǓ'O~@)"d"IYdɒ%K2b)L2Yd)8,Ydɒ%KP|d#ǒv%J4f 4<Q21aƒKdʔɌAQzd)QK.Yd %K%K,-3}N@B@9¨RL (ZcO>}A2hРA 4h \qE5Q%J;Yb#"F-ZhѣE=ZÎ%K(YzcΣE-zѣGңG(1j1hѣE=b"F`dɒ$Kq@A9 RBf~ QO>}11hРA 4HM ;XO*;jңGc jAgѢG=zh#F-Z4`D,YʢG=zѣE=Ò%K,10hѣE-jhG-c%KZ}#DΠA 4>vϟ>~2xh1hѢE-ZhѢG=bh(YѢG=zhѣG=zt%K&ѣE-zѢE-z4ȄKq}GA :ϟ?Ȑe 9rZPJU"fO>}g̑A 4 ;}ٲO>#ϟ?}HbNJ4ZFME=ZhѢEWXreѣG=zѣG-zh;&퐳ѣE=ZhѢG-ZHѠc^}N gA3D;}c 7qϟ8&db 9r4ϟ?}1aD?}3K;@B3jHѣG=zѢE-zѢG&E1bQ3f̨"D!&L# :}O>}Ž;Pd $A4HI=}ӧO> 揝+yåj$ȤJ*U-ZĉӢ(vӧOr D;{?}ϟ>ƴc?}ӧO>}ӧ.WvX@Ǐ=~ LRD!- d?~cǎMpEM?}ƴ O?ӧ>} ;vag A$h>}N=}hP#Ύ\yg3PhJ*U,PG ndQΟ>c ń>vc ϟ?̴cǎ(BӧO>}ӧO>}41D>}ӧO>}O>}xѧO>}ӧO>}ӧO>p>رCd>}ӧO>}QB;{QcBA 4 >vc>}" .qĉK 5jG*UTZ D3-QK nD=vcG>Iϟ?̀cN;vZӧO>}ӧO>}$1QΕ}çO>xO>}АE>}ӧO>}ӧO>uLıc$}ӧO>}c"={cG3 G>}ӧO;}hD8q⨉5jԴJ*UTAN 3c@G0&Za=}N;zLϟ?q(ıcǎ;QO>}ӧO>}$Qǎ;HӧO>}ӧO<}ƴS vӧO>}ӧO>}@cǎ;DӧO>}Ň;vرcǎ;0 4$}N1-LrD .qԨg 5jԨG*UTRU@fGM8jސ!>|N;z$O?vLıǎ;v!cO>}ӧO>$c;uZӧO>}ӧO>cLԩS.ӧO>}ӧO>/ةcgvYҧO>}SDŽ;vرcO;y2 }s犉?ƨQ.&ĉGM1jԘ2*UTRJUM̘138j⸉*Z$1N=vcϟ?vLıcǎ;vرJ?ӧO>}cN;vԡ >}O>u@đcG9uTӧO>}ӧO>Bةcǎ:vIO>};zǎ;xI"c$}(1"5jԨQ#ɘ1-ĉ 5jԴ@I*UTRJR 1c8jQe c̘aD (v٣?vLĩSǎ=vcǎcӧO>}^c;vԩD>}ӧO>vLS;ucN>}ӧO>I~رc;vԩSI>}㦅=zرcǎ;{رc >LF5jQĘ3cP&N.fhpE*UTRJ*UUh1 5qQ3a.ԨQ̘$&LG ;رǎ;vرcǎ8&ӧO?&ةSǎ:vةc }ӧO>}8pSN:uؑc 9}ӧO>cZԱcǎ;vرcǎ$I;vcǎ;vђ QG8qԨQF4cҌi%fd2B*UTRJ*URyŌP⨉#ɘ1cPF3jԘD  رcǎ;vرǎ/Աcǎ:vرSǎ(AçO>/ȱc9vԱSGvӧO6 رC;tȱc:Ds%;zرGN-Z03PF5jԨQ&3cҌ1D\Lr!*UTRJ*UT43f4qĘ1cƌ1Ō5jԨQƌ3 PA,qرcǎ;vIO>1ةcN;uةcǎ;vhHӧO>;̱#;tԱSǎ:lLӧO:&Աcǎ;vرSǎ;uIRDŽ;v$aD&[̘1c chF5qԬQ33č3pF*UTRJ*UTRņ b~p#ɘ1cƌca5jԨF5IZ@1(IZaF-vcŘ?-cǎ;vرcN;ui1O>IԩSǎ:rԱS;s1O> ԱcN;uةcǎ:vر‚ -LHbƌ3fΘ1c cpS5jԨQD1g̜3f p1qC*UTRJ*UTRJU P40˜1cƌ3& 3jԨQF1Œ͙3cΠcL!-@0A#Ʌ\ةc:vرSǎ;vԉcbN+-ԱS;vةS:u˜?-ԱcN;vr%!`0bh̘93jԜ1cL\2f+jԨQ 1hƌ&l񑀔*UTRJ*UTRJ*R ddٱ1cƌ3f!8ԘQF5-3ŏ4f̜As 3f̘9sL C 4hq%˕+vرS 2-ԱSΜ:rةcN:rԩ3M&$A Zʙ2cʌU̘9ƌ3i̘9ƌ&\ƀ3YQ33cƌ„r" *UTRJ*UTRJ*U"J>ƌ3f̘1cL@1F3Bdp3PʌIs̘3cΘ13ƌ NLf 1cƌ)$I$-ZhaA Z!C /^x1a„-ZhтE L$I2f3č133fƐ/\L@1s4fҘ1c $cp3 1\,Q1gJWLpIG*UTRJ*UTRJ*UT $K &@H2f̘1cƀF1e.1sƌ3fΜc˄$I2˘1cʌ3f̘1cƌ3f(3f̘2cʌ3̘1cƌSf̘4hQ&͘1fʌ133cƐł/_PŌ3gԜ9 cƌe̘1\p0J .;,r WG*UTRJ*UTRJ*UTR'&,2 (cƌbB3 2.cXrf3g̜9s̄$\pd1cƔS̘2cƌ3fY$11fL1cƔS̘3cƌ)3f \piAc3cΘc̘1f̌"\ƀB3fԨ1̏cp3f 1cƌ ~\r R D*UTRJ*UTRJ*UTRJP4@(3(25?~p3.cb3fƜ9s˄$\p…K\ƌ3f̘1cƌ)Sf WpĘ1eƌ3f̘1cƔ3f \p…1cҌcf̘3fƜ1B ._f 1fМ1c 1aƌe.\$١! (W\rŅ+V(J*UTRJ*UTRJ*UTRJ@Up4H,W\IB B@e .c2G1gƘ!C˄$\2 .Y@pS̘1cƌ3f Wp…Kcƌ3&͘4cƘ3f \p… $1sf̙1fΌ1c 1ctK,1cfL1\Ƅ2f̘$*FXPA W\rŅ+WXJ*UTRJ*UTRJ*UTRJ*UX0J W\rH2˘1\peL !f̘!B.\p… .\x2f̘1eƤ3 ,\p .IL3&͘1cƌ3f$\p… .\2ƌ1fƌAB0\2.\cf.]ƌc-\"dB( \r凅+PHJ*UTRJ*UTRJ*UTRJ*UT *4@\rʕ+I4$0A‚Ipe 1&9C.\p Å .\p˘1fƌ3˄+\p… .\X2f̘1fΌ13D.\p… .\de̙1cȌ .\p% 1c$1a*5DA +P\(P<\rE(-4@RJ*UTRJ*UTRJ*UTRJ*UTR*Ɉ$Pʕ+W@$(Iva.\XD.\p… .cp%(cƌ˄$\p… .\pI2˘1cƌD.\p… .\pď1gƌI".cpe 0\pQ"A&~\r劕+W\ (P4\" ]TRJ*UTRJ*UTRJ*UTRJ*UTRJգv@ʕ+P<@ń (PPJ"4hD.\p… .\pÅ +sf$\p… .\p%.cƌeB.\p… .\p… ?ƌb.\p%$@XaW\r *W@ *P"TRJ*UTRJ*UTRJ*UTRJ*UTRJ*U&Hb(WbЀ @B (P@I2a &40ACH1\p… .Y@13D.\p… .\p…K \ƌ2! .\p .\… .\h2 $;Lh0aDI@'-Z@rʕ+W (P@aBKTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTR5E X (P~, (P@D &@d&,0b !Cd D.\p… .\p… .I@p" .\p… .\"DH -LX`6@ (P@ (NZr+WhtH (Pа@*UTRJ*UTRJ*URJ*UTRJ*UTRJ*UTRJ*I- АJ(P@Ib*P@B$PhLH (P@ ;ZA@&"D`„ -L0a„Bx &L00a„ F0cǎPA(P@ (P@iJ(N4i(PZ  *UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*U`B$P@(Q@E&I$  (P@ (@ @ (P@% 4v0AP@ (P@ (PTh% P@ (P@a (K@D+P@Ȁ HH*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTR5  -@e'M$I$P\ (P@!I$4&$ (N@ (P $I D@ *P@ (2&$I$ @ (Phe L@  &L &!J*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*J-LPZ$P$ v@ (P@`" $I@(P$(P$I$B( (P@ $ L$I%'PZh (P0 $P@IL#TRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ@UTRJ*UTU`&4h`!(P@ $P (U$CI$I@I(P@ (P0$I$I$a! (V@ ~0I$I(I$i (P~LH %P~0a€ -41ACRTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTREOv11a4~$i% (I@a(P|L$I$I$ID I@(P4H`$I$I$P@ $QJ$O@E (Pdh%I &ѢB@$*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*Uh$0 LРdPLhp$I$I$II$>& (I(H$I$I$I$I|@EP(Q$I(O$I$I&@AC>| ZIJ*UTRJ*UTRP*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTR $d0A !B|haB $Ǝ$I$I$I$I8`" (I&H$I$I$I$I$I$I@I" 4@0!D!>L$`1TRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*U ؂B ,!ć!BL0@ &X14~$I$?I$I$I$A @@hРA-ZA -|0Q! $f*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJդ7EZ$0?ƅ &|-4h`AC4@4h`E-|a 4|C>h1A|hBBxRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTR(Ç>JLЀC>|a-Z|pD >|!ć>LL;(hÇ&0a2ĸyI*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*URJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*ULQ4Ƌ-.ha>Zh@ď?~G &!ć&&>|0p)UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UҪTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTTIO0uǏ?v` 4h` @ h`;|EOJTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*U;evolver-2.30c.dfsg/doc/columnbare.gif0000644000175300017530000027652311410765113020030 0ustar hazelscthazelsctGIF89a@,v      /////0/0//000//0/000/000?????@?@??@@@??@?@@@?@@@OOOOOPOPOOPPPOOPOPPPOPPP_____`_`__```__`_```_```ooooopopoopppoopopppoppp!!5 Image generated by AFPL Ghostscript (device=ppmraw) ,@,vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvviG>8>;YvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnQ:787_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvn0KX\W[/Xvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv8CXW[X4ZvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvrOvvvvvvvn(Hvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvn?vvvvvvvn.HvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvJ]YYWW[[WW]X@vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvn_vvvvvvvvvv'Hvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv"\vvvvvvvvvv'Gvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7vvvvvvvvvvv7vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv;Nvvvvvvv2_vvvnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvL7vvvvOvvvnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv:vvvvvvv,:vv8vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvBWvvvIBvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnvvvv;+WW\vvvT;vvvvQ 7vvvvvvvvvvvvvvvvvvvvvvvvvvvvv;vvvvvvM7vv=vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvW8vvvvvv_ \vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvc-vvvvv-@vvvvvvvvvn2&vvvvvvvvvvvvvvvvvvvvvvvvvvvvv88<88) vvvvvX<9vvYvvvvv,BvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7Wvvvvv-_v_nvvvvvvv5<==;87=7<:;>=:;;>P[XYXW[XW.vvvv Nv4Z]WWXYWYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvTCvvvvn\XXDnvvvgvvnO=_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvW6vvvvv%vvvfvvvvvn+"vvvvvvvvvvvvvvvvvvvvvvvvvvvvv>vvvvvvv7=vvJnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnavvvvvvvvvvM5vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv8vvvvvvvvvvv;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv__vvvvvvvv0*vvvN+cvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv._vvvvvvvv?.vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvkWW[W^XW[W[Zavvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvn#.XvvvgIPvvvvv.h0Invvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv)F._vvvnR"8=>&Gvvvvvvvvvvvvvvvv+2vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvEvvvvvvvvvvvvvvvvvvvvvvvv[vvvvGnl#vv:vvvvvvvvvvvvvvvvvvv=vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvPvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvm2KnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvv?LvoWv:vvvvvvvvvvvvvvvvvvv[vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvngvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvG*bvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvv48v:vvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvJvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvg1AnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgDvvvv5ivv(?v=vvvvvvvvvvvvvvvvvvvvCvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv+6vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvK#)\vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvLP:nv8vvvvvvvvvvvvvvvvvvvv5$vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvGvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvg:EnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvW=Ervv8vvvvvvvvvvvvvvvvvvvvOvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvT"*Wvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvv8vvvvvvvvvvvvvvvvvvvvnhvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvg==gvvvvvvvvvvn8&[vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv)*;:78:><>vvvvvvvvvvvvvvvvvvvvvJvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvO$PvvvvvvvQSvvvvvn2$nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvG&*Wv`Bvvvvvvvvvvvvvvvvvvvvvvvvvvvvv.0vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv9vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnB >gvvvK.nvvvvvvvvWnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvg"=gvvvvg," 3W_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvIvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvY,"Qgvvvvvg^gvvvW&vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv__vvvvvvvvY@vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvn?kvvvvL: Jvvv+Xvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvngvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvCvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvW. vvvvv[vgvvvh)vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv:NvvvvvGKvvvvvvvv%ng Yvvv;WvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvFvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv.1vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvg-vvvvvgvngvvn$vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvW8vvvvvYvv:=vvv&vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv1(vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvKvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnvvvvR Uvvv7Hvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvn&vvvvv7=vvHvvv1gvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvQvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvavvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvW-vvvvvvvvvvvgvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&gvvvvn.&vvvWIvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvggvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvGGvvvvvvvvvn_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvW%vvvvvvWavvvvN6gvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvOvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv9vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvO$gvvvvvvO_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv9Bvvvvvvvvvv.Gvv_-Hnvvvvvvvvvvvvvvvvvvvvvvvvvvv(4vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv=vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7 kW&7:=6:nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv0*nvvvvvviGvvvvvnK*_vvvvvvvvvvvvvvvvvvvvvvvvv?vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYWWZXX[Y9vvvnWY/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvW,UWWK%_vvvvvvvvva5Mnvvvvvvvvvvvvvvvvvvvvvv`vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvv\nvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgC9+vvvvvvvvvvvvvvI-_vvvvvvvvvvvvvvvvvvvvv\vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvEvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvG>[vv[vvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnivvvvvvvvvvvvvvvi/Gnvvvvvvvvvvvvvvvvvv9vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv3'vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvv*&[$MvY!vvvvvvvv;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvKvvvvvvvvvvvvvvvvvvM+[vvvvvvvvvvvvvvvv:vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvQvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvavvgvY&_vvvvvvvv=vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv..vvvvvvvvvvvvvvvvvvvvg2 @nvvvvvvvvvvvvv[vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvggvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv[vvvgHvvgv[vvvvvvvvvvZvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvKvvvvvvvvvvvvvvvvvvvvvvvO%.Wvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvQvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvUGvWvvvvvvvvvvvavvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgrvvvvvvvvvvvvvvvvvvvvvvvvg< AgvvvvvvvvvGvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv,5vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvn_vvWvvvvvvvvvvvCvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvQvvvvvvvvvvvvvvvvvvvvvvvvvvvO%&Uvvvvvvv3.vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvCvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv[vvvvLvvvvYvvvvvvvvvvv1-vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv%>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvgC:gvvvvL<:=9=<9=78vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvZvvvZ :>7:vWvvvvvvvvvvvTvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvBvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvX)&Ovv[Wvvvvvvvvvv=vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv[vvvW:>:=vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvHvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvGnvvvvvvvvvvvvvvvvvvvvvvvvv]vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnhvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYWvvv81vvOv;nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv2,vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvMnvvvvvvvvvvvvvvvvvvvvvvvvvv9vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv GvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvW\vvvG,vvH v8.cvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvLvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvK!nvvvvvvvvvvvvvvvvvvvvvvvvvvv7$vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*3vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYWvvvv9RWv9GnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgtvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvG.vvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvIvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWXvvvvnA;gvv9H.cvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvQvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvH*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvn_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_rvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\Wvvvvvvvvvv=vvk5 ?nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv%0vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvve?79GgvvvK+vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvGvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvXvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvva5<.">=88;>+vvvvvG!*Xvvvv@RvvvvvvvvlWWWWnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvBvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvU.QZWG&gH,vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv2*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv">=-gvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv)6nvvvvvvg.vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvL vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgnvvvvvvvvvvvvvvvvS&)vvvvB"ivvvvvvkHvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7;>><97888+WvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvv)Lvvvvvvvvvv#$vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvOvvvvvvvvvvvvvvvvvvg:*rvB.vvvvvvvvvv+Svvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv=vvvvvvvvvvW[vvvvv9vvvvvvvvvvvvvvvvvvvvvvvvvvvvX1vvvvvg8DuvvnnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvSvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvavvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv$7vvvvvvvvvvvvvvvvvvvvvJF&vvvvvvnXvvvnnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv=vvvvvv&vvvWXvvvvv:"vvvvvvvvvvvvvvvvvvvvvDNvvvvv&nvvvva GKnvvKBvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&5vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv?vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvBvvvvvvvvvvvvvvvvvvvvv gvvvvvvA8vvvvW7vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv=vvvvR6vvvWWvvvvvXvvvvvvvvvvvvvvvvvvvvp+nvvi.vvvvv#;vv.<=><:>7;;87;>;9<98'>vvvvv66;=<=87:7;9:7<9;>9>::797:<<9;7[X]\W[W][WWW>vvvvvvvvvWE\YWYXvIvvvvvvvvvvvvvvvvvvvvPv]:vvvvvOnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv8%vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv.4vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvOvvvvvvvvvvvvvvvvvvvvvvvvvv_1vvvvvvv<7vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\vv.Xvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvpgvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvGvvvvvvvvvvvvvvvvvvvvvvvvvv8IvvvvvvX[vvvv8Ivvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7vvvvvvvvvv[Wvvvvvvv 8vvvvvvvvvvvvvvvvvvvvvvvvvvnnvvvvvvvvvvO+vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvJvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv+.vvvvvvvvvvvvvvvvvvvvvvvvvvnnvvvvvvvvvvavvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*YWYWWWW][[DWvvvvvvv?vvvvvvvvvvvvvvvvvvvvvvvvvvvahvvvvvvvvTnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv+.vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvKvvvvvvvvvvvvvvvvvvvvvvvvvvvcgvvvvvvvvfnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv[WWYWW\\YXWWnvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvv_8kvvvvb-5nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvKvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv:vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvknvvvvvvvvvvvvvvvvvvvvvvvvvvve9nvvvvg9nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv[vvvvvvvvvvvvvvvvvvvvvvvvvvvvvH! [vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvPvvvvvvvvvvvvvvvvvvvvvvvvvvvvvN$YvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvDvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvOvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv$9vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv`vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv=&vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv$;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvCvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvEvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvXvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvQvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv@vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv?vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv1.vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv=vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvngvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv3,vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvOvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv`vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv8vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvJvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvSvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvggvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvDvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv+)vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvigvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvDvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvOvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv0&vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvJvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvUvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7"vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv.0vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvS vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvEvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvhnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv-4vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvOvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvCvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnhvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv4,vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvQvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv?vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnivvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvLvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvOvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv$=vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvGvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv+6vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvBvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv.5vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvKvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv QvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvIvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv#5vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv:vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv`nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvBvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv?vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvavvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv";vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv6$vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvbvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv"=vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvJvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv9vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvRvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv?vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv6*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv?vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvQvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvKvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv=&vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&8vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv=&vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv@vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv?vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv]vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvn_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv=vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv4&vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvn_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv8vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvHvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvO vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvGvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv9vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv1,vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvXvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvDvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvngvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv5+vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvKvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv;$vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvVvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvGvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvhrvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvDvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvTvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv,1vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvknvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv?vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvVvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv2,vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvngvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvEvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Svvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/.vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv$=vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvOvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvNvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvbvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv%/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvS vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv?vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvggvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*,vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvZvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv@vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvikvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvZvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvQvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvMvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv8vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvTvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv.4vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgrvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv:vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv)2vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvBvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvAvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvTvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv[vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvBvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvFvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv>"vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvZ>;>68=:77>=:7Y\vvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvnZZvvP%nCvWvvvvvvvvvvvvvvvvvvvvvvvvvMvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv8vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv))vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv>vvvv&WvvK>=9AZvvvvvvvvvvvvvvvvvvvvvvvvv@vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvSXvvcvv=vZvvvvvvvvvvvvvvvvvvvvvvvvv**vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvHvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv>vvKYvvvvvev]vvvvvvvvvvvvvvvvvvvvvvvvv=%vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv[vvvvXvv\vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvMvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvFvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv8vvvv[vvv_ vvvWvvvvvvvvvvvvvvvvvvvvvvvvvv+1vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvYvvOT*vWvvvvvvvvvvvvvvvvvvvvvvvvvvDvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvknvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7vvvvZvvv83vvvWvvvvvvvvvvvvvvvvvvvvvvvvvvMvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvv_nvvv\>Hvv[vvvvvvvvvvvvvvvvvvvvvvvvvv[vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Pvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv:vvvvIgvvvLYvvvWvvvvvvvvvvvvvvvvvvvvvvvvvv_nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv[vvvvvvvvvvvvvvXvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv"6vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvDvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv8vvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvZvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvQ:=;==47=<8:8OvvvvvvvvvvvvvvvvvvvvvvvvvvvBvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvAvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv6 vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvL[W[XX^<9^W[W\[Yvvvvvvvvvvvvvvvvvvvvvvvvvvv"9vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvn_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv9&vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvbvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvO vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvIvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvTvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvngvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv2*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvngvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv9vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvGvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv:&vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvavvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvKvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvIvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv= vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv.2vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv?vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv)5vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvLvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvngvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv3"vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv UvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvKvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvn`vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvanvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvKvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvP vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&4vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvHvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv))vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnhvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvEvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv1(vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv%>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvGvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvJvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&7vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvkrvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv.1vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv[vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvPvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvIvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvQvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvbvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&=vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7%vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv!:vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvDvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvDvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvFvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv?vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv5&vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv]vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvngvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv1&vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvOvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv[vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvGvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvTvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnkvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvAvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv3-vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnmvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvBvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvQvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv8"vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvGvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvIvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7 vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv-5vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvOvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvEvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvlnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv,/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvTvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv@vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnhvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv1.vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvOvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvLvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnkvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvIvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvOvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv!Zvv:>vvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv=vvvvDWvvv.Gv9vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7$vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvNvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvOvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvZvva>Yvv$?XgvvZvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7vvc7XvvCvng8vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv@vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv**vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvihvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv[vvvvZvv<>vXvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7vv_7\vv84vvY:vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv6 vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvavvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvIvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvQvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvv^vvAnvngWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv>vvvvYvvn3,v9vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvPvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvKvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvknvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv.1vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvXvvvvXvvvvvvWYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7vvvvYvvSUMg;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnkvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv0,vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvFvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvXvvvvYvvnvng]vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7vvvvWvvPvv37=vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv KvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvMvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvXvvvvWvvG9 IvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv:vvvvWvv%Nvv&E9vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv,6vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv=vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvZvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv[vvvvvvvvvgWgvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv=vvvvYvv_6 nvvvvvvvvvvvvvv:vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvEvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvEvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvXvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvW99888='<<<<8Lvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&:vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv1&vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv%7vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvKvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvXvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvOvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvGvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv8vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv6,vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvXvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvDvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvBvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvngvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv1*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvn`vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvO vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvEvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv)-vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvZvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvAvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvggvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvSvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvNvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv=vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Uvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv[vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv!4vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvi[X\AY[W[WWWWgvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv./vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv@vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvBvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgYY[AXYW[WWY[_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvXvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv1vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvIvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv2 vvvvvvvvvvvvvvvvvvvvvvvvvvv8vvI[vvLvvW;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&Qvvvvvvvvvvvvvvvvvvvvvvvvvvv>vvvvXvvv?.8>vWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv8"vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv(,vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv,Lvvvvvvvvvvvvvvvvvvvvvvvvvvv:vvvvXvv_nvgi8vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvn-vvvvvvvvvvvvvvvvvvvvvvvvvvvv=vvvvWvv_n99vYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvLvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvr,nvvvvvvvvvvvvvvvvvvvvvvvvvvv>vvvvWvvvv0v;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*vvvvvvvvvvvvvvvvvvvvvvvvvvvv:vvvvYvv?^-'kYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnbvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv,vvvvvvvvvvvvvvvvvvvvvvvvvvvv8vvvv]vvvvvnJ9vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvovvvvvvvvvvvvvvvvvvvvvvvvvvvv>vvvvWvv:89ZXvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvGvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnvvvvvvvvvvvvvvvvvvvvvvvvvvvv9vvvvWvv#bvv4:;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv3vvvvvvvvvvvvvvvvvvvvvvvvvvvv8vvvvXvvvvv9=vYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv5.vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&=vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvEvvvvvvvvvvvvvvvvvvvvvvvvvvvv:vvvvWvv'0v`]vvvvvvvvvvvvvv[vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvHYvvvvvn6&nvvvvvvvvvvvvvvvvvvvvv9vvvvvvvvvvvvvv;vX:>798;>:;9<;8<\vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv6>vvvvvvvvv[nvvvvvvvvvvvvvvvvvvvv777=7;>87==7;>:GvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvTvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvbvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvM,vvvvvvvvvWnvvvvvvvvvvvvvvvvvvvv9==<=>87;9<=:=;+vWvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvT.vvvgvvvv_cvP1vvvvvvvvvvvvvvvvvvvvvvvvvvvbvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv"4vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvAvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvY&vvvgvvvvvgvS2vvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvXvvvvcgvvvW>\vvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvrvvS>vvv1%.)v%gvvvvvvvvvvvvvvvvvvvvvvvvvvFvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvEvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv3 vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgvvO;vvvvnvv(WvvvvnCgvvvvvvvvvvvvvvvvvvv&vv_vv,G]4vvvvvvvvvvvvvvvvvvvvvvvvvv1&vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvQ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvg.vg9vvv!vv_2vvvg'vvvvvvvvvvvvvvvvvvv=vvvvvvvWvvNWvvSvnvXvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv8[vv[*>vv7"vvC&vvvvvvvvvvvvvvvvvvvvvvvvvvvQ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvogvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvASvvY(:vvva?vvvvO-Wvvvvvv_\WXW\W[[W[[X@W]W[YWXHvvvv[vvkvvW97;999=>>79<<=>:7;>>>>_vvv9>vv;7vvWv&vvvvvvvvvvvvvvvvvvvvvvvvvvnhvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv=vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv KvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvv]hvvv:9vv;;=;;==7=:>88><:;:<:;>;>:>G[[W[WW]WXWWWWWWW]YYY[WWX,Zvvv:Qvvv=>vvvvWvvvvZ.Pvvvvvvvvvvvvvvvvvvvv1%vvvvvvWvvvvXvvWvvgYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv[2vvv7:vvhnvU_(8=/ BvvvvvvvvvvvvvvvvvvvvGvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvngvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv`rvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvve1vvv>9vvvv[vvg.vvvg&vvvvvvvvvvvvvvvvvvvvPvvvvvv[vvvvXvv8&v7"v]vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnvvIGvvvJ 4v'Vvvvvvvvvvvvvvvvvvvvv_rvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvGvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvXvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnvvHHvvvva2vv:[vvvvnGgvvvvvvvvvvvvvvvvvvvvngvvvvvWvvvvNgvvv:.nv^vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvO5vvvvvvvvvvvX.G&WvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*,vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv#;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvO4vvvvvvvvvvva#vvvvvvvvvvvvvvvvvvvvvvvvvvvvvOvvvvvYvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv.Gvvvvvvvvv_ovg?rvvvvvvvvvvvvvvvvvv$8vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvHvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvGGvvvvvvvvvgnvvvvvvvvvvvvvvvvvvvvvvvvvvvvv-5vvvvvO:=77>9>:;=7;=7Pvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv5$gvvvvvv:nvvvc""Pvvvvvvvvvvvvvvvv9vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvknvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv[vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvL#svvvvvvGcvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv@vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvY$&887(Mvvvvvv=vg8 nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv`vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnWWYhvvvvvvvvvDhvvvvW+"OvvvvIY[W\]XWI[Avvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&9vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvCvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvGnvvvnXWW_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvn? ;gv8vvvvvvvFv;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv?vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv5,vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvMnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvW*&vvvvo" v7vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvSvvvvvvvvvvvvvvvvvvvvvvvvvvvvvInvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvn@ vvvv;(vOv8vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvkgvvvvvvvvvvvvvvvvvvvvvvvvvvvKnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvva(vvvv[vng7vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvAvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvTvvvvvvvvvvvvvvvvvvvvvvvvvvGnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvQvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvCvvvvvvvvvvvvvvvvvvvvvvvvLnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv3+vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv79>Lvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvggvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv:vvvvc4G'avvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv.-vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv:vvvvvvvvvvv7vvvvnnvvvvvgvv[vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Ovvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv:vvvvvnYgvk2DnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvKvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7vvvvvvvvvvv8vvvv2\vvvvP9vvXvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv.5vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv:vvvvvvvvv=vvvK,WvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvhnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvv;vvO]vvg9vv[vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv?vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgZXWWWWWXWEvvvvvg=ArvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYDQvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvv=vvnY[vvvX.9vvXvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvcvvvvvvvO&,YvvvvvvvvvvvvvvvvvvvvvvvvvWL&vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvFSYvvvvvvvv=vvvvYvvvv98vvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvFvvvvvvvvvg> ?ivvvvvvvvvvvvvvvvvvvvvv*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvoW1vvvvvvvv7vvvvYvvvv8vv[vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv>"vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvQ vvvvvvvvvvvvvvgA;vv^vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgivvvvvvvvvvvvvvvvW(&RvvvvvvvvvvvvvvvvvOvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvavvvvvvvnvvvv6_vvvvHGvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvo_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv OvvvvvvvvvvvvvvvvvvnB 4gvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvXvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvGvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*5vvvvvvvvvvvvvvvvvvvvvW-GvvvvvvvvvvvvvI8=Efvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv!vvvvvvv<WWWWXWWWWWXWWWSvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv5(vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvAvvvvvvvvvvvvvvvvvvvvvvvnH5gvvvvvvvvp2GXYYO-evvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvQ:7Kkvvn'WXY\WX[\\[gvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvv_,GvvvvvgXvvvvvvvgKvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvH5WW[K"%g.,lvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgpvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvZvvvvvvvvvvvvvvvvvvvvvvvvvvvnH0avn_vvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv,=nvvvvvviKvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvRvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv9vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvd'!Wvvvvn2>vvvnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv.Hvvvvvvvvvv#$nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv 3vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv>&vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnGvvvvv(4v?2vvv8MvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvK=vvnWvvvn?7gnnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvAvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgBvvvvvnvv gvvg-vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnvvD;vvn9S_HAvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvn_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv9[vvvvvnvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvc(vg;vvBvvLvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvMvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/gvvvvv2"W&WvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvHJvvv>>vv[[vvM&vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv=vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv2+vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv:YvvvvvvG>Y_vvv%vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv=Yvvv7;vvvvv9avvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv9%vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvG vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvDLvvvvv?kvnvvvn&vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvE]vvv9:vvvd.nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvg+vvvvv/&[$QvvvNDvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvW=vvv>>vvicvvvvovvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvqgvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv Qvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv,_vvvvvL=Xvvvvnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv98vv99:=Ic'avvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvGvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&4vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_nvvvvvvvvvv.Kvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv-[vvWWvvH7:7>L,Jnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv.*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvDvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvXnvvvvvvvv.>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnnvvvvvvvvvvK2vM*XvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvLvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgXE9%=_vvvnHSvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_gvvvvvvvvHvvvvh4 ?nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvknvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvXvvvvvvvvvvvvvvvvvvvvvvvvvvvvv(vvS-"Lnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_9gvvvv_"4vvvvvvvvU$,YvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvOvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv9vvvvvvvvvvvvvvvvvvvvaGW\WXWWW<>8nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvI"&2hvvvvvvvvvvvi=Dnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv>%vvvvvvvvvvvvvvvvvvvvWWvvvvvvvvvv<vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvR# Qvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv?vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv\vvvvvvvvvvvvvvvvvvvvX[vvvvH1vv79vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvg8 :ivvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv[vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvngvvvvvvvvvvvvvvvvvvv[Wvvv_nvNv:@vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvZ-"SvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvHvvvvvvvvvvvvvvvvvvvWZvvvWvv4>v=vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvr?7gvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvAvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv))vvvvvvvvvvvvvvvvvvvZ\vvvv(+'nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvoG0gvvv[:;;:8;>7<>>><9;vvvvvvvvvvvvvvvvvvvvvMvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&9vvvvvvvvvvvvvvvvvv[Wvvvn.._v9vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_*JnWvvvvvgvvvv_[vvYvvvvvvvvvvvvvvvvvvvvv.,vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvDvvvvvvvvvvvvvvvvvvWWvvvvvWXvvv>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnNvvvvQ>vvv)6nYvvvvvvvvvvvvvvvvvvvvvKvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvYvvvvvvvvvvvvvvvvvnZvvvvvvvvvv&&vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvG&vvvvvvvvvvvn_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvZvvvv;8vvvn*WvWvvvvvvvvvvvvvvvvvvvvO!vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv%7vvvvvvvvvvgavvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvXvvvv8:vvn?vvv[vvvvvvvvvvvvvvvvvvvv*.vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnpvvvvvvvvv`_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvZvvvv<9vvC&Y[YcJWvvvvvvvvvvvvvvvvvvvnhvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv__vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvLHvv9/> 8hvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnvvvvvvvv`_vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvZvvvvvvvvvvvvvvD&Ovvvvvvvvvvvvvvvvrvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv/vvvvvvv__vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvW^XW\XWYXWWYWXWInE =gvvvvvvvvvvvvvv@vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv<6gvbbvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnW[YWZW]WXWWWY\_vvvY.%Ovvbvvvvvvv<3gvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv_Lpvvvv_&._vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnB 1PWvvvv_;gvvvv[6vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv=<;<8<7<<89>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvQovvvvvvvvGcvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvX,cvv_gvvvvvvvvGvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvgrvvvvvvvvvvM)vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvnNOnnvvvvvvvvvvH3vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvI4v\vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv,`vvvvp_vvvivvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv7 WvvvvWvv)avvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvgY[Y+6vWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvhgvvvn#vvvvv"=v[vvv_6vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvlnvv_'vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvvvgnvXvvvvvvvvvvvvvvvvvvvvvvvvvvvvvg799>=99>9776 Yvvvvv_9 Mvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv?PvvvvvvvU*vvvvvYvvvvvYvvv[[vvvvv6&W7vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv=WvvvvvvvgvvvvvXvvvvv&HvvvW.7;8:>8<<8;:8;>7:88989;>>8:\vvvvvnvvXvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvOMvvvvvvRvvvvvvWXWWWYWW]\WWWWWXW[cvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv[vvvvrnvvvYvvvvvvvvvvvvvvvvvvvvvvvvvvvvv__vv_6vvvvvgvv_vvnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvva-vvvvvv.CvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvWvvvvUvvvvWvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv&/vvvvvvvvvvH;1vvv89;=;9<;1vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvn-8nvvvvvv[Yvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvn-SnvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvW>:;?gvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv;evolver-2.30c.dfsg/doc/moundbare.gif0000644000175300017530000046403711410765113017654 0ustar hazelscthazelsctGIF89a,u      /////0/0//000//0/000/000?????@?@??@@@??@?@@@?@@@OOOOOPOPOOPPPOOPOPPPOPPP_____`_`__```__`_```_```ooooopopoopppoopopppoppp!!5 Image generated by AFPL Ghostscript (device=ppmraw) ,,uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhA:8F`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuY'*HYXP/Ruuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu1'iuuuuuuo3-uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu3>JhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhX\^R9>Zuuuuuuuuuu`/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo;5YXXJ%huuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuH;Nuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuulXXXT?;;8^uuuuuLRISuuuj'uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhCuuuuuuulJuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuu->2uuXuuuuuuuuuuuuuuuuuuuuuuuuu`\ZYJ:;>2 QuuuuuRuuPuuu^>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo`uuuuuuuuuu)QuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuouquuXuuuuuuuudXXXH=?<2;=??9/\uuuuXXuu#??=>[X_\ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuL6uuuuuo]ZuuuuD=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu1Uuuuuu<9uuuuuuuuu`uuoZ^\X<><>#ZuuuhYuPou8<98\ZY_puuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu-LuuuuuuuuuuPuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu!4;Gquuuu0%-9:;@X[uuuXuuu^uXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu*>ouuuuuuuCouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=ouuu;*;8=BXXY^uuuuuuuuuuuuuuXuuui\uXlu^uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuJ0X\XXD&uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\ouuGuuu;4XYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuP TuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuT:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:=uuu7\uuXQuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu]uuuuuuuuuuu^uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/uH/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu6Wuuuuu`uuOuuuuuuuu<huuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu@X\YXZX^X[XXTuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZ.uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuud/uuuuuM+uuuX*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^^]XZXX^X\_YjuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH`XPuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu&\uuuuulYauuuujuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo'Tuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo`uuuuuuuuuu*Juuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu ?Puuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`PuuuuuuulHuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuud/auuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo7CXXZP-RuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuHY@!4Fo-uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoN@HuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuFouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuDu/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuoJuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuP1uXduuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuu+uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuu`XuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuOHuLouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuouuuo\X %ZZ\Z[XZouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuRooCuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`uuuu\XuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu<?u(uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuY\uuul?>?88huXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoLP\bud^uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^uuuuXXuuuh>??9huZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu]uuuuu*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuYXuuuuuuu>>uu[uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuNouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuX^uuuuuuluuu_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuu?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuCuuuuXXuuuuuu-HuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuuuu6 uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;uuuuZYuuuuuiuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;uuuuuuhSuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;uuuuXYuuuuu40uuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>uuuuuuu#uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>uuuuYYuuuuuYuuuuYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:'uuuuuuuJhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;uuuuXDYXZZX\_ZX[]^uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>uuuuuuuukKuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;uuuuqZY\[\XX4^Xouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;uuuuuuuuu$uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu@uuuuuuuuuuuuoOuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuDuuuuuuuuuThuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu]uuuuuuuuuuuuu+uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuuuuuuu6uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuY`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^uuuuuuuuuuGouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuuuuuuuuuuu)uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuoKuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuGquuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoE4huuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuu-uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`uuuuuuuuuuuuuuoJuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuLHouuuuQ1uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuluuuuuuuuuuu\fuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuusuuuuuuuuuuuuuuuu,uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuK.quuuuuuuuIouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuquuuuuuuuuuuuu-uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuf\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuQ/uuuuuuuuuuuM(uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuu+?8;::>8>9>;uuh?R%-h!uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuuuuuuuu^uuuu4uu9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu8uuuuAauuuPRu8uuuuuuuuuoDuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuGuuuu%luP>uuEuuouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuX uuuuuuuuuuu^uuuuPuuuuu:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=uuhPZuuWHOH:uuuuuuuuuu7uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuDhuuuuu;>uuoiuu]u;XuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuJuuuuuuuuuuuXuuuu T]iuuuuu9[uu%?uuP?uuuuuuuuuuhXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhXYXQ;8<;uuuu:9uuuuo?eu>Xuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuuuuuuuuu\uuuuHuu=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu8uuuuXuujouuuuu`4puu8Xuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu<uuuuuuuuuuuZuuuo;ouelu=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu<;ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuUXXXX?2XYYX[uuuuuuZuuuf/uuu`u=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZ@Y\^X^]XY[Y\]YZ=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH<'=?=?P[XYhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuE5uuuuuuuuuuuduuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuo/>uuu?uuuuuu^uuuuoB?Iuuu:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXXuuuXuuu,u%uuuuuuuuuuuuuu=XXouuuuuuuuuuVluuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;]uuuuuh+ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuu[uuhuu;uuuuuu78=>9><4>9KuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXXuuXAuuuD?Luu='+?<9CZYY,XY\X\YZZXYZX[Z.uuuuuuuuuuuuuu5uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuj)huuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuu\ uuhuu9uuuuuuuuuuuuuhIuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuui_^\X?>;-ZuuuYuuu> /u/;<;A^\]YuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuu>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu 3uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^uuuu8T'uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^uuuui%Puu;uuuuuuuuuuuuuuZluuuuiXYXP:;<76;?:IY[ZauuuuuuuuXXuuuXuu\ourh>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu]uuuuuuuuuuuuuuuuuuuuuuuZS\XYhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu.uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuu8?'JuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuX`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYX\Z@==86\uuuua#Guuu988*iuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXZuuuuuuuuu`Xhuu=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuauuuuuuuuuuuuuuuuuuuuuuuo:;uuuuuuuuuuuuuu.'uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuh:8;8<=;:=89?9:=Yuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuu+SuuuuuuuuuuuduuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoKuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu8Xuuuuub*3ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo[]XX>8<:% />:8=Y\XXouuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuH;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuX+uuuu]Xuu0Yuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,;uuuuuuuuuOouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoZ\\Y8?8>! /:?CXXXZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuu/0uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuuuuuuuuuZuuuuuuuuuuuuuuu>uuuuuuuuuuuuuh.ouuuuuuuuuuuu`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu+uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuouuuH>uuuuhuu,ZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuulX\^P=?9:598?HX\X`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu8uuuuuuuuuuuuuuuuuuuuuuuu`XZZquuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^uuuuu`ouu\uuuuuuuuuuuuuuu>uuuuuuuuuuuuuu"LYuuuuuuuuI-uuuuuouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuLouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuEUu`=:uup9uuj&uuuuuuuuuuuuuuuuueX\ZH:?<;19>9;uuuuuuuH&uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;9uuuuXuuu 89<>PZ\Xhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>'uuuuuuuuuuuuuuuuuuuuuu\quuuuuuuuu@/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuua^Auuuu9%tuu1<;;MY9/cuouuuuuu&MuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuujZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu<9uuuuXuuu:  >;>?TYYXluuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu1>uuuuuuuuuuuuuuuuuuuuuoquuuuuiYouuu4BuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuaXZYL>;<6uuuu8Xo(uu9?=8T\9uuuuuuuuuuuuuu `uuuDNuuuuuuhuuuuuW7uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu9;uuuu]uuu'huuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;:-&9;9;[XY\ouuXuuuuPuuhuuZuuuuuuuuuuuuuuu8uuuuuuuuuuuuuuuuuuuuuuT/uuuuuuuuuuu-Huuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuCJuuu::uuuuXuuh'uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuuuuuuuuuuuuuuuuuu[3uuuuuD+iZouuuuh&8:;>#'==;<\XZ[ouuuuuuuuuuuuuuuuuuuZuuuuu?LuuYuuuuuuuuuuuuuuuAuuuuuuuuuuuuuuuuuuuuuuuI$ouuuuuuuq)1uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu6 uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuouuujhuuuuoauu.ZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuEuuuuuuuuuuuuuuuuuuuuS9uuuuu9`uuuu(:<;EXXX\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuoD=8BXZYYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuXuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuJ-&Jquuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu(3uuuuuuuuuMuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuu\8uuuuu;=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`XZXX[ZXXXZYXduuuuuuuuuuuuuuuYuuuuuuuuuuuuuuuuuuuuuuuuuuuhXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuRhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuEPuuuuu`4uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuj%uuuuubouXuuuTuuuuuuuuuuuuuuuuuuuuuuuuuuuu<3uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXduuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu<uuuuuuuuuuuuuuuuuuuuuuuuuu)Iuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu93uuuuuuuuuuuuuuuuuuuuuuuuuuuu'>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuIouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?5uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu&85uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuuuuuuuuuuuuuuuuuuuuuuuuuu;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu-uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?uuuuuuuuuuuuuuuuuuuuuuuuuuuu;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuPhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu<uuuuuuuuuuuuuuuuuuuuuuuuuuuu>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuuuuuuuuuuuuuuuuuuuuuuuuuu<uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuGouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu3uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu9uuuuuuuuuuuuuuuuuuuuuuuuuuuu>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuuuuuuuuuuuuuuuuuuuuuuuuuu:'uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoHuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu<uuuuuuuuuuuuuuuuuuuuuuuuuuuu=$uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:(uuuuuuuuuuuuuuuuuuuuuuuuuuuu6>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu+uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoNuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu='uuuuuuuuuuuuuuuuuuuuuuuuuuuu49uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu7uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`Xuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu4?uuuuuuuuuuuuuuuuuuuuuuuuuuuu?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;uuuuuuuuuuuuuuuuuuuuuusX\\YY+^^YX]\\huuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXbuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuuuuuuuuuuuuuuuuuuuuXXuuuuuuuuuuuuuu8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuNouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu *uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu8uuuuuuuuuuuuuuuuuuuuuuuuuuuu8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>uuuuuuuuuuuuuuuuuuuuuuZXuuuuIuuuuuXhuu=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuqEuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuCquuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu9uuuuuuuuuuuuuuuuuuuuuuuuuuuuLuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuIuuuuuuuuuuuuuuuuuuuuuuZYuuuBuuuuoXuu?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu+uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoJuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuHuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuua4CouuuuuuuuuuuuuuuuuuuuX]uhuuu/Xuu;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuul\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuui,PuuuoL9uuuuuuuuuuuuuuuuuuu[\uuuZuuuuuXuu9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu#uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`ZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuubduuuuuuuuBouuuuuuuuuuuuuuuuu]XuuuZuuuuuZuu?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuKhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu+uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhouuuuuuuuuuI)uuuuuuuuuuuuuuuuuX_uuuXuuuuu\uu;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuJouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuhXXXX\HXXXY\XYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu'buuuuuuu3fuuu3\uuuuuuuuuuuuuuuuX[uuuXuuuuuXuu:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu6uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoDuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`*uuuuuuuQXuuuouuuuuuuuuuuuuuuuXXuuu\uuuuuZuu:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuiUuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu3uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuluuuuuuuuuuuuuuuuuuuuuuuYuuuuhXuuuoC9lu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu%uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuulPuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu]uuuuuuuuuuuuuuuuuuuuuuu]uuuu$=uuu5lZuuuuuuu`X]X\_\X^XX\Y`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`\uuuu9Yuuuuuuuuuuuuuuu\Xuuuuuuuuuuuuuu>>+uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu3-ouuuuuuuo'HuuuuuuuuuuuuuuuuuuXuuuu>9uu:;uuX^uuuuuuuYuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuo^\^[?9<=!'>8><&huuuul:9? +Xuuu4Zuuuuuuuuuuuuuuuuuuuu?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhIuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu= uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH(uuuuuuuuuuu+Tuuuuuuuuuuuuuuuuu^uuuu:8uu88uuXXuuuuuuu\uuuuuu6#uuuXuuoXZX\=;><&-9>;GY_XYuuuuuuuuuuuuuOBuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuu<uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhHuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuujuuuuuuuC`uuusouuuuo.RuuuuuuuuuXuuuu9>uuHuu9'YuuuuuuhEuuuuu\%Xuuu,;9=BXXXZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuouuuuuuuuHhuuu\7uuuuuuuuuuuuuuuuuuuuu8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXduuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu'uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu2UuuuuulSZuuuuL@uuu`uuuuuuuuuXuuuu>JZZX`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuD5uuuuuuuuuuuoouuuuuuuuuuuuuuuuuuuuu:5uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu )uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuQhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=Xuuuuou`/>XM=986,uuuuLJuuuN6uXuuuu'%8,?uuFZauuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/BuuuuuuuuuoXuuuuuuuuuuuuuuuuuuuuuu$:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuFpuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu3uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuX=TXXXiuYuuuuM:;)'uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhjoDuuuuuuuuuuuuuuuuuuuu^uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuIhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu.uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhXX\R[uuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu5 uuuuuuuuuuuuuuuuuuuu^uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuLouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu-uuuuuuuuuuuuuuuuuuuuuuu[uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuujXuuuuuuuuuuuuuuuuuuuYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu4uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoEuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuKhuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu'uuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuulTuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu5uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuuuuuuuuuuuuuuuuuuuuYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuIhuuuuuuuuuuuuuuuuuuPuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu'uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhWuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu0uuuuuuuuuuuuuuuuuuuuuuTuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuI=;4:=<99>7uuuuuuuuuuu>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuPhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu#uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhPuuuuuuuuuuuuuuuuuuuuu>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=uuuuuuuuuuuYuuuuuuuuuuu;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu 0uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuudX3R]Z\X\ZX\X\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu&uuuuuuuuuuuuuuuuuuuuu=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=uuuuu8`uuZuuuuuuuuuuu>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu2_XXXZXX^X\ZYXY.uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuPhuuuuuuuuuuuuuuuuuuuu;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>uuuuuuuuuuuuuuuuuuuu=.uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuu@uuXuYuuuuuuuuuuu3>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu-uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=uuuuXuuuuu:u:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=uuuuuuuuuuuuuuuuuuuu-9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuuuu`Rhu^uuuuuuuuuuu9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\duuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=uu?_uuuu@9u8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhKuuuuuuuuuuuuuuuuuuu?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>uuuuuu5 ouXuuuuuuuuuuu9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu)uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu8PuuYuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuufXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu9uuuuZuuuuu8:u8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoJuuuuuuuuuuuuuuuuuYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?uuuuuuuuuuu\uuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuuXuuuuu;>u9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu(uuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu9899::==98?;>8;<8?<;=;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuHquuuuuuuuuuuuuu`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,uuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuujXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoDuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuLpuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu%uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`Xuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu-uuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo@uuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuJhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu)uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`XuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu3 uuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuMquuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu.uuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuunTuuuuuuuuuFuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuua2?%#8;;><99<>>Puuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuq@uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuNouuuuuuuuuuuuFuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu"uuuuuuuuu>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\Xuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu5uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoXX6X\Z^[ZXouuuu8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuHiuuuuuuuu9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\\uuuu(uuuu<+ruXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuujUuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu]ZXZX\ZZX\XYAYuuuu8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?uuuuuuuu;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXXuugCuuu6'X+uZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu"uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuu`uuu^^uuuu=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu8uuuuuuuu:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuX_uuuuuu\:/ouXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuu#uuuZ[uuuu?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu&uuuuuuu8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^YuuuuuuX$X'uXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuue\uuu\Yuuuu:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuPjuuuuuu8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXXuuuuuuXuuu XZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuujHuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuuuuuuu^\uuuu=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu 3uuuuuu?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZYuuuuuuh uuuYZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu%uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuX]uuuuDuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu9uuuuuuYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuX\uuuuuuu'\<uYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuPluuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuuuuuuu_XuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoHuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXXuuuu>uuuo/*quXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu7uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuXZuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/uuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^Zuuuuuuuuuuuuuu[uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu<uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuu[YuuuuYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuX`uuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`HXYXXZXH=9\XXZYXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoLuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuXYuuuu^uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu )uuuueuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuub]uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu)uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuXXuuuuduuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuEouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu(uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZauuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoXYXXXXY"\\ouuuouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoHuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuIsuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\`uuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu+uu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuup@uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuGouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,uuuuuYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuua]uXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu+uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuusHuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuNouuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu*u]uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu.uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoAuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuNC+Ipuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu%uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu+ oEbuHuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuP.SYX]E3ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuKluuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu-uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuub,u8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuMhuuuuuuuTduuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuHsuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuP9.u>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuK/uuuuuuuuuu`ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu2uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoDuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuPu8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhuuuuul?>auuu]"uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhPuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu7uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuR]u92uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuumZZ[\ZXXX^XYXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu-^uuuui*5`uuu.`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu'uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhRuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu-Xu,Puuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^uuuuo ouXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu[[2-8uuuuuuu:ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu'FY^]Y6HuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuu4'uu9&uXuuuuuuuuuuuuuuZZYY@<8>+=uuuuuuuuo#,uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhNuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu8 uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoAuuuuuuuo?.uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuu>PuuVuXXF=<<)!6uuuuu8Vuu]uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu.uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhIuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuTuuuuuuuuuuH*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoYYXZ;<<8uuuuuuudPuX&>=>=X\XYouu`uuuuuuuBho'-uuuR0uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuX`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu#uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuT-uuuuuuuuuu`ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoMuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>ZuuuuuV`uuH'uuuP4:?;IZX_`uuuuuuuuuuuuuuuuuuuuuuuuuYuuuuu]uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuJluuuuuuuBhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoKuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu&iuuuuuuuuB`uuu\=<99RXXYhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuh'HYXX48ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`[uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu+`uuuuuu`#iuuuuX<;SYYXhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhK>8;Ruuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>Xuuuuub\uuuuuuI@uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\89=>9<8>9<9>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuIouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuP2uuuuu&7^\Y`uuu+huuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoBuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuCouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuouuuu!2uuiuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu, uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuutIuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuO6uuuuuuuuuuuu%XuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu-uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu.Huuuuuuuuuu=1uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu%uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`[uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu*2suuuuuuh'6uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuNhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu-uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuQ/VXZK,&Xuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuHouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`Fuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu)uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoJuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`Yuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu-uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuX`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuNpuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu+uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoAuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu@ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoIuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuud]uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuMhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu)uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu$Yuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu)uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuNXuuuuuuh'3uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYbuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH/ouuuuuuuuu@-uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuujouuuuu\duuuu7NuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuGouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,Yuuuuo-uuuouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoIuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuouuuuu+6uu SuuuCVuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu+uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuX:uuuuuXuu::uuuX5uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuua\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo[Z\]-Iuuuuu+3uu=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu+uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuqZ[[X;' Xuuuuuh>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuHpuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoZX]Z;9>8%)?<uq `uuuJ@uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu0 uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhXXXV=<=94:9?9HXZ[auuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuP9uuuuuoZ`uuuuX*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu'uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuudXZ[N;;>59>>;PXZ]huuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu.HuuuuuuuuuuaouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuKhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYY[XO;?<7;>>;TXXXhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo&AuuuuuuuuZbuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,!;9==PXXXluuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=LluuoT&/ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu<uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuR?;::?;9:8<9=9;5uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\YXXG9?8/%?>8?[\ZXouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuqM$ChuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhMuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu]uuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoX]XY?;:8#!<:;;YZXXouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu'uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuXHuuuPOuYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo\XX]=;?<' -<=8CY]XXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuVhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuoX8uubJS RZuuuuuuuuuuuuuuuuui]YXP9>;8!/:<?=91<>8MYXX\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuulIuuuu>2uuuu?:uuuuuu8'XuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuB#><89*\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXX]XA>9uu)FuuTXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuB&huuuuuuo',uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\ZXZE::8+&:=;:=?)<9?CX\ZXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuHJuu`]uu`uuduZ63<8:HXZZauuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:\uuuuuuXuuuuh289;HXXX`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:XuuuuuuXuuuuo HXYhuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuFHuuuuuuduuauY:uuh.uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`-uuuuuuuZuD uGJuuuuQBuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuouuuuuuHbuouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuO8uuuuuuuuiXouE>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu.Huuuuuuuuuuduuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo%:uuuuuuuuAouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuA@[XXYD;ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoT98?=Houuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;evolver-2.30c.dfsg/doc/square-e7.gif0000644000175300017530000004255111410765113017502 0ustar hazelscthazelsctGIF89ad  $$$'((,,,//0/0//000//0/000/222787877878887;;;??@?@??@@@??@?@@@?CCCGGHGHGGHHHGGHHGLLLOOPOPOOPPPOOPOPPPOTTTWWXWXWWXXXWWXXW[[[__`_`__```__`_```_cccgghghgghhhgghghklkoopopoopppoopopppotttwxwwxxxwwxwxxxw{{{!!B Image generated by AFPL Ghostscript BETA RELEASE (device=ppmraw) ,d5iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4e*c&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4i҄( >H &M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI7>|Ad'F 78pxI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI@M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4i:$Hكc@+I&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI4 =zG$( >1.iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤ &M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4i҄%"H˗@}p tĚ4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M8|T &M4iҤI&M4iҤI&MjҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4i$!ţ%#+, 'P=p (1V,QH&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤID V '2KI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4id D)|ɓ={⍏" #P@>رbC4$4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&4iҤI&M4iҤI&M4iҤI&M4iҤI&M_ XaĊ {๓gϗ8ʬxƇ> ;w\%P44iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4i#Kp|cϝ;wQQo꼑"PxsN*vF $A&M4iҤI&M4iҤI&M4iҤI&M4iҤI@M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&L@ t!=+bٓ'ϝ=xFț7o`YFiرs劝"k%I&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4ijD=B BN ˞=+ɣ;vy 7oyƒ%+عcN ;w؉#PK4ԤI&M4iҤI&M4iҤI&M+Xd*!RJ*UBc J,A"D{<ٳgϞ={Ν<*رcǎ;vج(P@ƒ#ȊR!B͊@سGL={b|cǎoy͜9oެY vرÎ;vHp&M4iҤI&M4iҤI&M4iҤM K,YZ$P %|\YH"oސYDB{ٳgϚyܹsgM ;vܱsN 7 (P@{H"D1|(P={Β!vر7ox͛:>,YǕ;vİsǎ; YI&M4iҤI&M4iҤI&M4iҔ%D=dtN@>|ʔ);$"D!BhI={ٳM,c;hzXR |\A+xsb|s;vbq͛70޼y6lVqJ;1عcTI&M4iҤI&M4iҤI&M4i DRJ,U˒L2ݹǞGkȑ#G}rΊ5*iĕٳg)ܹcǎ;v`y͛7uD3&7lجpƍ71T"P@_"ᡄ&M4iҤI&M4iҤI&M4iҤ)JT *1Tq#;I9rÑ#F'F@{R$%KpkVg@{h̑@c={)"A ,X`5oqSM(oVq nܸyL%+ (@_I&M4iҤI&M4iҤI&M4iAf=8|qpG%=zȆG}!ȑgO=yȒ;"a=1gO=vYhO@HgϚ8 س'О>{)@C7o޼y%Jlܰaf7lظq#c@ d#8hҤI&M4iҤI&M4iҤI&M-(P#GhG|qȑ#CE9J…>8gϞ@+ܩT R"İ{ٳgŘ'P{((F;{Ϙ+pqΛ7oDQ͚5nܬp7nd$(P@N4iҤI&M4iҤI&M4iҤI&M {Ivx1͛(Q`a6+ܸy l (P `&M4iҤI&M4iҤI&M4iҤiG9z‘#GTYȑ#G9(>2fO={XN, 9BRR%D4 BCɞ!hHdӧ>dVQa';vܨ&nָYf 7lظqӣ _ G@II&M4iҤI&M4iҤI&M4id#G|qȑ#G| #GHI'П@`ٳ'О51촰cHpTTR%)8 38|RED!A&P=3fE58İc'!BdI=#鳇̊=zc';ycg 6mڬiÆ 7nܸYQ;vʛ@S@&M5iҤI&M4iҤI&M4iҤI=d8#|,7nx͗"8$d 1.,CƇ;u^ر3ǎ/+jc;opSǎ*KA"D]I&M4iҤI&M4iҤI&M4iT"D!BGkԱc';vyCfB vرc ;oİcN ;vرcL={ٳH(P ;pBQ D(… $N ;s̱cv켱cǎ)vر!О"!B) 2iҤI&M4iҤI&M4iҤI&MV("D!';vYa;u(ć;tؑRE11رÎ;v؉fϝ=yٳ>s'=! (" ,1PM%D$رÎ;vر7vyS:v,A"D!BÑ24iҤI&M4iҤI&M4iҤI71)"DX$Ǝ;8ycΚK Ǝ;UةÍ;vbرcǎ51ɳg<+hO@B("D G@kp8)vؙcg;dxXS;vHcǎD""DAt&M4iҤI&M4iҤI&M4iҤIH,YdGvرcN*pر˒%lxcN)s쨰s玝vرcǚb$"DBT"c3'"DnP%@ #GG9r(GI&M4iҤI&M4iҤI&M4iK@| (v|ƍ7nVqf 5!VB͝;vT!cϊ1|21!BH8ph(Ѕ"XVA"D!zcϞ={`h1+`) O >d|TyȑG#Gpȑ@4iҤI&M4iҤI&M4iI&M4%hO 7U L +ܸq lְq oި!qŎ7Eb91fϞ@{YA&"DwpH'P>Z`; B"DVٳgϞ={lA"(>\ Gz8zȑ#G1Y#{=r :4iҤI&M4iҤI&M4iҤI&/pG@A` 7nظq7nܨP͛7o|c'FcP /8b Ǜ >(PEٳg"!B={ٳgϞ!R(+V\BƑ#9rH|8zg#G`&M4iҤI&M4iҤI&M4iҤ钂"l(P@`ƍ7nܬpƍ70x3͛7XH)̞=ڳB%+C? M={ L=ىgϞ={ٳg D!B(P9rÑ#Gx葕=brd .0,I&M4iҤI&M4iҤI&M4i!eE@5 h >ܸqƍnܬqC$Jo޼y|p,\fϞ=a(@VO=cGD!ٳgϞ={ "Da oȑ#=zǗ/sjS ǟJ\hҤI&M4iҤI&M4iҤI&M(%@ZA2oܸqfɚ5nxƛ7o޼yfL*VLr oٳ n hD$ٳgϞ?{IB"7{ٳgϞ={N+ AH*_9JѣGUQ0KJpTM4iҤI&M4iҤI&M4iҤI6(P؉Af6nܸY2+ԁ͛7o޼掝;vرcÞ={G>,Y̖={ٳgϟ?J.$pEG:bĈq#F,R:X$H }! 5R@ $HvpLÔG0S&_TR%K*I&M4iҤI&M4iҤI&M4idLcN ;lHa 7nܐx 7oy;vܱc{ٳF:"1_hϚ>zÎxsΝ;xR51b\I $;8$Mfł#>*eTΒ ޼yTRJ *iҤI&M4iҤI&M4iҤI&Mj,A ;vYBN/nܸYo꼁͛7oބcǎ;vȬxcϞ={V$P%R)BbϞ?{X'@Vs ;vܹsΒ;y,Y4iҤI&M4iҤI&M4iҤI&Daǎ;vTs6lVqCM7oy͛:*رcǎotٳg5p 4"D`ٳg % L;v&Oܬ`3M9uDM51رsgI={VٳgŞ@{V"2$*(P@*H;wعsN/w˞={ٳD!B H֬TRGo)c ,YdI4iҤI&M4iҤI&M4iҤI& p cǎ;1عc_|yfN(Q޼yC;yM={|| hɖIY`$G4| (P@|c;vر!O;oVgϞ={ٳb "D-2 ITRJ*=*RN,Y&M4iҤI&M4iҤI&M4iҤ KVرcN ;v! 5oyS'J7otc{)2f  AŽK`TIR%71 (Оvܱcǎ;vёN8{ٳgϞ={؃, I ;8TT,YK/,I&M4iҤI&M4iҤI&M4i%7w܉a;wHXQ͛7oD2M"yQ&O<+P@W(1&ITR%,0 䃌;vعc;vt| cϞ={ٳgϞ=sb bre$H A %E !raǒ%K,YdQ#hҤI&M4iҤI&M4iҤI&M̬#;vbعcǎ7ySΛ7jy㍝:1ɓgM=1 fJ{Vx3IRJ$UTopܱc;vرc  ` ,X|ԈQ# (G .Lp . 2dM4iҤI&M4iҤI&MjҤI&M -`ǎvرso޼y3RPr+X`c8bQG .\P… *\@x:pȠB'NHY%R,=G4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤɉ ,X@` @ 0` $HX@ $Hh<=hҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M5iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iA/bĈ#F &ĸ8p8 q!F1bЈ#4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&M4iҤI&MȬPp!Ƌ1H,P  8C Surface Evolver Documentation - Installation

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Surface Evolver Aquisition and Installation

This chapter explains how to get and install the Evolver. Evolver is written to be portable between systems. There are pre-compiled versions for Windows and Macintosh; source files and a Makefile are provided for unix/Linux systems. The distribution packages for various systems are available from the Evolver homepage. Each package also contains documentation and sample datafiles and scripts. The documentation subdirectory is named doc, and contains the manual in PDF format, an HTML version of the documentation (except for the mathematical parts), and a brief unix man page evolver.1. The HTML files are also used by the Evolver help command. The samples are in the subdirectory fe (which is the file extension I use for datafiles; it stands for "facet-edge," referring to the internal structure of surfaces in the Evolver). Below are instructions for standard packages: The complete manual in PDF format is separately available as manual230.pdf.

Unix version

Instructions for installing the Surface Evolver on unix-style systems.

Quick start:
1. Download evolver-2.30.tar.gz.
2. Unpack the Evolver archive.
3. In the src subdirectory, edit Makefile to uncomment the lines for your system.
4. Run "make".
5. Test by running "./evolver ../fe/cube.fe"

Detailed instructions:

1. The Evolver is distributed in a compressed tar archive evolver-2.30.tar.gz, available from http://www.susqu.edu/brakke/evolver. Get this file into a working directory. The packed archive is about 2MB, unpacks to about 5MB. You will probably need another 3 or 4 MB to compile.

2. Uncompress the archive with

      gunzip evolver-2.30.tar.gz 
Extract the files with
      tar xvf evolver.tar  
This will unpack into three subdirectories: src (source code), doc (the html version of the manual), and fe (sample datafiles). The working directory will also contain a PDF version of the manual, and a man page, evolver.1.

3. Install the man page: copy evolver.1 to some appropriate place on your manpath, such as /usr/local/share/man/man1. You may have to become root to have permission to do this.

4. Set the EVOLVERPATH environment variable: Evolver needs to find the initial datafile and sometimes other files (e.g. command files for the "read" command, the help documentation files). If the named file is not in the current directory, then an environment variable called EVOLVERPATH will be consulted for a directory search list. The datafile directory and the directory with the HTML documentation files should definitely be included. The format is the same as the usual PATH environment variable. Set it up as usual in your system, in .profile or .login or .cshrc or wherever:

Unix C shell:

     setenv EVOLVERPATH /usr/you/evolver/fe:/usr/you/evolver/doc 

Bourne shell:

     EVOLVERPATH=/usr/you/evolver/fe:/usr/you/evolver/doc
     export EVOLVERPATH 
See
here for setting EVOLVERPATH in Windows.

5. Change to the src subdirectory of your Evolver directory.

6. Modify Makefile for your system. Makefile begins with sets of macro definitions for various systems. If your system is listed, remove the comment symbols '#' from start of the appropriate lines. If your system is not there, use the GENERIC lines, or set up your own. If you do define your own, be sure to put a corresponding section in include.h.

7. In Makefile, edit the CFLAGS line to have the proper options (optimization, floating point option, etc.).

8. In Makefile, GRAPH should be the name of a screen graphics interface file. Use glutgraph.o if possible; most systems have OpenGL/GLUT graphics now. GLUT graphics uses a separate thread to display graphics, so if you use GLUT, you must put -DPTHREADS in CFLAGS and put -lpthread in GRAPHLIB. If not using GLUT, for primitive X windows graphics you can use xgraph.o. For no built-in screen graphics at all you can use nulgraph.o. GRAPHLIB should be the appropriate graphics library plus any other libraries needed.

9. If you want to use parallel processes on a multiprocessor machine, put -DPTHREADS in CFLAGS and put -lpthread in GRAPHLIB. Currently, the only calculations done in parallel are basic energies and named quantities. The number of processes actually done in parallel can be controlled with the -p n command line option.

10. If you want Evolver to be able to use geomview, include -DOOGL in CFLAGS.

11. If you want Evolver to operate in a higher space dimension than the the default maximum of 4, include -DMAXCOORD=n in CFLAGS, where n is the maximum space dimension. This sets the upper limit of dimensionality, and is used for allocating space in data structures.

12. If your system supports the long double data type, you can compute and print values in higher precision by compiling with -DLONGDOUBLE in CFLAGS. But this slows computations, and should be used only by precision fanatics.

13. If you wish to use the commands based on the METIS partitioning software (metis, kmetis, body_metis, and metis_factor), then you should download the METIS package from, http://www-users.cs.umn.edu/~karypis/metis/ and "make" the library libmetis.a (on some systems, make complains it cannot find ranlib, but the resulting libmetis.a still works). In Evolver's Makefile, add -DMETIS to CFLAGS, and add -lmetis to GRAPHLIB. You will probably also have to add -L metispath to GRAPHLIB to tell the linker where to find libmetis.a. Note that METIS is incorporated in the Windows executable. If you are using hessian commands on very large surfaces, then metis_factor can be much faster than the other sparse matrix factoring schemes in Evolver, and I highly recommend it.

14. From the shell command prompt in the src directory, run "make". This will produce the Evolver executable file named "evolver" in the src directory. If there are errors, hopefully you will only have to change the system-specific parts of Makefile and include.h to get things to work. If significant changes to other files are needed, let me know at brakke@susqu.edu.

15. Copy the evolver executable to someplace on your PATH, such as $HOME/bin or /usr/local/bin, or make a link someplace on your PATH to the evolver executable.

16. Test by opening a new shell and running "evolver cube". Now you should be able to follow the tutorials in the HTML manual or the printed manual.

Installing geomview

If your system does not have OpenGL/GLUT, I suggest you get the geomview package from The Geometry Center. There are pre-compiled binaries for many unix systems here. Follow geomview's installation directions, and make sure that geomview is accessible through your PATH. NOTE: The X windows versions of geomview seem to require some event to occur in the geomview display window before it will redraw after loading a new datafile. So if geomview seems hung, just run the mouse over the window to give it an event to wake it up.

MS-Windows 95/98, Windows NT/2000/XP/2003

The file evolver230-Win32.zip has the executable file evolver.exe along with the documentation and sample datafile subdirectories. Create a directory (such as C:\evolver), and unzip the distribution package there. You can leave evolver.exe there and add c:\evolver to your PATH, or you can copy evolver.exe to someplace in your PATH, such as C:\windows\system32.

You should also create an environment variable EVOLVERPATH telling Evolver where to search for various files. Do this by opening Control Panel/System/Advanced/Environment Variables, clicking New under System Variables, entering EVOLVERPATH for the Variable name, and c:\evolver\fe;c:\evolver\doc for the Variable value. You may add further paths of your own to this list if you wish.

To make Evolver start automatically when you click on a *.fe file, you can associate Evolver with the file extension .fe by opening My Computer/Tools/Folder Options/File Types/New, entering the File Extension fe, clicking OK, clicking Change, and browsing for the evolver.exe program. (This sequence of actions may vary on different Windows versions.)

The Windows version uses OpenGL/GLUT graphics. OpenGL is standard in Windows, and all the necessary GLUT components are included in the executable, so you don't have to install anything.


Macintosh OSX version

I am not a Mac person, and the Mac OSX version is just a port of the unix version, so there are no Mac bells and whistles.

Quick start:
1. Download Evolver230-OSX.tar.gz.
2. Open the Evolver230-OSX folder and double-click on the evolver file.
3. Evolver prompts for a file; tell it fe/cube
4. You should now be able to follow the tutorial in the manual.

Detailed instructions:

1. Download Evolver230-OSX.tar.gz.

2. The download probably created a folder Evolver230-OSX on your desktop. This folder really has a unix path something like /Users/yourname/Evolver230-OSX. These notes will mostly refer to the terminal command line, so you should be able to open a terminal and use a command line prompt.

3. The Evolver230-OSX folder has the executable file evolver, the samples folder fe, the documentation folder doc, a PDF version of the manual, and a man page evolver.1.

4. Move the executable to some place on your PATH, e.g.

   cp evolver /Users/yourname/bin 
or add the Evolver folder to your PATH.

5. You should also create an environment variable EVOLVERPATH containing paths to the fe and doc folders by placing the following line in your shell initialization file, with appropriate modifications. You can tell which shell you are running by running the ps command at the command prompt.

C Shell (csh, tcsh): put the following in /Users/yourname/.cshrc or /Users/yourname/.tcshrc:

 setenv EVOLVERPATH /Users/yourname/Evolver230-OSX/fe:/Users/yourname/Evolver230-OSX/doc 
Bourne shell (sh,bash): put the following in /Users/yourname/.profile or /Users/yourname/.bashrc:
     EVOLVERPATH=/usr/you/evolver/fe:/usr/you/evolver/doc
     export EVOLVERPATH 

6. Install the man page by copying evolver.1 to some place on your manpath, e.g.

     cp evolver.1 /usr/share/man/man1 
You may have to become root to have permission to do this.

7. Test by opening a new shell and running "evolver cube". Now you should be able to follow the tutorials in the HTML manual or printed manual.

8. If you wish to compile your own version of the Evolver, and have a C compiler, then follow the unix instructions.


Macintosh OS 9 version

There is a Mac PowerPC OS9 version available as Evolver220-OS9.sit.hqx. It includes a README file with Mac specific information and datafiles in Mac format.

The archive file contains:

  • README.MAC - Mac-specific information.
  • EvolverPPC - the application executable file.
  • fe - folder of sample datafiles and scripts in Mac format (CR as end-of-line).
  • doc - folder of HTML documentation and help files.
  • manual220.pdf - manual in Adobe PDF format.

Installation: All you need to do is unpack in a directory somewhere. The datafiles can go in a different directory, but then you will have to supply path names when using them.

Mac OS9 version idiosyncracies:

This is a bare-bones port, so there are few Mac bells and whistles like menus. There are two windows: a text window and a graphics window. The text window provides a command-line interface to Evolver. Commands are typed in and text output appears here. All text input will go to the text window when either the text or graphics window is in front, so you can keep the graphics window in front while typing commands. Dragging the mouse in the graphics window will cause the object to rotate in 3D around an axis perpendicular to the mouse drag and with an angle proportional to the length of the drag. The redraw occurs after the mouse is released, not during the drag. Redrawing occurs in the background, so there may be some delay until the picture appears.

At start-up, you will be asked to enter the name of a datafile in the text window. If the file is not in the same directory, you will have to give the path also. For a Mac, the path character is a colon. Leading colons on the path go to parent folders.

Interrupting a repeating command: Hit command-period. Will complete current command and return to command prompt.

Memory partition: The default partition size is 8 Meg. Evolver will run in 2 Meg, but your surfaces won't be able to be very complex. I suggest making the partition as large as you can. You can change the partition size with Finder GetInfo.


Back to top of Surface Evolver documentation. Index. evolver-2.30c.dfsg/doc/news_17.htm0000644000175300017530000001153411410765113017174 0ustar hazelscthazelsct Surface Evolver Documentation - Newsletter 17

Surface Evolver Newsletter no. 17

Back to top of Surface Evolver documentation.

                         Surface Evolver Newsletter 17
                              August 20, 2003

                     by Ken Brakke, brakke@susqu.edu


  Surface Evolver version 2.20 is now available at
  http://www.susqu.edu/facstaff/b/brakke/evolver/evolver.htm.
  This is the first full official new version release since
  version 2.14 in 1999.

  Since there have been various modifications and tweaks of internal
  algorithms, it would be wise to test the new version on your existing
  datafiles before trusting it fully.  Please let me know of any
  significant anomalies.


New features and changes:

  There are Macintosh versions for Mac OS 9 and Mac OSX.
  This will be the last Mac OS 9 version unless I hear demand from users. 

  Variables and element extra attributes can be multi-dimensional
  arrays.  The "print" command can print whole arrays or slices of arrays.

  Functions with arguments and return values and procedures with arguments
  have been added.

  A variable name can be declared local in a command block to restrict
  its scope to the block.  Prevents global namespace pollution. 
  
  Variables and procedures can be made "permanent" and survive loading
  of the next datafile.

  There is a simple man page for unix systems, evolver.1, which just
  describes the command line options.
 
  All versions can use OpenGL/GLUT graphics.  Can have a pull-down
  menu of graphics window commands, and multiple graphics windows with
  different camera angles.

  The "exec" command will interpret and execute a text string of Evolver
  commands.  Good for generating commands within a script.

  There is a FOR loop construct much like in C, but not quite the same.
  Example: for ( {inx:=1;rsum:=0;} ; inx < 10 ; inx += 1 ) rsum += inx^2;

  Warning messages generated early in loading a datafile can scroll off
  the screen, so if there were messages, a meesage is printed after
  loading is complete, and the user can reprint the messages with
  "print warning_messages".

  Wild cards are permitted in filenames both on the command line and in
  response to various Evolver prompts.  But be very cautious you do not
  get unexpected matches.  Useful when files have horribly long
  descriptive names and you get tired of typing them in.

  Constraints and boundaries can be designated by name as well as number,
  i.e. "constraint topwall" instead of "constraint 1". 

  "errprintf" command is like "printf" except it sends output to stderr; good
  for console output in scripts where standard output is being redirected
  to a file.
  
  "sparse_constraints" toggles a mode of projecting to constraints that
  takes advantage of sparsity in constraints.  Makes it practicable to
  do 100,000 bodies and more in reasonable time.  Default on.

  "augmented_hessian" toggles a mode of solving Hessian equations
  that takes advantage of sparsity in constraints.  You can now do
  hessian commands with 100,000 bodies and more in reasonable time.
  Default on.

  For fine control of facet colors, the simple color numbers are overridden if 
  the extra attribute frgb is defined: "define facet attribute frgb real[3]".
  Color component values should be in the range 0 to 1.

  The "pop" command can pop selected sets of vertices or edges.  Gives more
  control than the universal "o" and "O" popping commands.

  Special popping commands pop_edge_to_tri, pop_tri_to_edge, pop_quad_to_quad
  do topological changes common in foam evolution.

  Command sequences can be executed at the start of expressions by
  putting the commands in backquotes, followed by a comma.  Useful in
  constraint and boundary formulas and integrands.

  The width of individual edges in PostScript output can be controlled
  by defining the edge extra attribute ps_linewidth.

  The "equiangulate" command applies the equiangulation test and edge
  swap to selected edges, as opposed to the universal "u" command, or
  the non-testing "edgeswap" command.

  If you define it, a h_zero vertex attribute takes precedence over
  the global variable h_zero in various squared mean curvature calculations.

  Deleting edges and facets favors keeping triple edges (and higher
  valence edges) where there is a choice.

  The default length, area, and volume definitions can be substituted for
  by other named methods.  See length_method_name, area_method_name, 
  and volume_method_name in the documentation.


Back to top of Surface Evolver documentation. evolver-2.30c.dfsg/doc/news_02.htm0000644000175300017530000002475111410765113017173 0ustar hazelscthazelsct Surface Evolver Documentation - Newsletter 2

Surface Evolver Newsletter no. 2

Back to top of Surface Evolver documentation. Index.


		      Surface Evolver Newsletter Number 2
			      February 26, 1993

                    Editor: Ken Brakke, brakke@geom.umn.edu

Contents:
  Announcements
  Version 1.89 features
  A student project
  Bibliography

Announcements:

  The number of subscribers to this newsletter has passed 100.
  I really appreciate all the interest.

  Evolver version 1.89 is now available for ftp.

  There is now a Macintosh version of the Evolver.  The binary
  is available for anonymous ftp from geom.umn.edu as
  pub/evolver.sit.hqx.  This is a binhexed Stuffit 1.5.1 archive.
  It includes a README file with Mac specific information and datafiles 
  in Mac format.  Thanks to Jeff Weeks for providing a sample program 
  for setting up text and graphics windows and handling the event loop.  

Version 1.89 features:

  New arithmetic functions available are floor(), ceil(), and modulus.
  The modulus operator is '%' as in C. It does a real modulus operation:
    x % y = x - floor(x/y)*y, 
  but it works fine on integer values.  Useful for getting multicolored
  surfaces with `set facet color id % 15'.

  A couple of items to more fully capture the current state in a dump file:
  The internal viewing matrix (used for Xwindows graphics and Postscript
  output) is now included in a dump file and can be read from a datafile.
  User-defined commands are included in the dump file in a `read'
  section at the end.

  A `rebody' command has been added that recalculates connected bodies.
  Useful when a neck has pinched out.  If original body volumes were
  fixed, new bodies have their volumes set to their current volumes.

  Some internal Evolver variables can now be used in command expressions:
	vertex_count, edge_count, facet_count, body_count,
	total_energy, total_area, total_length, scale.

  Knot energies have been added.  One way to get a knot to look nice
  is to endow it with `electric charge' and let it repel itself into
  shape.  The potential is some inverse power of distance and is 
  integrated between all pairs of points.  Two varieties are
  implemented, corresponding to conducting or insulating wire.
  
 `dissolve' command added to erase elements and leave gaps in
  surface, unlike delete command, which closes gaps.  Can only
  dissolve elements not neede by higher dimensional elements.

  Command repeat numbers have been restricted to just three
  types of commands:
      1.  single letter commands that don't have optional arguments
	  (l,t,j,m,n,w have optional arguments)
      2.  command list in braces
      3.  user-defined procedure names
  This is to prevent disasters like
	 list vertex 1293
  which before would produce 1293 lists of all vertices.   And
  'l 2' now subdivides edges longer than 2 instead of doing 2 l commands.

  "dump" without argument will dump to default file name,
   which is datafile name with .dmp extension.

  SIGTERM is caught and causes dump to default dump file and exit.
  This is so scripts running in the background can be stopped with
  kill -TERM.  SIGHUP acts the same way, so losing a modem connection
  saves the surface.

  Fixed bug that was causing crashes on Decstations.  Bug has been
  present for a long time, but just showed up because Dec handles
  reallocation of memory a little differently from everybody else.

A student project.
From: "John Oprea" <OPREA@csvaxe.csuohio.edu>

I had two grad students do a comparison, using evolver, of minimal versus
harmonic surfaces spanning a given curve? This all arose because one
of our professors was telling students that a least area surface was
z=f(x,y) with f harmonic. He had done two things: (1) read Courant-
Hilbert where they use the standard perturbation argument to show that
a least area surface is given by a harmonic function WHEN THE CURVE
ISN'T TOO NONPLANAR (ie I think 4 th order partials are supposed to be
negligible) and (2) read that a parameterization of a minimal surface 
has harmonic coord functions WITHOUT PAYING ATTENTION TO THE ISOTHERMAL
COORD CONDITION. Anyways, these students took harmonic functions 
arising as the real or imaginary parts of analytic functions, lifted a
circle to the resulting surface as a curve on the surface and used this
as a bounding curve. They could, by hand, calculate the area of the 
harmonic surface bounded by the curve. They then used Evolver to evolve
the surface into a minimal surface (with fixed curve of course) and saw
how the area decreased, measuring the percentage change. Of course, just
as Courant - Hilbert says, for circles of small radius the harmonic
surface is close to minimal. Actually, if the radius was too small, it
seemed that the comparison didn't go so well --- but we think there was
truncation error or we needed to increase the number of fixed vertices or 
something along those lines. An outside possibility is that we somehow
jumped to a new critical point of the area functional, but I don't think
so. Also, they looked at examples like Enneper's surface where the 
least area surface for a bounding curve on Enneper's surface (1 < r < sqrt(3))
is NOT Enneper's surface itself --- ie Enneper is minimal but not
least area for some curves on it. Anyway, Evolver provided very 
beautiful illustrations of these principles. 'Nuff said. Bye.
                                                         John


Bibliography:

Frank Morgan and Jean E. Taylor, Destabilization of the
Tetrahedral Point Junction by Positive Triple Junction Line energy,
Scripta Metall. Mater. {\bf 25} (1991) 1907-1910.

Livia Racz (racz@navier.mit.edu) sends the following:

L.M. Racz, J. Szekely, "Solder Volume Estimation" in Handbook of Fine
Pitch Surface Mount Technology, J.H. Lau, ed., Van Nostrand Reinhold
Publishing Co., NY (in press).

 ==>This is a book chapter we were asked to write including an algorithm
 we developed to estimate an "optimal" volume for a particular lead wire
 and substrate geometry in surface mounted integrated circuits.  We used
 the Evolver to calculate the shapes of solder fillets with the different
 volumes we proposed.  


L.M. Racz, J. Szekely, K.A. Brakke, "A General Statement of the Problem
and Description of a Proposed Method of Calculation for Some Meniscus
Problems in Materials Processing", ISIJ International, Vol. 33, No. 2,
(February 1993).

   Revised reference from Newsletter 1.

L.M. Racz, J. Szekely, "Determination of Equilibrium Shapes and Optimal
Volume of Solder Droplets in the Assembly of Surface Mounted Integrated
Circuits", ISIJ International, Vol. 33, No. 2, (February, 1993).

 ==>This is the paper based on which we were asked to write the chapter.
 Basically the same contents, but more condensed and less literature
 review.

 N.L. Abbott, G.M. Whitesides, L.M. Racz, J. Szekely, "Calculating the
 Shapes of Geometrically Confined Drops of Liquid on Patterned,
 Self-Assembled Monolayers; A New Method to Estimate Small Contact
 Angles" submitted to Journal of American Chemical Society.

  ==>Abbott and Whitesides have developed a way to pattern a substrate by
  deposition and etching with which droplets of liquid (water in this
  study) can be confined to desired patterns with great control, and with
  this, a new, easier way to measure contact angles.  They were able to
  verify their method with a contact angle goniometer for angles > 15
  degrees, but for angles < 15 degrees, they compared their results to 
  calculations we did with the Evolver.  

  L.M. Racz, J. Szekely, "Development of Design Guidelines for Specifying
  Solder Volumes in Surface Mount Technology", Proceedings of ASME Winder
  Annual Meeting, Anaheim, CA (November, 1992).

   ==>Contents pretty much the same as that of chapter.  All the authors
   were invited to give these talks about their chapters, and these were
   published in the proceedings.

   L.M. Racz, J. Szekely, "An Alternative Method for Determining
   Wettability of Components with Dissimilar Surfaces", submitted to
   Journal of Electronic Packaging.

    ==> A very nice Evolver application.  Circuit boards, because of the way
    they are made, have two opposite sides that are wet by the solder alloy, and
    two opposite sides that are not, resulting in a rather odd looking
    meniscus.  These boards are dipped into the alloy to test for the
    wettability of the two sides that are supposed to wet.  The force of
    wetting per unit length is calculated, and that number is used as an
    "index" to determine whether that particular board is OK to use.  This
    method is fine if the two opposing non-wetting sides are proportionally
    not too large in wetted perimeter, unduly influencing the force of wetting
    determination.  Currently, with this method, they are throwing out lots
    of perfectly good boards.  We say that this method is only useful for
    certain geometries/areas.  The menisci shown as part of these arguments
    are calculated with the Evolver.  


From John Sullivan, sullivan@geom.umn.edu:

   @Article{willmore,
        Author = "Lucas Hsu and Rob Kusner and John M. Sullivan",
        Title = "Minimizing the Squared Mean Curvature Integral for
        Surfaces in Space Forms",
        Journal = "Experimental Mathematics",
        Year = 1992, Volume = 1, Number = 3, Pages = "191--208"}
   We report on numerical experiments with the evolver, minimizing the
   Willmore elastic energy for closed surfaces of different genus.

   @Article{besic,
        Author = "John M. Sullivan",
        Title = "An Explicit Bound for the Besicovitch Covering Theorem",
        Journal = "J. Geometric Analysis",
        Year = 1993, Volume = "???", Pages = "???", Note = "Submitted"}
   We show that the constant needed in the proof of the Besicovitch Theorem
   is equal to the number of spheres that can be packed into one of 5 times
   the radius; experiments with the evolver have found lower bounds for this
   packing number (which are probably optimal) in dimensions three and four.

End of newsletter 2.


Back to top of Surface Evolver documentation. Index. evolver-2.30c.dfsg/doc/catbare.gif0000644000175300017530000024742511410765113017301 0ustar hazelscthazelsctGIF89a,q      /////0/0//000//0/000/000?????@?@??@@@??@?@@@?@@@OOOOOPOPOOPPPOOPOPPPOPPP_____`_`__```__`_```_```ooooopopoopppoopopppoppp!!5 Image generated by AFPL Ghostscript (device=ppmraw) ,,qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\Amqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqm!=mqqqqqK^qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmcqqqqqqqqmbqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq+Vqqqqqqqqqqmmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq_*qqqqqqq26q7=qqqq<\qqR"(qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqgF- qqqqq1 3qqq2qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmG' bqqqqqqq=6qqqq $NqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmN21RV,qqqqqqqHGqqqW1;9^qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmN4 2Rnqqqq5Wqqqqqqqqqqmmq^ )%EeqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqZ6 ,FeqqqqqqqqmZqqqqqqqqm^qqqN qqa?% (ImqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqX: *Heqqqqqqqqqqqqqm!#C^qqqqqqqqqqqqqqqqqqqqqqqqq8qqqqqqqqqqqqqqqqqqqqqqqqqeM%%Fiqqqqq[VqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^E%#D`qqqqqqqqqqqqqqqqqqqqqqqqqqqqqYqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^A 'PmqXZqqqqOPqqWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqb\W\ZW[VX[VZmqqqqqqqqqqqeF$:Xqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq VqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqZ/!VqqqV eegqXqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq:qqqqqqqqqqVVqqqqqqqhI* ;ZqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqeqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpQ- XqqqIKqqVqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq8qqqqqqqEqqVWqqqeK+0\qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqeFVqqqqqR0mqZqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6qqqqqqCqq[PN1.Omqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVVqqqqqH0eqNmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq:qqqqq^!qqV0PmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVVqqq^iqq/;q;4Yqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq:qqqqmeqqX ,FiqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqNqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqXVqqq10qq+Cq;>dqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq;qqqq/KqqqVNqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq;%qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqYVqqqe 0+mqZgF$FeqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqX;qqq_;89fXZqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq:9qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqZWqqqqpXVqqqVqqqqc> )Nmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^>%qqqmZZVVn]Xqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq*:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVBZZ\VVV]XXVKqqqqqqqqX3;XqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqoVVVXmqqqqqqqqqqqqqqqqqqqqqqq_D!!>5qqqqqqqqqVWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqHqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqnVVWVXVWWXYW_qqqqqqqqqqqmJ, !@^qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqi+$69;6%-eqqqqqqqqqqqqqqqqq`>$=^qqq;qqqqqqqZqqVWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq[qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqiA$)GiqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqFiqqqqqqeJqqqqmEVqqqqqeH( 7[qqqqqqq8qqqqqqqqqqVYqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^7 2PmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqV(qqqqqqqqqq'Rqqq)qeM( :WqqqqqqqqqqqRXWZXVW[VVVReqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqN06Wqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpmqqqm=697qqmmq- .NmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmL& !@`qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq7SqqqqW =66qqqX%3OmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqe?%)IiqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqJ*qqqqqqq=mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq`8 3Tmqqqqqqqqqqqqqqqqqqe0#Hqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq<%^qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqDqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqR0;Vqqqqqqqqqqqqqq6Vqqqqm9_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf5qqqqq;DqVmqqq3eqqe-Kqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6-qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmF, C^qqqqqqqqq,Hqqqqqqqqm^qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"qqqqqqqqqVqqq!qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq99qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqiE(Ifqqqq:@qqqqqqqqqqmmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq)eqq _qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq=qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^: .Ne%qqqqq/ 3qqqV;qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq+eqqqqIV8qqqi,qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqR4OqqqqMqq ^qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqV)qqqqqX;?qqqq)Zqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq XqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqeH- qqqqqXVqq]qqq7Vqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq0IqqqqqqqqqqJ&qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqbqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq%qqqqqqq7 qqqqJRqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqHqqqqqqqqLqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq%qqqqqqq_Q VqqqTHqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq;%OqqqqN8qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqLVqq7VqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3bqqqq@-qqHqqq!oqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq XHqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqRqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqi)qqqqm /qqqe#qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqg0$qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq7qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq,Vqqqqqqqqqqq#iqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq8=qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmbqqqqqqqqq'FqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqYqq^qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq+;qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqm6qqqqqqbJqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqZqq?qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqLqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqH)6=9fqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqRqq7$qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq<,qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq=%qqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq[qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq<>qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq8m\eqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6qqqqqqqHqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVYqqqqNqqWqZqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqWqqqqqqq1-qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmVqqqqqqq^eq[qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq XqqqqqqqR qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmqZqqqqqm; SqqWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqiqqqqM=8287<776Nqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq=qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqXqWqqqqm;mqqqXqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq9qqqqqqqqqqq\qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq8:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqWqXqqqq)>qqqqqXqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^qqqqq;qqqq^qqXqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq+6qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqCqXqqqqZqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq7+qYqqqqqqqqqqqWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqPqqqqq=qqqq;1V`qqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq:7qVqqqqqqqqqqqXqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq9#qqqqq8qqqq1 =*qqXqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6qe<=<=<6 6:Wqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6;qqqqq6qqqqbmqq^q\qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqZqqqqqqqq*1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq+;qqqqq:qqqq^qqqWq[qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqWqqqqqqqqGqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqHqqqqq:qqqqCqpnqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqZqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^qqqqqqqqemqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqXqqqqq;qqqqm NqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqZqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq HqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqWqqqqq;qqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqHqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^qqqqqqqqqq5(qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmqqqqq7ZW\W\[ZZVVVRqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq9'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqP qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmqqqqqqfXZWWXY <]\iqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq9=qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqWqqqqqqqqqqmeqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqZqqqqqqqqqqqqq+1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq$7qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq=qqqqqqqqqqqFqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqWqqqqqqqqqqqqqFqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqRqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq=:qqqqqqqqqqq0-qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqDqqqqqqqqqqqqqkmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqZqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq-qqqqqqqqqqqqqqqqqqqqqqqqqqqqqq=qqqqqqqqqqqqR-qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqeqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqXqqqqqqqqqqqq:$qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqjeqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqY qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmqqqqqqqqqqqqXqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq VqqqqqqqqqqqqqeqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmqqqqqqqqqqqqqqZqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^qqqqqqqqqqqqqqmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq9qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\qqqqqqqqqqqqqq!7qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq@9:@gqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq<4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqZqqqqqqqqqqqqqq>qqqqqqqqqqqqqqqqqqqqqqqqqqqq_qqqqqqqqqqqqqqqQ/ZVVS+$Vqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq27qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqFqqqqqqqqqqqqqq^qqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqq1+mqqqqqqo%Hqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq?qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq;&qqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqPqqqqqqqqqqqqqqqepqqqqqqqqqqqqqqqqqqqqqqqqqq+8qqqqqqqqqqqqqqqqq32qq?eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqZDmqqqqqqqqqqqqqqqqqqqqqJqqqqqqqqqqqg5qqqqqG==mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq_qqqqqqqqqqqqqn6mqqqqqF^qqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqV:qqqqqQ>qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqn_qqqqqqqqm`qqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqe4qqqqqRqqWqqqWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqLqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqeqqqqqqqqqqqqq(Vqqqqqqqqqqmmqqqqqqqqqqqqqqqqqqmqqqqqqqqqqqqqqqqq,Fqq Vqqq%?bqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq(6:<<)!<8869:*qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqV qqqqqqqqqqqqW,qqqqq4 5qqqZ@XVXVDNqqqqqqqqqqqqqqqi?$)Feqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^qqqqqqq<#qqqqqqqqqqqqqqqqqqq]qqqqqqqqqqqqqqqqqqqqqqqYqqqqqqqqqqqqqqqqqqq#:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqV:1UnqqVqqqqqD+pqqVXqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqYqqqB;_qqqqqqqqqqq6=qqqqqqqqqqqqqqqqqqq Yqqqqqqqqqqqqqqqqqqqqqqq<qqqqqqqqqqqqqqqqqqqFqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmT08IqqqqC#mEqqY YqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqeFXqqqqqVqqqXqqqqqqqqqqqqq%9qqqqqqqqqqqqqqqqqqqeqqqqqqqqqqqqqqqqqqqqqqq9:qqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqeG* qqqq `qqVqq\gqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqeG' XqqqqqXqqqWqqqqqqqqqqqqqOqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq59qqqqqqqqqqqqqqqqqqqXqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq`A%%qqqq8 mqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmN2 VqqqqqVqqqWqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqq^qqqqqqqqqqqqqqqqqqqqqqqqEqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqZqqqq6qDqq8qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmR/3NmqVqqqqq^/qqqWqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqZqqqqqqqqqqqqqqqqqqqqqqqqWqqqqqqqqqqqqqqqqqqeqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqXqqqqqqqqq^qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmO4 ,JmqqqqqVqqqqqqqqqq]qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqNqqqqqqqqqqqqqqqqqqqqqqqqWqqqqqqqqqqqqqqqqqqWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqVq`qq@eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqY8 -LeqqqqqqqqqAWZVXV]WWXVGqqqqqqqqqqqqeqqqqqqqqqqqqqqqqqqq=qqqqqqqqqqqqqqqqqqqqqqqqmqqqqqqqqqqqqqqqqqqXqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqq^: Kqq *FmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqV<-HeqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqV qqqqqqqqqqqqqqqqqqq==qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq@qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqZqqqqqeYeqqq6qV61Rqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq_D E`qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqXqqqqqqqqqqqqqqqqqqq)8qqqqqqqqqqqqqqqqqqqqqqqWqqqqqqqqqqqqqqqqqqq70qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqq6qqqqmT5 ;^qqqqqqqqqqqqqqqqqqqqqqmVVVZmqqqqqqqqqqqqqqqqqqqqqqqdD%%@aqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq>qqqqqqqqqqqqqqqqqqqFqqqqqqqqqqqqqqqqqqqqqqqWqqqqqqqqqqqqqqqqqqq18qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqW6=9<8<6=666LqqqqqqqqkG*Eiqqqqqqqqqqqqqqqqj+!8:;qqqqqqqqqqqqqqqqqqqHqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqeqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq;)qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq*;qqqqqqqqqqqqqqqqqqq`qqqqqqqqqqqqqqqqqqqqqqq;5qqqqqqqqqqqqqqqqqqqWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq%eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq:;qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqJqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq58qqqqqqqqqqqqqqqqqqqYqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmIqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpZV1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq8qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq7;qqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqq;%qqqqqqqqqqqqqqqqqqqNqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqL7qqiqq8,qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq=qqqqqqqmq-Dq CqqqqqqqqqqqqqqqqqqqqqqqqqqqqXqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqGqqqqqqqqqqqqqqqqqqqqqqqqZqqqqqqqqqqqqqqqqqqX qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^E%$bqqqqqqH1mW :Wqqqqqqqqqqqqqqqqqqqqqqqqqmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^qqqqqqqqqqqqqqqqqqq6%qqqqqqqqqqqqqqqqqqqqqqqqmqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^@#7^qqeqqqqqqqq^Vmq$meB^qqqqqqqqqqqqqqqqqqqqmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqq68qqqqqqqqqqqqqqqqqqqqqqqmqqqqqqqqqqqqqqqqqqq=qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqiI* 8WqqqqqqqH/qqqqqqqqqq)Gqqm)kE%,HeqqqqqqqqqqqqqqqqXqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqWqqqqqqqqqqqqqqqqqqq%6qqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqq<=qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqeI) ;Xqqqqqqqqqqqq9+mqqqqqqm)Fqqqq^Iqqqqb; /SmqqqqqqqqqqqqXqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq7qqqqqqqqqqqqqqqqqqqPqqqqqqqqqqqqqqqqqqqqqqqXqqqqqqqqqqqqqqqqqqq.=qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmP, /PmqqqqqqqqqqqqqqqqV4VVVN)$VqqqqqqqqqqqqqqqqP/qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq=8qqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq[: -Keqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq[8qqqqNP4-qqZqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqeqqqqqqqqqqqqqqqqqqqqqqqq7qqqqqqqqqqqqqqqqqqqoqqqqqqqqqqqq<8=897:7<:=<8qqqqqqqW6 +JeqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmN4qqqqJqqeq]qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqWqqqqqqqqqqqqqqqqqqZ qqqqqqqqqqqqqqqqqqqqqqqqZqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq=qqqqqqqqqqqqq"7;Vqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^VqqqqF^qmmq6^qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqWqqqqqqqqqqqqqqqqqqq/6qqqqqqqqqqqqqqqqqqqqqq!qqqqqqqqqqqqqqqqq6#qqqqqqqqqqqqq7qqqVqqVqqV9qqe 11q/fqqqqq8qqqqqqqqqqqqq8qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqAqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^B# )Fmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq)OqqqqqqqqqqqqqqqqWqqqqqqqqqqqqqqqqqqb^qqqqqqqqG)qqqqBAqqVYqa 6qq63qmq:qqqqqq(WVVZVXWZXVXXX)qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqZqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqX74Nqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqm,qqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqmmqqqqqqqqqqH=qqO*A$Wqqq7eqqqqqqqqqqqqqqqqqqqqqqqqqqqT]qqqqqqqqqqqqq);7<6)%79<<88(qqqqqqqqqqqq5 qqq0 qqNqqqqhqqqqqqqq:qqqXqqq/>76q6qqqqqqqqqqqqqWqqqqZqq^-qqqqqqq^LXVZD6RVV\VZWVAqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq66qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqV5NqqqqNN5+qqqb-qqqqqqqq7qqqXqqV!q9?qqqqqqqqqqqqqqqeqqqqqqqqqqqqqqqqqqqqqqqqqqqqGqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq2:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqO:=9Hqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq4&qqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq@qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq mqqqqqqqqqqqqqq`qqqqqqqqqqqqqqqqqqqqqqqqqqqqqNqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqm^qqqqqqqqqqqqeqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqe mqqqqqqqqqqqqPqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq@qqqqqqqqqqqqZqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqlqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq7Zqqqqqqqqqqqq6!qqqqqqqqqqqqqqqqqqqqqqqqqqqqqq;%qqqqqqqqqqqq[qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq!Zqqqqqqqqqqqqq;7qqqqqqqqqqqqqqqqqqqqqqqqqqqqqq[qqqqqqqqqqqq?qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^DJqqqqqqqqqqqqqq'9qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^qqqqqqqqqqq=.qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqenqqqqqqqqqqqqqMqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq#:qqqqqqqqqqq19qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqFqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq NqqqqqqqqqqqqqWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqBqqqqqqqqqqq6qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq:%qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq'4qqqqqqqqqqqqq\qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^qqqqqqqqqqqWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq<6qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqJqqqqqqqqqqqqqmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqXqqqqqqqqqq Wqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq,<7=88:<:68;==;qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqR\VWWVWVVVZVWqqqqqmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq!7qqqqqqqqqqfqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqWqqqmeqqqq\VqqZqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqZqqqqqfVfqqqVqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq?qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqYqqqA6qqm++qXqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqR 8 NqqZqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqemqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVq^ 9qqE)qqXXqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqNqQqqZqqqqq?qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq PqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqWqqq=9qqeeqm `VqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqXqqqq'2q3#qqVqqqqq8,qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq,.qqqqqqqqRqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqXqqq6;qqqq%$qXqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq[qqqqZXqqVqqqqq;6qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqeWV5VVZ\ZVYV-$qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq]qqq8:qqqqqmIVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqXqqqq VqV qqZqqqqq8qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqq<@qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqW7qqq\qqn@qqqq:^qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqZqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqe!qqqqq6 _qqqeqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmqqqVqq@ VXXiT#@eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqSqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq-gqqqqLmR qqqV3qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq*eqq_/qq4JPD" )Fmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq=qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq9.qmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqjqqqqqqqqqqqG:qqqq[=4Oqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq9;qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqn!qqqqqe5'qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqG-mqqqqqqqqNqqqqqqqqpN1 :_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq*;qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqNDmqqV9qqqqqPO=qqqq"qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqNVqqqqqf--qqqqqqqqqqqqqgH!$FeqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqJqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq09=%qqqqqPqqYqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqm8-XqqqqqqqqqqqqqqqqqqcD +NmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq< qqqqq#Jqq ^qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqY21ZqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqXqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq_E$^qqqq^ 4)qqqZ=qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmP) Dbqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^D" -miqqqqqmZbqqqq!nqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqeG#$Gfqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqe89:;<8;8=:9HqqqqqqqqqqqeF+ 8Xq%qqqJ4qqqqqqqqqq'Fqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq_@ )RmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqWqqqqqqqqqqq8qqqqqqqeH+ 8VqqqqqN+mqqqq8+mqqqqqqo#KqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqY4.ZqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqXqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqWqqqq[7:;8Hq6qqqmH+ 5NmqqqqqqqqqqqqqqqqX5NXXN&#VqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmP* @bqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqFqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqYqqqqW77;3q5S05Omqqqqqqqqqqqqqqqqqqqqqq`9= +NmqqqqXqqqq^qqqq^\oqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq;6qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq[qqqqqqq)Vqq -JeqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqV11WqYqqqm qqq,1 `Xqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq7qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqZqqq:qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmP+ Bqq2qqemqHWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqZqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^8qqqqqq+Rqqq6qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqeA!;qqqqqqeJqqLWqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqVqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq^B#qqqqqmqqqq7qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqaIqqqqqqqqqVVYqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq&7P^qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqb>##B@qqqqqIqqqq!"B`qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq_> qqqqqqqq^qqqN7Vqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3qqqqqqqqiqqqq7qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqV7qqqqqqq mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqV6qqqqqqF*qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq#Fqqqqq`4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq-^qqqqqeqqqqq5Vqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqf"qqqqqqqqqqq^qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqK+qqqqqqqqq_mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqF%YqqqqqqJmqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqe,==8+Kqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\VVeqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq;evolver-2.30c.dfsg/doc/square-e3.gif0000644000175300017530000005271511410765113017501 0ustar hazelscthazelsctGIF89ad     $$#'(((''('((('+++//0/0/0/000/444778787788877878887<<yPtT!O82'SL!SL*QB)N:u, RJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJSX bkN}O82e NNL2EPPfe*T<,ެYs)RyxA)UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*CL.`!P59 :Ƀ?4p2eʔ)S&3#H$SL2e PBTQD"C!6*h)"EERh)UTRJ*UTRJ*UURJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTR !$:<`KPX9uԧ]h1eʔSL QL2eʔ8HB *ԓHJ2T(L"p2e)SAȃ2uԩTRJ*UTRJ*UTRJ*UTRJ$@ (P<IC]$"!!C tԩSN*Ny STR2EE8Tt谒T*SVx թS"@#Jęq 'P8qĉS"]tӥ8dåRB *T L ğOfpSPh EL(1D80)SPJ*UTRJ*UTRJ*UTRJ*UY`PN:uԩSNA9ԩSyrtԩSN:!TPB*CUtG:J*UT@*,x` NB"E8qĉJ,ȁrҥK.]X4*TPB*T 8}4'E382gMPdȄ`bC>:U )UTRJ*UTRJ*UTRJ*UTReStԩSN]baԩSN: J5N:u SRB5U!*TPB%}HJN>}qʡPB b!`!AFTRJ*UTRJ*UTRJ*UTRJUHNBu蔩SL0tRE:uԩSNu9 '/Fhtʔ)D>: >B:$ԨQf@ĉ'8uTIK.i"r%L0]zBQJ9MRK,]*:}'O.:q'O *T3BA$PJ*UTRJ*UTRJ*UTRJ*U8u )C,Jɍ?N:eԩSNaԩSNebSNʑ']2e)S<|: JɁǔSL:ʩSN:uԩS Fr>2e)SL}R B *T(F*A'N8q2'$:.qtI%.et E.UAQI\TɒK*X N>}iFO@}PTSfDA%TRJ*UTRJ*UTRJ*UTRJ*U.@ԩS*u9u)5'֜:u CN:u"Ck ĄChA蔩SL2剆!=$**P%N8qĉHNJȹtIӥL钡d.]tR'&MĢҥJ*]'+0ӧOT|ӧ5}'PJ E>}493 bS?TRJ*UTRJ*UTRJ*UTRJU4>:uԩ.N:uԩS,ǔ)SLe(S 9eꔩSL2  s(TPB ).8qĉ'NJ)AҥL.a K~XtҥK*a2J&tҥJX4 DK.aq'$81(N0$ ԧ/tt%B#PJ*UTRJ*UTRJ*UTRJ*U4JSN]rԩS@D L2eʔ)SL"SÔSN:eJQ4`4P+OL<1I%(qĉ'N8qREK2]RH,. RJ*]tŤJ.UQK.Ub1iҤ!.UT)O Cfp4#'P>'P!4R5p *UTRJ*UTR *UTRJ*UTR5@SL uTSL 1e'A2e,N]ZSc@>}iSJ Д(Q'N8qĉaH!C1d@E*UtҥKdR%K*2R@5,YTiHK.]t(q'NUp O<,qҥD0DhJ*UTRJ*UTRJ*UTRJ*UT9pԩS 蒣 NL2e*)SLSd2CIN>ӧ?3B eTQqf$N8qĉ?,tRK.jӆ7TRK.U%K*MhHKrtɒ%%y.]xRK*5PӧN:ʣ'N:|ZS')'9TRJ*UTRJ*UTRJ*UTRJUCN:uAL2eʔ)S].2eʔ)SHͧOB)ԧP*d*PȔr)%2]8qĉ'NHDCѥJ*UTR%2X8$g 9T K&YTiJ,*]jdRJ,iSҥK.U2d D>}N8qaN*ͨ4#I%FRJ*UTRJ*UTRJ*UTRJ*Udr 5㐨P"=!'ӥL"ĉ'N8!'ҟ *]TRJr 'RH"E X4RJ&M ri K*U4)OK.MT)OKC"aé'PXpĉfp鏏*t*UTRJ*UTRJ*UTRJ*UTJ ԩ5+֘2*HjL2eSL2FHB)ҧN>} IQa(%M2]ʡ'N8!bD$I,*TRK. ЌHEQHYC@*UdJX\TҤI\tҥJ# ]bq)C:q$L8IʠC'NPD%NTRJ*UTRJ*UTRJ*UTRJ h1eʔ)S4L)Sh2e(B*T(C@x(ӧ5OLřdK.qIN8q$!K" rҥJ*U#y")(RH"ɹEFyx$IAy4iR%K&MbqҥKĹtȦKtԉI9qĉ ]8qĉGNt *J*UTRJ*UTRJ*UTRJ*UT1 ǔ)SNȔNC2EG)Shd *O>(C2˥K.]tӥK%8qO5$E$ J*]T)E)RH=$!:4(R r!O%K*]4ҐK*]ʓҥK-.atINĉ'NȃJpUTRJ*UTRJ*UTRJ*UTRJ N2eRSL'CL!iԨP0 )TO.!)EK.]ĉӥK.É$AYɒ5V.]T!q"=cMH"E((tFH"5gƅ *UTR%KXTtRH.] Krg 'N8q $}t5%JHJ*UTRJ*UTRJ*UTRJ*U20e"L 9eT HB} DPB 5#TPB'".q9E:bH"I$)HVTD&!EERJH"E24"EE!i"ERIQC3IRHTXhȐ8.etҥ8qtҥKx|)ԧO\p3ӧO<4UTRJ*UTRJ*UTRJ*UTRSLuB'S]f0 *TPf)jT(1y> u 'Nt%N0""I$EIR$K,tH")(" ECE")RdAQ$EtD(HvCH"EɊ5 t\tK.t%N8KOB} 5._B}KNU,RJ*UTRJ*UTRJ*UTRJ*UtX2uʔ)5*ԧPp ěLB*%8q%N8eicf")H"Yd%r=(HT()R)"ER!H"E24#R$ "E)RHsҥK.]L.]._<*D8IN:q:I*UTRJ*UTTJ*UTRJ*UT:Uҩ?kРҧOB OP"8*Pxĉ'N:8et%LH`Y#I+HI!XT)H")R"RHHf(RHQ$EP(RHѣ ")HQ8 )H$Ed%'.]t%".]ҔI NJpӧNԉJLp4 TRJ*UTRJ*UTRJ*UTRJՎJ * > *T$$B *P3B!YÉ'N8K2fHZRd?@IQ$E")(*̀("E)2$H(H<(i͚H"E)$A.!aҥK.u%?:8q⤃._Bt 'N*?: 8J*UTRJ*UTRJ*UTRJ*U2*TP`(ԧP>0e*TRA 'N8qK2q'ǥBA"II$IЅH"ERIQ$E,"aG"E)!E)R%M$EQ!I$5N"E)RHV1TI'/])NN"ĉ'8q"ӧO.8uO<]8qҥUTRJ*UTRJ*UTRJ*UTRD1B  B jFPF U*S 8qĉ'NH"q)S&.]a)%K$I$) $+f葢H ń!EER <;)RI5]EɄ"E E )RH"%i͚J.aa%EC.q 'NĉS0BĉS'N]ĉ$ TRJ*UTRJ*UTRJ*UTRJ*x:*TP3F 'TOT jԨPcJ8a'N8qZ#'KtR I,I)?4NDt T) )H"E0I"EI#(d"ERIH"I IyTtRE.]bqIN8qDg 'N8J N8q'N*H*UTRJ*UTRJ*UTRJ*UT)OB4* 1B!TPD⏋?8qL2QRK,$I$ɒ$IX (R$Eu @2(R("E=G"EERIQEIQHfD$)RD9,I4ҥK.US"ϐ!]8qĉ'ID8qĉ 1ypĉ'N&pρ TRJ*UTRJ*UTRJ*UTRJՒ  ͚QF RBх',‰'NrdʔK.]ɒH,E$HER HqxP$E)HQ=)RH"E")B%H))RHf$IJ.UT"pĉ'N8 'N8S'NtÌ.8J*UTRJ*UTRJ*UTRJ*UBPf %1 ɄN8ĉ8q┃.b.]tR@$Y$)RD@IQ$EEs.3L1H"Gf(R(H)IEERIH$EBHTTҥK.) P'N8qI'N8q?:XTj!*UTRJ*UTRJ*UTRJ*UTRUBL(N 1jT(S S8] N8!*!"TҥKڬR$IkDD$EERI.]ER(& G"E)RIH]DRIQ$Et)RD,EĢҥK.UT5,pĉ'N98qĉ'NԨP)T< TRJ*UTRJ*UTRJ*UTRJ9B!HJ %J+$qĉ'9k8qĉ$HbaRK.]jD%I,8)5")HQ$?,xH"E").EHQ$Ez(bIQ$E)DeMH~H$I .UT%2Pt$$N8qS&N8)aŃQF G%B:I*UTRJ*UTRJ*UTRJ*UT*'T(N4X%*)Qq"ĉ'N8I"‰'N8)AEǓJ.]tҥ ,EJHT(H(GER")du‡H)H")Iњ5"y@$)R$KĩtҥKXdD?2pIN8q$T$+bȄUT Đ)TRJ*UTRJ*UTRJ*UTRJU34cFPqR N8qĉ N8qĉ .UtRK*I$"E H")RtH)RH"EyPI"E~X$O"Ej("E&H"ILK.]ᇓ8qC 'Np PB%&ȨRFІPRJ*UTRJ*UTRJ*UTRJ*UtG 3ĉ'bXpĉ'N:8qĉ'N0BQK.Yt)$IX)RH, )(R$~EҡHQ$E)TE $E)RH8HHIQH"ER(H$$YdɒDC.UT)N8ĉ'NkfX$'bB U HP`f2P  E#*UTRJ*UTRJ*UTRJ*UTRN8) 'P8!Qԅ'N8q'N8qb!d.]tR%,Ib)RH"IQ$EhDRɃ"E)H E)RI"E TDRHQ$P$IR$I$IbQҥJ%8]t$N8qℨJ UjTPN &HP PC TRJ*UTRJ*UTRJ*UTRJ?fTĩR$8q ϚObXpĉ?8qĩKL:.]zBѥJ.$ʚH"I$i$E"EGQ$EA")H"Ek(("EA2KH A3D9 IR$K$Yd)ǥJȹ.r8qĉ'ND8TjTRBS(CA8*TODJ*UTRJ*TRJ*UTRJ*UTj@EG'NfP©'N|$f'Nĉ<98]qҥC,\t D$I)RH,"ER萇H"E GH")IQ$E)R(΅C3 )(H K$I$IRH,.]ʣK.ɉ‰'N8qCPB *TOB}2SKB %F UTRJ*UTRJ*UTRJ*UTRʌ.]8qbHN:q)ԧ1.8q'NX\iK.]"SP8b)H"EHQHHD(R3)R(OE1RHQ(&"(H")Z $ XcI%K,I rI.q N8qĉ1Hf1*ԨPf *'CȬ *TPbRJ*UTRJ*UTRJ*UTRJ*UT"+LTĉO>}'8Y%Lr\)ӥH,ACRH"EʚH"PIQHd(RHQ")RH"E&D"EERIQ$C,H x$Id)H'r.qt Ktpĉ'N:JKB $TPB}4cFPy *UTRJ*UTRJ*UTRJ*UTRJ8T 'N )T/]8¡K.aҥK.]DD?"E)R$Ik(L$EECQ$EPHH ])ERIQ$Ex"$K"QKk&N8ar'N jFPL *}҅:*ԗ2dtL\\tҥK.!(GD$E)RHP((H:uj UTRJ*UTRJ*UTRJ*UTRj t'8ӧO>e`q&M.]ҥK.]D$J(RH<((H")D"E:4RЌ)EEEbIB*#)H"E$)R.ER K8qB ғPB Ӊ83>E2aƔ)Sh⏩SRJ*UTRJ*UTRJ*UTRJ*U̘eFN>}*HǧPBuZSb#.et S&0]t钟T AE H")("bI">()#C3"ER $$9*UH"I$R5%1ӥKrX8'^ )TPf *TP1)SLRɔ PLD*UTRJ*UTRJ*UTRJ*UT O|R *y~DNf|aqSK81ҥK@(ԦMJ*UZ!H$' 1)HQ Ex8)RH)HPtR%$E$)$IjDgȥL.]i8Б'ԧP>#TPB ?L2eʔ.LSTRJ*UTRJ*UTRJ*UTRJ%*!0ϧO mrIH8q K4]D&ΥK. isR,&UTҤ6,.D #F)c!)H"=:!RE#RJRH")a'2]tӥK8]―PB *T(+F *!h<ԩSL0e)N,xȣJ*UTRJ*UTRJ*UTRJ*UT!‚ 38qĉJ 帔%9Y.]FK*4RJ&Mt.")R.ft1D H"E(2EB㒥K.Uq(%I$a'NH\K.] J:f0 *TP *T(C+8b 9N25)L2$UTRJ*UTRJ*UTRJ*UTRN5T8u0'N0tĉ'N鏎K.e%.]t K*]4ɒk*aK$E r! 9"E(RHLDtJ.Ud)PK"IJD%N8))ӥL8]P qL| <<jƨP 1eԚ?LaQɔ?kL2eʔ)98RJ*UTRJ*UTRJ*UTRJ*Td N8q'N8qO0DtKtҥKA*Ud%K@YbR%DVX0i+E)ңHE*UtҥJEi ?8qĩDK8]tSJ"2Q ԧP.Y'ԧP@B"3Ĕ)S81e]La)SD*UTRJ*UTRJ*UTRJ*UTAdR N:t*$qĉ':qCKF.RҥK*TiҤJ, t)K*UKzDb)H")C#$5*UTRK$ (8q .d.qҥ..p25*O>)ԧ/|ISL2e)SLS* %TRJ*UTRJ*UTRJ*UTRJUEN8ͨTB'N8SOdDbqK.UTi$K,YFJ,*UTI*aQŌ*rEIQ$*HVTRK.U$$.qĉ'NJ\tR!35+> ӧO>3#3L2e)(L2e! !ԩSXQJ*UTRP*UTRJ*UTRJ*UTQp IfTI?,8qi'N8qTi!m.UTҥJOXti$*]qRJ&Md ĥK*y‚ŏ."5RK.UT+xJĉ'N8qqhӥC, e*TP|ӧOBu )<uʔ)SL)Zc)SLy2˩SN:*UTRJ*UTRJ*UTRJ*UTR%@?%t(.y@'(:q JXTtRK.ici$KmXxRI&MT$+X.UtRKr @!VTRJB 2'N8qΐKqZp˥J.]t)OK,Y`$KCMTRJXTtҥJ.UM"ɉCH!9XPN8qĉ]J$J(QL2eBѧO>dde.N)SLI)S2eԓ'Nsj CND*UTRJ*UTRJ*UTRJ*UTĄ8qӌO>͐IN@ueƧ]r(TҥKXTII&]"$K*]ҥJ*]tҐC\ҥK.]dDK.U’%E,*MtR%,MdIJ*]tR<#<qRK.])N 8qĉ'N8݉P+HL 1䉟=d0Ƞ)SL2u'L2eʔ)SLD O:u)S4B:uTXQJ*UTRJ*UTRJ*UTRJ*UH"XӧO^HrщS'k:}4SN<\tRKXd%KL4iR!.UTҥJ tIǡK.eR$Hĉ'N8qPB E*P`F?N2u)Sjp2eʔ)SL2e*2A(rԩSNԩSN}*UTRJ*UTRJ*UTRJ*UTR'C@K>}ÑJ0S'$>}KXXRK&]bItҥKs!.]tҥ<3p*a'N8qń$B2TP`Ô)S ČS>d!?dP kN:eԩ!N:uԩ!RJ*UTRJ*UTRJ*UTRJ*U@+%J\'OPQS'N:YӧOJĩ4D E*Utҥ.UTCѥK*1A䒦KXDt)&L N8qό81*TPFšFSL2uʔ)SxP2ԩSN:ʩSN$ԩS\:e)C, u J*UTRJ*UTRJ*UTRJ*UT1 *Ԙ%fd <_ A@N>}ԩO>} I15\tҥJ*TҚ!*]M.]ҥL.)'E:8qĉ%"A8$T(SLa)EAL2uʔ)SL RCBE]X QtԩSN:u*?L:e)EA\:˩Srt9u*STRJ*UTRJ*UTRJ*UTRJT>8`EL<*T) 38ԩO3>}SO"\TҥJ,U4DѤJ,](K.]t%M.](NJX 'NrL*ҌPBdƟS 1eʔ)S*ad)"]N:uR!C:u)C,>2uԩSN2U)K,NA)TrQJ*UTRJ*UTRJ*UTRJ*UHx`"O %V|0#TPFԧO:qӧO:}6*]4R%2k&RR~.mtӥ.d4idN8qZӅ'NbXJT(BSL)b)S )N,:uԩSNZFSP:eꔩSϟ?kЩSPBe*UTRJ*UTRJ*UTRJ*UTRG%944PAB *C&*}KB}CAB)aEѥK*RB%EC\t ӥK"tR8qĩR N8MT)Q )ȐKN2hJgjYc *T uԩSN:P(4(P@!C kFBuԩTRJ*UTRJ*UTRJ*UTRJNuGBf*TPAJPOkxӧO31*T=3jT+n8t%L.]DϥH2p'N<qSO)QBc%RL:uʔ4rH*TP)E@A%(y q C$T.,ք:uԩSN:uQEN*D$SNiJ*UTRJ*UTRJ*UTRJ*UTeqʔ]І Ba*8qRSNt̞QF#J<%M8]tPKeL('PIPFu(' ,<@ *TXxBE!9P9t(թTR:SN:ehHN:u)(PN:uԩSȃJTPTRJ*UTRJ*UTRJ*UTRTONz@&By*}BfTP *TPĔ8pХK.]tӥK9‰'N8qTHJ <@"<J*UTQ@N'ҩTRB ʐPN:uSXԩH,:uԩSN:ujM(,RJ*URJ*UTRJ*UTRJ*Ub9u)Fj<@&2.15'> QL25#TPF* +A~&ϥK.P'N8qĉA dX:u.:X(H"*:tXGDRJ*UJbtbP 2d!C 298 (PXx<@*UTRJ*UTRJ*UTRJ*UTt)SNZ≄CȐ e"ԨQFɣӗB *PB *!M2e'?]1A"H x%?k`a (,&$ Surface Evolver Documentation: Debugging

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Surface Evolver Debugging

There are several debugging features built into Evolver. Script debugging is meant for ordinary users; the others are meant mostly for Evolver developers (me).

Script Debugging

A 'script' here refers to any Evolver commands, whether they be in procedures or functions.

Debugging may be done the old-fashioned way, by putting print statments in code. Another alternative is to put subcommand in a script to let the user explore the state of the surface at a given point in a script. If you can't or don't want to modify the script, then you can set breakpoints.

Breakpoints

The user may set a breakpoint in an already loaded script with the "set breakpoint" command. The syntax is
  BREAKPOINT scriptname linenumber
where scriptname is the name of the function or procedure and linenumber is the line number in the file where the breakpoint is to be set. There must be executable code on the line, or you will get an error. linenumber may be an expression.

Breakpoints may be unset individually with

  UNSET BREAKPOINT scriptname linenumber
or as a group with
  UNSET BREAKPOINTS 

When a breakpoint is reached, Evolver will enter into a subcommand prompt, at which the user may enter any Evolver commands (although some commands, such as load would be very unwise). To exit from the subcommand prompt, use q or exit or quit.

Single-stepping: At the debug prompt, there is a special single-step command n. which will continue execution to the next line (to be precise, until the line number changes).

Stack trace: At the debug prompt, the whereami command will print a stack trace, showing the sequence of function or procedure calls made to reach the current spot.


Syntax Debugging

The error messages produced by Evolver's command parser try to be helpful, but sometimes are rather cryptic. Frequent errors are misspelled keywords, variables that are spelled the same as keywords, use of uninitialized variables, and missing semicolons. Check these before trying any fancy syntax debugging as described here. It is possible to print out the parsing steps, but this is not for the faint of heart, since it the printout is in very unfriendly YACC format. You will have to have an understanding of how shift-reduce parsing works to have a chance at understanding the output. The toggle debug controls the YACC trace feature. Example:

Enter command: debug
YACC debugging was OFF.
Now ON.
Enter command: print asdf
Starting parse
Entering state 0
Reading a token: Next token is token COMMAND_START_ ()
Shifting token COMMAND_START_ ()
Entering state 2
Reading a token: Next token is token PRINT_ ()
Reducing stack by rule 2 (line 158), -> @1
Stack now 0 2
Entering state 5
Next token is token PRINT_ ()
Shifting token PRINT_ ()
Entering state 180
Reducing stack by rule 454 (line 1555), PRINT_ -> print
Stack now 0 2 5
Entering state 308
Reading a token: Next token is token NEWIDENT_ ()
Shifting token NEWIDENT_ ()
Entering state 11
Reading a token: Now at end of input.
syntax error

Shifting token error ()
Entering state 316
Reducing stack by rule 391 (line 1295), NEWIDENT_ error -> rexpr
ERROR 2381: Syntax error: Unexpected new identifier 'asdf'.
  Input line so far:
print asdf


Memory Debugging

The c command gives a brief printout of memory usage, including element memory as calculated for the number of existing elements, and total allocated data memory. For example, on Windows:

Enter command:  c
Vertices: 50  Edges: 144  Facets: 96  Bodies: 1  Facetedges: 288
Element memory: 40280
Total data memory: 302122 bytes.
If the verbose toggle is on, then more detailed information (which may depend on your system) is printed:

Enter command: verbose
Verbose ON. (was off)
Enter command: c
Vertices: 50  Edges: 144  Facets: 96  Bodies: 1  Facetedges: 288
Element memory: 40280
    vertex size:  152 bytes;  number allocated: 156
      edge size:   80 bytes;  number allocated: 244
     facet size:   96 bytes;  number allocated: 196
      body size:  424 bytes;  number allocated: 154
 facetedge size:   40 bytes;  number allocated: 388
quantity size:    360 bytes
instance size:    656 bytes

blocks in use: 181    memory in use: 360740
blocks free:    16    memory free:    45080
Heap top: 03477000
Heap size: 48.71 MB
Physical memory size: 1072680960 bytes   Virtual memory top: 7FFE0000
Session:       4 blocks,      11200 bytes
Permanent:    77 blocks,     290922 bytes
Temporary:     0 blocks,          0 bytes
Total data memory: 81 blocks, 302122 bytes.

For gurus only. The memdebug command causes verbose information to be printed at every memory allocation, reallocation, or deallocation. Meant for me to use debugging Evolver itself. If Evolver was compiled with MEMSTRINGS defined, then the file and line location of each memory operation is also printed. It also causes heap checking to be done on some systems at each memory operation.


Gradient Debugging

For gurus mostly. The estimate toggle can be used to verify that gradients are being calculated correctly. When on, estimate will, for each 'g' step, print the energy change based on the gradient, that is, calculate the inner product of the velocity and energy gradient at each vertex and sum the products, and then also pring the actual energy change during the motion. For small scales, these numbers should be very close, since energy should change nearly linearly over short scales. For example,
Enter command: estimate
Estimation ON. (was off)
Enter command: m 1e-6
Scale fixed at 1e-006.
Enter command: g 2
Estimated energy change: -9.16981132075472e-006
Actual energy change   : -9.16979039722321e-006
  2. area:  5.99999083020960 energy:  5.99999083020960  scale: 1.00000e-006
Estimated energy change: -9.16972762183562e-006
Actual energy change   : -9.16970669440076e-006
  1. area:  5.99998166050291 energy:  5.99998166050291  scale: 1.00000e-006
However, if volume constraints or quantity constraints are not satisfied, then it may take a few iterations for agreement to be reached:

Enter command: body[1].target := 2
Enter command: estimate
Estimation ON. (was off)
Enter command: m 1e-6
Scale fixed at 1e-006.
Enter command: g 5
Estimated energy change: -1.20000000000000e-005
Actual energy change   :     2.74061897546020
  5. area:  8.74061897546020 energy:  8.74061897546020  scale: 1.00000e-006
Estimated energy change: -8.05951562616423e-006
Actual energy change   : -0.000107080144722715
  4. area:  8.74051189531548 energy:  8.74051189531548  scale: 1.00000e-006
Estimated energy change: -8.05945861736465e-006
Actual energy change   : -8.06006082854083e-006
  3. area:  8.74050383525465 energy:  8.74050383525465  scale: 1.00000e-006
Estimated energy change: -8.05934708554515e-006
Actual energy change   : -8.05931920133673e-006
  2. area:  8.74049577593545 energy:  8.74049577593545  scale: 1.00000e-006
Estimated energy change: -8.05923555500071e-006
Actual energy change   : -8.05920767277257e-006
  1. area:  8.74048771672778 energy:  8.74048771672778  scale: 1.00000e-006
Enter command:

For optimizing scale, when evolution is well behaved, the estimate will be around twice the actual change, since this is the ratio for a perfectly quadratic energy function. Example:

Enter command: estimate
Estimation ON. (was off)
Enter command: g 5
Estimated energy change:    -1.23332147258421
Actual energy change   :   -0.647118913175537
  5. area:  5.35288108682446 energy:  5.35288108682446  scale: 0.233721
Estimated energy change:   -0.420072251955455
Actual energy change   :   -0.203503019926857
  4. area:  5.14937806689761 energy:  5.14937806689761  scale: 0.212928
Estimated energy change:   -0.219347968060792
Actual energy change   :   -0.108607336477709
  3. area:  5.04077073041990 energy:  5.04077073041990  scale: 0.197501
Estimated energy change:   -0.130401658330236
Actual energy change   :  -0.0647701891524211
  2. area:  4.97600054126748 energy:  4.97600054126748  scale: 0.213378
Estimated energy change:  -0.0809651401128813
Actual energy change   :  -0.0401832076359208
  1. area:  4.93581733363156 energy:  4.93581733363156  scale: 0.198322
Enter command:

Beware that non-smooth evolution, for example with one-sided constraints, can invalidate the assumptions behind estimate.


Hessian Debugging

The hessian_quiet toggle can be turned off to print information on memory usage and workload during Hessian factoring (hessian, hessian_seek, ritz, eigenprobe commands). For example,

Enter command: hessian_quiet off
Enter command: eigenprobe 0
Sparse init alloc: 36897
Expanded hashtable size: 73794.
Expanded hashtable size: 147588.
Sparse entries: 61444  Final hashtable size: 147588
Hash extra probes: 256957
Variables: 12291  Original fill: 61446
Workspace: 8 bytes
Passes through main loop: 34
Total_fill:  538907
Total_flops: 7.25226e+007
Eigencounts:    3 <,  0 ==,  12286 >
There are some differences in output depending on whether the ysmp toggle is on (using the Yale Sparse Matrix Package) or off (using my home-grown minimal degree algorithm).

For gurus only. Evolver has a feature that permits numerical checking of the correctness of the named-quantity Hessian-calculating routines. The hessian_diff toggle causes Hessians to be calculated with finite differences of gradients rather than the routines in question. There are some limitations on what kinds of surfaces the numerical Hessian works on, primarily it won't work with volume or quantity constraints. But since any quantity can be tested as an energy, that does not affect its main purpose.

Some items for debugging and tweaking of my minimal degree algorithm:

mindeg_debug_level
Controls verbosity of debug messages.
mindeg_min_region_size
Smallest size region desired; smaller regions will be merged with parent node. Default 0.
mindeg_margin
How high to go above minimum degree in seeking good elimination. Default 5.

Iteration Debugging

The itdebug command toggles the printing of information during a 'g' command. It prints the scale factor for the move, the progress in convergence of the volume or fixed quantity constraints (in terms of the total error as a multiple of the tolerance), then the energy and volumes resulting from the move. An example using cube.fe with optimizing scale:

Enter command: g
Calculating volgrads.
First move, scale 0.1
Diff: 74.4218    Old_diff: 74.4218
Next diff: 5.17892
Diff: 5.17892    Old_diff: 74.4218
Next diff: 0.350987
first move:
scale1 0.1 energy1     5.31028806222661
Body             target volume        actual volume        pressure
  1                         1      0.999964901275881     2.26415094339623

0th move:
scale1 0 energy1                    6
Body             target volume        actual volume        pressure
  1                         1                      1     2.26415094339623

Doubling scale, scale 0.2
Diff: 284.517    Old_diff: 284.517
Next diff: 38.248
Diff: 38.248    Old_diff: 284.517
Next diff: 4.87311
Diff: 4.87311    Old_diff: 38.248
Next diff: 0.61629
scale2 0.2 energy2     5.11714524527486
Body             target volume        actual volume        pressure
  1                         1      0.999938371022819     2.26415094339623

Doubling scale, scale 0.4
Diff: 1032.7    Old_diff: 1032.7
Next diff: 261.432
Diff: 261.432    Old_diff: 1032.7
Next diff: 59.494
Diff: 59.494    Old_diff: 261.432
Next diff: 13.1576
Diff: 13.1576    Old_diff: 59.494
Next diff: 2.89075
Diff: 2.89075    Old_diff: 13.1576
Next diff: 0.634175
scale2 0.4 energy2     5.89816432999747
Body             target volume        actual volume        pressure
  1                         1       0.99993658248485     2.26415094339623

Final scale: 0.20612
Diff: 301.341    Old_diff: 301.341
Next diff: 41.6649
Diff: 41.6649    Old_diff: 301.341
Next diff: 5.45074
Diff: 5.45074    Old_diff: 41.6649
Next diff: 0.707497
  1. area:  5.12110729535848 energy:  5.12110729535848  scale: 0.206120
Enter command:

Back to top of Surface Evolver documentation. Index. evolver-2.30c.dfsg/doc/cube.htm0000644000175300017530000002263111410765113016627 0ustar hazelscthazelsct Surface Evolver cube example

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Example: Cube evolving into a sphere.

A sample datafile cube.fe comes with Evolver. The initial surface is a unit cube. The surface bounds one body, and the body is constrained to have volume 1. There is no gravity or any other force besides surface tension. Hence the minimal energy surface will turn out to be a sphere. This example illustrates the basic datafile format and some basic commands.

cube skeleton The initial cube skeleton, with vertices and edges numbered.

This is the datafile that specifies the initial unit cube:

// cube.fe
// Evolver data for cube of prescribed volume.

vertices  /* given by coordinates */
1  0.0 0.0 0.0 
2  1.0 0.0 0.0
3  1.0 1.0 0.0
4  0.0 1.0 0.0
5  0.0 0.0 1.0
6  1.0 0.0 1.0
7  1.0 1.0 1.0
8  0.0 1.0 1.0

edges  /* given by endpoints */
1   1 2   
2   2 3   
3   3 4  
4   4 1 
5   5 6
6   6 7  
7   7 8 
8   8 5
9   1 5   
10  2 6  
11  3 7 
12  4 8

faces  /* given by oriented edge loop */
1   1 10 -5  -9
2   2 11 -6 -10
3   3 12 -7 -11
4   4  9 -8 -12
5   5  6  7   8
6  -4 -3 -2  -1

bodies  /* one body, defined by its oriented faces */
1   1 2 3 4 5 6  volume 1

// end of cube.fe
The datafile is organized in lines, with one geometric element defined per line. Vertices must be defined first, then edges, then faces, then bodies. Each element is numbered for later reference in the datafile.

Comments are delimited by /* to begin and */ to close as in C, or from // until the end of the line as in C++. Case is not significant, and all input is made lower-case immediately. Hence error messages about your datafiles will refer to items in lower case, even when you typed them in upper case.

The datafile syntax is based on keywords. The keywords VERTICES, EDGES, FACES, and BODIES signal the start of the respective sections. Note that the faces are not necessarily triangles (which is why they are called FACES and not FACETS). Any non-triangular face will be automatically triangulated by putting a vertex at its center and putting in edges to each of the original vertices. Faces don't have to be planar. Note that a minus sign on an edge means that the edge is traversed in the opposite direction from that defined for it in the EDGES section. A face's oriented normal is defined by the usual right hand rule. The cube faces all have outward normals, so they all are positive in the body list. In defining a body, the boundary faces must have outward normals. If a face as defined has an inward normal, it must be listed with a minus sign.

That the body is constrained to have a volume of 1 is indicated by the keyword VOLUME after the body definition, with the value of the volume following. Any attributes or properties an element has are given on the same line after its definition.

Start Evolver and load the datafile with the command line

     evolver cube.fe
You should get a prompt
     Enter command:
Give the command s to show the surface. You should see a square divided into four triangles by diagonals. This is the front side of the cube; you are looking in along the positive x-axis, with the z axis vertical and the positive y axis to the right. On most systems, you can manipulate the displayed surface with the mouse: dragging the mouse over the surface with the left button down rotates the surface; you can change to "zoom" mode by hitting the z key, to "translate" by hitting t, to "spin" by hitting c, and back to "rotate" by hitting r. Hit the 'h' key with the mouse focus in the graphics window to get a summary of the possibilities. You can also give graphics commands at the graphics command prompt; this is good for precise control. The graphics command prompt is
     Graphics command:
It takes strings of letters, each letter making a viewing transformation on the surface: The most used ones are
           r   rotate right by 6 degrees 
           l   rotate left by 6 degrees
           u   rotate up by 6 degrees  
           d   rotate down by 6 degrees 
           R   reset to original position
           q   quit back to main command prompt  
Try typing rrdd to get an oblique view of the cube. Any transformations you make will remain in effect the next time you show the surface. Now do q to get back to the main prompt.

If you are using geomview for graphics, do command P option 8 to get a display, or just "P 8" for short. Geomview takes a couple of seconds to initialize. You can manipulate the geomview display as usual independently of the Evolver. Evolver will automatically update the image whenever the surface changes.

Now do some iterations. Give the command "g 5" to do 5 iterations. You should get this:

  5. area:  5.11442065156005 energy:  5.11442065156005  scale: 0.186828
  4. area:  5.11237323810972 energy:  5.11237323810972  scale: 0.21885
  3. area:  5.11249312304592 energy:  5.11249312304592  scale: 0.204012
  2. area:  5.11249312772740 energy:  5.11249312772740  scale: 0.204386
  1. area:  5.11249312772740 energy:  5.11249312772740  scale: 0
Enter command: 
Note that after each iteration a line is printed with the iterations countdown, area, energy, and current scale factor. By default, the Evolver seeks the optimal scale factor to minimize energy. At first, there are large motions, and the volume constraint may not be exactly satisfied. There may be an energy increase due to the volume constraint taking hold. At the end, the scale is 0 because the surface has converged as well as it can at this coarse a triangulation. (Different systems may not give a zero scale here due to numerics.) Volume constraints are not exactly enforced, but each iteration tries to bring the volume closer to the target. Here that results in increases in area. You can find the current volumes with the v command:
Body       target volume         actual volume        pressure
  1     1.000000000000000     0.999999779366360   3.408026016427987
The pressure in the last column is actually the Lagrange multiplier for the volume constraint. Now let's refine the triangulation with the r command. This subdivides each facet into four smaller similar facets. The printout here gives the counts of the geometric elements and the memory they take:
Vertices: 50 Edges: 144 Facets: 96  Facetedges: 288 Memory: 27554
Iterate another 10 times:
 10. area: 4.908899804670224 energy: 4.908899804670224  scale: 0.268161
  9. area: 4.909526310166165 energy: 4.909526310166165  scale: 0.204016
  8. area: 4.909119925577212 energy: 4.909119925577212  scale: 0.286541
  7. area: 4.908360229118204 energy: 4.908360229118204  scale: 0.304668
  6. area: 4.907421919968726 energy: 4.907421919968726  scale: 0.373881
  5. area: 4.906763705259419 energy: 4.906763705259419  scale: 0.261395
  4. area: 4.906032256943935 energy: 4.906032256943935  scale: 0.46086
  3. area: 4.905484754688263 energy: 4.905484754688263  scale: 0.238871
  2. area: 4.904915540917190 energy: 4.904915540917190  scale: 0.545873
  1. area: 4.904475138593070 energy: 4.904475138593070  scale: 0.227156
You can continue iterating and refining as long as you have time and memory.

Eventually, you will want to quit. So give the q command. You get

     Enter new datafile name (none to continue, q to quit): 
You can start a new surface by entering a datafile name (it can be the same one you just did, to start over), or continue with the present surface by hitting ENTER with no name (in case you pressed q by accident, or suddenly you remember something you didn't do), or you can really quit with another q.


Mound example. Back to top of tutorial.
Back to top of Evolver documentation. Index. evolver-2.30c.dfsg/doc/index.htm0000644000175300017530000025061711410765113017027 0ustar hazelscthazelsct Surface Evolver Documentation Index

Surface Evolver Documentation Index

This is an alphabetical list of all the HTML anchors in this documentation. Evolver keywords are in boldface. Do not use them for your own identifiers. If a word does not appear here, it is not a keyword.

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

Back to top of Surface Evolver documentation.
A
+ (graphics)
- (graphics)
a
A
abort
abs
ackerman
acos
acosh
actual_volume
addload
aggregate functions
alice
ambient_pressure
ambient_pressure_value
and
approx_curv
approx_curvature
approximate_curvature
area
area normalization
area_fixed
area_method_name
area_normalization
area_square
areaweed
arrays
arrayslice
arrow keys
asin
asinh
assignment
assume_oriented
atan
atan2
atanh
atomic values
attr values
attribute
attributes
augmented_hessian
autochop
autochop_length
autodisplay
autopop
autopop_quartic
autorecalc
average_crossings
avg
axial_point
B
b
B (graphics)
b (graphics)
backbody
backcolor
backcull
background
bare
bare edge
bare vertex
basic concepts
bezier_basis
__bhead_index
big_endian
binary numbers
binary_off_file
binary_printf
black
blas_flag
blue
bodies
bodies section
body
body density
body facets
body phase
body pressure
body volconst
body volume
body_count
body_dissolve_count
body_metis
boolean ops
bottominfo
boundaries
boundary
boundary decl
boundary_curvature
bounding box
break
break after warning option
break_after_warning
breakflag
breakpoint
breakpoints
brightness
brown
buck_knot_energy
bugs
bunch_kauffman
bunch_kaufman
burchard
bye
C
C
c
C (graphics)
c (graphics)
carter_energy
carter_power
case
catenoid example
ceil
central_symmetry
charge_gradient
chdir
check
check_count
check_increase
checking
circle_knot_energy
circle_willmore
circular_arc_area
circular_arc_draw
circular_arc_length
clear
clip view
clip_coeff
clip_view
clipped
clipped_cells
clock
close_show
color
colorfile
colormap
colors
column example
combinatorics
command definition
command language
command repetition
command separator
command verbs
comments
compound commands
compressibility energy
conditional
conducting_knot_energy
conf_edge
configuration space
conformal metric
conformal_metric
conj_grad
conjugate gradient
connected
connected_cells
conserved
conserved quantity
constant expressions
constexpr
constraint
constraint decl
constraint energy integrals
constraint_tolerance
constraint_tolerance decl
constraints
content
continue
control structures
convert_to_quantities
convex
convex constraint
coordinates
corona_state
cos
cosh
count
counts
cpu_counter
crossings
crystalline energy
cube example
cubocta
cubocta symmetry group
curvature_binormal
curvature_function
curvature_power
curvature_power
cyan
D
D
d
d (graphics)
darkgray
darkgrey
datafile header
datafile variables
datafilename
date_and_time
ddd_gamma_sq
debug
debug option
debugging
default symmetry
define
delete
delete_count
delete_count
delete_text
density
density_edge_length
density_facet_area
density_facet_area_u
deturck
diffusion
diffusion toggle
diffusion_coeff
dihedral
dihedral_hooke
dirichlet
dirichlet_area
dirichlet_elastic
dirichlet_mode
dirichlet_seek
display_origin
display_origin vector
display_periods
display_text
dissolve
dissolve_count
div_normal_curvature
do
doc top
doc top
doc top
documentation
dodecahedron symmetry group
dot_product
dump
dump_memlist
dynamic load library
E
e
e (graphics)
__e_constraint_list
echo option
edge
edge boundary
edge color
edge constraints
edge density
edge facets
edge length
edge noncontent
edge noncontent
edge orientation
edge tangent
edge valence
edge vertices
edge_area
edge_count
edge_delete_count
edge_diffusion
edge_dissolve_count
edge_divide
edge_edge_knot_energy
edge_general_integral
edge_k_vector_integral
edge_knot_energy
edge_knot_energy_normalizer
edge_length
edge_merge
edge_min_knot_energy
edge_pop_count
edge_refine_count
edge_scalar_integral
edge_tension
edge_torus_area
edge_vector_integral
edges
edges section
edgeswap
edgeswap_count
edgeweed
eff_area_sq_mean_curvature
effective area
effective polyhedral
effective_area
efixed
eigen_neg
eigen_pos
eigen_zero
eigenneg
eigenpair interpretation
eigenpos
eigenprobe
eigenprobe
eigenvalue accuracy
eigenvalues
eigenvalues
eigenvalues
eigenvector visualization
eigenvectors
eigenzero
elastic_basis
elastic_coeff
element attributes
element lists
element orientation
element representation
element_modulus
ellipticE
ellipticK
else
energies
energy
energy quantity
eprint
equi_count
equiangulate
equiangulation
ergb
error handling
errprintf
estimate
estimated_change
Euclidean metric
everything_quantities
evolver_version
EVOLVERPATH
EVOLVERPATH (Windows)
exec
exit
exit error option
exit warning option
exp
expr
expressions
exprint
extra attributes
extra decl
extra_boundary
extra_boundary_param
extrapolate
F
F
f
__f_constraint_list
face
faces
faces section
facet
facet area
facet bodies
facet boundary
facet color
facet constraints
facet density
facet edges
facet noncontent
facet normal
facet phase
facet tension
facet valence
facet vertices
facet_2form_integral
facet_2form_sq_integral
facet_area
facet_area_u
facet_colors
facet_count
facet_delete_count
facet_diffusion
facet_dissolve_count
facet_general_integral
facet_knot_energy
facet_knot_energy_fix
facet_merge
facet_refine_count
facet_scalar_integral
facet_tension
facet_torus_volume
facet_vector_integral
facet_volume
facetedge
facetedge_count
facets
fbrgb
fgagfa_coeff
file option
fix
fix_count
fixed edge
fixed facet
fixed quantity
fixed vertex
fixed_area
flip-rotate symmetry group
floor
flush_counts
for
__force
force_deletion
force_pos_def
foreach
form_factors
form_integrand
formula
free_discards
frgb
frickenhaus_flag
frontbody
frontcolor
full_bounding_box
full_gravity_method
function
function definition
function_quantity_sparse
functions
functions
G
G
g
G value
gap constant decl
gap energy
gap_constant
gap_energy
gauss_bdry_e
gauss_bdry_v
gauss_curvature
gauss_curvature_integral
general constraints
general_linear_elastic
generator_power
generators
genus2 symmetry group
geometric elements
geompipe
geomview
geomview command
geomview install
gfa_2_coeff
gfaafg_coeff
gfagfa_coeff
gga_coeff
global
global constraints
global_method
go
gradient debugging
gradient descent iteration
graphics
graphics actions
graphics commands
graphics related
gravity
gravity energy
gravity_constant
gravity_constant decl
gravity_method
gray
green
grey
gv_binary
H
H
h (graphics)
h_inverse_metric
h_zero
help
help (graphics)
hessian
hessian caveats
hessian command
hessian debugging
hessian intro
hessian iteration
hessian metric
hessian normal mode
hessian tutorial
hessian_diff
hessian_double_normal
hessian_epsilon
hessian_menu
hessian_normal
hessian_normal_one
hessian_normal_perp
hessian_quiet
hessian_seek
hessian_seek
hessian_slant_cutoff
hessian_special_normal
hessian_special_normal_vector
hexadecimal numbers
high_boundary
high_constraint
histogram
history
history list
hit_constraint
homothety
hooke2_energy
hooke2_power
hooke3_energy
hooke3_power
hooke_energy
hooke_length
hooke_size
I
i
id
ideal gas decl
identifiers
idiv
if
if then else
ignore_constraints
ignore_fixed
immediate_autopop
imod
implemented symmetries
include files
incompatible models
incompleteEllipticE
incompleteEllipticF
index
indexed elements
inertia
info_only
info_only quantity
initialization
input
instance modulus
instance_name modulus
instance_name value
insulating_knot_energy
integer
integral_order
integral_order_1d
integral_order_2d
integration_order
integration_order_1d
integration_order_2d
internal variables
interp_bdry decl
interp_bdry_param
interp_normals
interrupts
inverse_periods
is_defined
itdebug
iteration debugging
iteration step
iteration_counter
J
J
j
jiggle
jiggle_temperature
johndust
K
k
K
k_form_order
k_vector_order
keep_macros
keep_originals
Kelvin foam example
keylogfile
Klein hyperbolic metric
klein_area
klein_length
klein_metric
kmetis
knot_energy
knot_local_thickness
knot_power
knot_thickness
knot_thickness2
knot_thickness_0
knot_thickness_p
knot_thickness_p2
kraynikpopedge
kraynikpopvertex
kusner
L
l
l (graphics)
labelflag
labels
lagrange
lagrange command
Lagrange model
lagrange_multiplier
lagrange_order
lanczos
laplacian_mean_curvature
last_eigenvalue
last_error
last_hessian_scale
LEBweight
length
length_method_name
level set constraints
lexical format
lightblue
lightcyan
lightgray
lightgreen
lightgrey
lightmagenta
lightred
line splicing
linear
linear model
linear_elastic
linear_elastic_B
linear_metric
linear_metric_mix
list
list attributes
list bottominfo
list procedures
list topinfo
little_endian
lmc_mean_curvature
lmc_mobility
load
load_library
local
local_hooke_energy
log
logfile
loghistogram
longj
M
m
M
m (graphics)
Mac OS9 version
macOSX version
macros
magenta
matrix_determinant
matrix_inverse
matrix_inverse
matrix_multiply
matrix_multiply
max
MAXCOORD
maximum
mean_curvature
mean_curvature_integral
mean_curvature_integral_a
memdebug
memory debug option
memory debugging
memory_arena
memory_used
merit_factor
method
method instance decl
method instances
method list
method_instance
methods
metis
metis install
metis_factor
metis_readjust
metric
metric decl
metric_conversion
metric_convert
metric_edge_length
metric_facet_area
mid_edge
mid_facet
midv
min
mindeg_debug_level
mindeg_margin
mindeg_min_region_size
minimum
misc functions
miscellaneous top
mobility
mobility decl
mobility_tensor
mod
models
modulus
mound example
move
MPI commands
MPI compilation
MPI datafiles
MPI Evolver
MPI examples
MPI graphics
MPI invocation
MPI overview
mpi_debug
mpi_maxtask
mpi_task
multiprocessor option
N
N
n
named methods
named quantities
named quantities option
named quantity constraints
named quantity decl
named quantity energy
neo_hookean
neo_lambda
neo_mu
new_body
new_edge
new_facet
new_vertex
newsletter 1
newsletter 10
newsletter 11
newsletter 12
newsletter 13
newsletter 14
newsletter 15
newsletter 16
newsletter 17
newsletter 18
newsletter 19
newsletter 2
newsletter 3
newsletter 4
newsletter 5
newsletter 6
newsletter 7
newsletter 8
newsletter 9
newsletters
no autoconvert option
no renumbering option
no_display
no_refine
node_charge
nodisplay
noncontent
noncontent
nonnegative
nonpositive
nonwall
normal_curvature
normal_motion
normal_sq_mean_curvature
not
notch
notch_count
null_area
null_area
null_length
null_length
numbers
O
o
O
off
oid
old_area
old_force_ribiere
ometis
on
on_boundary
on_constraint
on_method_instance
on_quantity
one-sided constraints
one_sided_lagrange
ooglfile
opacity
OpenGL
operators
optimise
optimising_parameter
optimize
optimizing
optimizing scale
optimizing_parameter
options
or
orientation
original
overview
overview
P
P
p
parallel
parallel_exec
parameter
parameter scale
parameter values
parameter_1
parameter_1
parameter_file
parameters
parametric boundaries
pause
pdelta
periods
permanent assignment
permload
phase
phase decl
phasefile
pi
pickenum
pickfnum
picking
pickvnum
pinning
piping
pointwise constraints
poisson_ratio
polyhedral curvature
pop
pop_count
pop_disjoin
pop_edge_to_tri
pop_edge_to_tri_count
pop_enjoin
pop_quad_to_quad
pop_quad_to_quad_count
pop_to_edge
pop_to_face
pop_tri_to_edge
pop_tri_to_edge_count
pos_area_hess
post_project
postscript
postscript command
pow
precedence
pressure
pressure energy
print
print_profiling
printf
procedure
procedure definition
procedures
profiling
proj_knot_energy
ps_bareedgewidth
ps_colorflag
ps_conedgewidth
ps_crossingflag
ps_fixededgewidth
ps_gridedgewidth
ps_gridflag
ps_labelflag
ps_labelsize
ps_linewidth
ps_stringwidth
ps_tripleedgewidth
pscale
pscolorflag
Q
Q
q
q (graphics)
quadratic
quadratic decl
quadratic model
quadratic_metric_mix
quantities_only
quantity
quantity attribute
quantity Lagrange multiplier
quantity modulus
quantity pressure
quantity target
quantity tolerance
quantity top
quantity value
quantity volconst
quantity_name modulus
quantity_name pressure
quantity_name target
quantity_name value
quarter_turn
quarter_turn_period
quiet
quietgo
quietload
quietload option
quit
quotient spaces
R
r
r (graphics)
R (graphics)
random
random_seed
raw_cells
raw_velocity
raw_vertex_average
rawest_vertex_average
rawestv
rawv
read
read section
real
reasonable
rebody
recalc
red
redefinition
redirection
ref[A]
ref[AT]
ref[AV]
ref[AYC]
ref[B1]
ref[B2]
ref[B3]
ref[B4]
ref[B5]
ref[B6]
ref[BB]
ref[BM]
ref[BS]
ref[C]
ref[CS]
ref[DL]
ref[FHW]
ref[FS]
ref[FT]
ref[HK]
ref[HKS]
ref[KR1]
ref[KR2]
ref[KRS]
ref[KS1]
ref[MB]
ref[MF1]
ref[MH]
ref[MT]
ref[P]
ref[PP]
ref[PWB]
ref[RN]
ref[RSB]
ref[S]
ref[SG]
ref[SJ]
ref[T1]
ref[T2]
ref[T3]
ref[Te]
ref[TW]
ref[U]
ref[WM]
ref[WP]
refine
refine_count
relaxed_elastic
relaxed_elastic1
relaxed_elastic1_A
relaxed_elastic2
relaxed_elastic2_A
relaxed_elastic_A
renumber_all
reorder_storage
repartition
reset_counts
reset_profiling
return
reverse_orientation
rgb_colors
ribiere
Riemannian metric
ringblob
ritz
ritz
rotate
rotate symmetry group
rotation_order
runge_kutta
S
s
s (graphics)
saddle
saddle example
scalar_integrand
scale
scale factor
scale_limit
scale_scale
screen graphics
screw_angle
screw_height
screw_symmetry
script debugging
scrollbuffersize
self
self_sim_coeff
self_similar
set
SGI parallel
shading
shell
show
show
show_all_quantities
show_expr
show_inner
show_off
show_outer
show_trans
show_vol
showq
shrink
simon_knot_energy_normalizer
simplex decl
simplex model
simplex_k_vector_integral
simplex_representation
simplex_to_fe
simplex_vector_integral
sin
sin_knot_energy
single letter
single-stepping
sinh
sizeof
slice view
slice_coeff
slice_view
smooth_graph
soapfilm
soapfilm model
sobolev
sobolev_area
sobolev_mode
sobolev_seek
space dimension
space dimension decl
space_dimension
sparse_constraints
sphere_knot_energy
spherical_arc_area_n
spherical_arc_area_s
spherical_arc_length
spherical_area
spinning ring example
spring_constant
sprintf
sq_curvature_modulus
sq_gauss_curvature
sq_gaussian_curv_cyl
sq_mean_curv
sq_mean_curv_cyl
sq_mean_curvature
sqcurve
sqcurve2_string
sqcurve3_string
sqcurve_string
sqcurve_string_mark
sqcurve_string_marked
sqgauss
sqr
sqrt
square
square_curvature
square_gaussian_curvature
squared_curvature
squared_gaussian_curvature
squared_gradient
stability
stability_test
stack trace
star_eff_area_sq_mean_curvature
star_finagling
star_gauss_curvature
star_normal_sq_mean_curvature
star_perp_sq_mean_curvature
star_sq_mean_curvature
stokes2d
stokes2d_laplacian
stokes_type
stokes_velocity
stress_integral
string
string model
string_curve_tolerance
string_gravity
stringexpr
subcommand
sum
suppress_warning
surface dimension decl
surface tension
surface_cos_power
surface_dimension
surface_energy
surface_knot_power
SVK_alpha
SVK_elastic
SVK_lambda
SVK_mu
SVK_theta
swap_colors
symmetric_content
symmetries
symmetry decl
symmetry groups
symmetry_group
syntax debugging
system
T
t
t (graphics)
t1_edgeswap
t1_edgeswap_count
table of contents
table of contents
table of contents
tag
tan
tanh
target
target volume
target_tolerance
task_exec
temperature
tension
tetra_point
then
thicken
thickness
this_task
toggle commands
tolerance
topinfo
torus
torus declaration
torus display
torus model
torus periods
torus symmetry group
torus_filled
torus_filled decl
torus_periods
total
total_area
total_energy
total_length
total_time
transform_count
transform_depth
transform_expr
transforms
transparent
triple_point
true_average_crossings
true_writhe
tutorial
twist
twointor
U
U
u
u (graphics)
ulong
unfix
unfix_count
uniform_knot_energy
uniform_knot_energy_normalizer
uniform_knot_normalizer1
uniform_knot_normalizer2
unit mobility
unix version
unset
unstable surfaces
unsuppress_warning
user funcs
user-defined mobility
usr
utest
V
V
v
v (graphics)
__v_constraint_list
valence
valid_boundary
valid_constraint
valid_element
value
var expr
variable assignment
variables
vector_integrand
__velocity
verbose
version
vertex
vertex boundary
vertex constraints
vertex dihedral
vertex edges
vertex facets
vertex valence
vertex_average
vertex_count
vertex_dissolve_count
vertex_merge
vertex_pop_count
vertex_scalar_integral
vertexnormal
vertices
vertices section
__vhead_index
view generators
view matrix
view matrix
view transforms
view transforms
view_4d
view_matrix
view_transform_generators
view_transform_parity
view_transform_swap_colors
view_transforms
viewing matrix
visibility_debug
visibility_test
visibility_test
volconst
volfixed
volgrads_every
volume
volume_method_name
W
w
W
w (graphics)
warning_messages
where
where_count
whereami
whereami
while
white
whitespace
window_aspect_ratio
windows version
wrap
wrap_compose
wrap_inverse
wrap_vertex
writhe
wulff
wulff decl
wulff_energy
X
X
x
x (graphics)
xyz symmetry group
Y
y
yellow
ysmp
Z
Z
z
z (graphics)
zener_coeff
zener_drag
zoom
zoom (graphics)
zoom_radius
zoom_vertex

Back to top of Surface Evolver documentation. evolver-2.30c.dfsg/doc/evolver.10000644000175300017530000000572011410765113016743 0ustar hazelscthazelsct.\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples. .\"See Also: .\"man mdoc.samples for a complete listing of options .\"man mdoc for the short list of editing options .\"/usr/share/misc/mdoc.template .Dd Mon May 19 2003 \" DATE .Dt Evolver 1 \" Program name and manual section number .Os Darwin .Sh NAME \" Section Header - required - don't modify .Nm evolver .\" The following lines are read in generating the apropos(man -k) database. Use only key .\" words here as the database is built based on the words here and in the .ND line. .Nd The Surface Evolver, minimize energy of a surface .Sh SYNOPSIS \" Section Header - required - don't modify .Nm .Op Fl adehimqwxy \" [-adehimqwxy] .Op Fl f Ar file \" [-f file] .Op Fl pN \" [-pN] .Op Ar datafile \" Underlined argument - use .Ar anywhere to underline .Sh DESCRIPTION \" Section Header - required - don't modify The Surface Evolver is a program that minimizes the energy of a triangulated surface according to designated energies and constraints. This man page only documents command line options and environment variables. The full package and documentation are available at http://www.susqu.edu/brakke/evolver. .Pp \" Inserts a space Command line options (multiple letters may not be combined): .Bl -tag -width -indent \" Differs from above in tag removed .It Ar datafile Text file defining a surface. If omitted, you will be prompted. .It Fl a Autoconvert to named quantities when needed (default is on); use "-a-" to deactivate autoconversion. .It Fl d Begin with parser debugging on (equivalent to "debug" runtime command). Beware of copious output. .It Fl e Echo stdin to stdout; meant for testing piped input. .It Fl f Ar file After loading datafile, read commands from .Ar file , then command line prompt. .It Fl h Print help for command line options. .It Fl i Preserve datafile numbers for element id's, rather than renumbering. .It Fl m Begin with memory debugging on (equivalent to "memdebug" runtime command). Beware of copious output. .It Fl pN Run with N concurrent processes. .It Fl q Convert to named quantities at start (equivalent to "convert_to_quantities" runtime command). .It Fl w Exit immediately after any warning or error message; meant for batch runs. .It Fl x Exit immediately after any error message; meant for batch runs. .It Fl y Break to user prompt after any warning message. .El \" Ends the list .Pp .\" .Sh ENVIRONMENT \" May not be needed .\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1 .\" .It Ev ENV_VAR_1 .\" Description of ENV_VAR_1 .\" .It Ev ENV_VAR_2 .\" Description of ENV_VAR_2 .\" .El .Sh ENVIRONMENT VARIABLES .Bl -tag -width "EVOLVERPATH" -compact .It EVOLVERPATH Colon-separated list of paths automatically searched for datafiles, included files, or help documentation. evolver-2.30c.dfsg/doc/tutorial.htm0000644000175300017530000001146111410765113017553 0ustar hazelscthazelsct Surface Evolver tutorial

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Surface Evolver Tutorial

To get started using the Surface Evolver, read the Basic Concepts section for a quick introduction, then try the examples. Many more examples can be found at the Evolver Examples Web Page

Basic Concepts

The basic geometric elements used to represent a surface are vertices, edges, facets, and bodies. Vertices are points in space. Edges are straight line segments joining pairs of vertices. Facets are flat triangles bounded by three edges. A surface is a union of facets. (Actually, there is no separate surface entity in the program; all facets belong to one logical surface.) A body is defined by giving its bounding facets.

The term "surface", when used to refer to the entity upon which the Evolver operates, refers to all the geometric elements plus auxiliary data such as constraints, boundaries, and forces.

There are no limitations on how many edges may share a vertex nor on how many facets may share an edge. Thus arbitrary topologies are possible, including the triple junctions of surfaces characteristic of soap films.

Edges and facets are oriented for bookkeeping purposes, but there are no restrictions on the orientation of neighboring facets. Unoriented surfaces are thus possible.

A surface is deemed to have a total energy, arising from surface tension, gravitational energy, and possibly other sources. It is this energy which the Evolver minimizes.

No particular units of measurement are used. The program only deals with numerical values. If you wish to relate the program values to the real world, then all values should be within one consistent system, such as cgs or mks.

The initial surface is specified in a text file (hereafter referred to as the datafile) that may be created with any standard text editor. (The .fe extension I always use for datafiles stands for facet-edge, which refers to the internal data structure used to represent the surface. You may use any name you wish for a datafile.)

The basic operation of the Evolver is to read in a datafile and take commands from the user. The main command prompt is

     Enter command:
The most common commands are one letter (case is significant for these), sometimes with a numerical parameter. The most frequently used commands are:
     g n     do n iterations  ('g' is for 'go')
     r       refine the triangulation of surface 
     P       graphics output (option 8 for geomview, option 3 for PostScript)
     s       show surface on screen (Evolver's own simple graphics)
     q       quit 
There is also a more elaborate command language (in which case is not significant). Commands must be followed with the ENTER key; Evolver only reads complete lines.

An iteration is one evolution step. The motion for the step is calculated as follows: First, the force on each vertex is calculated from the gradient of the total energy of the surface as a function of the position of that vertex. The force gives the direction of motion. Second, the force is made to conform to whatever constraints are applicable. Third, the actual motion is found by multiplying the force by a global scale factor.


Cube example. Back to top of tutorial.
Back to top of Evolver documentation. Index. evolver-2.30c.dfsg/doc/news_07.htm0000644000175300017530000001317711410765113017200 0ustar hazelscthazelsct Surface Evolver Documentation - Newsletter 7

Surface Evolver Newsletter no. 7

Back to top of Surface Evolver documentation.


                      Surface Evolver Newsletter Number 7
                              January 24, 1994

                    Editor: Ken Brakke, brakke@geom.umn.edu

Contents:
Version 1.94.
Minimization via Hessian.
New features.
Bibliography update.

Version 1.94:

Version 1.94 is now available for ftp.  Still no new Mac version.
And be wary of the NeXT version, since I made some changes that
might possibly affect behavior of the NeXT interface, and I don't have
access to a NeXT at the moment to test it.

The uncompressed version of evolver.tar is being dropped from the
ftp directory.  If this causes anybody problems, let me know.


Minimization via Hessian:

  The Evolver's usual gradient descent method is a first order method,
using just the first derivative of energy.  A second order method might
give faster convergence, and furthermore tell the stability of a critical
point.  By using the Hessian matrix of second derivatives along with
the gradient, one has a quadratic approximation to the energy that one
can solve for a minimum (or other critical point).  The Evolver has had
an embryonic capability to do this for a while, but I did not publicize it
because it was very limited.  Now it has been improved to the point of
maybe being useful.

  The command to do one iteration is "hessian".  There are some restrictions 
on  its use.  The energies it applies to are area, constraint edge integrals,
simplex model facet areas, and these named quantity methods: edge_tension, 
edge_length, facet_tension, facet_area, vertex_scalar_integral, 
edge_scalar_integral, edge_vector_integral, facet_scalar_integral, 
facet_vector_integral, facet_2form_integral, and gravity_method.  Note 
particularly it does not apply to the string model or to ordinary gravity.  
One can fake strings in the soapfilm model with edge_length quantity, and use
gravity_method instead of ordinary gravity.  The constraints that are handled 
are body volumes, level set constraints, and fixed named quantities involving 
the afore mentioned methods.  Unfixed vertices on parameterized boundaries are 
not handled.  Some of these restrictions will be removed in future versions.

  The Hessian method should be tried only when the surface is extremely close 
to a minimum (or some critical point).  I advise evolving with other methods 
(like conjugate gradient) until the energy has converged to 8 places or so.
If you do try the Hessian from too far away, it is likely to explode your 
surface.  But if you do "check_increase ON", the surface will be restored to 
its previous state if the Hessian method would increase its energy.

  Running the Hessian method is liable to produce a number of warning messages 
when the matrix is not positive definite.  This is normal when there are 
constraints (such as volumes) or degenerate critical points (for example, 
due to translational degrees of freedom).  A message like 
  WARNING: sdrv: Not positive definite. Diag[25] = -3.84845; 
indicates the raw energy Hessian is indefinite.  If the constraints don't make 
the net Hessian positive semidefinite, then you will get a message like
  WARNING: Constrained Hessian not positive definite. Index 3 
where the index is the number of negative eigenvalues.  This may show up in 
the first few iterations.  Don't worry about it unless it shows up when the 
Hessian method has converged all the way.

  For degenerate critical points, nullspace directions generate messages like
    WARNING: sdrv: Diag[39] = 3.28904e-15; max in row: 3.1778e-15; adding 1
As long as the max in row is also small, all is fine. The ``adding 1'' is just 
an internal gimmick to get a nonzero pivot.

  The criterion for treating a value as zero in solving the Hessian is set 
by the variable "hessian_epsilon".  Its default value is 1e-8.

  To get a feel for the Hessian method, try it out with cube.fe.
For more details, see the Hessian section of the Technical Reference chapter
of the Manual.


New features:

  New named quantity methods: vertex_scalar_integrand, facet_2form_integral.

  Hessians for named quantity methods: edge_length, facet_area, 
  vertex_scalar_integral, edge_scalar_integral, edge_vector_integral,
  facet_scalar_integral, facet_vector_integral, facet_2form_integral,
  gravity_method.

  Added edge wrap as readable attribute.

  Added coordinate attributes for edges and facets.  Interpreted as
  edge vector components and facet normal components.

  Commands are added to history list after being successfully parsed,
  rather than after successful execution.

  Unfound files are treated as errors rather than prompting for new
  name, except for datafiles.

  New arithmetic operators: mod (synonym for %), imod, idiv.
  New arithmetic function: atan2(y,x).

  "Show" conditions for edges and facets are saved in "read" section 
  of dump file.

  Total energy is in a comment at the top of a dump file.

  PostScript output in case of string model in 3D has option for doing
  bordered crossings.

  w is synonym for coordinate x4.



Bibliography update:

X. Michalet, B. Fourcade and D. Bensimon, "Fluctuating vesicles of 
nonspherical topology," Phys. Rev. Lett. 72, 168, 1994.

End of Newsletter 7.


Back to top of Surface Evolver documentation. evolver-2.30c.dfsg/doc/news_19.htm0000644000175300017530000000370711410765113017201 0ustar hazelscthazelsct Surface Evolver Documentation - Newsletter 18

Surface Evolver Newsletter no. 19

Back to top of Surface Evolver documentation.


                       Surface Evolver Newsletter 19
                            August 20, 2005

                      by Ken Brakke, brakke@susqu.edu


  Surface Evolver version 2.26 is now available at
  http://www.susqu.edu/brakke/evolver/evolver.htm.
  This is the first full official new version release since
  version 2.24 in October, 2004.

  Announcement of interest to fans of Joseph Plateau:
  In my spare time, I have been doing an amateur translation of
  Plateau's famous 1873 book on soap films and surface tension
  phenomena.  You may find the results at 
  http://www.susqu.edu/brakke/PlateauBook/PlateauBook.html.

  New features and changes:

  PDF version of the manual has been improved, with bookmarks and links.

  Runtime help command now finds its keyword information from a new
  separate help text file, rather than extracting it from the HTML
  version of the documentation.

  Added "quietload" toggle to supress echoing of command files being
  read in; also corresponding "-Q" command line option.

  Quoted strings can be concatenated on input, for example,
     printf "This is a " "long format string.\n"

  New "reverse_orientation" command to internally reverse the orientation
  of selected edges and facets.

  Added "eigenvalues" array, so user can access all eigenvalues
  produced by "ritz", e.g. "print eigenvalues[2]".

  Added "binary_printf" command to write binary files for external
  applications needing a binary format.

  Piping of command output with | to external commands now works in
  Windows like it does in unix.


End of Newsletter 19

evolver-2.30c.dfsg/doc/elements.htm0000644000175300017530000016502211410765113017527 0ustar hazelscthazelsct Surface Evolver geometric elements

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Geometric elements

The surface is defined in terms of its geometric elements of each dimension. Each element has its own set of attributes. Some may be set by the user; others are set internally but may be queried by the user. It is also possible to dynamically define extra attributes for any type of element, which may be single values or vectors of values. Attribute values can be specified in the datafile, and queried with commands.

Elements: vertices, edges, facets, bodies, facet-edges.


Vertices

A vertex is a point in space. The coordinates of the vertices are the parameters that determine the location of the surface. It is the coordinates that are changed when the surface evolves. A vertex carries no default energy, but may have energy by being on a level set constraint in the string model, or by having a named quantity energy applied to it. The vertices of the original surface are defined in the vertices section of the datafile.

Attributes:


Edges

An edge is a one-dimensional geometric element. In the linear model, an edge is an oriented line segment between a tail vertex and a head vertex. In the quadratic model, an edge is defined by quadratic intepolation of two endpoints and a midpoint. In the lagrange model, an edge is defined by the appropriate order interpolation with the edge vertices. In the string model, edges carry a default surface tension energy proportional to their length. Edges may also carry energy by being on level set constraints in the soapfilm model, or by having named quantity energies applied to them. The edges of the original surface are defined in the edges section of the datafile.

Attributes:


Facets

In the soapfilm model, a facet is an oriented triangle defined by a cycle of three edges. In the linear model, a facet is a flat triangle. In the quadratic model, the facet is a curved surface defined by quadratic interpolation among the three facet corner vertices and the three edge midpoints. In the Lagrange model, lagrange_order interpolation is done among (lagrange_order+1)(lagrange_order+2)/2 vertices. Although individual facets are oriented, there are no restrictions on the orientations of adjacent facets. By default, a facet carries a surface tension energy equal to its area.

In the string model, a facet is a chain of an arbitrary number of edges. The chain need not be closed. Usually a facet is defined in the string model in order to define a body, so the space dimension is 2 and the facet is planar, one facet corresponding to a body. Facets carry no energy by themselves.

In the simplex model, a facet is a simplex of dimension surface_dimension defined by surface_dimension+1 vertices. The surface_dimension may be any dimension less than or equal to the space_dimension. The simplex is oriented according to the order of the vertices. By default, a simplex carries a surface tension energy proportional to its volume.

Facets may carry additional energy by having named quantity energies applied to them.

The facets of the original surface are defined in the faces section of the datafile.

Attributes:


Bodies

A body is a full-dimensional region of space. Bodies are not triangulated. Rather, they are determined by their boundary facets (or edges in 2D). These facets are used for calculating body volume and gravitational energy. Only those facets needed for correct calculation need be given. In the string model, usually a body corresponds to one facet. Bodies of the original surface are defined in the bodies section of the datafile.

Attributes:


Facetedges

A facetedge is a pairing of a facet and one of its edges, with orientation such that the edge orientation is consistent with the facet orientation. Facetedges are used internally by Evolver, and are seldom of interest to the user. They carry no energy. The C command will sometimes refer to facetedges if the surface is inconsistent. "Facetedge" can be used as an element generator. The attributes available are id, edge, facet, and extra attributes.

Element attributes

Below is a list of possible element attributes. The first few apply to all types of elements. Then come those applying specifically to vertices, edges, facets, and bodies. See Geometric elements for lists of attributes for each type element.

Attributes for all types of elements


id

Geometric element read-only attribute. The id of an element is a positive integer uniquely associated with that element. The Evolver will assign id's to elements read from the datafile in the order they are read, unless the -i command line option or keep_originals is in the top of the datafile, in which case the datafile element number is the id. In either case, you can access the datafile id with the original attribute. Examples:
   list vertex where id < 10
   set edge color red where id == 4 or id == 6 or id == 9
   foreach facet ff do { printf "%g  %g %g %g\n",ff.id,ff.edge[1].id,
      ff.edge[2].id,ff.edge[3].id } 

oid

Geometric element read-only attribute. The oid of an element is the "oriented id" of an element as used in an expression. It is the id number signed according to whether the use of the element is with the same or opposite orientation as the way it is stored. Example: to get an edge list for a facet as in the datafile, use oid instead of id:
   foreach facet ff do { printf "%g  %g %g %g\n",ff.id,ff.edge[1].oid,
      ff.edge[2].oid,ff.edge[3].oid } 

on_constraint

Vertex, edge, or facet read-only attribute. Boolean attribute for whether an element is on a given constraint. The full syntax of the attribute is "on_constraint n" where n is the number of the constraint. Examples:
   list edge where on_constraint 3
   print vertex[3].on_constraint 1

on_boundary

Vertex, edge, or facet read-only attribute. The status of whether an element is on a boundary can be queried with the Boolean attribute on_boundary. Elements can be unset from boundaries, but not set on them (since parameter values would be unknown). Examples:
  list vertex where on_boundary 1
  unset vertex boundary 2

on_quantity

Vertex, edge, or facet read-only attribute. Boolean attribute for whether an element contributes to a given named quantity. Actually, it tests whether the element is on any of the method instances comprising a quantity. The full syntax of the attribute is "on_quantity quantityname". Examples:
   list facet where on_quantity  center_of_mass_x
   print vertex[3].on_quantity blue_area

on_method_instance

Vertex, edge, or facet read-only attribute. Boolean attribute for whether an element contributes to a given named method instance. The full syntax of the attribute is "on_method_instance instancename". Examples:
   list facet where on_method_instance  center_of_mass_x_edges
   print vertex[3].on_method_instance blue_area_1

original

Geometric element read-only attribute. For elements read from the datafile, this is the number given to the element in the datafile, which may be overridden by an explicit original attribute value in the datafile line defining the element. The value is inherited by all elements of the same type that result from subdivision. For elements otherwise generated at run time, the original attribute value is -1. Example: to show which facets descended from face 1 in the datafile:
   set facet color red where original == 1

Named quantities as attributes

Geometric element read-only attribute. Named quantities and method instances can be applied to geomtric elements either in the datafile (by adding the quantity or method name to the line defining an element) or with the set command. Nonglobal quantities or methods can be unset for individual elements. The values for individual elements can be accessed using attribute syntax. Examples: Suppose there is a named quantity "xmoment" that can be evaluated for facets. Then one could give commands
   foreach facet do printf "%g %f\n",id,xmoment
   list facet where xmoment > 4
   set facet quantity xmoment where original == 1
   unset facet quantity xmoment

Extra attributes

Geometric element read-write attributes. If extra attributes have been defined in the datafile or with a define command, they can be accessed with attribute syntax. Extra attribute values in the datafile can be initialized for an element by adding the attribute name and value to the line defining the element. Extra attributes may also be arrays, initialized with standard nested bracket syntax. Example:
  define vertex attribute oldx real
  define vertex attribute vmat real[3][2]
  vertices
  1   2 0 0 oldx 3 vmat {{1,2},{3,4},{5,6}}
The command language can use the name with the same syntax as built-in attributes, and can define extra attributes at run time:
  set vertex oldx x
  define edge attribute vibel real[2]
  set edge[2] vibel[1] 3; set edge[2] vibel[2] 4
  print vertex[3].oldx
Attribute array sizes may be changed at run time by executing another definition of the attribute, but the number of dimensions must be the same. Array entry values are preserved as far as possible when sizes are changed.

The value of an extra attribute can also be calculated by user-supplied code. The attribute definition is followed by the keyword "function" and then the code in brackets. In the code, the keyword "self" is used to refer to the element the attribute is being calculated for. Example: To implement the lowest z value of a facet as an attribute:

 define facet attribute minz real function
	 {self.minz := min(self.vertex,z);}

Vertex-specific attributes


Vertex coordinates

Vertex read-write attribute. The coordinates of a vertex are its location in space. By default, these are Euclidean coordinates, but they may represent any coordinate system if the user defines appropriate length, area, volume, etc. integrals. But graphics always treat the coordinates as Euclidean. The individual coordinates may be referred to as x,y,z,w or x1,x2,x3,... In the vertices section of the datafile, vertices of the original surface have their coordinates given unless they are on a parametric boundary. Vertices on parametric boundaries have their coordinates calculated from their parameter values. Coordinates may be read or modified with the command language. Examples:
  foreach vertex do printf "%g  %f %f %f\n",id,x,y,z
  set vertex z z+.1*x

Vertex parameters

Vertex read-write attribute. Vertices on parametric boundaries are located according to the parameter values. Parameters are referred to as p1,p2,... Usually only p1 is used, since one-parameter curves used as boundary wires are most common. Such vertices in the original surface have their parameter values given in the vertices section of the datafile instead of their coordinates. Vertex parameters may be read or modified with the command language. Example:
  foreach vertex do printf "%g %f\n",id,p1
  set vertex[1] p1 1.2

Fixed vertices

Vertex read-write attribute. A fixed vertex will not move during iteration (except to satisfy level set constraints) or other operations, except if coordinates are explicitly changed by a "set vertices ..." command. A vertex may be declared fixed in the datafile by putting fixed on the line defining the vertex, after the coordinates. From the command prompt, one can fix or unfix vertices with the fix and unfix commands. Examples:
  list vertex where fixed
  fix vertex where on_constraint 1
  unfix vertices where on_boundary 1

Vertex constraints

Vertex read-write attribute. A level-set constraint is a restriction of vertices to lie on the zero level-set of a function. A constraint declared NONNEGATIVE in the datafile forces a vertex to have a nonnegative value of the function. A NONPOSITIVE constraint forces a vertex to have a nonpositive value of the function. A constraint may be declared GLOBAL, in which case it applies to all vertices. A vertex may be put on a constraint in the vertices section of the datafile by listing the constraint numbers after the keyword "constraint". See mound.fe for an example. In commands, the status of a vertex can be read with the on_constraint and hit_constraint attributes. The status can be changed with the set or unset commands. Examples:
  list vertex where on_constraint 2
  set vertex constraint 1 where id == 4 or id == 6
  unset vertex constraint 3

Hit_constraint

Vertex read-only attribute. Boolean attribute for whether a vertex exactly satisfies a given constraint. Particularly meant for vertices on one-sided constraints. The full syntax of the attribute is "hit_constraint n" where n is the number or name of the constraint. Examples:
   list vertex where hit_constraint 3
   print vertex[3].hit_constraint 1

__v_constraint_list

This read-only attribute gives access to the list of constraints a vertex is on. __v_constraint_list[1] is the number of constraints in the list, followed by the numbers of the constraints. Note that for named constraints, the internally assigned numbers are used.

Bare vertex

Vertex read-write attribute. Declaring a vertex "bare" says that a vertex does not have an adjacent edge (string model) or an adjacent facet (soapfilm model). Useful in avoiding warning messages. A vertex may be declared bare in the vertices section of the datafile by adding the keyword bare to the line defining the vertex. Example:
   list vertex where bare

Mid_edge

Vertex read-only attribute. True (1) if the vertex is on an edge but not an endpoint. Relevant in the quadratic model or Lagrange model. Example:
   list edge[23].vertex vv where vv.mid_edge

Mid_facet

Vertex read-only attribute. True (1) if the vertex is an interior control point of a facet in the Lagrange model. Example:
   list facet[23].vertex vv where vv.mid_facet

mean_curvature

Vertex read-only attribute, available in the string and soapfilm model. The mean curvature is calculated as the magnitude of the gradient of area (or length in the string model) divided by the area (or length) associated with the vertex, which is one-third the area of the facets adjacent to the vertex (or one-half of the length of adjacent edges). It is divided by 2 in the soapfilm model to account for the "mean" part of the definition. The sign of the mean curvature is relative to the orientation of the first adjacent facet (or edge) Evolver finds. This calculation can be done even if the vertex is on a triple junction or other non-planar topology, even if it doesn't interpret well as mean curvature there.

Vertex edges

Vertex read-only attribute. Generates edges attached to a vertex, oriented so vertex is the edge tail. The edges are in no particular order. Examples:
  list vertex[3].edges
  foreach vertex vv do { foreach vv.edge do print id }
Always use ".edges" to generate vertex edges; using "edges" with an implicit element, as in "foreach vertex do list edges" will list all edges in the surface over and over again.

Vertex facets

Vertex read-only attribute. Generates facets attached to a vertex, with positive facet orientation. The facets are in no particular order. Examples:
  list vertex[3].facets
  foreach vertex vv do { foreach vv.facet do print id }
Always use ".facets" to generate vertex facets; using "facets" with an implicit element, as in "foreach vertex do list facets" will list all facets in the surface over and over again.

Vertex valence

Vertex read-only attribute. The valence of a vertex is defined to be the number of edges it is a member of. Example:
  list vertices where valence == 6
  histogram(vertex,valence)

axial_point

Vertex read-write attribute. Certain symmetry groups (e.g. cubocta or rotate) have axes of rotation that are invariant under some non-identity group element. A vertex on such an axis must be labeled in the datafile with the attribute axial_point, since these vertices pose special problems for the wrap algorithms. If you are only using a subgroup of the full group, then you only need to label vertices on the axes of the subgroup. The net wrap around a facet containing an axial point need not be the identity. Edges out of an axial point must have the axial point at their tail, and must have zero wrap. Facets including an axial point must have the axial point at the tail of the first edge in the facet. It is your responsibility to use constraints to guarantee the vertex remains on the axis.

Triple_point

Vertex read-write attribute. For telling Evolver three films meet at this vertex. Used when effective_area is on to adjust motion of vertex by making the effective area around the vertex 1/sqrt(3) of actual.

Tetra_point

Vertex read-write attribute. For telling Evolver six films meet at this vertex. Used when effective_area is on to adjust motion of vertex by making the effective area around the vertex 1/sqrt(6) of actual.

vertexnormal

Vertex read-only attribute. This is an indexed attribute consisting of the components of a normal to the surface at a vertex, normalized to unit length. This is the same normal as used in hessian_normal mode. For most vertices in the soapfilm model, the normal is the number average of the unit normals of the surrounding facets. Along triple edges and such where hessian_normal has a multi-dimensional normal plane, the vertexnormal is the first basis vector of the normal plane. Example: To print the normal components of vertex 3:
 print vertex[3].vertexnormal[1];
 print vertex[3].vertexnormal[2];
 print vertex[3].vertexnormal[3]; 
The vertexnormal can also be printed as an array:
 print vertex[3].vertexnormal 

dihedral

vertex read-only attribute in the string model. This is the angle from straightness of two edges at a vertex. If there are less than two edges, the value is 0. If two or more edges, the value is 2*asin(F/2), where F is the magnitude of the net force on the vertex, assuming each edge has tension 1. Upper limit clamped to pi.

Squared mean curvature

Geometric element read-only attribute. SQCURVE is the squared mean curvature at a vertex. Valid only if squared mean curvature is part of the energy or in a quantity (but not the star versions of the squared mean curvature methods).

__force

Vertex read-only attribute. This is an indexed attribute giving the components of the force (negative energy gradient as projected to constraints). Meant for debugging use. This is not directly used for the motion; see __velocity.

__velocity

Vertex read-only attribute. This is an indexed attribute giving the components of the vector used for vertex motion in the 'g' command. The motion of a vertex is the scale factor times this vector. The velocity vector is calculated from the force vector by applying area normalization, mobilty, etc. Also, if a vertex is on a boundary, the velocity is projected back to parameters.

raw_velocity

Vertex read-only attribute Internal vertex attribute used when one-sided level-set constraints are present, so the Lagrange multipliers for said constraints can be calculated. This is the velocity before any projection to volume or level-set constraints. Not of interest to the ordinary user.

Edge-specific attributes


Length

Edge read-only attribute. Length of the edge. Examples:
 histogram(edge where on_constraint 1, length)
 print edge[3].length

Edge density or tension

Edge read-write attribute. "Density" and "tension" are synonyms. Energy per unit length of edge. Default 1 in string model, 0 in soapfilm model. The tension may be modified in the datafile edges section by adding "tension value" to the line defining the edge. The tension may be modified with the set command. Examples:
  set edge tension .5 where id < 10
  loghistogram(edge,density)

Fixed edge

Edge read-write attribute. For an edge to be "fixed" means that any vertex or edge created by refining the edge will inherit the "fixed" attribute. Declaring an edge fixed in the datafile will also fix all vertices on the edge. However, fixing an edge from the command prompt will not fix any vertices. An edge may be declared fixed in the datafile edges section by adding fixed to the line defining the edge. From the command prompt, one can fix or unfix edges with the fix and unfix commands. Examples:
  fix edge where on_constraint 1
  list edges where fixed
  set edge color red where fixed
  unfix edge[3]

Edge constraints

Edge read-write attribute. An edge may be put on a level set constraint. For such an edge, any vertices and edges generated by refining the edge will inherit the constraint. An edge may be put on constraints in the edges section of the datafile by listing the constraint numbers after the keyword constraint on the line defining the edge. Putting an edge on a constraint does not put its existing vertices on the constraint. In commands, the status of an edge can be read with the "on_constraint" attribute. The status can be changed with the set or unset commands. Examples:
  list edge where on_constraint 2
  set edge constraint 1 where id == 4 or id == 6
  unset edge constraint 3

__e_constraint_list

This read-only attribute gives access to the list of constraints an edge is on. __e_constraint_list[1] is the number of constraints in the list, followed by the numbers of the constraints. Note that for named constraints, the internally assigned numbers are used.

Edge boundary

Edge read-write attribute. If an edge is on a parametric boundary, then any edges and vertices generated from the edge will inherit the boundary. By default, new vertex parameter values are calculated by extrapolating from one end of the edge. This avoids wrap-around problems that would arise from interpolating parameter values. But if the interp_bdry_param toggle is on, then interpolation is used. The status of whether an edge is on a boundary can be queried with the Boolean attribute on_boundary. Edges can be unset from boundaries, and set on them (but care is needed to do this properly). Examples:
  list edges where on_boundary 1
  unset edges boundary 2

Edge wrap

Edge read-write attribute. When a symmetry group is in effect (such as the torus model) and an edge crosses the boundary of a fundamental domain, the edge is labelled with the group element that moves the edge head vertex to its proper position relative to the tail vertex. The label is internally encoded as an integer, the encoding peculiar to each symmetry group. Edge wrappings are set in the datafile. The torus model has its own peculiar wrap representation in the datafile: * for no wrap, + for positive wrap, and - for negative wrap. Wraps are maintained automatically by Evolver during surface manipulations. The numeric edge wrap values can be queried with attribute syntax. Example:
  list edge where wrap != 0
Unfortunately, the torus model wraps come out rather opaquely, since one cannot print hex. The torus wrap number is the sum of numbers for the individual directions: +x = 1; -x = 31; +y = 64; -y = 1984; +z = 4096; -z = 127040. Caution: even though this attribute can be written by the user at runtime, only gurus should try it.

Edge color

Edge read-write attribute. Color for graphics. The default color is black. Color may be set in the datafile, or with the set command. In geomview, the edge color will show up only for edges satisfying the show edge condition, and then they will have to compete with the edges geomview draws, unless you turn off geomview's drawing of edges with "ae" in the geomview window. Examples:
  set edge color red where length > 1
  show edge where color != black

Noncontent

Edge read-write attribute. When set, indicates this facet should not be used in volume calculations in the soapfilm model or facet area calculations in the string model. Useful, for example, if you want to have edges be part of a body boundary for display purposes, but want to use constraint integrands for greater accuracy in volume calculations. Example:
   set edge noncontent where on_constraint 1

Bare edge

Edge read-write attribute. Declaring an edge "bare" indicates that an edge does not have an adjacent facet (soapfilm model). Best declared in the datafile, by adding the keyword bare to the line defining an edge. Useful in avoiding warning messages. Bare edges are useful to show wires, frameworks, outlines, etc. in graphics. Example:
  list edge where bare

No_refine

Edge and facet read-write Boolean attribute. An edge with the "no_refine" attribute will not be refined by the r command. This is useful for avoiding needless refining of lines or planes that are used only for display. Giving a facet the no_refine attribute has no effect except that edges created within the facet by refining will inherit the no_refine attribute. So to avoid refinement of a plane, all edges and facets in the plane must be given the no_refine attribute. The no_refine attribute may be specified on the datafile line for an edge or facet, or the set command may be used. Examples:
  set edge no_refine where fixed
  unset edge[2] no_refine
  list edge where no_refine
  print edge[3].no_refine

Show

Edge and facet read-only Boolean attribute giving the current status of an edge or facet according to the show facet criterion in effect.

Edge orientation

Edge read-write attribute. Controls the sign of oriented integrals on an edge. Value +1 or -1. Useful when triangulation manipulations create an edge going the wrong way. Example:
  set edge[2] orientation -1

Edge vertices

Edge read-only attribute. Acts as a generator for the two endpoints in the linear and quadratic models, and for all vertices on an edge in the Lagrange and simplex models. Example:
   list edge[2].vertices
   list edge ee where ee.vertex[1].on_constraint 1 

Edge midv

Edge read-only attribute. In the quadratic model, gives the id of the midpoint vertex of an edge. Example:
  print edge[23].midv 

Edge facets

Edge read-only attribute. Generates facets attached to an edge, in order around the edge when meaningful, with facet orientation agreeing with edge orientation. Examples:
   list edge[2].facets
   foreach edge ee do print max(ee.facets,area)

Edge valence

Edge read-only attribute. The valence of an edge is the number of facets adjacent to it. Examples:
  list edges where valence == 1
  refine edge where valence != 2

Dihedral

Edge read-only attribute. The angle in radians between the normals of two facets on an edge. Zero if there are not exactly two facets. This attribute is not stored, but recalculated each time it is used. If there are not exactly two facets on the edge, the value is 0.

Edge tangent

Edge read-only attribute. The components of the edge vector in the linear model can be accessed as edge attributes x,y,z or x1,x2,x3,.... In a command, the vector between edge endpoints is used in quadratic model or lagrange model. But when used in an integral, the tangent is evaluated at the Gaussian integration points. Not defined in the simplex model. Example to list nearly vertical edges:
   list edges where z^2 > 10*(x^2 + y^2)

Facet-specific attributes


Facet area

Facet read-only attribute. The area of the facet. Example:
  list facet where area < .1

Fixed facet

Facet read-write attribute. For a facet to be "fixed" means that any vertex, edge, or facet created by refining a facet will inherit the fixed attribute. Fixing a facet in the datafile or at the command prompt does not fix any edges or vertices. A face may be declared fixed in the datafile by putting fixed on the line defining the face, after the coordinates. From the command prompt, one can fix or unfix facets with the fix and unfix commands.

Facet tension or density

Facet read-write attribute. Energy per unit area of facet; surface tension. Default 0 in string model, 1 in soapfilm model. May be set in the datafile by adding "tension value" to the line defining the facet. The density is inherited by any facets generated by refining. "Tension" and "density" are synonyms. Examples:
  set facet tension 3 where original == 1
  list facet where density < .4

Facet constraints

Facet read-write attribute. Putting a facet on a constraint means that every vertex, edge, or facet generated by refining the facet will inherit that constraint. Setting a facet on a constraint does not set any of its existing edges or vertices on the constraint. Facets may be put on constraints in the datafile by listing the constraint numbers after the keyword constraint on the line defining the facet, or with the set command. They may be removed with the unset command. Examples:

  list facets where on_constraint 1
  set facet[2] constraint 2
  unset facet constraint 1

__f_constraint_list

This read-only attribute gives access to the list of constraints a facet is on. __f_constraint_list[1] is the number of constraints in the list, followed by the numbers of the constraints. Note that for named constraints, the internally assigned numbers are used.

Facet boundary

Facet read-write attribute. If a facet is on a parametric boundary, then any facets, edges, and vertices generated from the facet will inherit the boundary. By default, new vertex parameter values are calculated by extrapolating from one vertex of the facet. This avoids wrap-around problems that would arise from interpolating parameter values. But if the interp_bdry_param toggle is on, then interpolation is used. The status of whether a facet is on a boundary can be queried with the Boolean attribute on_boundary. Facets can be unset from boundaries, and set on them (but care is needed to do this properly). Examples:
  list facets where on_boundary 1
  unset facets boundary 2

Facet color

Facet read-write attribute. Color of both sides of facet for graphics. Default is white. Datafile example:
  Faces
  1   1 2 3 color red
Command examples:
  list facets where color == red
  set facet[3] color green
  set facet color red where area > 2

Frontcolor

Facet read-write attribute. Color of positive side of facet for graphics. Default is white. Datafile example:
  Faces
  1   1 2 3 frontcolor green backcolor red
Command examples:
  list facets where frontcolor == red
  set facet[3] frontcolor green
  set facet frontcolor red where area > 2

Backcolor

Facet read-write attribute. Color of negative side of facet for graphics. Default is white. Set also when the "color" attribute is set. Datafile example:
  Faces
  1   1 2 3 frontcolor green backcolor red
Command examples:
  list facets where backcolor == red
  set facet[3] backcolor green
  set facet backcolor red where area > 2

Facet vertices

Facet read-only attribute. Generates vertices around a facet, oriented as the facet boundary. "vertex" and "vertices" are synonymous. In the string model, if the facet is not a closed loop of edges, the vertices will be generated in order from one end. If the given facet has negative orientation, then the vertices will be generated accordingly. Example:
  list facet[3].vertex

Facet edges

Facet read-only attribute. Generates edges around a facet, oriented as the facet boundary. "edge" and "edges" are synonymous. In the string model, if the edges of the facet do not make a closed loop, then the edges will be listed in order starting from one end. If the given facet has negative orientation, the edges will be listed accordingly. Example:
  list facet[3].edges
  list facet[-3].edges

Facet bodies

Facet read-only attribute. Generates bodies around a facet, first the body the facet is positive boundary of, then the body the facet is negative boundary of, if they exist. "body" and "bodies" are synonymous. Example:
list facet[3].bodies

Frontbody

Facet read-write attribute. The id of the body of which the facet is on the positively oriented boundary. Useful after creating a new body with the new_body command. As a read attribute, the value is 0 if there is no such body. Examples:
  newb := new_body; set facet frontbody newb where color == red
  print facet[2].frontbody
Frontbody also works for adding edges to a facet in the string model, but the added edge must be attach to one end of the edge arc, or close the arc.

Backbody

Facet read-write attribute. The id of the body of which the facet is on the negatively oriented boundary. Useful after creating a new body with the new_body command. As a read attribute, the value is 0 if there is no such body. Examples:
  newb := new_body; set facet[1] frontbody newb;
  set facet backbody newb where id == 2 or id == 4;
  print facet[4].backbody
Backbody also works for adding edges to a facet in the string model, but the added edge must be attach to one end of the edge arc, or close the arc.

Facet valence

Facet read-only attribute. The valence of a facet is the number of edges (or vertices) that it contains. Most useful in the string model. Example:
  list facets where valence != 3

Nodisplay

Facet read-write attribute. When set, suppresses the display of the facet in graphics. Can be set in the datafile by adding nodisplay to the line defining the facet. Can also be manipulated by the set and unset commands. No_display is a synonym provided since that's what I kept typing in. Example:
   set facet nodisplay where color != red

Orientation

Facet read-write attribute. Controls the sign of oriented integrals on a facet. Value +1 or -1. Useful when triangulation manipulations create a facet with an undesired orientation. Example:
   set facet[123] orientation -1

Noncontent

Noncontent

Facet read-write attribute. When set, indicates this facet should not be used in volume calculations. Useful, for example, if you want to have facets be part of a body boundary for display purposes, but want to use constraint integrands for greater accuracy in volume calculations. Example:
   set facet noncontent where on_constraint 1

Phase

Facet read-write attribute. If there is a phasefile, this attribute determines the edge tension of an edge between two facets in the string model. Example:
   list facet where phase == 1

Facet normal vector

Facet read-only attribute. The components of the facet normal vector may be referred to as x,y,z or x1,x2,x3,... in the linear model. Length is equal to facet area. In quadratic model or lagrange model, only the three facet corner vertices are used to calculate the normal. When used in integrals, the normal is calculated at each integration points. Not defined in simplex model.

Body-specific attributes

Body facets

Body read-only attribute. Generates facets bounding a body, with proper facet orientation with respect to the body. Example:
  list body[1].facets

Body density

Body read-write attribute. Density used for gravitational potential energy. It can be set in the bodies section of the datafile, or with the set command, or by assignment. Command examples:
  print body[2].density
  set body density 3
  body[2].density := 5

Body volume

Body read-only attribute. Actual volume of a body. This is the sum of three parts, in the soapfilm model:
  • An integral over the facets bounding the body. This is \int z dx dy normally, but \int (x dy dz + y dz dx + z dx dy)/3 if SYMMETRIC_CONTENT is in effect.
  • Any constraint content edge integrals applying to the body.
  • The body's volconst attribute.
In the string model, the parts are
  • An integral over the edges bounding the body's facet. This is \int -y dx.
  • Any constraint content vertex integrals applying to the body.
  • The body's volconst attribute.
Body volumes can be displayed with the v command, or with standard attribute syntax. Example:
  print body[1].volume
  foreach body where volume > 2 do print id

Body target

Body read-write attribute. The target volume of a volume constraint. May be set in the datafile, by the b command, or the set command. A volume constraint may be removed by the unset, or with the b command. Command examples:
   set body[1] target 23
  unset body target where id == 2
  print body[2].target

Volfixed

Body read-only attribute. Value is 1 if the volume of the body is fixed, 0 if not.

Body volconst

Body read-write attribute. A constant added to the calculated volume. Useful for correcting for omitted parts of body boundaries. Also used internally as a correction in the torus model , which will use the target volume to calculate volconst internally. In the torus model, the target volume should be set within 1/12 of a torus volume of the actual volume for each body, so the correct volconst can be computed. Each volconst will be adjusted proportionately when the volume of a fundamental torus domain is change by changing the period formulas. Volconst can be set in the datafile bodies section, or interactively by the set command or by assignment. Examples:
  print body[1].volconst
  set body[2] volconst 1.2
  body[2].volconst := 1.2
It is best to avoid using volconst except in the torus model. Rather, use edge content integrals so that the proper adjustments will be made if the boundary of the surface is moved, or rebody is done.

Actual_volume

Body datafile attribute. Actual_volume is a number that can be specified in the datafile definition of a body in the rare circumstances where the torus volume volconst calculation gives the wrong answer; volconst will be adjusted to give this volume of the body.

Body pressure

Body read-write attribute. If a body has a prescribed volume, this is a read-only attribute, which is the Lagrange multiplier for the volume constraint. If a body is given a prescribed pressure, then there is an energy term equal to pressure times volume. A body cannot have a prescribed volume and a prescribed pressure at the same time. Prescribed volume or pressure can be set in the bodies section of the datafile. If pressure is prescribed, then the value can be changed interactively with the b command, the set command, or by assignment. Examples:
  print body[2].pressure
  body[2].pressure := 1.3
  set body[2] pressure 1.3

Body phase

Body read-write attribute. For determining facet tension in soapfilm model, if a phase file is used.
Back to top of Surface Evolver documentation. Index. evolver-2.30c.dfsg/doc/cubebare.gif0000644000175300017530000024626611410765113017452 0ustar hazelscthazelsctGIF89a,o     //0/0//000//0/000/000?????@?@??@@@??@?@@@?@@@OOOOOPOPOOPPPOOPOPPPOPPP_____`_`__```__`_```_```ooooopopoopppoopopppoppp!!5 Image generated by AFPL Ghostscript (device=ppmraw) ,,ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooN599EioooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooKESSXO)3oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooE4ooooooo[looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooM5ooooooooo_9oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooob''ioo6_oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo[;oooo Do 8oob.ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSTS_oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo6594:46:93(ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo)Voooo,VKooo#oooooooooooooooooooooooooooooooooooooooooooooooooooooooooi7$3983"=oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo3oooooooooSooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooobSWSTXSTK38745::9  booooC5[ooo9ooooooooooooooooooooooooooooooooooooooooooooooooooooooooi"ZooooooS"ioooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo4oooo) 1oVoooooooooooooooooooooooooooooooooooooooooooooiUWTUSYXS?7556:65(  SooooioKooo1oooooooooooooooooooooooooooooooooooooooooooooooooooooooi"loooooooo[&oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo4oooWojoSoooooooooooo]SYSUWWUI85:588:4 -9447:96@TYXSSWWSoooooooooooooKGooooOo+)ooi'ooooooooooooooooooooooooooooooooooooooooooooooooooooooo3Woooo33:DooCKooooCRooooooooooooooooooooooooooooooooooooooooooooooooooooooiUWSSSUST55oooi' (oU86:38773TSWSSVTVbooooooooooooooooooooooooooooooooooooooooooooooo"oooo[ &ioo5WooooooooooooooooooooooooooooooooooooooooooooooooooooooooooofUV_ooo"ooi"oooooooooooooooooooooWTVSSSSXH49966942oood HDiV8935:354DSSSUWSWWoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooGCooooooooob ooooooooooooooooooooooooooooooooooooooooooooooooooooooo[6ooooS5ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo86oooooooooooooooooooooooooooooooooo('oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooUkoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo9?oooooooooooooooooooooooooooooooooobdooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo9oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo6Sooooooooooooooooooooooooooooooooooo4ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooobbooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo9SoooooooooooooooooooooooooooooooooooiSooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo*$oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooUoooooooooooooooooooooooooooooooooooo:oooooooooooooooooooooooooooiooooooooooooooooooooooooooooooooobbooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooToooooooooooooooooooooooooooooooooooooKooooooooooooooooooooooooooSoooooooooooooooooooooooooooooooooo6oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo[oooooooooooooooooooooooooooooooooooooF ooooooooooooooooooooooooooSooooooooooooooooooooooooooooooooookTooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooCoooooooooooooooooooooooooS1ooooooooooooooooooooooooooooooooooo@ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooUiooooooooooooooooooooooooU8oooooooooooooooooooooooooooooooooooo Doooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo3dSWTkoooooooooooooooooooN7ooooooooooooooooooooooooooooooooooooC o-oiGoWooooooooooooSSSUSWSS?68343:2 ooooooJ>oooob:oooooooooooooooooooooooooooooooooooooooooooooooooooooi$ioooooooo'[ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooobSSSSSWVP56Cooo2[o= ,9969656ASSWWSSZA Fi'looooobooooOEoooooooooE55:5 5441ooooooooooooooooooooooooooooooooooo*[oooocS_ooo"oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooiXSSSVTSVB:96848:+8ooo"iO o3 !58458655NU'UZS(>bo/*ioo3ooooooooooooooooooooooooo<*oooMDooooi ooooo*dooooooooo6oooooooooUooooooooooooooooooooooooooooooooooi%ooooL8 QooLHooooooooooooooooooooooooooooooooooo[UWZSWYXO7:77455: -85:347Cooo"ToooToooooooooooooooooooooooooo9ooo8UolSTSb4oooooooooooooooooooooooooobooooo iooooooooo[%oooooooooo6ooooDGoWooooooooooooooooooooooooooooooooooGLoooo_oibooooiTSSYVWSS@8984378' 79976333XSSUSWWVboooooooooooooooooooooooToooKO0 oWoooooooooooooooooooooooooo4oooUbod9557S:oooooooooooooooooooooooooooooooo[&iooooooob!ioooooooooo7oooHlS iYoooooooooooooooooooooooooooooooooo3[oooo [ooo/838:356CWSSWSSUUoooooooooooooooooooooooooooooooooooooooooooooooooooooooooWooooK3>ooXoooooooooooooooooooooooooo8oooooooooooo9ooooooooooooooooooooooooooooooooo["SoooooI"kooooooooooo3ooo92oo XToooooooooooooooooooooooooooooooooo3booooWoE"ooo"89764533VUYUSYSTcooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooToooooooooSooooooooooooooooooooooooooNTSS0ITZSSTWTooooooooooooooooooooooooooooooooooi>#Hooooooooooooo9oooc K:SToooooooooooooooooooooooooooooooooo;Soooo ioMooo oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooTYTSSSSTWYSooooooooooooooooooooooooooooooToooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo6oooo]:M [Uoooooooooooooooooooooooooooooooooob0ooooD 6SooW>ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooWoooooooooooooooooooooooooooooooooooooooooooKbooooooooooooooo:oooh*nU oToooooooooooooooooooooooooooooooooooioooo_ScoooiooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooloooooooooooooooooooooooooooooooooooooooooooGooooooooooooooo7oooo7 foWoooooooooooooooooooooooooooooooooooc ioooooooo>Ioooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo; ioooooooooooooo6oooooooooUoooooooooooooooooooooooooooooooooooo\boooooi*Ioooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooob*ooooooooooooooISSYM8CTSWGoooooooooooooooooooooooooooooooooooooi+#786**[oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooi*0 4ooooooooooooooooooB:oooooooooooooooooooooooooooooooooooooooooooo>koooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooS)oooooooooooooooooooo8:ooooooooooooooooooooooooooooooooooooooooooooKooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooS8oooooooooooooooooooo4@oooooooooooooooooooooooooooooooooooooooooooC0ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooU8oooooooooooooooooooo:Sooooooooooooooooooooooooooooooooooooooooooi iooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooloooooooooooooooooooooooooooooooooooooooooooo58oooooooooooooooooooo3WooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooYoooooooooooooooooooooooooooooooooooooooooooo:7ooooooooooooooooooooSoooooooooooooooooooooooooooooooooooooooooo ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSoooooooooooooooooooooooooooooooooooooooooooo3CooooooooooooooooooooWooooooooooooooooooooooooooooooooooooooooooooiiooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooU:oooooooooooooooooooooooooooooooooooooooooooo8SoooooooooooooooooooobooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooT5oooooooooooooooooooooooooooooooooooooooooooo8UoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooC3ooooooooooooooooooooooooooooooooooooooooooooSoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooohoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo78ooooooooooooooooooooooooooooooooooooooooooooSooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooWoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo:4ooooooooooooooooooooooooooooooooooooooooooooiooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo8Xooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo ooooooooooooooooooooooooooooooooooooooooooooS3oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo4SoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooY6oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo'SoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooA6ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooWoooooooooooooooooooooooooooooooooooooooooooo7:oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo_ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSoooooooooooooooooooooooooooooooooooooooooooo3AoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooS,oooooooooooooooooooooooooooooooooooooooooooo:SooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooiooooooooooooooooooooW:oooooooooooooooooooooooooooooooooooooooooooo9SooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooXooooooooooooooooooooK:ooooooooooooooooooooooooooooooooooooooooooooSooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSoooooooooooooooooooo66ooooooooooooooooooooooooooooooooooooooooooooUooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooY9oooooooooooooooooooo8:oooooooooooooooooooooooooooooooooooooooooooobooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooY:oooooooooooooooooooo7QoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooC8oooooooooooooooooooo7Woooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo[oooooooooooooooooooooooooooooooooooooooooooo89oooooooooooooooooooo2SooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooUoooooooooooooooooooooooooooooooooooooooooooo7:ooooooooooooooooooooTooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooS(oooooooooooooooooooooooooooooooooooooooooooo9VooooooooooooooooooooUoooooooooooooooooooooooooooooooooooooooooooo ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooT4oooooooooooooooooooooooooooooooooooooooooooo9SoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooT:oooooooooooooooooooooooooooooooooooooooooooo#Uooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo35ooooooooooooooooooooooooooooooooooooooooooooUoooooooooooooooooooo ooooooooooooooooooooooooooooooooooooooooooooYoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo7:oooooooooooooooooooooooooooooooooooooooooooo[ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooYoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo6JoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooT+oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo3SoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooV8oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo9WoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooP:ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooofoooooooooooooooooooooooooooooooooooooooooooo99ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSoooooooooooooooooooooooooooooooooooooooooooo94oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooobooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooWoooooooooooooooooooooooooooooooooooooooooooo4QoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooS5oooooooooooooooooooooooooooooooooooooooooooo9Soooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo\ooooooooooooooooooooT7oooooooooooooooooooooooooooooooooooooooooooo1ZooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooVooooooooooooooooooooC7ooooooooooooooooooooooooooooooooooooooooooooSooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooS!oooooooooooooooooooo37ooooooooooooooooooooooooooooooooooooooooooooTooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooS5oooooooooooooooooooo9?ooooooooooooooooooooooooooooooooooooooooooooiooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooS6oooooooooooooooooooo5Sooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooioooooooooooooooooooooooooooooooooooooooooooo>3oooooooooooooooooooo8SooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSoooooooooooooooooooooooooooooooooooooooooooo:3ooooooooooooooooooooUooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooWoooooooooooooooooooooooooooooooooooooooooooo:CooooooooooooooooooooWooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooX+oooooooooooooooooooooooooooooooooooooooooooo7Soooooooooooooooooooo_ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooU7oooooooooooooooooooooooooooooooooooooooooooo6WoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooK7ooooooooooooooooooooooooooooooooooooooooooooSooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooboooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo9:ooooooooooooooooooooooooooooooooooooooooooooUooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooWoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo97oooooooooooooooooooooooooooooooooooooooooooobooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooXoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo4Uooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo ooooooooooooooooooooooooooooooooooooooooooooU4oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo3WoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooU6oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo&SoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooC3ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooToooooooooooooooooooooooooooooooooooooooooooo88ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSoooooooooooooooooooooooooooooooooooooooooooo3>oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooS*oooooooooooooooooooooooooooooooooooooooooooo3SooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooiooooooooooooooooooooT3oooooooooooooooooooooooooooooooooooooooooooo6Soooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo ooooooooooooooooooooooooooooooooooooooooooooTooooooooooooooooooooT:oooooooooooooooooooooooooooooooooooooooooooo SooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSoooooooooooooooooooo79ooooooooooooooooooooooooooooooooooooooooooooSooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooT+oooooooooooooooooooo96oooooooooooooooooooooooooooooooooooooooooooo[ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooT6oooooooooooooooooooo:Kooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo/78:2(98837.\ooooooooooooo5Xoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo[ooooooooooooooooooooooooooooooooooooooooSoooboooooboSSooooooooooooo,UooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSooooooooooooooooooooooooooooooooooooooooToooooooS9oXToooooooooooooSooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSooooooooooooooooooooooooooooooooooooooooSo]ooo*7oWSoooooooooooooSooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooS6ooooooooooooooooooooooooooooooooooooooooSoooooooW9oTWoooooooooooookooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooW3ooooooooooooooooooooooooooooooooooooooooSoooooooS5oVToooooooooooooooooooooooooooooooooooooooooooooooooooooD8749 7:6485Sooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo;5ooooooooooooooooooooooooooooooooooooooooVoooooooS3oYSooooooooooooooooooooooooooooooooooooooooooooooooooooo5oooooooooooo9ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo75ooooooooooooooooooooooooooooooooooooooooUoooooooT9oSXooooooooooooooooooooooooooooooooooooooooooooooooooooo7oooCSooi69No4ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo3=ooooooooooooooooooooooooooooooooooooooooUooo/oooo[CoXTooooooooooooooooooooooooooooooooooooooooooooooooooooo7oo%Soo?Gb8ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo8UooooooooooooooooooooooooooooooooooooooooWoooooooooooTVooooooooooooooooooooooooooooooooooooooooooooooooooooo5ooV$Wo[ oo!@:ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo:SooooooooooooooooooooooooooooooooooooooooESSS85SWSSSSG[ooooooooooooooooooooooooooooooooooooooooooooooooooooo7ooo5SoWoo3::ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo!Soooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooojoooooooooooooooooooooooooooooooooooooooo6ooo7SoSoo-89oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooToooooooooooooooooooooooooooooooooooooooo5ooo4SoidoS9ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooodooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooVoooooooooooooooooooooooooooooooooooooooo3ooo9VooG +o7ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooT8oooooooooooooooooooooooooooooooooooooooo:oooooooooooo4ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooodooooooooooooooooooooS:oooooooooooooooooooooooooooooooooooooooo-YSTYSUUXWWZS7oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooUooooooooooooooooooooF:ooooooooooooooooooooooooooooooooooooooooooooSooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooU!oooooooooooooooooooo68ooooooooooooooooooooooooooooooooooooooooooooToooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo ooooooooooooooooooooooooooooooooooooooooooooS8oooooooooooooooooooo95ooooooooooooooooooooooooooooooooooooooooooooiooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooU6oooooooooooooooooooo3Uooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@8oooooooooooooooooooo:YooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooUoooooooooooooooooooooooooooooooooooooooooooo43oooooooooooooooooooo)WooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSoooooooooooooooooooooooooooooooooooooooooooo3BooooooooooooooooooooTooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooS2oooooooooooooooooooooooooooooooooooooooooooo:Uoooooooooooooooooooo\ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooU3oooooooooooooooooooooooooooooooooooooooooooo3SoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooN9oooooooooooooooooooooooooooooooooooooooooooo"Wooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooookoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo84ooooooooooooooooooooooooooooooooooooooooooooSooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo59oooooooooooooooooooooooooooooooooooooooooooobooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooToooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo:KoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooW8oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo9SoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooT5oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo2SoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooI9ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo[oooooooooooooooooooooooooooooooooooooooooooo55ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSoooooooooooooooooooooooooooooooooooooooooooo ooooooooooooooooooooSoooooooooooooooooooooooooooooooooooooooooooo33oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooT#oooooooooooooooooooooooooooooooooooooooooooo6ToooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooZ:oooooooooooooooooooooooooooooooooooooooooooo4Soooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo ooooooooooooooooooooooooooooooooooooooooooooWooooooooooooooooooooS7oooooooooooooooooooooooooooooooooooooooooooo$SooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooXoooooooooooooooooooo3:ooooooooooooooooooooooooooooooooooooooooooooSooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooV2oooooooooooooooooooo8:oooooooooooooooooooooooooooooooooooooooooooo`ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooT3oooooooooooooooooooo:HoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooM7oooooooooooooooooooo5Uoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooh*#9:5)'booooooooooooooooooooooooooooooooooooooooo:8oooooooooooooooooooo3ZooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooK$booooobSoooooooooooooooooooooooooooooooooooooooo:5ooooooooooooooooooooUooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo]#ooooooooo+]ooooooooooooooooooooooooooooooooooooooo3OooooooooooooooooooooUooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo_Dooi(ooooooooooooooooooooooooooooooooooooooo3UoooooooooooooooooooobooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooPDoooooi 6ooo=Soooooooooooooooooooooooooooooooooooooo1UoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooNWSWSSTUWUHooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo:iooooo78oooc6ooooooooooooooooooooooooooooooooooooooSoooooooooooooooooY*6)>iooooooooooooooooooooooooooooooooooooooooo^oooooooooooooooooooooooooooooo6ooooooooo4ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSTSSUTSSC8:994842oooooI7T:oooo"ooooooooooooooooooooooooooooooooooooooSoooooooooooooook&7iooooS[ooooooooooooooooooooooooooooooooooooooooSoooooooooooooooooooooooooooooo:oooooioo3oooooooooooooooooooooooooooooooooooooooooooooooooooooooooobSSVSWTSS395::949"oooobW;$booo*oooooooooooooooooooooooooooooooooooooooooooooooooooooo$[oooooooo*aoooooooooooooooooooooooooooooooooooooooS)oooooooooooooooooooooooooooooo:ooooo)oo6oooooooooooooooooooooooooUTYSSTSSC955:6690 33759:79PTTSSSSS^9boooc86*Soob4ooooooooooooooooooooooooooooooooooooooooooooooooooooo9KoooooUoooo"iooooooooooooooooooooooooooooooooooooooX9oooooooooooooooooooooooooooooo6ooooP0oo58:9:3469'77:5456;VSUSSSTYioooooooooooooooooooooooooooooooooNCooooooS4ooo9[ooooooooooooooooooooooooooooooooooooooooooooooooooooo"ooooo[ ooooUBoooC(ooooooooooooooooooooooooooooooooS'WSSSSYW<:68846:0ooobooo99635:778PTSSTSTS]oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(ioooooidooioooooooooooooooooooooooooooooooooooooooooooooooooooooWFoooo[ooooooUPbSXUSZSTL67687954 "ooo",9 :o5734 Uoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooj5ioooooooooooooooooooooooooS oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooodboooooooooooooooooooooooooooooooooo7Voooooooooooooooooooooooooo7oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo%&oooooooooooooooooooooooooooooooooo3SooooooooooooooooooooooooooUiooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooojboooooooooooooooooooooooooooooooooSooooooooooooooooooooooooooo'9ooooooooooooooooooooooooooooooooooooiooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo6oooooooooooooooooooooooooooooooooXooooooooooooooooooooooooooobhoooooooooooooooooooooooooooooooooooXoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooiWoooooooooooooooooooooooooooooooo[oooooooooooooooooooooooooooo2&oooooooooooooooooooooooooooooooooooVooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooEoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooi[ooooooooooooooooooooooooooooooooooY,ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo Dooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo5ooooooooooooooooooooooooooooooooooZ8oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooO oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooiSoooooooooooooooooooooooooooooooooM8oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo;oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooDooooooooooooooooooooooooooooooooo38ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooViooooooooooooooooooooooooooooo oooooooooooooooooooooooooooooooo Coooooooooooooooooooooooooooooooo:3ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"6oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooX oooooooooooooooooooooooooooooooo9SooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooCTSTUSSWVSEooooooooooooooooooooooooooooooooooooooooooooooooooooooo4ooooooooooooooooooooooooooooooo4SooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSoooooooooWoooooooooooooooooooooToooooooooooooooooooooooooooooooooSioooooooooooooooooooooooooooooo*TooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooXooob CoWoooooooooooooooooooooSoooooooooooooooooooooooooooooooooo&.ooooooooooooooooooooooooooooooSooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooUooo9GoRoWoooooooooooooooooooooW*oooooooooooooooooooooooooooooooooofboooooooooooooooooooooooooooooTooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooWooooo=FoSoooooooooooooooooooooW:ooooooooooooooooooooooooooooooooooo,%ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSooooo;")oSoooooooooooooooooooooW9oooooooooooooooooooooooooooooooooooiSoooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSoooCboooSooooooooooooooooooooo75oooooooooooooooooooooooooooooooooooo3oooooooooooooooooooooooooooo oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooSooo:T+oWooooooooooooooooooooo33ooooooooooooooooooooooooooooooooooooj UooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooUoooo<3loooW5ooooL::boof&ooooooooooooooooooooooooooooooooooooooooooooooooooooooo<[ooooii"BooLEoooooooooooooobSXUXUTSS8:97:965&:6953:68SSSSUUTWiooooooooooWooo* Surface Evolver Documentation: graphics

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Surface Evolver graphics

Contents


Overview

Surface Evolver graphics consists of drawing edges and facets. There is a single graphics driver routine which produces colored edges and facets in 3D and sends them to a set of display routines. There are three main sets of display routines:
  • Native screen graphics, built-in Evolver graphics of various qualities on various platforms,
  • Interface to the Geomview 3D viewer, for Unix/Linux users without OpenGL/GLUT graphics compiled into Evolver,
  • PostScript files, for everybody wanting to make 2D images for publications, web sites, etc.

Native screen graphics

The Surface Evolver has the ability to produce its own screen graphics directly. The Windows version has nice OpenGL/GLUT graphics, which should also be available on any Unix/Linux/Mac OSX system. The Mac OS 9 version has some simple graphics, and there is a primitive X-windows graphics module for Unix/Linux systems that for some reason can't do OpenGL. Those compiling unix versions must link in the appropriate graphics module.

Screen graphics appear in their own window but can be controlled by typing graphics commands at the "graphics command: " prompt in the main window. OpenGL graphics can also be controlled by mouse and keyboard actions in the graphics window.

Main prompt commands relevant to screen graphics:

The native screen graphics view is controlled by a view transformation matrix, which may be specified in the datafile, and which is dumped by the d or list topinfo commands. The view matrix may be changed with graphics mode commands. The view matrix elements may be read or set at runtime by view_matrix[i][j], where the indices start at 1. In particular, one can write command scripts to save and reload particular view matrices; see saveview.cmd in the distribution package. The view matrix does not affect geomview.

The display consists entirely of facets and edges. Special edges (fixed edges, boundary edges, constraint edges, triple edges, bare edges) are always shown, unless you make their color CLEAR. The individual facet edges can be toggled with the graphics mode command `e'.


Graphics mode commands

When the native graphics display is invoked by the 's' command or the various `show' commands, the Evolver enters graphics mode, with the prompt `Graphics command: '. A graphics command is a string of letters followed by RETURN. Each letter causes an action. Some commands may be preceded by an integer count of how many repetitions to do. Example command: 15u2z, which does 'u' 15 times and 'z' twice. Rotation commands may be preceded by a real number giving the degrees of rotation; an integer will give a repetition count with the default angle of 6 degrees. A real number is indicated by including a decimal point.

Repeatable commands:

u
Graphics mode command. Tip up. Rotates image about horizontal axis, default 6 degrees. Example: `15u' does 90 degree rotation, `15.0u' does 15 degree rotation.
d
Graphics mode command. Tip down. Rotates image other way, default 6 degrees. Example: `15d' does 90 degree rotation, `15.0d' does 15 degree rotation.
r
Graphics mode command. Rotate right. Rotates about vertical axis, default 6 degrees. Example: `15r' does 90 degree rotation, `15.0r' does 15 degree rotation.
l
Graphics mode command. Rotate left. Rotates about vertical axis, default 6 degrees. Example: `15l' does 90 degree rotation, `15.0l' does 15 degree rotation.
c
Graphics mode command. Rotate clockwise about center of screen, default 6 degrees. Example: `15c' does 90 degree rotation, `15.0c' does 15 degree rotation.
C
Graphics mode command. Rotate counterclockwise about center of screen, default 6 degrees. Example: `15C' does 90 degree rotation, `15.0C' does 15 degree rotation.
z
Graphics mode command. Zoom. Expands image by factor, default 1.2. Examples: `z' zooms by 1.2, `2z' zooms by 1.44, '2.0z' zooms by 2.
s
Graphics mode command. Shrink. Contracts image by factor, default 1.2.
arrow keys
Graphics mode command. Move image in appropriate direction. May be prefixed by a real number, which is multiple of thirds of screen width to move. Default move is 1/12 screen width. May not work on all terminals.

Non-repeatable commands:

R
Graphics mode command. Reset viewing angles to original defaults and rescale the image to fit the viewing window.
m
Graphics mode command. Center image in viewing window.
e
Graphics mode command. Toggle showing all the facet edges.
h
Graphics mode command. Toggle hiding hidden surfaces. When ON, takes longer to display images, but looks better.
b
Graphics mode command. Toggles display of bounding box. Useful for visualizing orientation. In the native graphics window, the 'o' key does the same thing.
t
Graphics mode command. Reset mode of displaying torus model. Choice of raw unit cell, clipped unit cell, or connected bodies.
w
Graphics mode command. Toggles display of facets entirely on constraints. For a one-sided constraint, applies to facets whose vertices all hit the constraint. "w" stands for "wall".
B
Graphics mode command. Toggles display of facets on boundaries or equality constraints.
v
Graphics mode command. Toggles showing of convex and concave edges in different colors. "v" stands for "valleys".
+
Graphics mode command. Increments color number used for facet edges.
-
Graphics mode command. Decrements color number used for facet edges.
?
Graphics mode command. Prints help screen for graphics commands.
q ,x
Graphics mode command. Exit from graphics mode, and return to main command mode.

OpenGL graphics

Ideally, you have a version of the Evolver that uses OpenGL/GLUT for its screen graphics. OpenGL is standard on Mac OSX, most unix systems, and Microsoft Windows. Tbe graphics display is invoked with the 's' command, which leaves you at the graphics prompt, which you should quit 'q' right away since graphics commands are better given in the graphics window. There are many mouse and keyboard actions that can be performed in the graphics window itself. Holding down and dragging the left mouse button moves the surface continuously, and the clicking right mouse button picks vertices, edges, and facets. Picked element id numbers are printed in the console window. With the graphics window in the foreground, these keyboard commands are active:

h Print a help screen on the console window.
r Rotate mode for left mouse button.
t Translate mode for left mouse button.
z Zoom mode for left mouse button (and use F to focus on particular vertex).
c Clockwise/counterclockwise spin mode for left mouse button.
+ Widen edges.
- Narrow edges.
b Decrement edge front bias by .001.
B Increment edge front bias by .001 (to show edges more clearly).
R Reset the view.
m Center the image.
M Have the right mouse button bring up a menu instead of picking.
P Have the right mouse button do picking instead of menu (default).
p Toggle orthogonal/perspective projection.
s Toggle cross-eyed stereo.
e Toggle showing edges, regardless of "show edge" condition.
f Toggle showing facets obeying "show facet" condition or no facets.
F Move the rotate/zoom origin to the last picked vertex.
G Start another graphics window with independent camera.
o Toggle drawing a bounding box.
g Toggle Gourard (smooth) shading.
x Close the graphics window.
arrow keys Translate a bit.
And some more advanced commands most users will never use, but are listed here for completeness:
H Print advanced help.
a Toggle using OpenGL element arrays.
i Toggle interleaved elements in OpenGL arrays.
I Toggle indexed OpenGL arrays.
S Toggle OpenGL triangle strips.
Y Toggle strip coloring (I was curious as to what OpenGL triangle strips would look like).
D Toggle using a display list.
Q Toggle printing drawing statistics.


Geomview graphics

Excellent screen graphics on Unix systems can be done through the free 3D viewing program geomview. Geomview can be started with the P command, option 8. One caution: geomview does not deal well with object sizes below 1e-5, so displaying micron-size objects using MKS units is ill-advised.

Main prompt commands relevant to geomview:

  • geomview or P 8 to start geomview
  • P 9 to end geomview
  • geompipe to pipe geomview input someplace else
  • geomview string to send user commands to geomview
  • gv_binary toggle to control interface mode
  • view_4D to toggle sending 3D or 4D info to geomview
  • D or autodisplay for toggling automatic redraw when the surface changes. Default is automatic redraw when geomview is started.

The View Matrix

The mapping of coordinates from ambient space to the graphics display is done by a matrix called the view matrix. The matrix uses homogeneous coordinates, thus is a square matrix whose size is one larger than the dimension of the ambient space. The view matrix can be initialized in the top of the datafile, or its elements may be read or set at runtime by view_matrix[i][j], where the indices start at 1. In particular, one can write command scripts to save and reload particular view matrices; see saveview.cmd in the distribution package. The view matrix is saved in dump files.

Multiple view transforms.

Evolver can display multiple transformations of a surface simultaneously, for all of the possible graphics displays. Each transform is defined by a matrix in homogeneous coordinates, which for a 3D surface means 4x4 matrix which has a 3x3 rotation matrix in the upper left, a translation vector in the last column, and a row (0,0,0,1) on the bottom. There are two ways to specify the transform matrices:
  1. List each matrix individually in the datafile; see view_transforms.
  2. List a set of transforms in the datafile (see view_transform_generators) and use the transform_expr command at run time to generate a set of transforms. I much prefer the second way.
The final list of transforms may be accessed by the matrix view_transforms[][][]. Because some transformations may reverse the front and back sides of surfaces, transforms can be made to swap frontcolor and backcolor attributes of facets; see view_transforms or view_transform_generators) for syntax. The read-only array view_transform_swap_colors[] has a 1 entry for the transforms that do swap

Picking elements.

One of the big advantages of using geomview or the OpenGL version is that you can pick vertices, edges, and facets in the geomview window by right-mouse-clicking, and the id numbers of the picked objects will be printed in the main window. Be careful when picking; it does not always work as you might hope. It may be necessary to zoom in on the surface to get a clear shot at the element you want. Be wary when the element returned is 1; that seems to be a common response when Evolver is confused as to what element was picked. Also, Evolver polls geomview for pick results only when at a prompt awaiting user input.
Picked vertex, edge, and facet numbers are stored in the internal variables pickvnum, pickenum, and pickfnum, respectively. The 'F' key command on the graphics window sets the rotation and scaling center to the pickvnum vertex. Pickvnum is settable with ordinary assignment commands, so the user can zoom in on any vertex desired.

Note: Since vertices are not drawn individually, Evolver reports a vertex as picked only when two edges with a common vertex are simultaneously picked. Therefore a vertex at the end of a single edge cannot be picked.


Clip view of surface

It is possible to have the graphics display clip the surface with multiple clipping planes. A clipping plane is defined by a plane of the form ax + by + cz = d. The visible volume is ax + by + cz <= d. Up to 10 clipping planes may be stored in the array clip_coeff[][], with the first plane coefficientsa,b,c,d stored in clip_coeff[1][1] through clip_coeff[1][4], etc. The user does not have to create clip_coeff[][]. To use clip view, first set the coefficients of however many clip planes you want and then use the clip_view toggle. For example, to get a vertical clipping plane parallel to the y and z axes and a little in front of them:
   clip_coeff[1] := 1;
   clip_coeff[2] := 0;
   clip_coeff[3] := 0;
   clip_coeff[4] := .2;
   clip_view;
With OpenGL graphics, the first clip plane plane can be varied interactively by hitting the 'l' key (lower case L) in the graphics window and dragging the mouse horizontally. The 'k' key will make mouse dragging change the orientation of the clip plane. Hit 'r' or 'c' or 't' to get back to another mouse mode. 'L' will turn off clip_view.

Clip view works separately, and after, torus model viewing modes such as clipped and connected, so it is no problem to have them together.

In case clip_view and slice_view are both in effect, slice_view operates instead of clip_view.


Slice view of surface

It is possible to plot a cross-sectional slice of a surface. The slice is defined by a plane of the form ax + by + cz = d. The coefficients a,b,c,d are stored in the array slice_coeff[] (which the user does not have to create). To use slice view, first set the coefficients and then use the slice_view toggle. For example, to get a vertical slice parallel to the x and y axes and a little in front of them:
   slice_coeff[1] := 1;
   slice_coeff[2] := 0;
   slice_coeff[3] := 0;
   slice_coeff[4] ;= .2;
   slice_view;
The cross-section will be in the form of line segments of the same color as the facets they are sections of. With OpenGL graphics, the slice plane can be varied interactively by hitting the 'l' key (lower case 'L') in the graphics window and dragging the mouse horizontally. The 'k' key will make mouse dragging change the orientation of the clip plane. Hit 'r' or 'c' or 't' to get back to another mouse mode. 'L' will turn off slice_view.

Slice view works separately, and after, torus model viewing modes such as clipped and connected, so it is no problem to have them together.

In case slice_view and clip_view are both in effect, slice_view operates instead of clip_view.


PostScript files

The Surface Evolver can generate PostScript files by either the postscript command or the P command option 3, or just "P 3". The image is the same one shown with the native screen graphics, so one should use the s command and graphics mode commands to get the image looking as desired. The variable brightness can be used to set the median gray level. The PostScript image is put into an 8 inch square at the lower left of the page.

With the P command, you will be prompted for options.

Show grid lines?
This is asked if you are graphing a 2D surface. If you reply 'y', all triangle edges will be plotted. If 'n', only special edges will be plotted (triple junctions, borders, etc.; this can be controlled with the show edges command). Default 'n'. The postscript command uses the ps_gridflag toggle to control this.
Do colors?
If you reply 'y', edges and facets will be plotted with their color attributes and shading (if activated). If 'n', then all edges are plotted as black, and all facets as white with shading. Default 'n'. The postscript command uses the ps_colorflag toggle to control this.
Do crossings?
This is asked if the surface is 1-dimensional (the string model) and the dimension of space is at least 3. If you reply 'y', a 3D effect will be created by plotting edges back to front, with each edge plotted first as a thick white line and then as a thin black line. This creates a broken back line and continuous foreground line at each crossing. Default 'n'. The postscript command uses the ps_crossingflag toggle to control this.
Do labels? (i for ids, o for originals)
This PostScript P 3 command subprompt gives you a chance to put numeric labels on vertices, edges, and facets, which is useful for debugging or modifying a datafile. Edge labels are slightly displaced toward the head of the edge, and facet labels are signed according to which side of the facet is visible. Choose 'i' or 'y' for the current element id, or 'o' for the original element number. If you don't want any labels, just hit RETURN. The postscript command uses the ps_labelflag toggle to control this. The relative size of the labels can be controlled with the ps_labelsize variable, whose default value is 3.0.
Enter file name (.ps will be added):
Give the name of the PostScript output file. A ".ps" extension will be added if ".ps" or ".eps" is missing. Not a good idea to just hit RETURN, since that will produce the file ".ps".
The linewidth of PostScript edges may be controlled by the user. Widths are relative to the image size, which is 3 units square. If the real-valued edge extra attribute ps_linewidth is defined, that value is used as the edge width. Otherwise some internal read-write variables are consulted for various types of edges, in order:
ps_stringwidth - edges in the string model, default 0.004
ps_bareedgewidth - "bare" edges, no adjacent facets, default 0.005
ps_fixededgewidth - "fixed" edges, default 0.004
ps_conedgewidth - edges on constraints or boundaries, default 0.004
ps_tripleedgewidth - edges with three or more adjacent facets, default 0.003
ps_gridedgewidth - other edges, default 0.002

The bounding box listed in the PostScript file is normally the actual extent of the surface in the window (i.e. the bounding box is never bigger than the window, but may be smaller). The full_bounding_box toggle will force the bounding box to be the full window. This is useful in controlling the image size while making a series of images of different views or evolution stages of a surface.

Visibility testing. PostScript files of complicated surfaces may contain a high proportion of facets not visible in the final image. This takes much extra file space and rendering time. There is an option to process the list of PostScript facets to eliminate the non-visible facets, the visibility_test option command. For my own debugging purposes, there is a visibility_debug toggle, which causes printing of verbose information; don't use it.


Other graphics related features.

This section has links to other topics you should consult.

Internal Evolver state information relevant to graphics:

Datafile features that are relevant to graphics are: Main prompt commands that are relevant to all graphics are: Back to top of Surface Evolver documentation. Index. evolver-2.30c.dfsg/doc/square-e5.gif0000644000175300017530000005453011410765113017500 0ustar hazelscthazelsctGIF89ad    ###'(',,,/000/000/344778787788877878887;;;??@?@??@@@??@?@@@?CCCGGHGHGGHHHGGHGHHHGKKKOOPOPOOPPPOOPOPPPOSSSWWXWXWWXXXWWXWXXXW[[[__`_`__```__`_```_cccgghghgghhhgghghhhgkkkoopopoopppoopoppposttwwxwxwwxxxwwxwxxxw|||!!B Image generated by AFPL Ghostscript BETA RELEASE (device=ppmraw) ,dO:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:u)S :uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:u)N:uԩSN:uԩSN:u)QpjԸsfR:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uꔩ6.N1eʔ)SL=Ɖ(~::uԩSN:uԩSN:uSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uT'/.8 (P@r"C 10)SN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:u SN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:u1QDHD%J(QD% JE1x0tԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:ԩSN:uԩSN:uԩSN:uԧ2\rbHĀqG(QD%JT^"EB$DN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uddȐ!C:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN2DEJa N:ub44")R$J #FAZ"F2Ǐ?~c'$F-zѣGe,e` PP̠A?(I(QD%J(Q@ͩSN:uԩSN:uԩSN:uԩSN:u*R!B%*P!BDJ,QDreLJ~Ҁ(qEǏ?~Ǐ?~t = O;,ZhQ(L)EGBJTPBEK"%J(Q:uԩSN:uԩSN:uԩSN:uԩS%J&-%ʄ%J JJ &") D%J5abR&M4i!8;-ZhѢE?~' w5*aǏ?~Ǐ?~F;@?~D`EI@Dȏ? #%$%TPD*T;%"QJaͩSN:uԩSN:uԩSN:uԩSN:&(QD"'H 82RB ;B *TP2t%JT',",i M4i BhѢE-vǏ?~"J ?~)K.X` .Xd?~ -bT "DFx'&L0aJSCB *T(M1 PPdh8$JT N:u SN:uԩSN:uԩSN:u)Q"%(Qb @SPB*TPB I(B0j)&K "iҤIS ~"ĢE)ZJ?~Ǐ?~g,wd?~a?ʸXE-" 0a„ 0TFDPD E#TPBiAHTN:uԩSN:uԩSN:uԩSN:uꔨ=B %*TP *PѠ/0U &L"1 :-"D!B5,Z菉7~?~Ǐ8 Ν>wqfѢE-Zh ,wGĠ; Ǐ]L FwJZ"E ӥ4`„)F&Lj*T(B )PD! tԩSN:uԩSN:uԩSN:uԩSNpPNPB UBTPB  ;&L"aĂА"DEXhwǏd?^2 ;s -ZhѢEebsϝ> ?~?~`T!BROj &LLdʔ8By*T DISN:uԩSN:uԩSN:uԩSN:uSNL`d *TB *T2`t ӟ`t "DhE!"̢Em2Ǐ?~XǏ?~pϟA.玟;~hѢEds?w|(wĝ?~ -"Dh!BLi&"aJ%&L09qP`h%*P:uԩSN:uԩSN:uԩSN:uԩS@IėPD*T(M0`H%L0aD?C"B"! A9?e?~ğ?qΝ;wܹsLA-bd?w΅@qǏAeb ?JHE1$L0a„QL2aʄ+0e$*(QD2AԩSN:uԩSN:uԩSN:uԩSNj@@Q*P2a)L+0] &L !E"DH!B@Y?~ǏvO ! 󧆗;wܹΝ;wHhwsϝ;x( (wϟ? HB )"4$B$` &LNdʄ L2A`PD %JTN:uԩSN:uԩSN:uԩSN:u)DP=Bep'&L2t H"a”&&ZDhEʰhH??w@>'ϟ? Ν;~KEw@s'?w'C@QgП$QϟA5YDN,G"L0at"&L2a„ ҄Nj($M$QB!PtԩSN:uԩSN:uԩSN:uԩSN!*PdA!L0a„)"L0a„  <$?2DKIǏ?QƏ?vϠA sΝ;wܹΝ;^`dsϝ=~sϝ!^ $hO2dg?~!%ZDhE20„  0a” &L+ S@izÁSN:uԩSN:uԩSN:uԩSN:uSND%*2eڄ)LX\T)&Lb!Bq!B`( ?~?~fР?cΝ;~sϝ;~?wN={(P A8(gEA~ Ϡ ""B-Js&L <S%L0a  ::uԩSN:uԩSNxԩSN:uԩS@`%*5 &L2azRH0aJcQ,"D!` P揟?~?Q3AwsΝ;ws '$8N;~ٳg 8 h2iZϟ?wV0@"B±hѢ2'0a´ &L8e*30a %LhqԩSN:uԩSN:uԩSN:uԩSNRw0aԢH0a” S L0a(âEmr"!DrC?~Ǐ?~aO3ϟADsΝ;~ܹsN~xpgϞ={ΈH@@(?lP(-"hэ;-&L70e„i M4mIs &L0atN:uԩSN:uԩSN:uԩSN:u)N0a„ &'p SLb` ӥi-Ză!B "DНwXbǏ?~5ϟ?pN;wsN w"OΔgϞ;{sNA 莃IƠ?Z@D"BYh"X0a &H "iꤩL0a„ S 2!sԩSN:uԩSN:uԩSN:uԩSNI &L0);2a&LXThѢ "D"BJO ϟe@(gРAG <}s玟!%0 F?~)RX,5 hP@w 0ÛAq%"D!eѢE D&LjIS'M 0a)R$L2eũSO:uԩSN:uԩSN:uԩSN:u0a„ N"Y  0EZ$aѢEPD!B#"J?~C68Pϟ? ϟ?玞;X2D $41OB"̞;~1H B tA3O?(RDQ-Z@BeS'N:ij S$L0Y SH::uԩSN:uԩSN:uԩSN:uԩS,\ &L98u)Lʜ HE-Z!Zd 8^Ǐ?~ڬAAϟ?xN;wdP(#G"Ǐ?Ҝ)R$B'h% P @珟?j RCEXh"Bl M4qIF0a4DL2ejԩSN:uԩSN:uԩSN:uԩSN` L0aѩN\`(ȥ;.-ZhѢ "D!?.xI?~ -hПAsN;yΜ#H=ď?~ H"Eg?ydDhР@e2D? ca ~ZD(E-Z(M 4iĩ&Nʤ %2e” N:uԩSN:uԩSN:uԩSN:u)p\ 8:iL 0ehQ"B D!C(L ?~?mty?w9QOHA(R?~Ǐ?D)RH^`K AjhOA "-ZHѢEeѤ&M6uB K`Ʉ)S&L0!xsԩSN:uԩSN:uԩSN:uԩSNU#L@IS'Mѣ̢E-ZGE"ß?jǏ?~?hgAj#ÝH A H<Ǐ?~!RH"E/+yEРA2?j GBXE)r! B4uĉ.- S&L2eéSN:uԩSN:uԩSN:uԩSN: H0aԢN4u -Zh"F]"d!&]TÏ?.Q4hЛ $bH 4 1bH"Ǐ?~Ǐ(J"E)RH@A $BIA~ Π?mF BD(âE-Z XN:!L0e  ::uԩSN:uԩSN:uԩSN:uԩS:hs"&4iꤩ&4L,ZТE!EL 珃A~3ȏ(Iϟ?A( A H E F?~Ǐ?~'RHgNBK h2ϟ^ N"B<-ZhѢA Xh +hiȠ4C2eD p8ԩSN:uԩSN:uԩSN:uԩSNB@&.X0ՈԩSNv(sĢE-ZN BdП?RǏ?Ɵ?Ϡ?N|B(ңG"1bĈQǏ?~NHg$E N ^(AH ?x$h HhE2ǝE-ZEMeHDo5N:uԩSN:uԩSN:uԩSN:u)hrq M4Q%QJ,ZhѢEl"ЃASÏ?~f ?5 Ϡ?%<)R$H2Ǐ?~Ǐ?~z pǔ(QJ*sBA$B~ŋ AKE-$/e-ZhѢE[Nb71bĈQB9uԩSN:uԩSN:uSN:uԩS! mtҴĠEĢE-Z$!BX ~ϟm r0hР?4 E"TD@"A*?~Ǐ?`i(QJ*%JTQ'3 .@ $NB_,Z( HhѢE-Z(ʁ7A#FwxD%©SN:uԩSN:uԩSN:uԩSN:% @?1r4)F1ZTQ(&-ZhQ B4hП?0g?~@ ş?g eHʔR?Ǐ?~8~Ǐ?e HP@RҠHA)R2-ZCH"a)ӈF0ZhH"A)2d(ϟ?O6O"B)#F$`„%Pi\@ʔI&0E)RHF&Q`8($C 8"E)RHprh괅ĝAdX E"$H<-ZLH"Et#F EHezDSN:uԩSN:uԩSN:uԩSN:Uj@bD& #Fx@R$F^,ȏ(?~@ 珟AgЛ-"H"1R2 SL)RH"E"s=e!Bp8Q&RH"E2D&?gѢ:;-#H e`ωH"E)>EP %"EA(H :uԩSN:uԩSN:uԩSN:uԩSI4#cQH',b$F)hQ E珟?RQ?Ơ?DYDE)L+ N&N2e"RH"Er= Ɲ= ",%N`Y)R$ \4i"àE-ڱhѢ?.@"F2x(P.,ZhѢE-2 =Ҥ)R3AND(RH=D)R$ \@iqԩSN:uԩSN:uԩSN:uԩSNu !F4"a!É4-"h-"ТE(!"ɝ?~)"DhEθ#N:uԩSwN!tGĞ@ "$(P A)9Ξ;}'@ DP B."G@}E-ZhQ"E)RJ"XOH")H6)ON:uԩSN:uԩSN:uԩSN:uԊ=0qD ,ZhѢEXhQE-#J$H^hdQϟ?12!DТ8Xjd@AD/:uԩ]1= 4(PA ?ws,5"D!B Dg",ON.YhѢECʜ)RH"EK?="E)ʙH"@ LZ\9uԩSN:uԩSN:uԩSN:uԩS"HH"eE-Z("BZ B<"A $&VDQ"A'@ )Kp\tSKC`Z@ !BD(,ws?w BC!B@(!E@рɢET#RJ"E)H") ARR#S$2azԩSN:uԩS:uԩSN:uԩSN:u A DT)R i-ZhѢE<%"hѢE.D:)RH ED(IDԩS$D1SN|(;5"D;"DhP B@sgϝ=wܹs"P="DЙ E*'D$!w2ez3dS&L2ApԩSN:uԩSN:uԩSN:uԩSN',.EăE-ZƢE-"TPH'"=QH"HdQN:pq(:uE-*S!B"DО e ž={ٳϝ;wܹ) B!B"DOH"=zT p H"E H"U$(Qj@”)L0qʄ RNN:uԩSN:uԩSN:uԩSN:u!e$HRXѢE<,Zh("E'RG"9D?<:uMODtHDF=Z> DP BrPXΞ;~Ξ;bXBC!B"D!B^z@b(R@2x` &L+"E)R"Et)ҟh`)S m2aʔ @N\(9uԩSN:uԩSN:uԩSN:uԩS 4FJ"Ȅ ːE-RfѢEND)4"EQ0E: NE:upS1Zh#Fe"D!B"D!hX;~sϝ>}sK="D!B $NHETfB?^j<(RH SH*@pSLȄ)L2E BԩSN:uԩSN:uԩSN:uԩSN:& TĤML5,ZLB8s `Hx RHԈ  E:u) 80bhѣE-@@"P Bb,X` ,P^Ĉ#j"D!B"D<"A R$H`3"EJtRK.UR$H2aRL2E²#iN:uԩSN:uԩSN:uԩSN:u)/"4E0!R;N0et"͢Eev(ZtFH"E$#RG"E?"tԩ.x,b"F-bE !ADP@.\(0J*QD"%ʕ(t1Ċ!h"!Bb$#H 1 :2Ĉ)40Eɋ ~.)-2EFB4pԩSN:uԩSN:uԩSN:uԩSNG>.EZ)SH%,ZTcѢ~"ET)R ELLԩSN|"F-zѣ  "@ݩ"đ32C;uܹ8wܹL?~!B"D(@ 1 !.4CI$D *E/Q|针7@@BFQ$SPN:uԩSN:uԩSN:uԩSN:uC" 'R L0a"aѢ)RH"(H01:SN@AE-bhQ#Fx!P Ba"?wpC;w܁8wsNw N8P@Q&@)ңG=D,ꄉNH)Ң<"qѩN\4PD!*P0l:uԩSN:uԩSN:uԩSN:uԩSq H12Ą 7%XPH"UTFH"EğN@0EQ@HÈѢE-!!Bᄘ;~gH;e2sΝ;wܹ8w\=~Ǐ D`C(HSN\xaHX@TI&P:u! MDUfPD"$`ԩSN:uԩSN:uԩSN:uԩSN:N'!x'&L0aH"E)R$&h"E"t"?CJ8ϙ!4u( o5ZԈB?~N< ܁ ;p܁s8wI3Ν;QbǏ;~ AxSN:uIOhS':u'S"(*TB%JnN:uԩSN:uԩSN:uԩSN:u)1D"A 0a”)S"E)RH")RfxR" NDtԩSN:]RDB1N<~g?~sN ws8w܁sF;pܙ?}㇏?|iǙN:uԩSN}s?wΝ;wrΝ;w܁s;sN{?}sN&=pqS'P:u(PBaB! h%J ( !QD%*SN:uԩSN:uԩSN:u)N:uTD 45\ԀQN .(ɏ1d'E:u ՉN:uԩSN8Ń4}s?ws ws';wsΝ;wrΝ;t8Ǐ;~sO?"%R!/'`xԩSNpxԩSNw`8(P N^Nad*P~QB H=l:uԩSN:uԩSN:uԩSN:uԩS%J(Q}202eF2D*@ ,JAĄO@u թS/:uԩSI^\) ~8s?ws?z@ܹ8wD;ws8ws ?w?w`A p"E QugP: SQNadʔ)SN2U)S@A@@ԩSN:uԩSN:uԩSN:uԩSN:%L(QDQ 2P"C8J?>} T'PuxtԩSH^\)RHJ"t感;~玟;~`q;wܹ;wܹsN wܹΝ;%?w !Bɰ(RH"EDE~&` S?Q`@S"SL2UN)SL* mN:uԩSN:uԩSN:uԩSN:u)8Dd &Sb2 8в($:u )(PB-ЩSH^\)RH"1!BLxs?~gB;wܹsΝ As;w&ܹs;wIsO?}O /-"D̉H"E)RHwp1ϟ%`eʔ)LPN eʔDe LH8uԩSN:ԩSN:uԩSN:uԩSN"L(SL2e)SL2eO ` S(P@}BH^\)RH"EB"?wɓ/.ܙs;ppN;w܉;p܁sXs>c`"D!B)RH"E)8hϟPL2eOLbXD%*SN:uԩSN:uԩSN:uԩSN:ujC02eTL"eTPL* 1` 9.Nx)RH"E)R$/C"D!BsO~!B"DH"a)RHLED;w( (PyrK L^jt2%J(QLx:uԩSN:uԩSN:uԩSN:uԩSt$FSHAcʔ)ReN`2U)SL2J()RH"E)RH62!D玟;~9SΝ;pܹΝ;w$Ν(0Ν;psN ,~㇇B"DCJر)RH"ESN\( *TPBs"J(QD@ԩSN:uԩSN:uԩSN:uԩSN:(QԉP0(cʔ)S@!,w2ȅH"E)RH"Eq!B"DЙ~2wܹs;wŝ;wdHs8wܹs;w6 "D!BsbQH"EN:DGĝPD JTPB1 -"%JeN:uԩSN:uԩSN:uԩSN:uԩ4D%J&(%@ 1hr'RPB KP΀)RH"E B"D"B1᧏;g28ws;܁S;wܹ8wd?~D D!B-"D3'"E)B}(P*T(QB(QD-)Q$8uԩSN:uԩSN:uԩSN:uԩSNX$J(RD;B *T(QC *8:u "E)RH"dH!B"D ,~QΝ;w܁sΝ;w)SΝiܹs;w܁sΝ;R!B"D!B#RHQ\t TN:4B QD%JOeN0$ SN:uԩSN:uԩSN:uԩSN:uH QDIO()TPB *T&@ )TN)RH\!D!B"D!B2ĝ;pܹsΝ;wsNeds8ws;wܱ2NB"Dh!B"D^"]q)TN:SN@y!)TDEJ)QD%jQr:uԩSN:uԩSN:uԩSN:uԩSt%J~D*%J0 *TP2ԩSN:S~"ES!B"D!B"'Q܁s玝;wܹN;wdP8wܩsN;wܹs΄^"D!Bxŋ/^DL"I!2D $Dp@ dȐ!C @ԩSN:uԩSN:uԩSN:uԩSN:աOA"%J(RDꄥMBm'TPBu թӧO@]r$ 8"D!B"D!Bg@0J)QD% .\pC.`hРA 4hA @Æ 2r,QH1ZEZ$ԩSN:uԩSN:uԩSN:uԩSN:uԩSN:upJ,% (Q:Ǐ?~ &%F9r$ "EɀpHE+RD (ZQE"ԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN@@tC:ܰ2*VPK>2DhE-tԩSN:uԩSN:ԩSN:uԩSN:uԩSN:uԩSN:uԩSEN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:u* ;evolver-2.30c.dfsg/doc/news_10.htm0000644000175300017530000000710511410765113017164 0ustar hazelscthazelsct Surface Evolver Documentation - Newsletter 10)

Surface Evolver Newsletter no. 10

Back to top of Surface Evolver documentation.



                      Surface Evolver Newsletter Number 10
                              December 16, 1994

                    Editor: Ken Brakke, brakke@geom.umn.edu

Contents:
  Query on curvature.
  Version 1.97


Query on curvature.
  From Christian Burger <burger@pc1442.phys-chemie.uni-marburg.de>:

     I am interested in the investigation of physical systems which are
     usually highly symmetric two-phase systems separated by an interface
     which should be some kind of minimal surface. Very often, the systems
     are bicontinuous, i.e., two unconnected 3D network domains live in a
     common matrix of surrounding matter (I'm speaking in physical terms
     here, not mathematical). Naturally, I'm using the evolver with the
     torus model. The domains have defined volumes and are incompressible.
     Physical examples for such systems are micro-emulsions,
     polyelectrolyte-surfactant complexes, block copolymers, etc. It is
     likely that curvature plays an important role for these systems. If the
     structure unit building up the interface is not symmetric this leads to
     a `spontaneous curvature', i.e., a finite H_ZERO in terms of evolver
     nomenclature.  If you are aware of any work in this direction people 
     have done so far using the evolver, I'd like to get some references on it.

Version 1.97:

 Version 1.97 is now available for ftp (except Mac version hasn't
 been updated yet.)

 Changes from 1.96 are mostly a lot of obscure bug fixes.  But
 there are some new features of general interest.

 New Hessian commands: "eigenprobe value" will print the number of
 eigenvalues of the energy Hessian that are less than, equal to, and
 greater than the given value.  It is OK to use an exact eigenvalue
 (like 0, often) for the value.   Useful for probing stability.
 "lanczos value" will do a little Lanczos algorithm and report
 the 15 nearest approximate eigenvalues to the given value.  Do not
 use an eigenvalue for the value! Not real polished yet.

 The default way of factoring the Hessian is with routines from the
 Yale Sparse Matrix Package (YSMP), patched a bit to cope with zeroes
 on the diagonal.  I've been tinkering around with my own version
 of minimal degree factoring that is a little more aware of the
 surface structure.  It can be activated by "ysmp off" and deactivated
 by "ysmp on".  My version seems to have 10-20% less fill, and so
 be a little faster.  It also seems to not have as many spurious
 zeroes on the diagonal.  Further, in an attempt to cope with
 indefinite Hessians, there is a toggle "bunch_kauffman" that enables
 a version of Bunch-Kauffman factoring in my version.

 Certain named quantity attributes can now be referred to in expressions.
 For quantity qqq, qqq.value is the current value, qqq.target is 
 the constrained value, qqq.modulus is the same as the modulus in the
 datafile definition, and qqq.pressure is the Lagrange multiplier
 for a constrained quantity.  You can assign values to qqq.modulus
 and qqq.target.

 The dump command 'd' now saves almost all toggle states (except some
 debugging toggles) if not the default values.


Back to top of Surface Evolver documentation. evolver-2.30c.dfsg/doc/ringblobbare.gif0000644000175300017530000041311511410765113020317 0ustar hazelscthazelsctGIF89a,u      //0/0//000//0/000/000?????@?@??@@@??@?@@@?@@@OOOOOPOPOOPPPOOPOPPPOPPP_____`_`__```__`_```_```ooooopopoopppoopopppoppp!!5 Image generated by AFPL Ghostscript (device=ppmraw) ,,uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuq>*^uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZOuuuuui*,uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuP$ouuuuuuuuP&uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuugpuuuuh]huuuH<'*<=:[Y[iuuuuuuuuuuuu8u;uuuRAuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuug\Y^889/%8=8WZ^`uuuuuuuuuuuuuuuuuuuuu.Zuuuup*,ouuu$ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoXX^F=:/?=>HYX`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoouuuuuuuuuuJ=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuX\]F:<25<8IYX]uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYouuuuuuuuL(uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXXXJ;<;7<>DX\Xsuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`Muuuuug-Puuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`ZXP?=>&(9<:XXXguuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuB.iu>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`\[XZ]\XXY\XouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuugXX\=:>.'<;9PZX`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuRuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu9uuuuuuuuuuXXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo\YXB;8/8>8NZXbuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu9ouuuu+huuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu8uuuuuK?SuuXZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^XXA9;64;?JXXXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuLuuuuauuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu9uuuu8X?uY\uuuuuuuuuuuuuuuuXXYI=;;3>?C\XXquuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuNuuuu23`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=uuuuYu]uX]uudXXR9<<$/99=X[Xguuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`)uuue>oocuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=uuuu=X6uX"?>:RX\`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuuuuuocuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo\\X?<>+uuuuL 9\u\;8>SXX`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuub>JQDuuuuuuuuoguuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo\YZF::7uuuuguguXCuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuaouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuugG%$IsuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXZ\L?880;?G\Z]o>uuuuquouXXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuucouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu9!PouuoHAuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuue]XT>?9%-?99\YXhuuuuuuuuuuuuuu;uuuu3%Z%3uXYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuu`suuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuIuuuuuuuu@)uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuukZXX<:;' ?;:QY[huuuuuuuuuuuuuuuuuuuuuuuuuuuu;uuuuuI;JYXXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuQ\YYX\\]XX\SkuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuH.uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu&iuuuuYXZ\uuuf0uug\uuuu^Z]I9?969XuuuuX >=uuuLEuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuu)IuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoouuuuoZ`uuuouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^uuuuuuuuuuuuuuuuuuuuuuuu.HuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuQ(uuuuuuuuuo:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuu"[uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuNguuuuuu[Juuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuub\X`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuo`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuug-"<=8>.i+Juuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo63?=69ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuoCZXX\YXXXYX3uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoTZuuuuauuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYXuuuuuuT`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^uuuuuuuuuuuuuuuuuuuuuuuuuuuY\uuugXXX[`u8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXnuuuup`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`suuuuuuuuoauuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuu\XuuuM'uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuquuuuuuo`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuALuuouuu31b@JuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuu\]uuuuuu;Buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^?6guuuuuuuaouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\Xuuuuuuuuuuuuu?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuujYX\>>?.1uuuuuuuuu\YuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuZZuuuuuouuu;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuHKuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuX\uuugZuuuX>Eou>uuuuuuuuuuuuuuuuuuuuuuuuuuuuoXXXE8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuubouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuugIuuuuuuKbuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZXuuo/;uuYP?u8uuuuuuuuuuuuuuuZZXF=953>?uuuuuuuMuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuX]uuuuYVuuuu:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhkuuuuuuuupbuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXZu`;uu'Iuu\?uXYYK8?9589@X][ouuuuuuuuu"ouuuuuU88>Ig/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuuuuuuuuuuuuuuuuuuuuuuuu\^uuuuuuuuuu3uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuI,uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`uuuuo\guuukuuuuuhMouuuuuuuuuuuuuuuuuuuuuuu\\uuu8=uubouo`+?9?YY]guuuuuuuuuuuuuuuuuuuuuuuX?uuu>uuZ=899J.auuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuu`IZ\[XX\Y^Y9/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuN-uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH9uuuua 5LuuuJNuuu`@uuuuuuuuuuuuj\X\89?-Xuuu98uuuu[:u%::=QXZ`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu'\uuuuuuuuuuH6uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuN*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuN-uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu'ouuuu&Euoouuuuo:H\\A;:+Xuuu?>uuu4Xuu;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoPuuuuuuuuHauuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu0HuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuO*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuXuuu4=>HXYF]uuu=YZZXY][Z^Y\\HuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/HuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuB7uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=#oZXuuu*5;8FXXYouuuuuuuuuuuuZ\uuu<>uub;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuug1?gu9uuuuuuuuuuuuuu>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu.Muuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu)Muuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>7Zuuu'XH&@uuuuuuuuuuuuuuuuuuuuuuu\XuuugguuYX_XYo;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^uuuu0ZuuuuR>uu9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,Huuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu(Kuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`ouuguuuuuug&uuuuuuuuuuuuuuuuuuuuuuuYZuuuuuuuuuuuuu;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^uuX,Xuug4;uu;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,Juuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu(Ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu.guuuu9&u; uuuo'uuuuu`ouuuuuuuuuuuuuuuuuuuuuuugQ[\^XXZZY^]ZYYRuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuug]]uuoX,:uuuu>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu[uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoauuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu#Xuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu#`uuuuuuuuuud%uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuXuuuu<9uu>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu[uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoauuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo`uuuuuuuu`ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuXuuuu98uu9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuq`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo'(guuuug5uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuu]uuuu>8uu9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoguuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoauuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuX-%&+ZuH/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuu5`uuuuIOuu:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu]uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuaouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuqauuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuu?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuubouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuI/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZX^X\\ZXZX^XXXX8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuL*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXDXXXYZ^\X]ZYYZ2uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuM4uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuobuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXXuuuoguuuuZ`uu;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuduuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuudouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu.P\XYYXYXXXXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoduuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYXuuuA=uuu/'*u9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH,uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu&uuuuuuuuuuuuuRXbuu;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH-uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH.uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuu>%oPuu?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuudY#(P]XX7RuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZYuuu?8uu7/u;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuL+uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuJ/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?uuuuQuubu;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuua&guuuuuuo11uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXXuuu>>uuHuuc?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu@=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH+uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=uuuu'IuuXu9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuF(uuuuuuuuuuE:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXXuuu?8uu'QuuX?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu+MuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH.uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=uuWoXouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/Huuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,Nuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>uuuu9Puguu>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuTZ^\XZYZX\\Z[XEXuuuuuuuuuuuuuuuuuuuuVQuuuuuu2uuu6uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\[uuuuuuuuuuuuu>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu)Juuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu-Luuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?uuuua9Vuu=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuu`uuuuo\ouX\uuuuuuuuuuuuuuuuuuuu-Puuuuuuuuuuuuuu2guuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`NX\YR>@YYXZXX\CuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu)Kuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=uuuuuoYguuuXuuuuuuo/;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuuuugug_71[uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuup`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoduuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuGouuuuuuuuu=AuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuaX[V><;#(uuuuuuuuuu\(?9:XXXguuuuuua,ouuCTuuuuuuuuuug-uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoT\^XYY][XZouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuouuIuuuuX:Xu%iuuuuuuuuuuuuuuuuuuuuuuuuuuuuuiZXY::>/uuuuuuuuuuYMuuuuuuuuuuuuuuuuuuuuj1uuuuouuuXuuuuuXuu#buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuusouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuus7uuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuJ9uu\uuuPXTg$uuuuuuuuuuuuuuuoX\ZB<;.;8:H\Nuuuuuuu[uX_YuuuuuuuuuuuuuuuuuuuuuuuuuuuuL.uuuuuuuuuuH9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`quuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuc;;=Muu^uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu(iu4uuuUuQuuuXZ\B;<61=;HZXYuuuuuuuuuuuuXuuuuuuu9-u'8X]uuuuuuuuuuuuuuuuuuuuuuuuuuuuuN)ouuuuuuu=-uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuudouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuu?>9Huu]uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu'uuuuuuuouou83>?FYXZouuuuuuuuuuuuuuuuuuuuuuuuuXuuuu;uuuu1PuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuu.JuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:/<:9\\]guuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuu[Xuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`9;9?<;?;9=>9DuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuo&uuguu[uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuouuu^uuuuP8Zu;\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuO.uuuuuuuuuuuuuuuuuuuuuuuuuuuu=uuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuu'+\#:uuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuO/uuuuuuuuuuX/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH.uuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuouuu3 4uXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuI:*)Xuuuuuuuuuuuuuuuu>uuXDuuXXuu[Xuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`X_^ZZXYZ\J9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuQ8;<uuuYuuLauu<8]uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu+JuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuguuuu<uuMDuuuuuuuuuuuuuuud5uuo7uZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu'Zuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu[uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu<kuuuuuuuuuuuT2uuuu,guuuuuuuuuuu>uuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoauuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuP"NX'uuuuuuuo ouuuu?[uuuuuuuuuuu)XXX\XZZ]YXXYYIuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuscuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuug[XZ<:=.uuuuuuu=9uuuuu;XuuuuuuuuuuuZZYXYXY^X\]X`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuus`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoX\XC::5uuuuuuu guuuuu1auuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuobuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZXXI;??59;A[X/au5`uuuuu`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuqouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuud]YV;9:);>?YX^suuuuuuuuuuuXMuuuZ.uuuuuI;uuuuuY4uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`suuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuEYZYXXZXXX\IuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuugXXP;8<'&=89ZZXguuuuuuuuuuuuuuuuuuuuuuuuuuCauuuuu"fuuuuuuuuuuoquuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuu`Xuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuug\XX9+ '98;Q]^duuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoauuuuuuuuo-\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuueouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuu:5puXuuuuuuuuuuuuuuuuuuuuoX[^D;?3>=;JXX\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo9uuuuuuYXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`suuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuu`uu9\u^uuuuuuuZ\[H<9?'@ZX[suuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuI$<;69ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuX&uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuu>7dYuuuH,?:?X_XsuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH-uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuug^\U;<=%Zuuu://pu;$;89XYXguuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoNuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuugX]\9??)Xuuu:/uu+FuL;TYY`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuV'uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH-uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoXXYE=:79;&&9?9XXXkuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuu^uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu 6@fuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu@buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuugXXZ88:/"=;8TZX`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuN\YXXXXX][XVuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu]uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu',uugF==8Houuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo^YXD:85;?=I[YYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo\I%F//RYXZM:ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\YXH8:=5;8EZZ[ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuY`uuuuuuuZouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`XXT;><+>;:\XZouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`ouuuuuuuuu`ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuugXXP=9:$&<=;X\Yguuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuguuuuo5>;,'?8>VXX`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuub/uuuuu#=u'Fuuuuuuuuuuuuuuuuo]XY@?<68:?\ZXouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^Xo :uuu^,;;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu==uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuug=#Bsuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,>?<<==;9>8<;>*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu<8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo6&PuuuoP'8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?uuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoPuuuuuuuuB+uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu9uuuuuuu&:\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu"duuuuuuuuuuH:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuugJuuuuu!\uXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;uuuuuuYou7>\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu<?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\ZZF;<3-uuuuuNuuu'uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=uuuuuuuA$oXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXX\H:?<5??DX\Xouuuuuuuuuuu[uuuo'uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuubXYP==<'.;9?ZX\kuuuuuuuuuuuuuuuuu?Xuuuuuuu7duuu:Nuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu.?::;<<=9;;>?=-uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuugXXX>=8)'8AXXZouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuui+=993>g9SuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuub\ZT>98'+=:9X[XguuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXZXcuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuVXXXZZ\XXXXZYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuugXZ[9:>+ <=?QZY`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu%PuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuu`uuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo]YXG:=49=8IXZYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu7duuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu]uuuuuuXuuuXuuuuuuuuuuuuuuuuuuuuYXZK::72<9E\XXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?3`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu99uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuouuuXuuuuuuaXYT??>.89D\X[ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`Tp`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu]uuuuu#Xuuu,:=?\YXguuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuugouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu]uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuun\ZX99>/-uuuuL7uuuu';>6uuuh\YXou/IYXXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuaouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuugL5`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZ\XXX9;Y\\]X[X:?96=:BJuuug=<>8guXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuubquuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuug+N\\V,ZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuubXX+uuuuuuuuuuuuuu-<;8[YXsuuuuuuuuuuZuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuHkuuuuuuo/>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuug[XX:=9$uuuuL`uuuuo uu??=X\XguuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuu9uuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuO/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuX'uuuuuuuuuu9Duuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo\XX:9<)uugL\uuuu1uu]uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuI.uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuouuuuuuNuuuu&guuuuW`uuuuuuuuuuuuuuuuuuuuuoY[YF??7>:=HZZXuuuuu:uuY=[uuuP3uu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYYXZXX\XZXXX`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuJ*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuCKuuuuuuDuuuuk.uuuABuuuuuuuuYX^L;>?689DZ^Youuuuuuuuuuuuuuuuuu9uuuuYuuoouuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuJ/uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuu$ouuuuuuuuuugH6:'.:=8ZZXguuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu8uuuuXuu`uuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuK6uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuu'uuuuuuuXuuuuu3%:<:T[Xguuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>uuuuXuX`Yuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,KuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuXuuuuu:7>=SXY`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>uuuuZuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/Nuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuu[uuuuu0goYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;uuuuXuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu-IuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuu'uuuuuuuYuuuuuuuuuZ)uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;uuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu.OuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuu@WuuuuuuXuuuug'uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?uuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu#Xuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuouuuuuus`uuuu*XuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuugX\\Z/.XXZXXXYouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuL/uuuuuuuuuuH)uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoeuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuA(ouuuuuuuE`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu<?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuR2XYXYF@buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=Quuobuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoZuuuuuuuuuu[uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu9;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuu50uuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuusquuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu89uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuKuuduXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu<>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuXXuu\u\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuubouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu]uuuuu='uuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuaouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu8?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuu`RXuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuubouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuNduu<>uXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuufuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuG1uuJuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuL.uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/Luuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu]uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=Fuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu-JuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu.Huuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu9=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu*KuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu*Huuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu9;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu+IuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu)Kuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu==uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu<:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuq`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuudouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuL/uuuuuuuuuuuuuuuuuuuuuuuuuuuuu^uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuT*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu8;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuJ)uuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuM.uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuK/uuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH+uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu=>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuK6uuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuL.uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/KuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuJ*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu-IuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu3Muuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu.Huuuuuuuuuuuuuuuuuuuuuuu^uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu*Iuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu::uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo`uuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoduuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu98uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuobuuuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu99uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuupouuuuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuudouuuuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuohuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu<:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuufouuuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuubXX\YX\ZX\\XPuuuuuuuuuuuuuuuuuuuuuuuuuuu<>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuaouuuuuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuWuuuuuuokuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuu>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuubuuuuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^uuuub:uuuZuuuuuuuuuuuuuuuuuuuuuuuuuuu=?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuI+uuuuuuuuuuuYuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuZuuuuuu<;uuuXuuuuuuuuuuuuuuuuuuuuuuuuuuu9>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuJ/uuuuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuuu=?uuuXuuuuuuuuuuuuuuuuuuuuuuuuuuu?>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuM.uuuuuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuu?>uuuXuuuuuuuuuuuuuuuuuuuuuuuuuuu=;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH)uuuuuuuu\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuYuuuuuu>?uuu\uuuuuuuuuuuuuuuuuuuuuuuuuuu<uuu\uuuuuuuuuuuuuuuuuuuuuuuuuuu<>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu+IuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuXuuuuuuXXuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuu>:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu+NuuuuuZuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\uuuuuuuuuuuQuuuuuuuuuuuuuuuuuuuuuuuuuuu;'5X\XXL2puuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu(Huuuuuuuuuuuuuuuuuuuuuuuuu8?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuhuuuuuuu\ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/Juuuuuuuuuuuuuuuuuuuuuuuu9=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu_uuuuuuuuuubuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`uuuuuuuuuuuuuuuuuuuuuuu=8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu'puuuu` :uuuJ9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuobuuuuuuuuuuuuuuuuuuuuuu<;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`6uuuuo]oLuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuq`uuuuuuuuuuuuuuuuuuuuu>:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu^0ouuu2auuuud7uu<9uuu2buuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuobuuuuuuuuuuuuuuuuuuuu>;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuR:duuuuuuuua:cuuuHPuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoauuuuuuuuuuuuuuuuuuu9:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuugXYT>==uuuuuuuZ9QuuuY?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoquuuuuuuuuuuuuuuuuu<=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuugXZX9>9-uuuuuhiuuUuuuJTuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuaruuuuuuuuuuuuuuuuu:9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoXXX@<8599?\?ZuuuuVuuM'uuu*`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`puuuuuuuuuuuuuuuu;YXXYXX\XX\>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`XXT=>=!,<9>ZZYouuuuuuuuuuuuuuuuuuuuuu-?uuuuu auuuuuYXouuu?uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu>uuuuuuuuuu8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuugXZ];:?%%<>>V\^guuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuupouuuuuuuuuH(uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuT/uuuuuuuuuuuuuu?:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuuJou>uuuuuuuuuuuuuuuuuuuuuuuuoY]X89;-=;;RYZ`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuud[uuuuuuu:/ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH/uuuuuuuuuuuuu9:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu8uuuq`u<-u8uuuuuuuuuuoXXXG:>4<9?LXX\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo2A\XX7LuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH)uuuuuuuuuuuu=:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuub7uuPu?H==;7?=FXZYpuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoP?:FauuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuI/uuuuuuuuuuu?>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`_]S98uuuuuusDu?/?8:XXYguuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuH/uuuuuuuuuu>>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuum^^_::<+?uuuuuFDuu8%>;=SXZguuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu3Nuuuuuuuuu:8uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo\^XA<;.=uuuu+)ouuu9uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,Mu'AZ\Xouuuuuuuuuuuuuuuuuuuu?uuuH0u=uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu/uuuuu>:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuub_XP?>:#/>;?XYYguuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu:uuuuuuuuuu:uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuc;Puuuu8>uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuiZZX;=>.'9=>P]Yauuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu.XXX]]XXYY\*uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?-uuuh++guuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuoY\XA:92>>>HXYfuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuug55<8FX^XuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuI:ouuuuuo8%ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`\[K8??-;>AXX\ouuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuugKuuuuuuuuuXuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuaXXT8=; +;8=X\^guuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuLAuuuuoD?YuuuG;uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuug]XY9<=)':;:SZY`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu-STuuu%ouuuuuuuuuuuuuuuuqYYXE?>3;48;FYX\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?\uuuuo`uu>'uuu])<8\uuuuua=ouuuuj/`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuIHuuuuoduuuuuuPCuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuo#uuuuJ4uuu1`uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu)auuug_XYXduuguuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuiquuuuuuuuuuauuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu`ouuuuuuuo#Luuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuub"8`uuuk9auuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuX/-Suuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;evolver-2.30c.dfsg/doc/addload_example.fe0000644000175300017530000000611311410765113020613 0ustar hazelscthazelsct// addload_example.fe // Demonstration of using the addload command to load multiple // copies of the same file. Basic surface is that of mound.fe, // a liquid drop on a plane. // Each separate mound will have its own contact angle, implemented // by having a contact angle attribute for each edge. // The "setup" script below invokes addload multiple times on this same // datafile, moviing each instance to its appropriate location. // Note that addload does not execute the "read" section of a datafile, // so such "setup" scripts are ru only once. // Programmeer: Ken Brakke, brakke@susqu.edu define edge attribute angle real // interior angle between plane and surface, degrees gravity_constant 0 // start with gravity off #define WALLT (-cos(angle*pi/180)) // virtual tension of facet on plane constraint 1 /* the table top */ formula: x3 = 0 energy: // for contact angle e1: -(WALLT*y) e2: 0 e3: 0 vertices 1 0.0 0.0 0.0 constraint 1 /* 4 vertices on plane */ 2 1.0 0.0 0.0 constraint 1 3 1.0 1.0 0.0 constraint 1 4 0.0 1.0 0.0 constraint 1 5 0.0 0.0 1.0 6 1.0 0.0 1.0 7 1.0 1.0 1.0 8 0.0 1.0 1.0 9 2.0 2.0 0.0 fixed /* for table top */ 10 2.0 -1.0 0.0 fixed 11 -1.0 -1.0 0.0 fixed 12 -1.0 2.0 0.0 fixed edges /* given by endpoints and attribute */ 1 1 2 constraint 1 /* 4 edges on plane */ 2 2 3 constraint 1 3 3 4 constraint 1 4 4 1 constraint 1 5 5 6 6 6 7 7 7 8 8 8 5 9 1 5 10 2 6 11 3 7 12 4 8 13 9 10 no_refine fixed /* for table top */ 14 10 11 no_refine fixed 15 11 12 no_refine fixed 16 12 9 no_refine fixed faces /* given by oriented edge loop */ 1 1 10 -5 -9 2 2 11 -6 -10 3 3 12 -7 -11 4 4 9 -8 -12 5 5 6 7 8 7 13 14 15 16 no_refine density 0 fixed /* table top for display */ bodies /* one body, defined by its oriented faces */ 1 1 2 3 4 5 volume 1 density 1 read setup := { // Read in 3 x 3 arrangement of drops. The first has already been // read in, so we need to read in 8 more copies of this datafile. // We use a vertex attribute to distinguish between old and new // elements; for more elaborate massaging of new input you could // use more attributes on more types of elements. define vertex attribute load_marker integer; for ( row := 0 ; row < 3 ; row += 1 ) for ( col := 0 ; col < 3 ; col += 1 ) { if ( row > 0 or col > 0 ) then addload datafilename; // Move and mark vertices foreach vertex vv where load_marker == 0 do { vv.x += 2*row; vv.y += 2*col; vv.load_marker := 3*row+col+1; // different loads get different marks }; }; // Now assign contact angles to contact line edges foreach edge ee where on_constraint 1 do ee.angle := 90 + (ee.vertex[1].load_marker - 5)*10; } // end setup setup // From here on, we have the usual evolution commands re := {refine edges where on_constraint 1 } // Typical evolution gogo := { re; g 5; r; g 5; r; g 5; hessian; hessian; } evolver-2.30c.dfsg/doc/twointor.gif0000644000175300017530000032216611410765113017561 0ustar hazelscthazelsctGIF89aa,}    ###+++//0/0//000//0/000/222778787877;;;??@?@??@@@??@?@@@?DDDKKKOOPOPOOPPPOOPOPPPOTTT[[[__`_`__```__`_```_bbbkjkoopopppoopopppopppwxw|||!!5 Image generated by AFPL Ghostscript (device=ppmraw) ,a,}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y,8Us}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}k ;cz}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}U>sy\6 &=c}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}7jyyyyyyyrT6-Fc}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y& syyyyyyyyyyyyyjL/5Mo}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}n 'yyyyyyyyyyyyyyyyyyyyj>' :Ws}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}VLyyyyyyyyyyyyyyyyyyyyyyyyyy\>>cy}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}5cyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyys\6 ">c}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyykT6'Fd}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}q .yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyjL/5Mk}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}TLyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyj>' 8gy}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|wyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyys\7 &:c}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}o/yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyykT6.l}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}SLyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyjL/G}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}- jyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyL0 &y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}ysyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy/Tja&d}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}k6yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyx]jjjj>;}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}STyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyj&jjjjjjjUu}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}- jyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyL7jjjjjjjjjj.\}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y syyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy/TjjjjjjjjjjjjL5z}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}g6yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyys]jjjjjjjjjjjjjj^ k}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}I\yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyj&jjjjjjjjjjjjjjjjjj/J}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y) ryyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyL>jjjjjjjjjjjjjjjjjjjjM !{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}t'syyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy' Qjjjjjjjjjjjjjjjjjjjjjj] m}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}c>yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyu ajjjjjjjjjjjjjjjjjjjjjjjjj79}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}I\yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyj.jjjjjjjjjjjjjjjjjjjjjjjjjjjjU y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y+ ryyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyL@jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj&c}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}k'wyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy' RjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjL5}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}WLyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyu ]jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjja t}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}e/Lryyyy|yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy].jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj.M}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}k' 6Twyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy>LjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjM)|}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}z L]\\U>'6]syyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyys' Ujjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj`k}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y 4\\\\\\]]\M7'  '>\yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyo ajjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj7E}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}&.\\\\\\\\\\\\\]^M5! /Ljyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy\2jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjT $|}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}'\\\\\\\\\\\\\\]\\\\\\L5 6Tryyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy>Ljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjja&c}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}N\\\\\\\\\\\\\\\\\\\\\\\]\\\D.6\wyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyw' Ujjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj>>}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}N U\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U>'6\yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyr ajjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjU y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}kU\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]]]M7'  '>\yyyyyyyyyyyyyyyyyyyyyyyyT/jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj.U}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}sL]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\L4 /Ljyyyyyyyyyyyyyyyyyy6OjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjL1y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y>\]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]\L. 6Tryyyyyyyyyyys ]jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjal}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}4\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]\\\\\]\>'6\syyyyyj jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj/Q}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}(]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]\UD' >\T7jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjT y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}7\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]\\\M7' Tjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj_q}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}Q U\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]\\]]L.4jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj7;}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}k U\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjU y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}oL\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj&h}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y L\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'UjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjC5}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y 5]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\@7jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjaw}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}..]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj.T}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}5\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\] jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjM*y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}O ]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\^jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjar}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}U U\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]7>jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj/W}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}kM]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U&jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjD}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}yL\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjja}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y7\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj.t}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}5]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]5LjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjLN}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}'\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\M.jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj,}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}I\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}M U\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj&y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}oU]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\.OjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjAU}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}kL\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]@/jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjja5}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}yD]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} 1\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}-'\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'Ujjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj7e}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}1]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\7>jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjU7}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}O \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U&jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj&}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}pSo}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}c U\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\] jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}U1Ok}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}kM]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\!^jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj/m}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}/.>' ;Ys}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y L\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\4LjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjLE}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}yyyyyyy\6 7dy}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y5\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]M.jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj.}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}l /yyyyyyyyyyys\6 ,Kc}}}}}}}}}}}}}}}}}}}}}}}}}}} .]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]\jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}NLyyyyyyyyyyyyyyyyykT/'Ql}}}}}}}}}}}}}}}}}}}}}'\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj.s}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}/cyyyyyyyyyyyyyyyyyyyyyyyjL/ /Ts}}}}}}}}}}}}}}}T\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\.SjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjL\}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}{yyyyyyyyyyyyyyyyyyyyyyyyyyyyyy\>' ;[s}}}}}}}}}M U]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\L/jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjb/}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}r /yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyys\6&9gy}}}oU\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}MLyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyysT6*<L\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\] jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj&y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}) iyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyylL/>\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]'Ujjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj7c}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|syyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy5]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]D7jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjU>}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}k6yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy']\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}STyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy6\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}' jyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyL U\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\!^jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj/l}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}ysyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyj U\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\7@jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjRG}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}f6yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyjL\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U.jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}HTyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyytL\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y( lyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'5]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj.s}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}s'tyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyt?]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\5LjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjLT}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}d>yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyB.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\L.jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj'}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}H\yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}z' ryyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyT& ]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj&y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}s &y{yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyLP\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\].MjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjCW}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}U>yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyrM55\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\B7jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjja1}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}<\yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy67MM\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|% wyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyMMM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}o 'yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyL.MMMU\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]'Ujjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj7g}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}ZLyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyLMMM/7]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\7@jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjN8}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}7jyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyymMMMME\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]T&jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj&}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}->jyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'BMMMMM ]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]\ jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}'/Lryyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyys MMMMMM\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\&jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj/k}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}M \\>. 6Tr|yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyL.MMMMMM.@]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\5LjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjLG}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}Y U\\]\\]U>'6\syyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy MMMMMMM>']\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\M.jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj)}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}kU]\\\\\\\\\]]U7' '>\yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyjMMMMMMMM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}sL]]\\\\\\\\\]]\\]]]M7 'Ljyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'>MMMMMMMM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj.s}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y7\\\\\\\\\\\\\\\\]\\\\\\\L2 /LqyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyysMMMMMMMMM&L]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\.Tjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj>U}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}5\\\\\\\\\\\\\\\\\\\\\\\\\\\]]]>. 6Tkyyyyyyyyyyyyyyyyyyyyyyyyyyy>/MMMMMMMMM6.]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\L/jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjja2}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}.]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]\\\]U>'6\syyyyyyyyyyyyyyyyyyyyyyMMMMMMMMMMM]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}7]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]\\]]U7' '>\yyyyyyyyyyyyyyyyy\MMMMMMMMMMM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]] jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj&y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}M \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]\\M7 'LjyyyyyyyyyyyyKMMMMMMMMMMMM]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'Ujjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj7c}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}i U\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]]]\\]]L5 .LryyyyyyrMMMMMMMMMMMM57\]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\>7jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjU>}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}mL\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]\\]>. 6Try65MMMMMMMMMMMML&\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y L\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]]UB'MMMMMMMMMMMMMM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y 5]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U7' MMMMMMMMMMMMMMU\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Ujjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj4o}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}".]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ &MMMMMMMMMMMMMM'>\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U>jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjMK}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}+\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\& MMMMMMMMMMMMMM>'\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U _jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj&}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}L]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]77MMMMMMMMMMMMMM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]&>7 Mjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}Q U\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\MMMMMMMMMMMMMMM]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\>>>>1jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj.s}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}kT\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\] MMMMMMMMMMMMMM&L\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]. >>>>>>/bjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjLM}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}kL]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]MMMMMMMMMMMMMM7.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\47>>>>>>>7Ljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj.}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}yB\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]].6MMMMMMMMMMMMMM\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\L7>>>>>>>>>>'.jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}/]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\L&MMMMMMMMMMMMMM ]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\M/>>>>>>>>>>>>5`jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj&z}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}},']\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\MMMMMMMMMMMMMMM\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U .>>>>>>>>>>>>>>>>jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjD\}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}5\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\MMMMMMMMMMMMMM65]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ &>>>>>>>>>>>>>>>>>.&jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj_9}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}M \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'>MMMMMMMMMMMMMM]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\>>>>>>>>>>>>>>>>>>>7 NjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjM t}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}X U\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\>.MMMMMMMMMMMMMM ]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]'>>>>>>>>>>>>>>>>>>>>>>7jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjC#}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}rM\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\MMMMMMMMMMMMMMU\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/>>>>>>>>>>>>>>>>>>>>>>>>/]jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj.Y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}yL]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ MMMMMMMMMMMMMM/7\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\@7>>>>>>>>>>>>>>>>>>>>>>>>>7 Tjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjja> }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}z7]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\LMMMMMMMMMMMMME\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\L/>>>>>>>>>>>>>>>>>>>>>>>>>>>>&/jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjT >5I}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}1\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]7/MMMMMMMMMMMMMM ]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U/>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>/^jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj>8Mz}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}-\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\UMMMMMMMMMMMMMM]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U &>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>Ljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj.'6M>'}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}E]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ MMMMMMMMMMMMMM+B\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>'.jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjja/7LMc}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}Q U]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\MMMMMMMMMMMMMM>'\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>@5 UjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjM55LMM}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}kU\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/6MMMMMMMMMMMMMM]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\1 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>@jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj715LMM.O}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}mL]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\MMMMMMMMMMMMMMM\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\77>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>@.&ajjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj&'55DMMM y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y L\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\] MMMMMMMMMMMMMM&L]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]\L7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7 Pjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjja/45DMMM73}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y4\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]MMMMMMMMMMMMMM5.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]M/>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>1jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjM555:MMMMs}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}-.]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\.7MMMMMMMMMMMMMM\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>/]jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj75552LMMML}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}-]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\L&MMMMMMMMMMMMMM \]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\] &>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7Tjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj&'55555DMMM&U}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}M \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\MMMMMMMMMMMMMM#M`\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>&.jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjb /155555>MMM }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}\ U\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ MMMMMMMMMMMMMM/7\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\.>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>5]jjjjjjjjjjjjjjjjjjjjjjjjjjjjO15555555>MM57}}}}}}}}}}}}}}}}}}}}}}}}}}}}}mU\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'>MMMMMMMMMMMMMK%]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]57>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>jjjjjjjjjjjjjjjjjjjjjjjjjj7555555555>MMt}}}}}}}}}}}}}}}}}}}}}}}}}}}}sL\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\B+MMMMMMMMMMMMMM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\L7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>.&jjjjjjjjjjjjjjjjjjjjjjjj'1555555555,}}}}}}}}}}}}}}}}}}}}}}}}}}}}y 7]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\UMMMMMMMMMMMMMMU\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\L/>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7 Ujjjjjjjjjjjjjjjjjjjja /555555555557Md}}}}}}}}}}}}}}}}}}}}}}}}}}}}4\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\] MMMMMMMMMMMMMM'>\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U />>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7jjjjjjjjjjjjjjjjjjP555555555555/7M}}}}}}}}}}}}}}}}}}}}}}}}}}}}']\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\] LMMMMMMMMMMMMM>'\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]U &>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>/bjjjjjjjjjjjjjjj7555555555555557-S}}}}}}}}}}}}}}}}}}}}}}}}}}}>\]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]75MMMMMMMMMMMMMM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7 Mjjjjjjjjjjjja'5555555555555555 y}}}}}}}}}}}}}}}}}}}}}}}}}}M \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\MMMMMMMMMMMMMMM\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\!>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>&/jjjjjjjjjjU /5555555555555415'}}}}}}}}}}}}}}}}}}}}}}}}}}e U\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]] MMMMMMMMMMMMMM&L\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\1>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>/ajjjjjjjL/55555555555555411w}}}}}}}}}}}}}}}}}}}}}}}}}kL\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\MMMMMMMMMMMMMM7.^\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\A7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7Ljjjjj/&5555555555555555554-}}}}}}}}}}}}}}}}}}}}}}}}}yL\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\.6MMMMMMMMMMMMMM]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\L5>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>'.jja.555555555555555551/ }}}}}}}}}}}}}}}}}}}}}}}}}y5\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\L&MMMMMMMMMMMMMM ]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U/>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>5 L /55555555555555555/.y}}}}}}}}}}}}}}}}}}}}}}}}}&.]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]MMMMMMMMMMMMMMQ\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U &>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>5555555555555555555's}}}}}}}}}}}}}}}}}}}}}}}}}.]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\MMMMMMMMMMMMMM54]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\&>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>6&/555555555555555555/k}}}}}}}}}}}}}}}}}}}}}}}}}M]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]'>MMMMMMMMMMMMMM\]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>75555555555555555551/M}}}}}}}}}}}}}}}}}}}}}}}}}}7\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\>'MMMMMMMMMMMMMM5M]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]. >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 55555555555555555555 P}}}}}}}}}}}}}}}}}}}}}}}}}}c\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\MMMMMMMMMMMMMM '7M\\\\\]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]57>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>55555555555555555555 '}}}}}}}}}}}}}}}}}}}}}}}}}}}- ]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ MMMMMMMMMMMMM.''DU]]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\L7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> /5555555555555555555$}}}}}}}}}}}}}}}}}}}}}}}}}}}y ]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\%LMMMMMMMMMMMM......'.>]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\M/>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>&/5555555555555555555z}}}}}}}}}}}}}}}}}}}}}}}}}}}G&>\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\7/MMMMMMMMMMM7 ...........&  4L]\]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U .>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>./1555555555555555555' y}}}}}}}}}}}}}}}}}}}}}}}}}}}} 45'\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\UMMMMMMMMMMM&................  #7M\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ &>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>/'5445555555555555555'k}}}}}}}}}}}}}}}}}}}}}}}}}}}}U57 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ MMMMMMMMMMI......................  '7U]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7'5555555555555555555/l}}}}}}}}}}}}}}}}}}}}}}}}}}}}}&/57\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]MMMMMMMMMM&..........................''B\\]]\\\\\\\\\\\\\\\\\\\\\\\\\\\'>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>75555555555555555555/S}}}}}}}}}}}}}}}}}}}}}}}}}}}}}s 557&L]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]45MMMMMMMMM................................& .>]\\]\\\\\\\\\\\\\\\\\\\\\4>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 55555555555555555555 =}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}/'5577.]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]OMMMMMMMM/.....................................  4L\\\]\\\\\\\\\\\\\\]@7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 5555555555555555555/+}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}5555M]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ MMMMMMMM'.........................................."  '7M]]\]\\\\\\\\\L/>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>& 55555555555555555555}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}M5555M ^\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\MMMMMMM> ................................................  '>U\\\\\\\U/>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>&/5555555555555555555y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} 55555MM\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\.7MMMMMM&....................................................' .>\\U &>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>//5555555555555555555's}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}r5555LM55\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\L&MMMMMM..........................................................& >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>/'5555555555555555555.m}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}..555DMMF\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]MMMMM................................................................& >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7&5555555555555555555/W}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y 555BMMMM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ MMMMM ....................................................................& 7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>55555555555555555553N}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}G55>MMMMMU\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'>MMM7 ......................................................................'>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>55555555555555555555 5}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} 557MMMMMM/D\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]>'MMM&.......................................................................>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 55555555555555555555*}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}j/6LMMMMMM>'\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\UMML.........................................................................'/>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>&55555555555555555555|}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}/5MMMMMMMMM ]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ MM&...........................................................................&>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>./5555555555555555555' y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}t 5LMMMMMMMMM\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]%MM.............................................................................& 7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>/.5555555555555555455'k}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}7&HMMMMMMMMMM&B\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]755..............................................................................' />>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7'1555555555555555541/k}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}DMMMMMMMMMMM7.]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\M5'................................................................................>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>75555555555555555555/M}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}N'MMMMMMMMMMMMM\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]. ..................................................................................& 7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 55555555555555555555 E}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}>MMMMMMMMMMMMM\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\] .................................................................................... '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 55555555555555555555 .}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}MMMMMMMMMMMMMMM\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\_.......................................................................................>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> /5555555555555555555%}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} MMMMMMMMMMMMMM54]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\&......................................................................................'/>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>&/1555555555555555554 z}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}L7MMMMMMMMMMMMMM\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' .......................................................................................&>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>//5555555555555555555'{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}d*MMMMMMMMMMMMMM ]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\477 ......................................................................................" 7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>/'5555555555555555555.k}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}MMMMMMMMMMMMMMU\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]>7>>>......................................................................................' />>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7'5555555555555555555/g}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}MMMMMMMMMMMMMM/7\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]L/>>>>>/ '......................................................................................>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>755555555555555555555M}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}7>MMMMMMMMMMMMMI%\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U/>>>>>>@7&......................................................................................&7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>55555555555555555555 5}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}V/MMMMMMMMMMMMMM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U &>>>>>>>>>>&....................................................................................... .>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 55555555555555555555+}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}yMMMMMMMMMMMMMM\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\>>>>>>>>>>>>5'......................................................................................>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>&/5555555555555555555}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}MMMMMMMMMMMMMM+B\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\>>>>>>>>>>>>>>>......................................................................................'5>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>'/5555555555555555555&y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}/MMMMMMMMMMMMMM>'\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]4 >>>>>>>>>>>>>>>>>. .......................................................................................&>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>//1555555555555555555's}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}Q5MMMMMMMMMMMMMM ]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\47>>>>>>>>>>>>>>>>>>7 ......................................................................................&7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>5'5555555555555555555/k}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}tMMMMMMMMMMMMMM\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]L7>>>>>>>>>>>>>>>>>>>>>......................................................................................'/>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>75555555555555555555/M}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} MMMMMMMMMMMMMM&L\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\L/>>>>>>>>>>>>>>>>>>>>>>>/ '......................................................................................>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 55555555555555555511 O}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} MMMMMMMMMMMMMM7.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U .>>>>>>>>>>>>>>>>>>>>>>>>>7......................................................................................! 7>>>>>>>>>>>>>>>>>>>>>>>>>>55555555555555555551 -}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}G7MMMMMMMMMMMMMM]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ &@>>>>>>>>>>>>>>>>>>>>>>>>>>>&....................................................................................... .>>>>>>>>>>>>>>>>>>>>>>>> 55555555555555555555#}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}k&MMMMMMMMMMMMMM ]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>/'......................................................................................>>>>>>>>>>>>>>>>>>>>>>&/5555555555555555555y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}MMMMMMMMMMMMMMM\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>......................................................................................'6>>>>>>>>>>>>>>>>>>>./4455555555555555555' y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}MMMMMMMMMMMMMM55]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\5>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>' .......................................................................................&>>>>>>>>>>>>>>>>>/'1155555555555555555'o}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}7>MMMMMMMMMMMMML]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\>7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7 &......................................................................................&7>>>>>>>>>>>>>>7'5555555555555555551/r}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}h'MMMMMMMMMMMMMM ]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\L5>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>......................................................................................'/>>>>>>>>>>>>75555555555555555555/S}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}MMMMMMMMMMMMMMU\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U/>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>/ '......................................................................................>>>>>>>>>>> 55555555555555555555 >}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}MMMMMMMMMMMMMM/7\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U &>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7 ...................................................................................... 7>>>>>>>> 55555555555555555555'}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}5EMMMMMMMMMMMMM>'\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ &>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>&......................................................................................' .>>>>>>& 15555555555555555553}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}W/MMMMMMMMMMMMMM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>/'......................................................................................>>>>&/555555555555555555/z}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}sMMMMMMMMMMMMMM\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\.>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>......................................................................................&5>//145555555555555555'5}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} MMMMMMMMMMMMMM&>]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\17>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>' .......................................................................................'514555555555555555Q}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}+MMMMMMMMMMMMMM7.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\L7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7 &......................................................................................&555555555555555555 k}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}N5MMMMMMMMMMMMMM\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]L/>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>......................................................................................55555555555555555/y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}kMMMMMMMMMMMMMM]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U/>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>/ '..................................................................................../555555555555555/'=}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}MMMMMMMMMMMMMML\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U &>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7 !..................................................................................' 5555555555555555Z}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}MMMMMMMMMMMMMM55\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>................................................................................. '555555555555555 r}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}>7MMMMMMMMMMMMMM\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>/'...............................................................................55555555555555/ y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}c'MMMMMMMMMMMMMM ]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]/ >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7.............................................................................5555555555555'7}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}MMMMMMMMMMMMMMU]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\77>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>'............................................................................5555555555555\}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}MMMMMMMMMMMMMM/7\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\L7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>5 &.........................................................................&555555555555 m}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}8FMMMMMMMMMMMMMI\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]M/>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>........................................................................ .5555555555.$y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}U/MMMMMMMMMMMMMM ]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U '>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>. '.....................................................................' 5555555555&L}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}zMMMMMMMMMMMMMM]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ &>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7 &...................................................................555555555c}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}MMMMMMMMMMMMMM+B\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>.................................................................55555555/ u}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}-MMMMMMMMMMMMMM>'\\\]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7 #...........................................................5555555.)y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}M5MMMMMMMMMMMMMM ]]]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\57>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> /' &....................................................../555555&E}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}yMMMMMMMMMMMMMM7M]\\\]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]>7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> /555555& '................................................' 555555j}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} MMMMMMMMMMMMMM '7U\]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]L/>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>& 555555555555/& '...........................................'/555/ s}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}MMMMMMMMMMMMM5''>U\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\T/>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>&/55555555555515005.  .......................................5555'.y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}E7MMMMMMMMMMMM&.....& .C]]]\]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\U &>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>//1555555555555555555/csP/ .................................555M}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}k&MMMMMMMMMMML ...........&  "/L\\]]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>5'41555555555555555553T}}}}}}qE-&............................/55k}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}MMMMMMMMMMM&................&  $7M]\]\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7&54455555555555555555 <}}}}}}}}}}}}cL&'......................&5/ y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}MMMMMMMMMMM......................  '7U\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\1 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>55445555555555555555*}}}}}}}}}}}}}}}}}yj;&.................. .'.}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}9>MMMMMMMMM/..........................''DU\\]\\]]\\\\\\\\\\\\\\\\\\\\\\]57>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>55555555555555555555}}}}}}}}}}}}}}}}}}}}}}}sU; ............''Q}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}g'MMMMMMMMM'...............................& .>\\]\\\\\\\\\\\\\\\\\\\\\]L7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 55555555555555555555&z}}}}}}}}}}}}}}}}}}}}}}}}}}}}kT/ &.......m}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}MMMMMMMM> .....................................&  4L]]\]\]\\\\\\\\\\\\\L/>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>&55555555555555555555'w}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}cL,&..y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}MMMMMMMM.........................................."  7M]\\\\\\\\\\\\U .>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>./5555555555555555555/k}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}yc>  0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}3LMMMMMMM................................................  '7U]]\]\\\\ &>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>/'5555555555555555555/R}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}yY8M}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}U/MMMMMM*....................................................''>U\\>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7'5555555555555555555/ L}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}xMMMMMM '.........................................................& >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>755555555555555555555 *}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} MMMMM> ...............................................................& 7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>55555555555555555555%}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}.MMMMM ...................................................................'/>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 55555555555555555555y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}N5MMML......................................................................&>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>&45555555555555555555'y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}k&MMM........................................................................ 7@>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>'/5555555555555555555.m}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}MMM.........................................................................' />>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>//1455555555555555555/e}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}&MM7 ...........................................................................>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>5'45555555555555555555N}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}77M&............................................................................& 7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>711555555555555555555 2}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}c/L............................................................................... .>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 55555555555555555455.}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}&................................................................................>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 55555555555555555544}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}..................................................................................'5>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> /5555555555555555555'|}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}/ ....................................................................................&>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>&/5555555555555555555'l}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}W'.....................................................................................!7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>//1555555555555555555/k}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y.......................................................................................'/>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>/'5455555555555555555/R}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}M.......................................................................................>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7'55555555555555555555 E}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}r '......................................................................................& 7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>755555555555555555555 '}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}z/......................................................................................' .>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>55555555555555555555}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}X.......................................................................................>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> /5555555555555555555y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}s&......................................................................................& 5>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>&55555555555555555555'y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}};.......................................................................................'>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>./5555555555555555551/m}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}c '......................................................................................>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>/.5555555555555555555/V}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|&&.....................................................................................)'/>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>7'55555555555555555554S}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}J.......................................................................................&>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>755555555555555555555 (}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}o '......................................................................................& 7>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 55555555555555555555+}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y.&......................................................................................' />>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 55555555555555555555|}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}Q.......................................................................................>>>>>>>>>>>>>>>>>>>>>>>>>>>>> /5555555555555555555' y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}w'......................................................................................&7>>>>>>>>>>>>>>>>>>>>>>>>>>&/5555555555555555555'k}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}4....................................................................................... .>>>>>>>>>>>>>>>>>>>>>>>>//5555555555555555555/r}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}Y .......................................................................................>>>>>>>>>>>>>>>>>>>>>>5'14555555555555555555Q}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y&......................................................................................'6>>>>>>>>>>>>>>>>>>>7&55455555555555555555 :}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}7.......................................................................................'>>>>>>>>>>>>>>>>>>55555555555555555555(}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}o '......................................................................................7>>>>>>>>>>>>>>>/5555555555555555555}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}z&&......................................................................................' />>>>>>>>>>>>> 55555555555555555555&y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}H.......................................................................................>>>>>>>>>>>&45555555555555555555'u}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}k'......................................................................................& 7>>>>>>>>./1555555555555555555/k}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}z/......................................................................................' .>>>>>>/'5555555555555555555N}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}\.......................................................................................>>>>7'1455555555555555555'T}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}s&......................................................................................& 6>71145555555555555555U}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}>.......................................................................................'5555555555555555555 l}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}f '...................................................................................... 555555555555555555/y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y &......................................................................................&155555555555555555'>}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}F.....................................................................................45555555555555555U}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}l '...................................................................................55555555555555555 r}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y,&.................................................................................555555555555555/y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}O................................................................................ .55555555555555&>}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}v '.............................................................................& 5555555555555/c}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}0............................................................................&555555555555/ s}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}U ...........................................................................555555555555..y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y&........................................................................55555555555&F}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}7......................................................................./5555555555c}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}k '....................................................................& 555555555/ y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}y&'.................................................................. '55555555..y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}L..............................................................55555555E}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}vU> $........................................................5555555q}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}rM4 '.................................................../55555/ y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}cE. &............................................. 55555'.}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}yj7" '........................................ .5555M}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}zU7 ...................................'5555k}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}tW0 ..............................55/ y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}kQ5 &.........................55'.}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}gE.&...................5M}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}yi> '.............. . k}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}yW= .........' y}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}yU1 .... 0}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}kO1M}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}cK. k}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}>}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}};evolver-2.30c.dfsg/doc/biblio.htm0000644000175300017530000002235411410765113017153 0ustar hazelscthazelsct Surface Evolver Documentation - Bibliography

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Bibliography

[A] F. Almgren, "Minimal surface forms," The Mathematical Intelligencer, vol.4, no. 4 (1982), 164-172.

[AT] F. Almgren and J. Taylor, "The geometry of soap films and soap bubbles," Scientific American, July 1976, 82-93.

[AV] V. I. Arnol'd, Catastrophe Theory, 3rd ed., Springer-Verlag, 1992.

[AYC] J. Ambrose, B. Yendler, and S. H. Collicot, "Modeling to evaluate a spacecraft propellant guaging system," Spacecraft and Rockets 37 (2000) 833-835.

[B1] K. Brakke, The motion of a surface by its mean curvature, Princeton University Press, Princeton, NJ (1977).

[B2] K. Brakke, "The surface evolver," Experimental Mathematics, vol. 1, no. 2 (1992), 141-165.

[B3] K. A. Brakke, "Minimal surfaces, corners, and wires," Journal of Geometric Analysis 2 (1992) 11-36.

[B4] K. A. Brakke, "The opaque cube problem video," Computing Optimal Geometries, J. E. Taylor, ed., American Mathematical Society, Providence RI, 1991.

[B5] K. A. Brakke, "Grain growth with the Surface Evolver," Video Proceedings of the Workshop on Computational Crystal Growing, J. E. Taylor, ed., American Mathematical Society, Providence RI, 1992.

[B6] K. A. Brakke, "The opaque cube problem,", Am. Math. Monthly 99 (Nov. 1992), 866-871.

[BB] K. A. Brakke and F. Baginski, "Modeling ascent configurations of strained high-altitude balloons," AIAA Journal 36 (1998) 1901-1920.

[B7] K. A. Brakke and F. Morgan, "Instabilities of cylindrical bubble clusters,", Eur. Phys. J. E 9 (2002) 453-460.

[BS] K. A. Brakke and J. M. Sullivan, "Using Symmetry Features of the Surface Evolver to Study Foams", in Mathematics and Visualization, ed. K. Polthier and H. Hege, Springer Verlag, Berlin, (1997).

[C] M. Callahan, P. Concus and R. Finn, "Energy minimizing capillary surfaces for exotic containers," Computing Optimal Geometries, American Mathematical Society, Providence RI, 1991.

[CS] S. Collicot, "Convergence behavior of Surface Evolver applied to a generic propellant-management device," J. Propulsion and Power 17 (2001) 845-851.

[DL] D. Dobkin and M. Laszlo, "Primitives for the manipulation of three-dimensional subdivisions," technical report CS-TR-089-87, Department of Computer Science, Princeton University, Princeton, New Jersey. (April 1987).

[FHW] M. Freedman, X. He, and Z. Wang, "On the energy of knots and unknots," Annals of Mathematics 139 no. 1 (1994) 1-50.

[FS] G. Francis, J. M. Sullivan, R. B. Kusner, K. A. Brakke, C. Hartman, and G. Chappell, "The Minimax Sphere Eversion", in Mathematics and Visualization, ed. K. Polthier and H. Hege, Springer Verlag, Berlin, (1997).

[FT].H. J. Frost and C. V. Thompson, "Computer Simulation of Grain Growth," Current Opinion in Solid State and Materials Science 3, 361 (1996).

[HK] J. Hale and H. Kocak, Dynamics and Bifurcations, Springer-Verlag, 1991.

[HKS] L. Hsu, R. Kusner, and J. Sullivan, "Minimizing the squared mean curvature integral for surfaces in space forms," Experimental Mathematics 1 (1991) 191-208.

[KR1] A. Kraynik and D. Reinelt, "The linear elastic behaviour of a bidisperse Weiare-Phelan soap foam", submitted to Chem. Eng. Comm.

[KR2] A. Kraynik and D. Reinelt, "Simple shearing flow of a dry Kelvin soap foam", J. Fluid Mech. 311 (1996) 327.

[KRS] A. Kraynik, D. Reinelt, and F. van Swol, "Structure of random monodisperse foam," Phys. Rev. E 67 (2003) 031403.

[KS][KS]} R. Kusner and J. M. Sullivan, "Comparing the Weaire-Phelan Equal-Volume Foam to Kelvin's Foam", Forma (The Society for Science on Form, Japan) ed. R. Takaki and D. Weaire, KTK Scientific Publishers. As book by Taylor and Francis Ltd (1996).

[KS2]} R. Kusner and J. M. Sullivan, "Mobius Energies for Knots and Links, Surfaces and Submanifolds" in Geometric Topology, Proceedings of the Georgia International Topology Conference, August 1993, ed. W. Kazez, AMS/IP Studies in Advanced Mathematics, vol. 2, part 1 (1997) 570--604.

[MB] X. Michalet and D. Bensimon, "Observation of stable shapes and conformal diffusion in genus 2 vesicles", Science 269 (4 Aug 1995) 666-668.

[MH] H. Mittelmann, "Symmetric capillary surfaces in a cube", Math. Comp. Simulation 35 (1993) 139-152.

[MF1] F. Morgan, "Harnack-type mass bounds and Bernstein theorems for area-minimizing flat chains modulo $\nu$," Comm. Part. Diff. Eq. 11 (1986) 1257-1283.

[MT] F. Morgan and J. E. Taylor, "Destabilization of the tetrahedral point junction by positive triple junction line energy," Scripta Metall. Mater. 25 (1991) 1907-1910.

[PWB] R. Phelan, D. Weaire, and K. Brakke, "Computation of equilibrium foam structures using the Surface Evolver," Experimental Mathematics 4 (1995) 181-192.

[P] W. Press, B. Flannery, S. Teukolsky, and W. Vetterling, Numerical Recipes in C, Cambridge University Press, New York, 1988.

[PP] K. Polthier and U. Pinkall,"Computing discrete minimal surfaces and their conjugates," Experimental Math. 2 (1993) 15-36.

[RN] R. J. Renka and J. W. Neuberger, "Minimal surfaces and Sobolev gradients", SIAM J. Sci. Comput. 16 (1995) 1412-1427.

[RSB] L. M. Racz, J. Szekely, and K. A. Brakke, "A General Statement of the Problem and Description of a Proposed Method of Calculation for Some Meniscus Problems in Materials Processing", ISIJ International, Vol. 33, No. 2, (February 1993).

[S] R. Sibson, "Locally equiangular triangulations", Comput. J. 21 (1978) 243-245.

[SG] G. Strang, Linear Algebra and its Applications, Academic Press.

[SJ]J. M. Sullivan, "Sphere Packings Give an Explicit Bound for the Besicovitch Covering Theorem", J. Geometric Analysis 4 (1993) 219-231.

[T1] J. E. Taylor, "The structure of singularities in soap-bubble-like and soap-film-like minimal surfaces," Ann. Math. 103 (1976), 489-539.

[T2] J. E. Taylor, "Constructing crystalline minimal surfaces," Seminar on Minimal Submanifolds, E. Bombieri, ed., Annals of Math. Studies 105 (1983), 271-288.

[T3] J. E. Taylor, "Constructions and conjectures in crystalline nondifferentiable geometry," Proceedings of the Conference in Differential Geometry, Rio de Janiero, 1988, Pittman Publishing Ltd.

[Te] J. Tegart, "Three-dimensional fluid interfaces in cylindrical containers," AIAA paper AIAA-91-2174, 27th Joint Propulsion Conference, Sacramento, CA, June 1991.

[TW] W. Thompson (Lord Kelvin), "On the division of space with minimum possible error," Acta Math. 11(1887), 121-134.

[U] A. Underwood, "Constructing barriers to minimal surfaces from polyhedral data," Ph.D. thesis, Princeton, 1993.

[WM] D. Weaire and S. McMurry, "Some Fundamentals of Grain Growth," in Solid State Physics: Advances in Research and Applications (eds. H. Ehrenreich and F. Spaepen), Vol. 50, Academic Press, Boston (1997).

[WP] D. Weaire and R. Phelan, "A counter-example to Kelvin's conjecture on minimal surfaces", Phil. Mag. Letters 69 (1994), 107-110.


Back to top of Surface Evolver documentation. Index. evolver-2.30c.dfsg/doc/twointorbare.gif0000644000175300017530000032034711410765113020412 0ustar hazelscthazelsctGIF89a^,w      /////0/0//000//0/000/000?????@?@??@@@??@?@@@?@@@OOOOOPOPOOPPPOOPOPPPOPPP_____`_`__```__`_```_```ooooopopoopppoopopppoppp!!5 Image generated by AFPL Ghostscript (device=ppmraw) ,^,wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww\=]pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwN(9YpwwwwwQwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwNDYA0 SwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwIJ(pwwwwwYF0;Uiwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww[pwwwkGJwi5wwwwwwwwwwQ  2JawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwYwww/iwwwwwwwwwwwt0BwdM4 6Jawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwa4Fwwwwp0wwwwwwwwwwwwwwwwwwwweL?6BZwwwwwwwwwwwpcwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwYwwwwp=Qwwwww9bwwwwwwwwwwwwwwpYwwwwwwwwwwwmS<)E^wwwwwwKJwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwNcwm3'8>wwwwww wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwiQ?.A[ppIwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwsm,,iw'wwwwwwCUwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpY=( -iwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww8.mwww0QwwwwwdwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpY?# @]pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww-,iwwwwwwwwwwwiwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp? ?QiwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwYewwwwwwwwwNIwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwY;pwwwwwwwwwwwwwwwCI* wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwi)0ipewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwY wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwi/0iwwwpawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwYIpwwwwwwwwwwwwwwwwwwww?aj9wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwM#&9)WwwwwwpawKRwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwYLwwwwwwwwwwwwpVwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwbpwwwwwrZ=0(;]pwwwEiwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww?kwww_Wwwwwwwwwwwwwwwp:pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwbpwwwwwwwwwwwI $\iwwwwww>iwwwwwwwwwwwwwwwwwwwwp0.iwwwwwwwwwwwwwwwwwwwwwwwwww ewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp0awwwwwwwwwwwwwwwwwwwwwwwwwwicwwwwwwYH0 ;TmwbiwwwwwwwwwwwwwwwwwwM Dwwwwwwwwwwwwwwwwwwwwwwwww;pcwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwJ'@wwwwwwwwwwwwwwwwwwwwwwwwwww<Twwwwwwwwwwww`C,LwwwwwwwwwwwwwwwIMqwwaEewwwwwwwwwwwwwwwwwwwwwwwwwU%wpdwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww-pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwYG*1QjwwwwwwwwwO Mwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp-wwwwwwwwwwwwwwwwwwwwwwwwwwwIQwwwwwwwwwwwwwwwwwwwwp:  >Riwp: `wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpbwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpUUwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww9#YwwwwZG. Ywwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwqbwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp[Y]wwwwwwwwwwwwwwwwwwwwwwwwwwZPwwwwwwwwwwwwwwwwwwwwwiewwwwwwwwwwwaI/MwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpIww]pwwwwwwwwwwwwwwwwwwwwwwwwww(rwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwcTwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwafwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwrKwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwi?wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww;wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwbewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpIwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.kwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpLwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp/wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww*wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwccwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpLwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwtcwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwFpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwweewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww IwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpAewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww]\wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,JwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwtKwapwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwtFwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww0OwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpKwwwapwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww0wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwgcwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.EwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpMwwwwwapwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww0.wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpOwwwwwwwamwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww Jwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwbcwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,*wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpIwwwwwwwwwbawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww--wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwi;?wwwwwwwwwwwO*Ywwwwwwwwwwwwwwwwwwwwwwwwwwwwwwprwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww0)wwwwwwwwwwwwwwwwwwwwwwwwwwwwewwwwwwwwwwwww) Cwwwwwwwwwwwwwwwwwwwwwwwwwww9pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwcewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,,wwwwwwwwwwwwwwwwwwwwwwwwwwwwwQ&Swwwwwwwwwwwwwk6;Ywwwwwwwwwwwwwwwwwwwwwwwwwwp Icwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwabwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww30wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.wwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwwwwwwwwie8wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwbaw=:wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwK+wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww-, wwwwwwwwwwwwwww9apwwwwwwwwwwwwwwwwwwwwwwwwwwwwwLiwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwweawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwK.wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww00w (wwwwwwwwwwwwwww;wapwwwwwwwwwwwwwwwwwwwwwwwwwwww[Xwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwa wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwK0wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww-,wweiwwwwwwwwwwwwwwwpcwwapwwwwwwwwwwwwwwwwwwwwwwwwwwww;wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp]HawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwIpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,0wwwwwwwwwwwwwwwwwwwwwwwwwcpwwwwwwwwwwwwwwwwwwwwwwwwwww&wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww[(wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwMpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww-)wwwwwwwwwwwwwwwwwwwwwwwwwwwapwwwwwwwwwwwwwwwwwwwwwwwwww, wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww]Y4SwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwOtwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww80wwwwwwwwwwwwwwwwwwwwwwwwwwwwwapwwwwwwwwwwwwwwwwwwwwwwwww;pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwEawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwKpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwI,wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwNpwwwwwwwwwwwwwwwwwwwwwwww[[wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwKpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwI/wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwJpwwwwwwwwwwwwwwwwwwwwwwwpEwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwFwQrwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwN+wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwMpwwwwwwwwwwwwwwwwwwwwwww*wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwgawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww9apwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwKpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwLtwwwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwecwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww qwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwKpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwJpwwwwwwwwwwwwwwwwwwwww:wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwweawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp2>SwwwwwwwwwwwwwwwwwwwwwwwwwwwwwJpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwJpwwwwwwwwwwwwwwwwwwwwRewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwgawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww2EwwwwwwwwwwwwwwwwwwwwwwwwwwwwMpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwIqwwwwwwwwwwwwwwwwwwwaNwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwOwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwL;wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwMp wwwwwwwwww>wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww<6wwwQRwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww(wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwK,wwwwwwwwww^_wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwJ\www_0wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwYwwwwwwwwwwwwwwwwwwwwwwwwwwwwwadwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwJiwwwwwwwwwqCwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwj&YwwwUawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww>pwwwwwwwwwwwwwwwwwwwwwwwwYpwsbwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwa9wwwwwwwwww6wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwRZwww8pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwiC;UiwwwwwwwwwwwpiwwwwwwwwwwwwwwwwwwwwwcNcwwwwwwwNwwwIRwwwwwwwwwwwwwwwwwwwwpQwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwIpwwQ@wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww .]Dwwwwwwwww.wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwe0kwwww=iwwwwwwwwwwwwwpwwwwwwaM7 2Lfp$hwwwYbpwwwwwwwwwwwwwwwwwp:wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwMpwwOpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww3)p]wwwwwwwwFpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwmiwwwwY pwwwwpwwwwwwwwwwwwwwwiiwwwwwwwwwwwaJ2 bwww@pwwwwwwwwwwwwwwwpKwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwJpwwO wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwC ^wwwwwwwZ]wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww wwZ ;'awwwww9]wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww44qwwwwwwwwwwwwww Kwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww*"9>wwwwww[&PwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwYJwwwwwwwwp:wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwMKI Cpj,wwwwwpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpYh-8Jwwwwwwwwwwww.MwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwMF/ ?K]= IwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwO/pwwwwwwwwww0wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwiKwwwp(iwwwwwMwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,p=ww./*Jawwwwwww+MwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwLpwwww_ Iawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww4*wwwwwwwwwwww(wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaMwwwwwwwwwwwpewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwk*wQaww.8]  7Mbw,HwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwIpwwwwww#wpT7Iawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwE,wwwwwwwwp:wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww:iwwwwwwww9jwwwwiR9 0wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwMpwwwwwww@wwww? eM4 8NawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwIKwwwwwwwwwwwwwQZwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwKA_YK4%wwwwwwww@wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwGap(wwwwwwwww(wwwwwwwwww^pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwOpwwwwwwww_YwwwwO;wwwwiQ<.EYwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwiIwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwq:YwwwwwwwwwwwwwwwpaZ.Zwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww+JZwwwwwwwww&wwwwwwww[9pIpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww9pwwwwwwwwwiDwwwwwARwwwwwwwwwoR= +AZwwwwwwww(/wwwwwwwwwwwwwwZEwwwwwwwwwwwwwww2wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp: [wwwwwwwwwwwwwwwwwwwq:wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww) iAwwwwwwww9wwwwww[ >pwwwJtwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwYpwwwwwwwwwww6wwwwwwwwwwwwwwwwwwwwwiY;&(?[pww?+wwwwwwwwwwwww'rwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@Zwwwwwwwwwwwwwwwwwwww^@wQgwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww:iwwwwwww\:ZZZANvwwwwwwPpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwi:4pwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwwwwwwwwp[@%&>#iwwwwwwwwwwj>wwwwwwwwwwwwwwww4wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww, ;Oewwwwwi>?wwwwwBppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww;.mwwwwwwwwiAwwwwwwwwwwOpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwW=wwwwwwwwwwwww9wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp]>,4Tiwwwwwww*iwwwwwwwwwwwwwwwwPiwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.0aO1 8;  iwwwwwQH=wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww9QwwwwwwwwwwwpwwwwwwwwwwwwIiwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwKtwwwwwwwwwwwwwLiwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwJ?Qiwp,wwwwwwwwwwwwwwwww]Vwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,0wwwwwwaJ42IcwwwwwGDwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww Iwwwwwa/wwwwMrwwwwwwwwwwwwwwIKwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwJ0wwwwwwwwwwwwww)pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww(KkwwZF0bwwwwwwwwwwwwwwwwww?wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,Fwwwwwwwwwwwi/0D^awwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwi$wwwwww/wp<0awwwwwwwwwwwwwwwwJ6YcwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwL)wwwwwwwwwwwwww*pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwVSwwwwwwwwwweN5wwwwwwwwwwwwwwwwwww&wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww/JwwwwwwwwwwwwwwTpiQ= Ywwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww:iwwwww[7?YW,wwwwwwwwwwwwwwwwwwYwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwI0wwIYwwwwwwwwwwww/-wKqwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwAiwwwwwwwwwwwwwwwwww+wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww0MwwwwwwwwwwwwwwwwQ@wwwwwiA':Ypwwwwwwwwwwwnpwwwwwwwwwwwwwwp$wwwwwp Ywwa(twwwwwwwwwwwwwwwwwwWQawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwH,wwwwwwwwwwwwwwww0-wwwMpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww[[wwwwwwwwwwwwwwwwww;pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpPwwwwwwwwwwwwwwwwwwamwwwwww0ZwpZ:$#9_pwwwwww pwwwwwwwwwwwwwJYwwwwd,pwwwwpwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwwwwwwwwGYwwwwwwwwwwwwwwwwwwwwwc 8Iaa-iwwwwwwwwwwwwwZZwwwwwwwwwwwwwwwwwwwwwwwwacwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww0-wwwwwwwwwwwwp:6pwwwwwwwwwwww)@\wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww]Ywwwwwwwwwwwwwwwwww<wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpPwwwwwwwwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwwww=iwwwwaL5 :pwwwwwwwwwwwwwwww;wwwwwwwwwwwwwwwwwwwwwwwwwgawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww0-wwwwwwwwwwwwwD>wwwwwwwwwwwwwY ?wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpCwwwwwwwwwwwwwwwwwwQewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpNw>wwwwwwwwwwwwwwwwwwwwwwwwwwJQwwwwwwwwwwwwwwwwwwwwwwitwwwwwwwwwwweI pwwwwwwwwwwwwwwwwww'wwwwwwwwwwwwwwwwwwwwwwwwwwpTwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww*,wwwwwwwwwwwwwwwwLpwwwwwwwwwwwwww/wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwiIwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpKwwakwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwDpwwwwwwwwwwwwwwwwww$wwwwwwwwwwwwwwwwwwwwwwwwwwwpMwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww/,wwwwwwwwwwwwwwwwI wwwwwwwwwwwwwww2J/wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwiwwwwwwwwwwwwwwwwww>wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpMwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwOKwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwY]wwwwwwwwwwwwwwwwww;wwwwwwwwwwwwwwwwwwwwwwwwwwwwpKwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww00wwwwwwwwwwwwwwwwKp wwwwwwwwwwwwwww9wP/wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww(wwwwwwwwwwwwwwwwww wapwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwvIwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwb0wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwqwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww/0wwwwwwwwwwwwwwwwwwwwwwwwwwwwww/0wwwwwwwwwwwwwwwwcewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww),wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww?wwwwwwwwwwwwwwwwwwY_wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpIwwwwwwwwwwwwwwwwwwwwwwwwwww' ]wwwwwwwwwwwwwUwwwwwwwwwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwww wCiwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.0wwwwwwwwwwwwwwwwwwwwwwwwwwww/6wwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,/wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwpFwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwqOwwwwwwwwwwwwwwwwwwwwwwwwwwww41 wwwwwwwwwwwwwwV7?iwwwwwwwwwwwwwwwwwwwwwwwwwwwQ0wwwwwwwwwwwwwww@b*JXwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.+wwwwwwwwwwwwwwwwwwwwwwwwww,Kwwwwwwwwwwwwwwwweawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.,wwwwwwwwwwwwwwwwwwwwwwwwwwwwwww5wwwwwwwwwwwwwwwwwww/wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpMwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwIBwwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww*)wwiawwwwwwwwwwwwwwwwaiww-Nwwwwwwwwwwwwwwwwcawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,*wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwImwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwNVYwwwwwwwwwwwwwww Ipwwwwwwwwwwwwwwwwwwwwwwwwwwa@7P wwwwwwwwwwwwwwjKwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,,w9wwwwwwwwwwwwwwww:w0Nwwwwwwwwwwwwwwwwpawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,.wwwwwwwwwwwwwwwwwwwwwwwwwwwww^Uwwwwwwwwwwwwwwwwww:wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpcwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwIpPwwwwwwwwwwwwwwww0wNpwwwwwwwwwwwwwwwwwwwwwwwwwU&pwwwwwwwwwwwwwwwwwprwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww/+?wwwwwwwwwwwwwwww?0Jwwwwwwwwwwwwwwwwpgwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,0wwwwwwwwwwwwwwwwwwwwwwwwwwwww=wwwwwwwwwwwwwwwwwwQcwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwIpwpcwwwwwwwwwwwwwwwwpkwwNpwwwwwwwwwwwwwwwwwwwwwwwwwww5wwwwwwwwwwwwwwwwwwLwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww)bwwwwwwwwwwwwwwdKwwwwwwwwwwwwwwwwpawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww-,wwwwwwwwwwwwwwwwwwwwwwwwwwww"wwwwwwwwwwwwwwwwwwaMwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwNpwwwwwwwwwwwwwwwwwwwwwwwwwWpwwwwwwwwwwwwwwwwwwwwwwwwwwIiwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp090wwwwwwwwwwwwww*jwwwwwwwwwwwwwpYwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,,wwwwwwwwwwwwwwwwwwwwwwwwwww0wwwwwwwwwwwwwwwwwww=wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwIpwwwwwwwwwwwwwwwwwwwwwwwwwwwebwwwwwwwwwwwwwwwwwwwwwwwwwbTwwwwwwwwwwwwwwwwww8wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpiwwwwwwwwwwwwl(pwwwwwwwwwwwwrJwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww0/wwwwwwwwwwwwwwwwwwwwwwwwwwBpwwwwwwwwwwwwwwwwp> wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwweawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwNpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwwwpwwI?$$??0Iwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww/awwwwwwwwwwwwwR.wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp(.wwwwwwwwwwwwwwLpwwwwgwwwwwaIwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwweawwwwwwwwwwwwwwwwwwwwwwwwwwwwww90wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwtJwwwwwwwwwwwwwwwwpHwwwwwwwwwwwwwwwww wwwwpYB07YpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwnPwwwwwwwwwwwww89Dwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww/0wwwwwwwwwwww?cwwwwwwwwwB[wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwwwwwwww/-wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpMwwwwwwwwwwwwwwww*wwwwwwwwwwwwwwwwLZwwwwwwwwwb.(9]pwwwwwwwwwwwwwwwwwwwwwwwwww0kbYwww@bwwwwwwwwwwwww/MwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwLpwwwww.AH"@Wkwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwatwwwwwwwwwww@wwwwwwwwwwM;Crwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwcewwwwwwwwwwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@a>wwp .wwwwwwwwwwwp+LwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwNtwwwwwwNYwwi 0;Qiwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww-YwwwwwwwwwwwwUawwwwwwwwwppwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwapwwwwwwwwwwwwwwwwwwwwwwwwwwwwww$]wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwrw?RwwA] *pYwwwwwwwwwq; wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwItwwwwwwwpHwww.ww^E.@Qiwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp/wwwwwwwwwwwwwiIwwwwwwwwwwIpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwcpwwwwwwwwwwwwwwwwwwwwwwwwwwwwww/.wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwd&r<]wwwwwpewwwwwwwwwwp< @,wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwNtwwwwwwwww0wwII)wwwwwwwYD09Qiwwwwwwwwwwiawwwwwwwwwwwwwww<jwwwwwwwwwwwwww8wwwwwwwwww( wLqwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwdpwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.)wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwZQwwwwwwwww:wwwwwwwi8/YwwK,wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwJtwwwwwwwwww wwwwwwwwwwwwwwwwwaK6  @Riwwwww>Qwwwwwwwwwwwwwr$wwwwwwwwwwwwwwwwwwwwwwwwwwwwwMvwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwapwwwwwwwwwwwwwwwwwwwwwwwwwwwwww./wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwQBXwwwwwwwRawwwwi..iwwwwwK,wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwtV awwwwwwwwwww9w`MwwwwwwwwwwwwwwwwwwwwaP4 wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaO7;TiwwwwwPYwwwwwwwwwwwwwwwwcWwwwwwwwwwwwwwwwwfcwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwIpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp/+wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp,wwwwwwwwwww pwwwwwwwwwwwww0 pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwa*wwwwwwwwwwwwwwqwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp @8 8Nawwwwwwwwwwwwwwwwwwqwwwwwwwwwwwwwwwwwwwwwwwwwww,,wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwqMwwwwwwwwwwwwwwwwc4wwwwwwwwwwwwwwp/wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww @wwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwwwwwwwwwpJwwwwwwwwwwwwwwwwwwwwwwwwIpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpJwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww4wwwwwwwwwwwwwwwwwwY[wwwwwwwwwwwwwwwwwwwwwwwwwwww,0wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpKwwwwwwwwwwwwwwwwaHYwwwwwwwwwwwwwww *0wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwawwwwwwwwwwwwwwwww9wRSwwwwwwwwwwwwwwwwwwwwwwwwwpOwEwwwwwwwwwwwwwwww,wJpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpMwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpEwwwwwwwwwwwwwwwwwwwwwwwwwwwww.0wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww(Pwwwwwwwwwwwwwwwwaa[iwwwwwwwwwwwwwww0w0/wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp bwwwwwwwwwwwwwwJIVQ/wwwwwwwwwwwwwwwwwwwwwwwwwwtI@wwwwwwwwwwwwwwww@OtwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpMwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww?wwwwwwwwwwwwwwwwwww+wwwwwwwwwwwwwwwwwwwwwwwwwwwwww00wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.Kwwwwwwwwwwwwwwwwcawk?wwwwwwwwwwwwwwwwaSww,0wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww9mwwwwwwwwwwwww1 pwwwwwwwwwwwwwwwwwwwwwwwwwwwrcwwwwwwwwwwwwwwi+wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpIwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwKkwwwwwwwwwwwwwwwwww'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwww*,wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww+Mwwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwww=,wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww (a'mwwwwwwwwwwwwww3/wwwwwwwwwwwwwwwwwwwwwwwwwwwwSY@wwwwwwwwwwwwwwACDpwwwwwwwwwwwwwwwwwwwwwwwwwwwpMwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwweUwwwwwwwwwwwwwwwwww<wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww0+wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww0;wwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwwwwwI(qwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwt'i?wwwwwwwwwwwwwwwwww,NwwwwwwwwwwwwwwwwwwwwwwwwwwwtpwwwwwwwwwwwwpawwwwwwwwwwwwwwwwwwwwwwwwwwrNwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww6wwwwwwwwwwwwwwwwwwQ[wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww//wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww0,wwwwwwwwwwwwwwwwbcwwwwwwwwwwwwwwwwwwwwwwwwwwwwwNpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww[Zwwwwwwwwwwwwwwwwwq[wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwX,/wwwwwwwwwwww0/UwwwwwwwwwwwwwwwwwwwwwwwwwwwwpOwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwiFwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,/wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww+.wwwwwwwwwwwwwwwwaewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwItwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpDwwwwwwwwwwwwwwwwww6[wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwi+wwwwwwwwww)iwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpPwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwiwwwwwwwwwwwwwwwwww3wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww00wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.-wwwwwwwwwwwwwwwwabwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwMpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww2wwwwwwwwwwwwwwwwwwbQwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww--wwwwwwww0.wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpLwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww /wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.0wwwwwwwwwwwwwwwwwwwwwwwwwwwwwww0)wwwwwwwwwwwwwwwwacwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwMpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww#p=pwwwwwwwwwwwwwwI?wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww/-wwkjwwwwwwwwwwwwwwwpZwwL/wwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww[pwwwwwwwwwwwwwwwwwwwwwwwwwwwww9wwwwwwwwwwwwwwwwww[[wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwN/wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwrJwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwKp=wwwwwwwwwwwwwwwwww2[wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww)/w wwwwwwwwwwwwwww;wM%wwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwbawwwwwwwwwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwww;wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwq!wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp`wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwS^wwwwwwwwwwwwwwwwwpewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww+* wwwwwwwwwwwwwww>Kpwwwwwwwwwwwwwwwdcwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwcawwwwwwwwwwwwwwwwwwwwwwwwwww.wwwwwwwwwwwwwwwwwww#wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww:awwwwwwwwwwwwwwwwwwwwwwwwwwwwwpawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwjLwwwwwwwwwwwwwwwwww7[wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.qwwwwwwwwwwwwwwpwwwwwwwwwwwwwwwecwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwwwwApwwwwwwwwwwwwwwwwww#YwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwtwwwwwwwwwwwwwwwwwwwwwwwwwwYipawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww7wwwwwwwwwwwwwwwwwwYQwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwd .Nwwwwwwwwwwwwwi9wwwwwwwwwwwwwetwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwacwwwwwwwwwwwwwwwwwwwwwwwww[Ywwwwwwwwwwwwwwww[$=`pwwwwwa iwwwwwwwwwwwwwwwwwwwwwCYwwwwwwwwwwwwwwwwwwwwwwwwwwBrawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwww;wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwi  pwwwwwwwwwwww.'Uwwwwwwwwwwwwepwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwww?wwwwwwwwwwwwww[ )Y9(=,9wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.ewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww8wwwwwwwwwwwwwwwwwww(wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwmF0wwwwwwwwwwwM?pwwwwwwwwwwwwwwgpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwbcwwwwwwwwwwwwwwwwwwwwwwww(wwwwwwwwwwww_ 9.iwwwwpY;)wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww0*wwwbrwwwwwwwwwwwwwwwOpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwr9wwwwwwwwwwwwwwwwwwwJHwwpKNwwwwwJMwwwwwwwwwwwwwwpYwwwwwwwwwwwaP1+wwwwww_Lwwwwwwwwwwwwwwwwchwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww) wwwwwwwwwwwwwwwwwww(wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww/-weawwwwwwwwwwwqYwwMqwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwrwwwwwwwwwwwwwwwwwQ4r9B/wwwwww wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwkT80BYwwLwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww:pwwwwwwwwwwwwwwwwww';[pwwwwwwwwwwp[wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww/&cwwwwwwwwwwwwCwJpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwN?/3wwwwwwwwwwwwwwwww  [w.twwwwwQBwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp@>:$8iwwwwwwwwwwwbawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww]]wwwwwwwwwwwwwwwwww $:`pwwwp;wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,ewwwwwwwwwwwww:MpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwPKwwwwwwwwwwwwwww@ _www:Hwwwwwp pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww^/`wwwiY@&!;^pwwwwwwadwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwrEwwwwwwwwwwwwwwwwwWJwZB,cwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww7wwwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwrJwwwwwwwwwwwwwJiwwwwwwwwwwswwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwi<:wwwwwwJ (@Ypafwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,wwwwwwwwwwwwwwwww wwwwwwwYA#:UiwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwjHwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpOwwwwwwwwwwi"OwwwwwwwwwY wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.pwwwww\IwwwwpZ=#awwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwa=wwwwwwwwwwi%1 8Kewwwwwwwwwwwwwwwwwwwwwwwwwww<wwwwwwwwwwwww(NwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpMwwwwwwwi.(=\YH:wwwwwwww0wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwYwm0wiiwwwwww[wwwwwwwwwwp?awwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@wwwwwwwwwwwwwwww0pwwwwwwwwwwwwC+wweI3 2IbwwwwwwwwwwwwwwwwwwwwwwOewwwwwwwwwwwC?iwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp Kwwwwi-,iwwwwwwwwwwwwwLM?wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwY_0iwwwwwwwwwwwwwwwwwwp?[wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwQawwwwwwwwwwwwwwi0wwwwwwwwwwwwww2wwwwwwwaL@  8BZwwwwwwwwwwtBwwwwwaSwwwwwwwwwwKjwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww/AwY$4iwwwwwww, wwwwwwwwww Bjwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww7.wwwwwwwwwwwwwwwwwp; _wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwmNwwwwwwwwwwwwww:iwwwwwwwwwwwwwwwwwwwwwwwwwwwwiU;/BYwwwwwP8wwwww=wwwwwwwwwKpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww0@pwwwwwwwwwFtwwwwwwwwKQpiwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww / wwwwwwwwW?`ppR*^wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww8wwwwwwwwwwwwwt'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwiQ: 0HYp/6wwww wwwwwwwwKpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwa*HYwwwwwwbYwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww awwwwwwww= fwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww=Ywwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp\?&Yww+wwwwwwwOpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwa_]<%,?+?e%wwwcwYIwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwWwwwwwwwwwwwjAwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww3wwwwwwwwwwwwawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpZ<& mwwwwwIpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwcbwwwwwtY=&&& wwYe#pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpawwwwwEawwwi0DwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwKowwwwwwwwww[ wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp);@/ J8?pmwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwEKwwwwwZJwk0*a(wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwYQwwwwwwwwj+>wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwa9pwwwwwY.pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpawwwwwwwwwwwwwp !( wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww a00kwDYwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww;wwwwwwww(wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwH]pwwwwwwAawwwwwwwwwwwwwwwwwww-awwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww-wwwwwwwwwi0EpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwmDwww0Lwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp:Qwwwwwwwwwwwwwwwwwwwwp0wwwpcwwwwwp[;&%>YpwacwwwwwwwwwwwwLLwww]5wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww=wwwwwwwww>ipCwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww0NY4Ewwww,IwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwS<(1wwwwwwwwwwwwwwwwwwwwwwwwwLHwwwwwwwwwwwp[=."&Lwwwwwwwwwwwww] 8]pwaBVwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwYYwwwwwwwp(wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww:IiPwwwwwww,Pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww5pwwwwwwwwwwwwwwwwwwwwww1wwwwwwwwwwwwwwwwwwwww[E-3^pwwwwwwYIJMwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpDMwwbCwEYwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww1wwwwwwww*:wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp"wwwwwwwwwwwwwwwwwwwwwwwNiw]IwwwwwwwwwwwwwwwwwwwwwM!@Yrw IwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwHK%wwJawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww wwwwwwwww(iwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpIk@wwwwwwwwwwwwwwwwwwwwwt\wwwwwwwwwww_B-Jwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwuwwww (wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwIiwwwwwwwwwO9wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww(IwwHrwwwwwwwwwwwwwwwwwwwwwwww(0iwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp0wwwwKwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwbUwwwwwwwwwwMwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,Iwwwwwwwwwwwwwwwwwwwwwwwwwwwww+ ,wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww<IJwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,wwwwwwwwwwwwwwwwpLwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww*)wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww-Mwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww(wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww(wwwwwwwwwwwwwwwwwpNwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww;-wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww/?wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww'Awwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@wwwwwwwwwwwwwwwwwwpJwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwN0wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp(.wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww?pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwQewwwwwwwwwwwwwwwwwwpLwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwP0wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwrL.wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwY1wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwiIwwwwwwwwwwwwwwwwwwwpIwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwI#wwwwwwwwwwwwwwwwwwwwwwwwwwwwi$pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww1wwwwwwwwwwwwwwwwwwwwpMwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwKpwwwwwwwwwwwwwwwwwwwwwwwwwwwwY OwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwrcwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpIwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwIpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww-pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpbwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww6wwwwwwwwwwwwwwwwwwwwwwpIwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwMpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@-,wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwNkwwwwwwwwwwwwwwwwwwwwwwpMwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwJpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwJ0w wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww\VwwwwwwwwwwwwwwwwwwwwwwwpJwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwKpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwJ0wwkiwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwqYwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww=wwwwwwwwwwwwwwwwwwwwwwwwqMwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwQpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwN'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpEwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww'wwwwwwwwwwwwwwwwwwwwwwwwwpOwwwwwwwwwwwwwwwwwwwwwwwwwwwwwapwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwNpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaDiwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww0wwwwwwwwwwwwwwwwwwwwwwwwwwpMwwwwwwwwwwwwwwwwwwwwwwwwwwwapwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwOqwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww>0wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww<pwwwwwwwwwwwwwwwwwwwwwwwwwwpIwwwwwwwwwwwwwwwwwwwwwwwwwcpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwKtwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpEYiwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwY_wwwwwwwwwwwwwwwwwwwwwwwwwwwtKwwwwwwwwwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwLuwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww"awwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpAwwwwwwwwwwwwwwwwwwwwwwwwwwwwpJw$$wwwwwwwwwwwwwwwIwbawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwMpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@iawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww.wwwwwwwwwwwwwwwwwwwwwwwwwwwwwpK'wwwwwwwwwwwwwww;dbwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwVtwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwAwaawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww"Xwwwwwwwwwwwwwwwwwwwwwwwwwwwpwwwwwwwwwwwwwww/cwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwcpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwchwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww'I>GVwwwwwwwwwwwwwwwwwwwwwwwwwwpZRUwwwwwwwwwwwwww!:Uwwwwwwwwwwwwwwwwwwwwwwwwwwwwwetwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww(wwwwwwwwwwwwwwwwwwwwwwwwwwwawwwwwwwwwwwww@>wwwwwwwwwwwwwwwwwwwwwwwwwwwwapwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwweawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww-JwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpM#IwwwwwwwwwwwaKpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwbcwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwacwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwmwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww]IwwwwwwwwwpDwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwabwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpJwwwwwwwp]wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwabwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwdawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww&wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpIwwwwwpRwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwweawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwCiwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpLwwwpKwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwcawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwcawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwYYwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpIwpKwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwebwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwabwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww9wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwqBLwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpcwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwcewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww(wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpMwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwbawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww(wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwQGwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwrawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwbawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww9wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwtUwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwadwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww^YwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwipOwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwcewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp=(=QowwwwpNwwwwwwwwwwwwwwwwwwwww;iwwwwwwwwwwwwwwwwwwwwwwwwwww=Kwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp>[wYF. _wwwwwwwwwwwwwwwwwwwww wwwwwwwwwwwwwwwwwwwwwwwwwwwTKwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwccwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp9 Ywwwwwwww]A, ?QiwwwwwwwwwwwwwwwwwwMYwwwwwwwwwwwwwwwwwwwwwwwwwww-+&pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwweaw?9wwwwwwwwwwwwwwwwwwwwwwwwwpZpwwwa//iwwwwwwwwwwwwwwK=Qmwwwwwwwwwwwwpwwwwwwwwwwwwwwwwwwwwwwwwwwwi&pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwacwwwwwwwwwwwwwwwwwwwwwwwww[ .iwwwwwwwwwwwwwwwwwp[w[F.>Wmwwwwwww.HwwwwwwwwwwwwwwwwwwwwwwwwwwwPpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwc(wwwwwwwwwwwwwwwwwwwwwwwwwwa@]wwwwwwwwwwwwwwwwwwwwr wwwwwwYB) ?Qiw4Ywwwwwwjpwwwwwwwwwwwwwwwwww*.wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwaawwwwwwwwwwwwwwwwwwukwwwwi0awwwwwwwwwwwwwwwwwwwwwwmawwwwwwwwwww]D8Uwwwwww pwwwwwwwwwwwwwwww+,wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwY'wwwwwwwwwwwwwwwwwwwwY+3*]wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww@@Tiwcpwwwwwwwwwwwwww0,wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww_R, [wwwwwwwwwwwwwwwwY?YSiwwwwwpN0wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwY%iwwwwwwwwp?pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww_H. ?? dwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwEAwwwwwwwwwwwwWewwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwbL53Iawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww/:wwwwwp;pwwwJpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwi- 5Kfwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwqwwwwww3mwNI=\wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwVpcP> 3G]wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwKZwwwwwm,IKwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwa/wwwwwjT= 0FYwwwwwwwwwwwwppwwwwwwwwwwwwwwwwwwwwpIwwwwBawwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwYawwwwwwwwwwmQ=*GYrwwwwwwpwwwwwwwwwwwwwUIwwwwi?pwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwp];('?YpwR pwwwwwwwwwwww wwwq;QwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpY?%$[wwwwwwwwwwa:wp:QpwwtGpwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwpYE-5Uiwwwwww/e9$Zwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwi3  9Qki,iwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww *iwwwwYA5 +iwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwiiwwwwwwwwwwwaM==iwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww;evolver-2.30c.dfsg/doc/hints.htm0000644000175300017530000004133511410765113017040 0ustar hazelscthazelsct Surface Evolver Documentation: Hints

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

HINTS

This is a collection helpful hints gained from my own experience with Evolver and from helping others.
  • Evolver works in dimensionless units, and the default settings work best when size, surface tension, volume, etc. are near 1. If you decide to work in units that give very large or small numbers, you may have to adjust parameters such as scale_limit, target_tolerance, and constraint_tolerance.

  • When drawing a sketch for constructing the initial datafile, make it as big as you can. You will have lots of notation to put on it. Number all vertices, edges, and facets. Put orientation arrows on the edges, and indicate the orientation of facets (I like to use curved arrows around the facet numbers).

  • Initial faces should be convex. Although Evolver handles nonconvex faces, the triangulation algorithm is very simple-minded, and the triangulation of a nonconvex face can be ugly. Just put in an extra edge or two to divide the face into a couple of convex faces.

  • Make separate constraints for edges with constraint energy or content integrals, and for edges without. Even if the other edges are fixed, it is much easier to check that the integrands are correct when only the precisely needed edges are on constraints with integrals.

  • If you don't have all your elements numbered consecutively (which usually happens due to numbering schemes you use, or adding or deleting elements), run Evolver with the -i command line option so mouse-picking reports the same element numbers as in your datafile. You can instead put "keep_originals" in the top of your datafile for the same effect.

  • Make sure all your body facets are oriented properly. Evolver will complain if there are mismatched facet orientations on an ordinary edge, but fixed edges, constrained edges, etc. are exempt from this checking. A good way to check is by coloring, for example:
       set body[1].facet color green 

  • Make sure vertices, edges, and facets are on their proper constraints. You can check visually by coloring, e.g.
      set edge color red where on_constraint 1
      set facet color green where on_constraint 1 
    You can't color vertices directly, but you can get close to the same effect by refining a couple of times and coloring edges adjacent to vertices:
      foreach vertex vv where on_constraint 1 do set vv.edge color blue 

  • Check that all the energies, volumes, quantities, etc. in your initial datafile are correct. See the section below for more details on how to check in great detail.

  • If you are doing liquids with contact lines on solid walls, I suggest making the first datafile with all the boundary surfaces of the liquid represented explicitly as facets, and then make a second version of the datafile using constraint energy and content integrals to replace the facets on the fixed walls. It is far easier to get the energies and volumes right in the first version, but it is also far more prone to problems during evolution. Use the first version to check the correctness of the second version, and use the second version for serious work.

  • If your edges on curved constraints try to short-cut the curve, there are several ways to discourage that:
    • Make a second guide constraint, so that the intersection of the two constraints define guiderails for vertices to run along. By using vertex attributes to customize the guide constraint, you only need one guide constraint. For example:
          define vertex attribute guides real[2]
          constraint 1 
          formula:  x^2 + y^2 = rad^2   // curved constraint
          constraint 2 
          formula:  guides[1]*x + guides[2]*y = 0  // radial guide planes  
      Then you can set the guide coefficients at runtime with
         set vertex.guides[1] -y where on_constraint 1
         set vertex.guides[2] x where on_constraint 1  
    • If you understand exactly what energy or volume condition is encouraging the short-cutting, you can adjust the energy or content integrand on the curved constraint to compensate enough to eliminate the encouragement. This basically means calculating the surface area of the gap between the edge and the curved constraint, or the volume bounded by the gap.
    • Declare the curved constraint CONVEX. This adds an energy roughly proportional to the gap area. This is simple to do, and works if you set the gap_constant high enough (you should leave the gap constant as low as will work, however), but you cannot use any Hessian commands if you use convex constraints.

  • Run at low resolution before refining. A good evolution script usually winds up having alternating refining and evolultion. Having many triangles not only takes a long time to calculate, but motion can propagate only one triangle per iteration. Don't over-evolve at a particular refinement. Remember it's an approximation. There is not much point in evolving to 12 digits precision an approximation that is only accurate to 4 digits.

  • Groom your surface triangulation with V (vertex averaging), u (equiangulation), l (long edge division), and t (tiny edge deletion). It may take some experimenting to get the right sequence, along with refinements. It may be better to divide certain long edges than simply refine the whole surface. However, overdoing it may be counterproductive to convergence; sometimes the converged surface doesn't want to be entirely equiangulated or averaged, and you can get into an endless loop of iteration and grooming. Once you work out a good script, write it down in a handy command at the end of the datafile for easy use.

  • Use the dump or d commands to save your evolved surface regularly. Remember that Evolver has no undo feature to roll back disastrous commands.

  • Use conjugate gradient mode for faster gradient descent, but not too soon. Use regular gradient descent to adjust to volume or constraint changes. Conjugate gradient should be used only when regular motion has settled down. Conjugate gradient assumes a quadratic energy function, and may get confused when it's not. Conjugate gradient may need to be toggled off and on to make it forget its history.

  • During gradient descent (including conjugate gradient), keep an eye on the scale factor. The scale factor should remain fairly steady. A scale factor going to 0 does NOT mean convergence; it means the surface is having trouble. However, a good scale factor may depend on refinement and other considerations. See the section on reasonable scale factors.

  • Second-order Hessian convergence is much faster than first-order gradient descent, when Hessian works. So my advice is to use gradient descent just to get to where it's safe to use hessian hessian_seek. Actually, hessian_seek is pretty much always safe to use, since it makes sure energy is decreasing. I have found circumstances where hessian_seek does an amazingly good job as an iteration step, even though the surface is nowhere near convergence.

  • Beware saddle points of energy. A symmetric surface, e.g. a blob of solder on a pad or around a wire, may seem to converge with gradient descent, but just have reached a saddle point. Use the eigenprobe command to test for stability, and if not stable, use the saddle command to get off the saddle point.

  • Judging convergence in gradient descent is tough. If iterations run at a more or less constant scale factor and energy isn't changing much, and running in conjugate gradient mode for a long time doesn't change much, then you're probably in good shape. But use the eigenprobe command to make sure, and hessian to finish off convergence.

  • If you intend to use quadratic mode or Lagrange mode for higher precision, evolve in linear model first until the final stage, since it is much quicker and there are more triangulation grooming commands available.

Checking your datafile

You should always check your initial datafile to be sure it is doing exactly what you want. It is easy to get signs on integrands wrong, or apply quantities to the wrong elements. When you load the initial datafile, the initial energy, body volumes, and quantities values should be exactly what you expect, either from hand calculation or from another datafile you trust. In particular, when using constraint integrals to replace omitted facets, I suggest you make a separate datafile with facets instead of integrals just for checking the agreement between the two.

With the named methods and quantities feature, it is possible to get very detailed information on where numbers are coming from. If you give the "convert_to_quantities" command, every energy, volume, and constraint integrand will be internally converted to named methods and quantities (although the user interface for all remains the same). These internal quantities are ordinarily not displayed by the 'v' or 'Q' commands, but if you do "show_all_quantities" then they will be displayed. Further, 'Q' will show all the component method instances also. For an example, consider the following output:

 Enter command: convert_to_quantities
 Enter command: show_all_quantities
 Enter command: Q
Quantities and instances:
(showing internal quantities also; to suppress, do "show_all_quantities off")
 1. default_length                        64.2842712474619  info_only quantity
                            modulus       1.00000000000000
 2. default_area                          4.00000000000000  energy quantity
                            modulus       1.00000000000000
 3. constraint_1_energy                 -0.342020143325669  energy quantity
                            modulus       1.00000000000000
 4. constraint_2_energy                 -0.342020143325669  energy quantity
                            modulus       1.00000000000000
 5. body_1_vol                            1.00000000000000  fixed quantity
                             target       1.00000000000000
                            modulus       1.00000000000000
    body_1_vol_meth                      0.000000000000000  method instance
                            modulus       1.00000000000000
    body_1_con_2_meth                     1.00000000000000  method instance
                            modulus       1.00000000000000
 6. gravity_quant                        0.000000000000000  energy quantity
                            modulus      0.000000000000000
Here's a detailed explanation of the output of the Q command above:

default_length - total edge length, using the edge_length method. This would be the default energy in the string model, and I guess it really doesn't need to exist here. But it's an info_only quantity, which means it is only evaluated when somebody asks to know its value.

default_area - the default energy in the soapfilm model, and included in the energy here, as indicated by "energy quantity" at the right.

constraint_1_energy - the energy integral of constraint 1, using the edge_vector_integral method applied to all edges on constraint 1.

constraint_2_energy - the energy integral of constraint 2, using the edge_vector_integral method applied to all edges on constraint 2.

body_1_vol - the volume of body 1, as a sum of several method instances. body_1_vol_meth is the facet_vector_integral of (0,0,z) over all the facets on the body. body_con_2_meth is the integral of the constraint 2 content integrand over all edges on facets of body 1 which are edges on constraint 2.

gravity_quant - the total gravitational energy of all bodies with assigned densities. This quantity is always present even if you don't have any bodies, or don't have any body densities. But you'll notice the modulus is 0, which means its evaluation is skipped, so the presence of this quantity doesn't harm anything.

You can find the quantity or method contribution of single elements by using the quantity or method name as an attribute of elements. Using a quantity name really means summing over all its constituent methods that apply to the element. For example, in plates_column,

 Enter command: foreach edge ee where on_constraint 2 do printf "%d  %f\n",id, ee.body_1_con_2_meth
5  0.000000
6  0.000000
7  1.000000
8  0.000000
Enter command: foreach edge where constraint_1_energy != 0 do print constraint_1_energy
  -0.342020143325669

Reasonable scale factors.

Trouble in evolving is usually signaled by a small scale, which means there is some obstacle to evolution. Of course, that means you have to know what a reasonable scale is, and that depends on the type of energy you are using and how refined your surface si. In normal evolution, the size of the scale is set by the development of small-scale roughness in the surface. Combined with a little dimensional analysis, that leads to the conclusion that the scale should vary as L2-q, where L is the typical edge length and the units of energy are lengthq. The dimensional analysis goes like this: Let D be the perturbation of one vertex away from an equilibrium surface. In general, energy is quadratic around an equibrium, so
E = D2Lq-2
So the gradient of energy at the vertex is
grad E = 2D Lq-2
The motion is the scale times the gradient, which we want proportional to D, so
scale * grad E = scale * 2 D Lq-2 = D
So scale is on the order of L 2-q. Some examples:
Dimensional Dependence of Scale
EnergyEnergy dimension Scale Example file
Area of soapfilm L2 L0 quad.fe
Length of string L1 L1 flower.fe
Squared curvature of string L-1 L3 elastic8.fe
Squared mean curvature of soapfilm L0 L2 sqcube.fe
In particular, the scale for area evolution is independent of refinement, but for most other energies the scale decreases with refinement.

Another common influence on the scale for area evolution is the surface tension. Doing a liquid solder simulation in a system of units where the surface tension of facets is assigned a value 470, say, means that all calculated gradients are multiplied by 470, so the scale decreases by a factor of 470 to get the same geometric motion. Thus you should set scale_limit to be the inverse of the surface tension.


Back to top of Surface Evolver documentation. Index. evolver-2.30c.dfsg/doc/square-e4.gif0000644000175300017530000005344111410765113017477 0ustar hazelscthazelsctGIF89ad     $$$''('(''(((''('(((',,,//0/000//0/0444787788878;;;??@?@??@@@??@?@@@?CCCGGHGHGGHHHGGHGHHHGLLLOOPOPOOPPPOOPOPPPOTTTXWW[[[__`_`__```__`_```_cccghgghhhgghghhhgjjjoopopoopppoopopppotttwxwwxxxwwxwxxxw{{{!!B Image generated by AFPL Ghostscript BETA RELEASE (device=ppmraw) ,d#E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)R?sLa"D!"F*TD)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)R$"E)RH"E)RH"E)RH"E)RH"E)RH"E)RHݘ!  48ҥd"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E5jԨQFB5jԈ"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH]80QF5j4(K .1ĈS"NFQӅâH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E `H"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"ErD%EGb jԨQF}(ӈ A@QFb4AMH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E0RH"E)RH"E)RH"E)RH"E)R$E8bi?5jFG ?u1h2QѨQ#2Q$RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"QQdP#:hӨQ.1 jԨQ *dРAe4؈ F! (RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E):5SK#]RJF5aРA0dh ?%4ѨQF^&)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH")RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH5Q@FF]N jT#14jԨQFb4hPA74hTiԨQ8D)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RHF)RH"E)RH"E14ph4(Fh#OF5jԨQ24hPAdh=5j T"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RHtj)JAmRiԨKUʔ)SLE(3h C ǐ!C Tƀge OL$ &"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)RH"E)R?uSgΜ9vؙEEA]RI1舎AL(PK.] Cnj 4h C4hРB`bA 0!#G)HH"E)RH"E)RH"E)RH"E)RH"E)RH"E)R$"E)RH"%ϝ;w܉SN.SLdʔ)M<"B"D0bć`@4h4ti4HF%Gnj%#ϟ?Be4 4@ *Th!*Mt2hРATpKF!)RH"E)RH"E)RH"E)RH"EҤ #FAĈ>&L0a„ 0cƌ3f̘1cƌ3zPR ]ȐBLA ThР<4jԨF5#FԔPHǑ]bhC@ϟ??tO$ 4PA %*\R4hnTqQF8)RH"E)RH"E)RH"E)RHK0^tӥK.b ꒂ5j֬Qf͚5kԨQ\2YF5j֬Qf5jPJ< 4hB 4hFF5jԨQGh舎F0 AT^)'П@QxRdB 1РAebÅ  <#QQQ#."E)RH"E)RH"E)RH"E)R$+04hРA 4hРAeRQA5kԨQf5jԬY3f..֬QF5kԨQF5%EA dhРB(ӨQFYIQ(ӨQ 46*O?GTb xB͠AJ *4 6xxс @ 1ˌA\&i(H"E)RH"E)RH"E)RH"E@ e 4hРA 0X\$Ϡ? R&4AxF3ax 4h  Surface Evolver Documentation - Constraints

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Constraints and Boundaries

The usual mode of operation of the Surface Evolver is to minimize energy subject to constraints. There are two broad categories of constraints:


Level set constraints

A level-set constraint is a restriction of vertices to lie on the zero level-set of a function. The formula may include any expressions whose values are known to the Evolver, given the particular vertex. Most commonly one just uses the coordinates (x,y,z) of the vertex, but one can use variables, quantity values, or vertex extra attributes. Using a vertex extra attribute is a good way to customize one formula to individual vertices. For example, if there were a vertex extra attribute called zfix, one could force vertices to individual z values with one constraint with the formula z = zfix, after of course assigning proper values to zfix for each vertex. A level set constraint may have several roles:

  • Vertices may be required to lie on a constraint (equality constraint) or on one side (inequality constraint). A constraint may be declared GLOBAL, in which case it applies to all vertices. See mound.fe for an example.
  • A constraint may have an energy vectorfield associated with it that is integrated over edges lying in the constraint to give an energy. This is useful for specifying wall contact angles and for calculating gravitational energy. Integrals are not evaluated over edges that are FIXED. See mound.fe for an example. In the string model, the energy integrand is a single component evaluated on vertices on the constraint.
  • A constraint may have a content vectorfield associated with it that is integrated over edges lying in the constraint to give a volume contribution to a body whose boundary facets contain the edges. This is useful for getting correct volumes for bodies without completely surrounding them with otherwise useless facets. It is important to understand how the content is added to the body in order to get the signs right. The integral is evaluated along the positive direction of the edge. If the edge is positively oriented on a facet, and the facet is positively oriented on a body, then the integral is added to the body. This may wind up giving the opposite sign to the integrand from what you think may be natural. Integrals are not evaluated over edges that are FIXED. See tankex.fe for an example. In the string model, the content integrand is a single component evaluated on vertices on the constraint.
  • A constraint may be declared CONVEX, in which case edges in the constraint have an energy associated with them that is proportional to the area between the straight edge and the curved wall. This energy (referred to as "gap energy") is meant to compensate for the tendency for flat facets meeting a curved wall to minimize their area by lengthening some edges on the wall and shortening others, with the net effect of increasing the net gap between the edges and the wall. See tankex.fe for an example.

Level set constraints are declared in the top section of the datafile. They may be applied to vertices, edges, or facets. Constraints are usually applied to vertices and edges, as in mound.fe. Remember that you need to apply a constraint to an edge to get that constraint to apply to vertices created on that edge by refining. Sometimes one applies constraints to facets, usually to get the facet to conform to a predetermined shape. Be sure that the constraints applied to a vertex are linearly independent at the vertex.

Constraints are usually applied in the datafile vertices, edges, and faces sections, but they may also be set or removed with the set or unset commands. Example:

   set vertex[4] constraint 4
   unset edge constraint 1 where id < 10
It does not hurt to unset an element that isn't on the constraint. When a vertex is set to a constraint, the vertex coordinates are immediately projected to the constraint. Setting an edge on a constraint does not set its vertices. Likewise for facets.

One-sided constraints

If a level set constraint is declared NONNEGATIVE or NONPOSITIVE in the datafile, the vertices subject to the constraint must stay in that part of the domain of the level set function. It is usually unwise to give edge integrals to edges on one-sided constraints, or to declare them CONVEX. Whether a vertex exactly satisfies the constraint may be queried with the vertex hit_constraint attribute. The 'g' iteration step will check for a vertex wanting to leave a one-sided constraint it has hit, but hessian commands do not; therefore it is wise to intersperse 'g' with hessian or hessian_seek when there are one-sided constraints involved.

Example: Suppose one wanted to keep a bubble inside a spherical tank of radius 5. Then one would define the constraint in the datafile

constraint 1 nonpositive
formula: x^2 + y^2 + z^2 = 25
For purposes of evaluating nonnegativity or nonpositivity, all terms are shifted to the left side of the formula. One would then apply this constraint to all vertices, edges, and facets of the bubble surface.

If you define the real-valued vertex extra attribute one_sided_lagrange, the Lagrange multipliers for vertices hitting one-sided constraints will be recorded. one_sided_lagrange may be defined as an array. If a vertex hits more constraints than the size of one_sided_lagrange, then the first ones that fit will be recorded.



Parametric "boundary" curves and surfaces

Vertex locations may be given in terms of parameters on a parameterized curve or surface. Such curves or surfaces are called "boundaries" in Evolver terminology, since they are usually used as boundary curves of surfaces, for example a soap film on a wire loop could have the wire implemented as a boundary. Vertices, edges, and facets may be deemed to lie in a boundary. For a vertex, this means that the fundamental parameters of the vertex are the parameters of the boundary, and its coordinates are calculated from these. Vertices on boundaries may move during iteration, unless declared fixed. See cat.fe for an example.

Boundaries are defined in the top section of the datafile. Vertices on boundaries are listed in the datafile with their parameter values instead of their coordinates, with "boundary n" appended to each such vertex definition. Edges and faces on boundaries are defined as usual, but with "boundary n" appended to each definition. So the datafile has lines like these:

boundary 1 parameters 1
x1:  cos(p1)
x2:  sin(p1)
x3:  0.75
...
Vertices
1   0.0  boundary 1
2   pi/3 boundary 1
...
Edges
1   1 2 boundary 1
...

Putting an edge on a boundary means that vertices created on that edge will be on the boundary. An edge on a boundary must have at least one endpoint on the boundary, for use in extrapolating the boundary parameters of any created vertices. Extrapolating instead of interpolating midpoint parameters solves the problem of wrap-arounds on a boundary such as a circle or cylinder. However if you do want interpolation, you can use the keyword INTERP_BDRY_PARAM in the top of the datafile, or use the toggle command interp_bdry_param. Interpolation requires that both endpoints of an edge be on the same boundary, which cannot happen where edges on different boundaries meet. To handle that case, it is possible to add extra boundary information to a vertex by declaring two particular vertex extra attributes, extra_boundary and extra_boundary_param:

interp_bdry_param
define vertex attribute extra_boundary integer
define vertex attribute extra_boundary_param real[1]
Then declare attribute values on key vertices, for example
vertices
1    0.00  boundary 1   fixed extra_boundary 2 extra_boundary_param 2*pi
If the extra_boundary attribute is not set on a vertex when wanted, Evolver will silently fall back on interpolation.

Putting a face on a boundary means that all edges and vertices created from refining the face will be on the boundary. In this case, the boundary should have two parameters (or whatever the dimension of the surface is). This is good for getting a surface to conform to a known parametric shape.

Edges on boundaries have energy and content integrals like level-set constraints edges, but they are internally implemented as. named quantities.

Whether an element is on a particular boundary can be queried with the on_boundary Boolean attribute. Elements can be removed from boundaries with the unset command, but they cannot be set on boundaries. A typical use of unset is to define an initial surface using a 2-parameter boundary, refine a couple of times, then unset. Examples:

  list vertex where on_boundary 2
  unset vertex boundary 1 where on_boundary 1
  unset edge boundary 1
  unset facet boundary 1
It does not hurt to unset an element not on the boundary.

Vertex parameters can be accessed in expressions as the attribute p1 (and p2,... for further parameters). Vertex parameters can be changed with the set command. Example:

  print vertex[5].p1
  set vertex p1 p1+.1 where id < 4
  vertex[2].p1 := 3
It is not an error to access the parameters of a vertex not on a boundary as long as some vertex is on a boundary (so that space is allocated in the vertex structure for parameters).

A general guideline is to use constraints for two-dimensional walls and boundaries for one-dimensional wires. If you are using a boundary wire, you can probably declare the vertices and edges on the boundary to be FIXED. Then the boundary becomes just a guide for refining the boundary edges.

NOTE: A vertex on a boundary cannot also have constraints.


Named quantity constraints

See fixed named quantities.
Back to top of Surface Evolver documentation.

evolver-2.30c.dfsg/doc/square-e1.gif0000644000175300017530000006523611410765113017501 0ustar hazelscthazelsctGIF89ad   $$$''('(''(((''('(+++/0//000//555778787788877878887:::??@?@??@@@??@?@@@?DDDGGHGHGGHHHGGHGHHHGKKKOOPOPOOPPPOOPOPPPOTTTWWXWXWWXXXWWXWXXXW[[[_`__```_```_ddcgghghgghhhgghhgkkkoopoppppossswwxwxwwxxxwwxwxxxw{{{!!B Image generated by AFPL Ghostscript BETA RELEASE (device=ppmraw) ,dSJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*(F-ZѢC%BQFRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ**D%rt!G!Ŋ+X` *V,J*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*U*RJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*U*T9% !J(@&>0&LDB% ,QH2 &F`„&B0@Ç>|Ç->!UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*TRJ*UTRJ*UTRJJ!D9Z("3U( 1H Fȇ>>ha&PthM0"> p@Ď(m2tP X * *TPB *T}0JRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTR*СC1bt!G_TrJ)XLL` $PC  @#QDi#QDȇ&>| #Qc$|\> *TPBKkHB *T}TR~J*UTRJ*UTRJ*UTRJ*UTRJ*UT&>`>|aG(MDA(FdLJ$@>EP჉`pMBP pK#4hP)TPB U#!vфJӚ(h!N|&F>`G&" R@`Ev`I;t#T}aT)TJA`*UTRJ*UTRJ*UTRJ*UTRJ*UTR]A3)TPB &B )@x(mD `$JCmv$ > @5&BE O-*THjҢE&|X *TP&FL0%4B *TPcÎ(^|H hZ|0!#-v! *A5ićPTRJ*U*TRJ*UTRJ*UTRJ*UTRJ*UTRJF@Rt|0B*SPB &-B *TPE*I-hrH@et(*T|eG5PiZcE /Pp@QZ)T֘X *TPTKv|؁Ú`H;FH|رCk|p`(k\D-`ˡ&;Z|h@ /PB 5;Z JJ*UT)&RJ*UTRJ*UTRJ*UTRJ*UTRJ*)fJ*U-ΐ2 &-BUP(&Zh H`B *T"1⃑5F>A#Zx ̇5hc&TPq" ;B *T|XC&QL"BP"դ TPdaG5"B *TThB U$5FZ|G*Tk>ٴfG^"Bꌂ ->R'P.Q4a@ODy$ʓ'M(`@&xiB *TPBՄTPB *T|h*TPBꐑ7$ԩSPERJB /M4w4iҤI2eʔS1FJJ*HTRJ*UTRJ*UTRJ*UTRJ*UTR@QRL:UT)G 4Î(kiB *T}hHQ|B *Tk|b< Ҥ>|cɓ'tpRP:eʔ)SLuiA#Z|e&TPBTPB *TPBԙ-hŇ,J2u MHBT @Z4!M>A*/ ZBe*T(Ȕ)SN`ʔ)nTRJ*UTRJ*UTRJ*UTRJ*UTRJ*J*uTRf>jBRjÍ-Q4iI(M@`LJA<Ѕ2uԩSLL)MJBu)SNr.J*U (M>d&P4Ç>|ФI#>|@SPBuJT)T(BR} uRPCTLjBGSL2uSLtR ȤJ*UTRJ*TRJ*UTRJ*UTRJ*UTn*UTRn1USN*uT)TJei @ @(@)SP:UԨgN0ԩSL2C$SJyi)SLbDS(2eRJ*uH -Z!*TNBU RPaLZ*uR1AeԙLHdRZ4!*Sg(0!#RM(A MH=iȔ)SF2e*M7>|h*UTRJ*UTRJ*UTRJ*UTRJ `TR:`TR4}sRgΔ2U U)TJ}jԙ&P:u)SH8TL2eʔ)@2eчSL2e*K PN2RL:eSLäTRP*J:(tE3L2u USP"FRv`儂TN*AS)TJjф:PB #NPBuBMH™SZBuԓLyrԩSNҔ@TRJ*UTRJ*UTRJ*UTRJ*UTRJBR}pTꔩRJ} ԩRJrDQ)TZ:TSJTStn2u)SYZ:eʔSfr2ejT%L2e)RH25*SN:uT RL*UJ2cTRδhqԩSP"դRJjtTON(9eJBF(*u R>s)Tg(p)RM(ԩSF=iŨSNҢ0RJ*UTRJ*UTRJ*UTRJ*UTRJ*'J1!ԩRP*uIRJ:ӂTRJ@ U)TPe؂T)TN唩RZd1Uʔ)SLZrƔL:u)SL1r:L2U)S:U*"7>*UTRJ:Uꓙ-ΐBuRJ*UԇOJ*u惚Snh:)TP ɍZ0!@>|h@3GN:uJ7hPR*UTRJ*UTRJ*UTRJ*UTRJ*UTLpTRL*TP:uHSP:u ЎRNjT)TN2eS>2e)SL4uT 4*uԩSL:U*ȓJ:u)SJO-Δ*UTR70*UTRJ}2C!RJ*UTRgΔ*UTRf>dqԢT)T5K J*UT< ZP*U'@-aI((C%JPTRH*JTRJ*UTRJ*UTRJ*UTRJ*UTRC34*eTR)SN:eʔ)TFrPRL*e*R$&LZ|h(QD %J(QD $ GL2e S>8*UԩRJ52RTRJ*U*ˇCJ>)UTRJPTRJ*e3YZPT)RyjTRJ8KRH}pIRJ2@IRJ">J*ERJ*UTRJ*UTRJ*UTRJ*UTRJ*,y(aƇLr!MN*uTSJ@@iÇ7> @n|*UTRK*UCRJ*UJ,J僙OJ*UʈRL*UT)@ DTRJP |Ⴧ>|!@&LɓORJ2SRH*ETR(ECRH"UT)RfjTRJ*EԧMTR{|JTR(ÞRJ#UTRJ*UTRJ*UTRJ*UTRJ*UTR}TRH*ET)3:pP*ETRJ*UԘ@*UTRJ*UFR>*UTRJAT)2fJ*UTR UԊNH*UTRJ0T8*UTR>*UTH*UT)RJRZ|*UT9&" <*ETO-H*áR()UJaRJ@J*RO@|"U*ORJ*UTRJ*UTRJ*UTRJ*UTRJJ RJ*ET)RfjTTRJ*UT)37J}PRJ*EL RHQRTRJ2STRJ} UTRJQRq*UTRHaiTRjL*U'LJj `R}SR&R҇R&}RJjC!OR J*UTRJ*UTRJ*UTRJ*UTRJ*U<>*U)R(UIJ*ERct*U RH"UJ*URH*eFRJ*eLRHSRHPT)RH*UK)R*ET)R|hq3(*UT)RH"UTRJTE-&*ER>*E*9H:RJp(RJ*ŞJ*% |J* J>P*UԧJJA*UTRJ*UTRJ*UTRJ*UTRJ*UTR1BTRHARt"UTR(UT)R>*ET25J*UT)J)J*ULRJ"UTH*eRH*UJIRJ"URJPTRH*EjSxÇJ0Ç>|D L*UT)RH҆TRDԇOJ*E %'>*UTC)UTCP(UT)J-QTR|`NTR*UTRJ*UTRJ*UTRJ*UTRJ%A)RH2cTRJATRJ}QRJQRTRdj*ET)RJCH*UTRȐ)ULRJ*E)J*H*UT 0H"E !RH*URjDQBLRH"UTRJ*ER@ RJ*ERRH"RfĔ*UTRPR<` @A=z|b RJ!RJ*ERJ*UTRJ*UTRJ*UTRL*UTRJ*(tJ*eRJ*ER RJ2STRH"U%R UTRJ*C)36J*UT)RH"H"UTRJTTfH*ETC@H"ERHB/̐*ETRJ*UTR>|!E)$)(*E)RJ*EO@ZPPTRH1BA%J`ɓ'O_(QŇ->|hI -`aD &|J*UTRJ*UTRJ*UTRJ*UTRJ*UT;Z*eFR*UTR5&*eFRJ*UT)J-JQTRH*UT),'j*E)RJ*U5H"UT)RH* R5"URH} UTRH)RḨRJ*E)RhD)RHE:T)RH19DT)RJáRy8q),"ER(t8DT)R CT|>|C@('OOH""E/H"E)RH"E)<'|)RH"(P@"EJJ J*E<*h"d CH"E6&}ST)RH2B)RV:D)R` )RH*UTRJ*UTRJ*UTRJ*UTRJ*UTJhC)RH"E<5XD"E)RH"EMN"ERH!)R5&"E)RHiH"E)RHRC(GQT0)RH՘D xD"E)RHE)Mt(#J3)RH"E)PN`9T<-h"ET(P!E)Rj"RH)CԡHmJ"ԧXJ*5 @TRJ*UTRJ*UTRJ*UTRJ*UTRJ)RJ"E)R>h)RH"E& H1RH"E!RHaB)RH)R"E)RH"( H"KX*˧H"E)RDQBC I0 5E  4 %m$C<)h"T(RN"E)RHA)RJj"5)R8}CjR R>B3HaA;RJ*UTRJ*UTRJ*UTRJ*UTRJ*"H"E)R4QDNH"E)R"E RH"UT 3H"ERH"EFRH*)RH"E  RD!5jԧQHHY$L()QD"E O(P)RH"E)hEOB:A)/'>"EKRHjH"ũR5&"E K O>"E͇O(*UTRJ*UTRJ*UTRJ*UTRJ*UT!E)RHqp)Rb"E)Rn!E)R4"E),XH"E),mH"E/H"ERH"EԡH}"'RH}"HQ8dHH"E'RH"E>}錇CH"E'RH"(@AT(RBtOp)M(H"E! ()E)RXjAT)Rq>tRR("@NTRJ*UTRJ*UTRJ*UTRJ*UTR)RH"U )RJ@)RH)1CRHQB)R_j"E)R>} E)35H"E)R@"E)M4}D R> EO@HCgHH E'RH}"ԧ/4>}ӧON!E)QH}"E)RQ4h MH"E!#HRRl"U)R>@)Rj'RH:D!RH  RJ*UTRJ*UTRJ*UTRJ*UTRJ3H"E RH"EJLRHRRH"E&R U)RH"Uc)RxN"E)RH B)R>(RQ|"(RHӧOt4pD*)RH}"E)P^h|ӧO>} RH}"E)Rj ԧO4"E )R6|"E)M4*)RH"ERH"EP%H"EJSH*ETPJ*UTRJ*UTRJ*UTRJ*UTRJ*UT=>"EH"E)MH"u)RH"U)3dHI)RH*ET }D)RH"E)RHXO>B*O>"ƧO>}H>}"E'RӧO>}'L'"E)R}ӧO>} (RH:'R>2RcS RH"Eԡ >|Æ(Ƈ@|'OI*UTRJ*UTRJ*U*RJ*UTRJ*UTRU)<)H"ERH1CMH"E)RH}A)RH"E)R(h)RH"E) HO>DJ'RH}" ωO>}ӧOhp)RD!ӧO>}ӧO>}J)RHy'R@} ԧJHDPB"T4"u I유)RB )RH2ԁ%J@@TRJ*UTRJ*UTRJ*UTRJ*UTRJ@R>h"E)RH"郦H"E)RH"}ӧO>}FH>AӧO>}ӧO>}FHӧO>}ӧO>y9(G4H}"E*)R%b5H"MH"E*XH"ET '4"E_RJ*UTRJ*UTRJ*UTRJ*UTRJ*>h"E)RH"ETV`Q |pHJ Ir%J(Q$@$hh HH"PBD*O|'M(}ӧO>}ӧOhаCƧO>}ӧOСC:t@ȧO>}'P@}􉎆HHi ET(RHiRrRH"%#("E*)Q'4)A)R<)R8H*UTRJ*UTRJ*UTRJ*UTRJ*UT%L|Ň i!RH"E 4")RH"ԗ#'H}""'RH}rTO4ӧO>}ӧO>ir 4hÉ$F1Ĉ#Gr&h0O>}ӧO4!$'R@iBr' 4"EJ)Q4Ҵ)RH}B RHRMH QTRJ*UTRJ*UTRJ*UTRJ*UTRB I5v"ET(R'RH")@'HBO>"D*T(RH" L%OD" R>ӧO>}':Qh0at(ӧO>}ӧO>}}Dʚ(Fi:rΧO8T(RB& _H")R_:h"E*R R("E RJ*UTRJ*UTRJ*UTRJ*UTRJ@H"EPD@KRHDO8h"EOHD*)M4DԧO>D)RDiM@"T( HӧO>D 'б'@>iDO>}ӧO>}y>iԄO>} #'Nx@'#'ThRrΧO>}Z哑B" RHB)HA)R,4)RmJ*UTRJ*UTRJ*UTRJ*UTRJ*UT&h E)RHDA(P>DOH) H"'R8"RH}"%'RxӧO>2MT'Jt0AJ>}'@&yҤt~ӧO>}'J|IS':}ӧO>} #4NpOD}IO E )PH"EFRD!ETHQB&_HJ&UTRJ*UTRJ*UTRJ*UTRJ*UTR (R@ E PH%C)RH)RD*)RHXC)RF}DOHS:>}'G^Nh 'hDӧO>}ӧO4&M&BӧO>}'JlISN5ӧO>}ӧO>}H@>DӧO'>}ePH(RJh")R"uD)RHBJTRJ*UTRJ*UTRJ*UTRJ*UTRJRB"E/>"(RHƧOB"(RHb'RHB)PHӧP|I'Q>}HF>}ӧO>}ӧO>y铦O4}N:>}ӧO4IS'M4}4ɄO>}ӧO>}!'4I'RHq'@4 e' O>BAHI!%mH"EGJ*UTRJ*UTRJ*UTRJ*UTRJ*U*%H"E)/@H"EԧHD RH"T5B"O>D*(RH}%Q>}:AӧO>}% @ӧO>}ӧO>}&M4yɓ&MvӧO(iࣩO:iꤩO;h|ӧO>}EäO>QԧP>}ZPiDH"Eԗh"E)M E)RJph*UTRJ*UTRJ*UTRJ*UTRJ*UT>C)QNE'RD!I& DD )Rh"JMH}" ( >D @>}ħO>ӧO'| t>}ӧO>}dD˦M]G'M4}I'M4}rҧO>}DO>ii /H}IO>Ƀ R4" EJ)Q6P E)RH )RH@TRJ*UTRJ*UTRJ*UTRJ*UTRJU'R4i(R>}")R}"T(R>: ) >E)RD!'%^>}ӧ(tH}ӧOxiӧN8AӧO>} M6uI'O>iI&xP!O>iꤩN>}IS >} :6}ӧOh|ӧO'H}'MT(RH J<_(I$$JG *RJ*UTRJ0UTRJ*UTRJ*UTRJ*UH}:)RH"E5|!T(RHER4%'RH} Okj|IԧO)O>'M4}􉒑t>}iIӧO>iƧODqO>i:)RDiZD 'Ƀǎ#R (G%,J*UTRJ*UTRJ*UTRJ*UTR*UTD! )RBD'RN!*)RB E)M *O>D* M}ӧO>}tӧO>}&M4}IӧO&1A'@&iI&M&ѡň hhadG &1bD ;Lh È#MБC ;6}ӧO>}&.5>}˧P>}9AKP(hC)RHQC)RHi'UTRJ*UTRJ*UTRJ*UTRJ*UTRaI)RH}"E'RO}TäO>}'M:mgc 4Lha :4}i& x4ءSN>iIӧCtСcĈ#0BgҧO>i铦O>eѠ铦H}'Pv)RHRBH"ETRJ*UTRJ*UTRJ*UTRJ*UTRJ @5J 9 i:qЧO>}ӧOQ| @4}Ҥ'M>}$:F4# :O>}ӧO?9ӧ#M>i铦O<}t &%3iI&MѤI%Mx4Ѥ&M4q'M4ȢIS >}DLj 4ӧOz4('QtN(铇/4DT(RDB)R|!UB RHQ0*UTRJ*UTRJ*UTRJ*UTRJ*UTD)RH"%("E*)R>E O'4DO>%jM '>Dq'Q>ӧ348mӧOeF<}ڔe&M'M4}IӧO4e&M4Q2'M8i&O>iE&Mh铦O>McD^|:Ot>ӧO'4ҠO@DԗHt)M"@TRJ*UTRJ*UTRJ*UTRJ*UTRJ@ PH"E*55HT(R>Ej FyBOH}"t| kD}铇O4LIӧiD#&M4itCM:}ӧO1 <Ј'RBEY HH )PD MHiҰsRJ*UTRJ*UTRJ*UTRJ*UTRJ*՗H"'R4h"E*PB")@>'RHD*'M}2ӧOE9q‹OIh8 'Й铦O>}iH4iҤI&#&&iɓ&O}HO4DH"&Rk"EJS/jH*UTRJ*UTRJ*UTRJ*UTRJ*UT!EjO4ip)RQ֐")Q(h"ԧDDONħO>}"&/4< #t:}IO>y'y4iҤI&M4y`'M4m4IM4iI&M4Q҄E$M>}ԉM4iҤI&MӧO4}ӧO&i򉆗O>DӧF})RhD E*)@"@T(RhD TTRJ*UTRJ*UTRJ*UTRJ*UTRq(RHjaO>i:AJOH}OT(RH)ӧ(x>}铨O>}tC:>}铧O}'Mhɓ&@>ӧONh&_HZT(RHP RB"$RJ*UTRJ*UTRJ*UTRJ*UTRJjGH"%J(R>D&R>OB}D'RHCӧO%DӧO>} Nhh'M4iӧOФI&M4iҤI&MhI&X4a¤I%M4iD&J49E&Y4iҤI&M4iҤI&M-v|I'M>511铦O>e'R>EYC'Rh|PH$EJS/@H"E/J*UTRJ*UTRJ*UTRJ*UTRJ*UT "ENOH"E/^F"'RH  R>郦O>}ӧO>} I# >utH:>}铦O>1I&M4iҤI&M4iiIMhҤI%M4i҄I&M4iI #0hҤI&M4iҤI&M4iⓧO>Ma'M<} M>ȧO>}DIH E*)M")R*UTJ*UTRJ*UTRJ*UTRJ*UTRI@ꓣ H)RHe E OBO@}"IG>}%*ԉO>}'L}IӧOdh铦O:y!&M4iҤI&M4iҤIS6;0bH 0`F ؁#fѤI&M4iҤI>yʣO>i&Mt4hSB}"Iԧ(6!EJ)RQ AI>aCTRJ*UTRJ*UTRJ*UTSJ*UTRJU ME)R>}"E# @} )OHBӧvD}ӧOJ|ԧOh'N4}tȈ :>uDM4iҤI&M bĄ 4y&M(iҤ&M4iҤI%M,EC$M1bĄL1Ҧ!MhtF!N>i'M>yAONhӧP>}@)@TP|PH"E$RJ*UTRJ*UTRJ*UTRJ*UTRJ*4E*)H"E '}EO>(RQ|I'Q>IԧOD5i'O>qɓO,4it(#Lha M4i74iҤI&J(iҤ%M4YB&<"iҤI&M4iҔ#FLh A:8}􉓦O>}S >}&'Q>}"iMD)R|DR>H*UTRJ*UTRJ*UTRJ*UTRJ*UTPP$#F9$#hOH}D@ӧO>}çO>}CӧO>qO>}DɈ #`B'm iҤI&M4iҤL(Y1H%L4iD&M4i%K(G&M4iҤI&M4iҤIS #L0AKO>i'M(eD O>}"ӗ("E)RHEis!RH2TTRJ*UTRJ*UTRJ*UTRJ*UTR"O}ӇO>}r O>uӧN4uԉN$0sH&M4iҤI&M4iҤI,(iʤ <4iDI&M(i G(QD <4iҤI&M4iҤIY4EҐӡ3F4'O4}4IO>} ^H )RH"5I)Mڔ"ET9RJ*UTRJ*UTRJ*UTRJ*UTRJ RJ"E)RHp)RH҄%j|`ҧO>}OG|I!M>ii'M>1A:(DjAM4iҤI&M4iҤI&3-(eD)&J0Ѥɒ&M4iҤI(QҤ%M0ѤI&M4iҤI&M-hҤ).>}鐗'49sH'M4ӗ_HE&XH"EH)R@J*UTRJ*UTRJ*UTRJ*UTRJ*U*S̐"E)RH:AH"E)RH"U#A8ӧO>I3>u&Jgvh0ѤϧO:iȣIS;$iҤI&M4iҤI<eR&J4Qrd&K,iDI%GPҤ%M(ird"&M4iҤI&MfdѤI&MZx'N>i(K &1Aa_HDF)Q)R!EHM(UTRJ*UTRJ*UTRJ*UTRJ*UTR@)RH"C$R"E)RH"E ȗ(k>Z3>}ç(F<}P&h4IӧO:}ځE&M4G&M4iҤI&M4ʔL4QD4QҤI&M-%M(QDIX4iҤI&M4I&M4iȧO>}SO>}ӄF_D"%O!E͇NHT)R|DjTRJ*UTRJ*UTRJ*UTRJ*UTRJE @$RH*E@H")RH"E !'R@'tLhӄ Y}ӧO>iE&M4iҤю4iҤI&M4hIL4QK`pI&Mv`D&J(iDI&L^ФI&MjE&M4iҤI&&06}I'M4i'#4OHr'R("ERH"uá6JJ*UTRJ*UTRJ*UTRJ*UTRJ*U* 5H"EfH"ERH"E)<5>"u)R>"EJ>РaǙI:mӧO>})M4iҤI&MLhI&M4i2DS&J(iʔIӥK.iM2eiA&M(iD &J0a҄&M4ijE&M4iҤI&MИ&O?i'J&| RH"RHҤ )RXܐ"U)M'$*UTRJ*UTRJ*UTRJ*UTRJ*UTD)RJ"E)R@"E)R>h" T G>D)RHiJ@ơO>iԩӧO>}I% 4iҤI&M4iɄM4iDEJ4eҔI%J.i&Ghjc"&M(iDI&M4QDɒ%,&"iD#&M4iҤI&M4idFäO>i&=}% m@D)4""(JHIQ)RHaATRJ*UTRJ*UTRJ*UTRJ*UTRJE@ )R E)RHEB)R4Q)P_|"'R>tЧC4hIӧM:}ҤSO'hҤI&M4iI&Mf4Ѥ)G*UR&J4]DIӥK4ijqL(iD&J(i &L4M4iҤI&M4iҤI<&}铦O^LPӧO(шO|DT(R)RH"E @RJ*UTRJ*UTRJ*UTRJ*UTRJ* HBA)RH"E)RHi'R>iT(RB"O>}:DšO:}'MdѤI&M4iҤI&M4idH$ (i„I6m`bH ZЀDA L)D 0`@DLڴqI&M4iҤI&M4y'NhӧO>})J RHt)R>"6H"G `J*UTRJ*UTRJ `TRJ*UTRJ*UTA)RH"E(mH"Uc O>"O>" ԗ>}'G4hI'M>iI&M4iҤI&M4i`B@(etp%J(YD &M4QD&,0ybd&41NM4iҤ)<i@ӧO>}ӧOh4h ER>"Ej)@NQ҂CJ@*UTRJ*UTRJ*UTRJ*UTRJ*UTR}XC$RH"E)R" E RB}"' H}"54>}ӧO>}JDN}ӧO>}D)R@")R4PRJ*E*ATRJ*UTRJ*UTRJ*UTRJ*UTRJ@ }ӧC44DIC$M4jĈviF&M4iQɑ ,0et%J4Q%KȴD%:i&M4eD)%M`p# Y4iҤI&M4iҤI&My FӧO>}% _H2)R49KH*UT), RJ*UTRJ*UTRJ*UTRJ*UTRJ*U*@(i%I!E HDJ)RH}@O(PӧO>ӧO>}bDCF4`H<4iҤI&M4i:%JnD%J(QI%J(%J(yǒ%J(Y)&J29z&J0I&M4iҤI&M4i҄'#4>rdÉFӧO>8B)R4)iTI5&RTR>ȓ*UTRJ*UTRJ*UTRJ*UTRJ*UT$(UTR1äh"P>D'R4iӧO>}ӧO1D  iҤI&MjҤI&M4i  J4Qń#M(Q%J,Qr%J(QD;4QDISL2Qr&M4aDM4iҤI&M4iҤ) M4e@ӧO>bDÉF 4YT)RH} E KIJ*FTRJ*UTRJ*UTRJ*UTRJ*UTR RH*UR>PrCAH(tH:AӧO>}铣3F6'/44G&M4iҤI&M4iҤ)ƗJ(QD P%J(QDiJ(QD%J0`I%J2iʤM0a &LLbhҤI&M4iҤI>iI@>}ӧO>bD 8T)RH")RX`)ETRJ*UTRJ*UTRJ*UTRJ*UTRJ*UJ*UR}Ф)Ryx'RD˧O>}rƈ@ӧOtLhҤI #4iҤI&M4iҤIS#2eDI%MiBI%M(iTIJ(QD%(Q%J4QDI ,,YDI%M0a2C&M4iҤI&,4>iI&M4ӧO>}ӧO>yI RH"UԤJ"%RPI*UTRJ*UTRJ*UTRJ*UTRJ*UT}|"URJ! H"Ej)D"ˎOp@>}ӧOiI'MhI&M4iҤI6&4QDI%J(eJ4QR(QD%J(QDR &(QdI4i@M*Q¤ 4iҤI&MXhhI'M4u#ʧO>}ӧO>}rBӧ$*<"ET)RZ"EC² UTRJ*UTRJ*UTRJ*UTRJ*UTRrTRJ:AS)R>*UT)RHizSELӧO>}ӧO>U0I&O>itHM4iҤI&M08D%J(i҄%JFZ`DI&@QD%J(QD%JФɒ5&(iD%M4U҄&Jx0DҤI&MYf| N:iIS&4(}ӧO>}D(RH)RHr)RJR҂9RJ*UTRJ*UTRJ*UTRJ*UTRJ KRJ"u&RHr)RJ"ECh8ӧO>}ӧO>}!'M4i铦OMNҤI&M4a&J(QD%M(a%J4D%J(QD%J(Q%Kx08DI&J*U&M4a &M褩O>iꤩ'MhӧO>}rPRHEJ*URH*Eԡ JJ*UTRJ*UTRJ*UTRJ*UTRJ*UO*UMH"URH"ER4h" G>}ӧO>}M8iI&M4i:d&M4iL(QDI%M(YDx,XC%J(QD%J(QD%,apDI&J*QDIS%M(a„ɒ# y4a'M4uI'M4}'O>}D RHE'Rx(!EJHT)RH)UTRJ*UTRJ*UTRJ*UTRJ*UTRhQԡH"UR)H"E)RiD)RhHӧO>}/iI&&iҔEHL(iDIK(iD&J=0b͚5mڴY"@!"H @|`Ȑ!F1"Ĉ LڴiӦM6m$0B&M4i铦M:iII&|H MDDjT(RHR"E)R:1@ITRJ*UTRL*UTRJ*UTRJ*UTRJuC<"E)RH٘TT)RHҠ)R>}"F>}ӧ.44yҤIS'M4iҤ&M4%O K(iM5D!bB"$0hQ"!@PifM6kڬafG(Qˆ<4QjM5mִiӦ F1!@ LѠA#FCΡN:y/H"E(R>'<:)R>)pRJ*UTRJ*UTRJ*UTRJ*UTJ*U*5>h"E)RH" )RH:AP>"EPhӧO4hҤɓO4iҤ&M1cHh#Bڴiӆ%J(QD F7ZPD%J(QD%J(QD J0DI%K,eDIK4Y GFhx괩'NСClj#Fhh@cG(iOFE)RHiR>1`TRI*UTRJ*UTRJ*UTRJ*UTRJ*UTePD)RH"EԇRI"u&RH"E'RH :iI'M1cĈ ;40bL:hC%M(YD&J(QT# t(Q҂%J(QD%J(QD (Qb%J(iL4Yɒ4irbҦM4}ҤIS'M>iI'?d8O%I>hA%J੣T 1pTRJ1"UTRJ*UTRJ*UTRJ*UTRJ*UTRpE ,H"ER5&"u(&RHD)QH"E)(hÈ 240bC4iҤIOLJ(QD&J(iD&L/PDҕ(QD%J(QDEQDX0QDI%M2]%MhdIy4iɓO4iI'Ny:L MHE)RD hTRJ*UORJ*UTRJ*UTRJ*UTRJ*UTRJGR )RHBMHD)RH١ɉ'N `BäN4iҤI&M4}IO4I&G&PDI&J(QDɒ%KD%J`hD%J(QD%JFСD%J(RL(itIӥKZ|ѤIKa0I'M4iIOlӧOt('RH"EOHr#χJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*U*E(*U,-ZDġH"$hp8$ @HҤϧ3tҤI&M>i&M4eɒ&M4i%J4i %K(1A%J(Qd J(QD%J(ݸB%J(QD M4eDI&J(%M,iEN<}Ҥ'M>0ӧO<}&RH"E*'RHҴL)R|ATRJAP*UTRJ*UTRJ*UTRJ*UTRJ*UTR)`RTRJtƇhh/x"}"E'RHIO>`DO>iҤI&M4}2B%M,iҤ G4eD%M(9%J(QD t(Q%J(Qr%J(QD%JXZhRL4Q҂&M4YDI&MLhLҤI'M4}ʣҧO>}':'RH}"!RJ*U*OL*UJTRJ*UTRJ*UTRJ*UTRJ*UTRJ%f@RJ*UTRYXi)RH}"EOH"EJ<>}S 4iIO4iҔGC$M(iҤI&LxСD&J(Q%J(QD%JaC%J(Q3%J(QD%J(ah‘&J(id&M4i&M4i2!&M4iGO>}ӧO>QD)RH"5*ʊOJ"UT)R&ոAT)#RJ*UTRJ*UTRJ*UTRJ*UTRJ*UfJ2UTRZ`)EjR B}"EԨOH"%& >}ӧOhI&M4iiH(iҤ&J4ir &J.iJ(QD%J(QCJ(Q"J(QD%J(QD G(i҄#M4iҤI%J(ԤIy>i)J>}'O>} xH}"EJ ,H"ERJ*ER(*UTRJ*UTRJ*UTRJ*UTRJ*UTx|(UTR}ST)R&ըԧOH"E*T(M|ӧO>})C4iҤI$ x4QҤI&M4iDIJ4QD"(QD%J(QD%J-PD<(QD%J(QD%Jt08D@0Y҄I&J4iҤ)&MLd)J>}ӧO~ӧO(BT yJ"ETRH"ER&ոGTRJ*UTRJ*UTRJ*UTRJ*UTR@SSJaTRJ*EjR HDJ'R4铧M>uӧO4}!:tСC:ti@(QD ?|Ç>|!RJ*UTRJ*UTRJ*UTRJ*UTRJ*Յ JRJ*ER&)R4iӧO>}ӧO>}dD$MvdѤI&M4QҤI&M4i G8D%J(QD$J(QD_ZPDҚ5kڬY͚6k`ȍ&(1bĈ0` 0h@`⃈&>`"D}ӧO>}'@d١M6mڴiӦM6m1b &< 0` 0`` &>PCVTREJ)XH2"D)BPCJJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*TRJ*UTRB$A Surface Evolver Documentation: named quantities

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Named quantities and methods

This is the systematic scheme of calculating global quantities such as area, volume, and surface integrals that replaces the original ad hoc scheme in the Evolver. Briefly, methods are built-in functions, and named quantities are combinations of instances of methods. See the ringblob datafile for an example. The original ad hoc calculations are still the default where they exist, but all new quantities are being added in the named quantity scheme. Some new features will work only with named quantities. To convert everything to named quantities, start Evolver with the -q option or use the convert_to_quantities command. This has not been made the default since named quantities can be slower than the originals.

The sample datafiles qcube.fe, qmound.fe, and ringblob.fe contains some examples of named quantities and instances. The first two are quantity versions of cube.fe and mound.fe. These illustrate the most general and useful methods, namely facet_vector_integral, facet_scalar_integral, and edge_vector_integral, rather than the faster but more specialized methods such as facet_area. My advice is that the user stick to the old implicit methods for area, volume, and gravitational energy, and use named quantities only for specialized circumstances.


Chapter contents:


Named methods

A "method" is a way of calculating a scalar value from some particular type of element (vertex, edge, facet, body). Each method is implemented internally as a set of functions for calculating the value and its gradient as a function of vertex positions. The most common methods also have Hessian functions. Methods are referred to by their names.

See Implemented methods for a list of available methods. Adding a new method involves writing C routines to calculate the value and the gradient (and maybe the Hessian) as functions of vertex coordinates, adding the function declarations to quantity.h, and adding a structure to the method declaration array in quantity.c. All the other syntax for invoking it from the datafile is already in place.


Method instances

A "method instance" is the sum of a particular method applied to a particular set of geometric elements. Some methods (like facet_area) are completely self-contained. Others (like facet_vector_integral) require the user to specify some further information. For these, each instance has a specification of this further information. Method instances are defined in the datafile, and may either be unnamed parts of named quantity definitions or separate named method instances for inclusion in named quantities. The separate named version is useful if you want to inspect instance values for the whole surface or individual elements. An instance total value can be printed with the A commands, or may be referred to as "instancename.value" in commands. The instance name itself may be used as an element attribute. For example, supposing there is an instance named moment, which applies to facets. Then typical commands would be
  print moment.value
  print facet[3].moment
  list facet where moment > 0.1
Modulus. Every method instance has a "modulus", which is multiplied times the basic method value to give the instance value. A modulus of 0 causes the entire instance calculation to be omitted whenever quantities are calculated. The modulus may be set in the datafile or with the A command or by assignment. Example commands:
  print moment.modulus
  moment.modulus := 1.3
A method instance may be declared to use a different modulus for each element by specifying an element extra attribute to use for that purpose. The extra attribute has to have already been declared. Example:
define facet attribute mymod real
quantity myquant energy method facet_area global element_modulus mymod
Of course, it is up to the user to properly initialize the values of the extra attribute.

Orientation. Some methods, those that logically depend on the orientation of the element, can be applied with a relative orientation. When applied to individual elements in the datafile, a negative orientation is indicated by a '-' after the instance name. When applied at runtime with the set command, the orientation will be negative if the element is generated with negative orientation, i.e. set body[1].facet method_instance qqq. The methods currently implementing this feature are: edge_vector_integral, string_gravity, facet_vector_integral, facet_2form_integral, facet_volume, facet_torus_volume, simplex_vector_integral, simplex_k_vector_integral, edge_k_vector_integral, gravity_method, and full_gravity_method.


Named quantities

A "named quantity" is the sum total of various method instances, although usually just one instance is involved. The instances need not apply to the same type of element; for example, both facet and edge integrals may be needed to define a volume quantity. Each named quantity is one of four types:
  • "energy" quantities which are added to the total energy of the surface;
  • "fixed" quantities that are constrained to a fixed target value (by Newton steps at each iteration); and
  • "conserved" quantities are like fixed, but the value is irrelevant. The quantity gradient is used to eliminate a degree of freedom in motion. Rarely used, but useful to eliminate rotational degree of freedom, for example. Will not work with optimizing parameters, since they do gradients by differences.
  • "info_only" quantities whose values are merely reported to the user.
This type is initially set in a quantity's datafile declaration. A quantity can be toggled between fixed and info_only with the "fix quantityname" and "unfix quantityname" commands.

The value of a quantity may be displayed with the A or v commands, or as an expression "quantityname.value". Furthermore, using the quantity name as an element attribute evaluates to the sum of all the applicable component instance values on that element. For example, supposing there is a quantity named vol, one could do

  print vol.value
  print facet[2].vol
  histogram(facet,vol)

Modulus. Each quantity has a "modulus", which is just a scalar multiplier for the sum of all instance values. A modulus of 0 will turn off calculation of all the instances. The modulus can be set in the datafile declaration, with the A command, or by assignment:

 quantityname.modulus := 1.2 

Target value. Each fixed quantity has a target value, to which the Evolver attempts to constraint the quantity value. Each time an iteration is done ( g command or the various Hessian commands), Newton's Method is used to project the surface to the constrained values. The target value can be displayed with the A or v commands, or as "quantityname.target". It can be changed with the A command or by assignment. Example:

  print qname.target
  qname.target := 3.12

Volconst. A quantity can have a constant value added to it, similar to the body attribute volconst. This quantity attribute is also called volconst. It is useful for adding in known values of say integrals that are omitted from the actual calculation. It can be set in the quantity's datafile definition, or by an assignment command.

Pressure. Each fixed quantity has a Lagrange multiplier associated to it. The Lagrange multiplier of a constraint is the rate of energy change with respect to the constraint target value. For a volume constraint, the Lagrange multiplier is just the pressure. Lagrange multipliers are calculated whenever an iteration step is done. They may be displayed with the v command in the "pressure" column, or as an expression "quantityname.pressure".

Tolerance. A fixed quantity can have a tolerance attribute, which is used to judge convergence. A surface is deemed converged when the sum of all ratios of quantity discrepancies to tolerances is less than 1. This sum also includes bodies of fixed volume. If the tolerance is not set or is negative, the value of the variable target_tolerance is used, which has a default value of 0.0001.

Function quantities. Instead of being a simple sum of methods, a named quantity can be an arbitrary function of named method values. The datafile syntax has "function expression" instead of a method list. For example:

  method_instance qwerty method facet_scalar_integral
     scalar_integrand: x^2
   quantity foobar energy function qwerty.value^3
Note the method name is used with a "value" suffix. Also note that the method values used are global values, not element-wise. Quantity functions can do Hessian operations, if the component methods have Hessians. Such hessians can become quite memory consuming in default dense matrix form; there is a toggle command function_quantity_sparse that will cause sparse matrices to be used.

Example. The sample datafile column.fe contains some examples of named quantities and instances.

Future. It is planned that eventually all energies and global constraints will be converted to named quantity system. However, existing syntax will remain valid wherever possible. Starting Evolver with the -q option will do this conversion now.


Implemented methods

The currently implemented methods are listed here, grouped somewhat by nature.

0-dimensional

1-dimensional

2-dimensional

2-D Curvatures

General dimensions

Knot energies

Elastic stretching energies

Weird and miscellaneous


Method descriptions

The descriptions below of the individual methods give a mathematical definition of the method, what type of element it applies to, definition parameters, which types of models it applies to, any restrictions on the dimension of ambient space, and whether the method has a Hessian implemented. Unless specifically noted, a method has the gradient implemented, and hence may be used for an energy or a constraint. The definition parameters are usually scalar or vector integrands (see the datafile declaration for full syntax). Some methods also depend on global variables as noted. The sample datafile declarations given are for simple cases; full syntax is given elsewhere. Remember in the samples that for quantities not declared global, the quantity has to be individually applied to the desired elements.

0-dimensional


vertex_scalar_integral

Named method. Description: Function value at a vertex. This actually produces a sum over vertices, but as a mathematician, I think of a sum over vertices as a point-weighted integral. Element: vertex. Parameters: scalar_integrand. Models: linear, quadratic, Lagrange, simplex. Ambient dimension: any. Hessian: yes. Example datafile declaration:
quantity point_value energy method vertex_scalar_integral
scalar_integrand: x^2 + y^2 - 2x + 3

1-dimensional


edge_tension or edge_length

Named method. Description: Length of edge. Quadratic model uses Gaussian quadrature of order integral_order_1D. Element: edge. Parameters: none. Models: linear, quadratic, Lagrange. Ambient dimension: any. Hessian: yes. Example datafile declaration:
quantity len energy method edge_length global

density_edge_length

Named method. Description: Length of edge, multiplied by the edge density. Quadratic model uses Gaussian quadrature of order integral_order_1D. Element: edge. Parameters: none. Models: linear, quadratic, Lagrange. Ambient dimension: any. Hessian: yes. Example datafile declaration:
quantity len energy method density_edge_length global

edge_scalar_integral

Named method. Description: Integral of a scalar function over arclength. Uses Gaussian quadrature of order integral_order_1D. Element: Parameters: Models: linear, quadratic, Lagrange. Ambient dimension: any. Hessian: yes. Type: edge. Parameters: scalar_integrand. Example datafile declaration:
quantity edge_sint energy method edge_scalar_integral
scalar_integrand: x^2 - 3*y + 4

edge_vector_integral

Named method. Description: Integral of a vectorfield over an oriented edge. Uses Gaussian quadrature of order integral_order_1D. Element: edge. Parameters: vector_integrand. Models: linear, quadratic, Lagrange. Ambient dimension: any. Hessian: yes. Orientable: yes. Example datafile declaration:
quantity edge_vint energy method edge_vector_integral
vector_integrand:
q1: 0
q2: 0
q3: z^2/2

edge_general_integral

Named method. Description: Integral of a scalar function of position and tangent over an edge. The components of the tangent vector are represented by continuing the coordinate indices. That is, in 3D the position coordinates are x1,x2,x3 and the tangent components are x4,x5,x6. For proper behavior, the integrand should be homogeneous of degree 1 in the tangent components. Uses Gaussian quadrature of order integral_order_1D. Element: edge. Parameters: scalar_integrand. Models: linear, quadratic, Lagrange. Ambient dimension: any. Hessian: yes. Example datafile declaration: the edge length in 3D could be calculated with this quantity:
quantity arclength energy method edge_general_integral
scalar_integrand: sqrt(x4^2 + x5^2 + x6^2)

edge_area

Named method. Description: For calculating the area of a body in the string model. Implemented as the exact integral of -y dx over the edge. Valid for torus model, but not general symmetry groups. You may have to set the quantity volconst attribute in the torus model, since the area calculation is ambiguous up to one torus area. Element: edge. Parameters: none. Models: linear, quadratic, Lagrange. Ambient dimension: 2. Hessian: yes. Example datafile declaration:
quantity cell1_area fixed = 1.3 method edge_area

edge_torus_area

Named method. Description: For 2D torus string model body area calculations. Contains adjustments for torus wraps. You may have to set the quantity volconst attribute in the torus model, since the area calculation is ambiguous up to one torus area. Element: edge. Parameters: none. Models: torus; string; linear,quadratic,Lagrange. Ambient dimension: 2. Hessian: no. Example datafile declaration:
quantity cell_area fixed = 1.3 method edge_torus_area

string_gravity

Named method. Description: To calculate the gravitational potential energy of a body in the string model. Uses differences in body densities. Does not use gravitational constant G as modulus (unless invoked as internal quantity by convert_to_quantities). Element: edge. Parameters: none. Models: string linear, quadratic, lagrange. Ambient dimension: 2. Hessian: yes. Orientable: yes. Example datafile declaration:
quantity cell_grav energy modulus 980*8.5 method string_gravity

hooke_energy

Named method. Description: One would often like to require edges to have fixed length. The total length of some set of edges may be constrained by defining a fixed quantity. This is used to fix the total length of an evolving knot, for example. But to have one constraint for each edge would be impractical, since projecting to n constraints requires inverting an n x n matrix. Instead there is a Hooke's Law energy available to encourage edges to have equal length. Its form per edge is
   E =  | L - L_0| ^p 
where L is the edge length, L_0 is the equilibrium length, embodied as an adjustable parameter `hooke_length', and the power p is an adjustable parameter `hooke_power'. The default power is p = 2, and the default equilibrium length is the average edge length in the initial datafile. You will want to adjust this, especially if you have a total length constaint. A high modulus will decrease the hooke component of the total energy, since the restoring force is linear in displacement and the energy is quadratic (when p=2). As an extra added bonus, a `hooke_power' of 0 will give
 E = -\log|L-L_0|.
See hooke2_energy for individual edge equilibrium lengths. Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration:
parameter hooke_length 0.3   // will apply to all edges
parameter hooke_power  2     // the default
quantity slinky energy method hooke_energy global

hooke2_energy

Named method. Description: Same as hooke_energy, but each edge has an equilibrium length extra attribute `hooke_size' (which the user need not declare). If the user does not set hooke_size by the time the method is first called, the value will default to the current length. Hooke_size is not automatically adjusted by refining. It is the responsibility of the user to reset hooke_size after refining; you could re-define the 'r' command
   r :::= { 'r'; set vertex hooke_size hooke_size/2 }
to take care of it automatically. The power of displacement used is given by the internal read-write variable hooke2_power, which has default value 2. Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration:
parameter hooke2_power  2     // the default
define edge attribute hooke_size real
quantity slinky energy method hooke2_energy global
...
read
r;r;set edge hooke_size length

hooke3_energy

Named method. Description: Same as hooke2_energy, but uses an elastic model instead of a spring. The energy is
energy = 0.5*(length-hooke_size)^2/hooke_size.
The exponent can be altered from 2 by setting the parameter hooke3_power. If the internal variable frickenhaus_flag is nonzero, then the energy is taken to be 0 if the length is less than the equilibrium length. Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration:
parameter hooke3_power  2     // the default
quantity slinky energy method hooke3_energy global
...
read
r;r;set edge hooke_size length

local_hooke_energy

Named method. Description: Energy of edges as springs with equilibrium length being average of lengths of neighbor edges. Actually, the energy is calculated per vertex,
 E = ({L_1 - L_2 \over L_1 + L_2})^2 
 
where L_1 and L_2 are the lengths of the edges adjacent to the vertex. Meant for loops of string. (by John Sullivan) Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
quantity slinky energy method local_hooke_energy global

dihedral_hooke

Named method. Description: Energy of an edge is edge length times square of angle between normals of adjacent facets. Actually, e = (1 - cos(angle))*length. Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration:
quantity bender energy method dihedral_hooke global

sqcurve_string

Named method. Description: Integral of squared curvature in string model. Assumes two edges per vertex, so it just uses the first two edges it finds at a vertex; see sqcurve_string_marked for more complicated topologies. Value zero at endpoint of curve. Value is calculated as if the exterior angle at the vertex is evenly spread over the adjacent half-edges. More precisely, if s1 and s2 are the adjacent edge lengths and t is the exterior angle, value = 4*(1 - cos(t))/(s1+s2). Other powers of the curvature can be specified by using the parameter parameter_1 in the instance definition. If parameter_1 is not present, then the internal read-write variable curvature_power is used, which defaults to 2. Also see sqcurve2_string for a version with intrinsic curvature, and sqcurve3_string for a version that uses a slightly different formula to encourage equal length edges. Element: vertex. Parameters: parameter_1. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration:
quantity sq energy method sqcurve_string global
parameter_1 3

sqcurve2_string

Named method. Description: Integral of squared curvature in string model, but with an intrinsic curvature. The value zero at endpoint of curve. The value is calculated as if the exterior angle at the vertex is evenly spread over the adjacent half-edges. More precisely, if s1 and s2 are the adjacent edge lengths, h0 is the intrinsic curvature, and t is the exterior angle, then value = (sin(t)/((s1+s2)/2)-h0)2. The intrinsic curvature h0 may be specified either with a global variable h_zero or a real-valued vertex extra attribute h_zero. Element: vertex. Models: linear. Ambient dimension: 2 Hessian: no. Example datafile declaration:
define vertex attribute intrinsic_curvature real
quantity sq2 energy method sqcurve2_string global

sqcurve3_string

Named method. Description: Same as sqcurve_string, but uses a slightly different formula to encourage equal length edges The value zero at endpoint of curve. The value is calculated as if the exterior angle at the vertex is evenly spread over the adjacent half-edges. More precisely, if s1 and s2 are the adjacent edge lengths, h0 is the intrinsic curvature, and t is the exterior angle, value = 2*(1 - cos(t))*(1/s1+1/s2). Element: vertex. Models: linear. Ambient dimension: any Hessian: yes. Example datafile declaration:
quantity sq3 energy method sqcurve3_string global

sqcurve_string_marked

Named method. Description: Integral of squared curvature in string model. Same as sqcurve_string, but only "marked" edges are used, so the topology of edges can be more complicated than a loop or curve. The marking is done by declaring an integer-valued edge attribute named sqcurve_string_mark and setting it to some nonzero value for those edges you want to be involved, usually two at each vertex to which this method is applied. Value zero at vertex with only one marked edge. Value is calculated as if the exterior angle at the vertex is evenly spread over the adjacent half-edges. More precisely, if s1 and s2 are the adjacent edge lengths and t is the exterior angle, value = 4*(1 - cos(t))/(s1+s2). Other powers of the curvature can be specified by using the parameter parameter_1 in the instance definition. Element: vertex. Parameters: parameter_1. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration:
define edge attribute sqcurve_string_mark integer
quantity sqmark energy method sqcurve_string_marked

sq_gaussian_curv_cyl

Named method. Description: Integral of the squared gaussian curvature of a surface of revolution. The generating curve is set up in the string model, and this method applied to its edges. The axis of rotation is the x-axis. Element: edge. Models: linear string. Ambient dimension: 2 Hessian: yes. Example datafile declaration:
quantity sqgausscyl energy method sq_gaussian_curv_cyl global

sq_mean_curv_cyl

Named method. Description: Integral of the squared mean curvature of a surface of revolution. The generating curve is set up in the string model, and this method applied to its edges. The axis of rotation is the x-axis. This method will do intrinsic curvature by means either of a global variable h_zero or a real-valued vertex attribute h_zero. Element: edge. Models: linear string. Ambient dimension: 2 Hessian: yes. Example datafile declaration:
define vertex attribute h_zero real
quantity sqcyl energy method sq_mean_curv_cyl global

metric_edge_length

Named method. Description: In the string model with a Riemannian metric, this is the length of an edge. Element: edge. Parameters: none. Models: linear,quadratic,simplex. Ambient dimension: any. Hessian: yes. Example datafile declaration:
string
space_dimension 2
metric
1+x^2 y
y   1+y^2
quantity mel energy method metric_edge_length global

klein_length

Named method. Description: Edge length in Klein hyperbolic plane model. Does not depend on klein_metric being declared. Vertices should be inside unit sphere. Element: edge. Parameters: none. Models: linear. Ambient dimension: 2. Hessian: no. Example datafile declaration:
quantity kleinlen energy method klein_length global

circular_arc_length

Named method. Description: Edge length, modelling the edge as a circular arc through three points, hence useful only in the quadratic model. If not in the quadratic model, it evaluates as the edge_length method. The presence of this quantity has the side effect of automatically toggling circular_arc_draw, causing edges to display as circular arcs in the quadratic model. Element: edge. Parameters: none. Models: quadratic. Ambient dimension: any. Hessian: yes. Example datafile declaration:
quantity arclen energy method circular_arc_length global

circular_arc_area

Named method. Description: Area between an edge and the y axis, with the edge modelled as a circular arc through three points. Useful in the quadratic model; in other models it is the same as edge_area. Element: edge. Parameters: none. Models: quadratic. Ambient dimension: 2. Orientable: yes. Hessian: yes. Example datafile declaration:
quantity arcarea energy method circular_arc_area global

spherical_arc_length

Named method. Description: Edge length, modelling the edge as a spherical great circle arc between its two endpoints, which are assumed to lie on an arbitrary radius sphere centered at the origin. This method is meant for modelling string networks on spheres, and is suitable for use with the length_method_name feature for substituting the default edge length calculation method. Note that this method is an exact spherical calculation in the linear model, so there is no need to refine edges or use higher order models for accuracy. Edges are graphed as spherical arcs (actually, lots of segments). Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: yes. Example datafile declaration:
parameter rad = 2
constraint 1
formula: x^2 + y^2 + z^2 = rad^2
length_method_name "spherical_arc_length"

spherical_arc_area_n, spherical_arc_area_s

Named method. Description: Area on a sphere between an edge (considered as a great circle arc) and the north (or south) pole. This is an exact calculation in the linear model. Meant for calculating the areas of facets in the string model with the string network confined to a sphere of arbitrary radius centered at the origin. There are two versions of this method, since calculation of facet areas by means of edges necessarily has a singularity somewhere on the sphere. Spherical_arc_area_n has its singularity at the south pole, and spherical_arc_area_s has its singularity at the north pole. Thus spherical_arc_area_s will work accurately for facets not including the north pole in there interiors; a facet including the north pole will have its area calculated as the negative complement of its true area, so a body defined using it could get the correct area by using a volconst of a whole sphere area. If the singular pole falls on an edge or vertex, then results are unpredictable. With these caveats, these methods are suitable for use with the area_method_name feature for substituting the default edge area method. If you do a facet as an explicit quantity, you are responsible for applying or unapplying the quantity after topology changes!! Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Orientable: yes. Hessian: yes. Example datafile declaration:
parameter rad = 2
constraint 1
formula: x^2 + y^2 + z^2 = rad^2
area_method_name "spherical_arc_area_s"

2-dimensional


facet_tension, facet_area

Named method. Description: Area of facet. Does not multiply by facet density; density_facet_area does that. Quadratic model uses Gaussian cubature of order integral_order_2D. Beware that this is an approximation to the area, and if the facets in the quadratic or Lagrange model get too distorted, it can be a bad approximation. Furthermore, facets can distort themselves in seeking the lowest numerical area. By default, changing the model to quadratic or Lagrange will set an appropriate integral_order_2D. Element: facet. Parameters: none. Models: linear, quadratic, Lagrange, simplex. Ambient dimension: any. Hessian: yes. Example datafile declaration:
quantity farea energy method facet_area global

density_facet_area

Named method. Description: Area of facet, multiplied by its density. Otherwise same as facet_area. Element: Parameters: Models: linear, quadratic, Lagrange, simplex. Ambient dimension: any. Hessian: yes. Example datafile declaration:
quantity farea energy method density_facet_area global

facet_volume

Named method. Description: Integral of z dx dy over an oriented facet. Valid in the torus domain. Not valid for other symmetry groups. You may have to set the quantity volconst attribute in the torus model, since the volume calculation is ambiguous up to one torus volume. Element: facet. Parameters: none. Models: linear, quadratic, Lagrange. Ambient dimension: 3. Hessian: yes. Orientable: yes. Example datafile declaration:
quantity vol fixed = 1.3 method facet_volume

facet_scalar_integral

Named method. Description: Integral of a scalar function over facet area. Uses Gaussian cubature of order integral_order_2D. Element: facet. Parameters: scalar_integrand. Models: linear, quadratic, Lagrange. Ambient dimension: any. Hessian: yes. Example datafile declaration:
quantity fint energy method facet_scalar_integral global
scalar_integrand: x^2+y^2

facet_vector_integral

Named method. Description: Integral of a vectorfield inner product with the surface normal over a facet. The normal is the right-hand rule normal of the facet as defined in the datafile. Uses Gaussian cubature of order integral_order_2D. Element: facet. Parameters: vector_integrand. Models: linear, quadratic, Lagrange, simplex. Ambient dimension: any. Hessian: yes. Orientable: yes. Example datafile declaration, for volume equivalent:
quantity fvint energy method facet_vector_integrand
vector_integrand:
q1: 0
q2: 0
q3: z

facet_2form_integral

Named method. Description: Integral of a 2-form over a facet. Meant for ambient dimensions higher than 3. Uses Gaussian cubature of order integral_order_2D. Element: facet. Parameters: form_integrand (components in lexicographic order). Models: linear, Lagrange, simplex. Ambient dimension: any. Hessian: yes. Orientable: yes. Example datafile declaration in 4D:
quantity formex energy method facet_2form_integral
form_integrand:
q1: x2     // 12 component
q2: 0      // 13 component
q3: x4     // 14 component
q4: 0      // 23 component
q5: 0      // 24 component
q6: x3*x2  // 34 component

facet_2form_sq_integral

Named method. Description: Integral of the square of a 2-form over a facet. Meant for ambient dimensions higher than 3. Uses Gaussian cubature of order integral_order_2D. Element: facet. Parameters: form_integrand (components in lexicographic order). Models: linear. Ambient dimension: any. Hessian: no. Orientable: no. Example datafile declaration in 4D:
space_dimension 4
// symplectic area
// Correspondence: z1 = (x1,x2)  z2 = (x3,x4)
#define DENOM ((x1^2+x2^2+x3^2+x4^2)^2)
quantity symplectic_sq energy method facet_2form_sq_integral global
form_integrand:
q1: -2*(x3^2 + x4^2)/DENOM    // dx1 wedge dx2 term
q2:  2*(x2*x3-x1*x4)/DENOM    // dx1 wedge dx3 term
q3:  2*(x1*x3+x2*x4)/DENOM    // dx1 wedge dx4 term
q4: -2*(x1*x3+x2*x4)/DENOM    // dx2 wedge dx3 term
q5:  2*(x2*x3-x1*x4)/DENOM    // dx2 wedge dx4 term
q6: -2*(x1^2 + x2^2)/DENOM    // dx3 wedge dx4 term


facet_general_integral

Named method. Description: Integral of a scalar function of position and normal vector over a facet. Uses Gaussian cubature of order integral_order_2D. The components of the normal vector are represented by continuing the coordinate indices. That is, in 3D the position coordinates are x1,x2,x3 and the normal components are x4,x5,x6. For proper behavior, the integrand should be homogeneous of degree 1 in the normal components. Element: facet. Parameters: scalar_integrand. Models: linear, quadratic, Lagrange. Ambient dimension: any. Hessian: yes. Example: The facet area could be calculated with this quantity:
quantity surfacearea energy method facet_general_integral
scalar_integrand: sqrt(x4^2 + x5^2 + x6^2)

facet_torus_volume

Named method. Description: For 3D soapfilm model, calculates body volume integral for a facet, with corrections for edge wraps. You may have to set the quantity volconst attribute in the torus model, since the volume calculation is ambiguous up to one torus volume. Element: facet. Parameters: none. Models: linear,quadratic,lagrange. Ambient dimension: 3. Hessian: yes. Orientable: yes. Example datafile declaration:
quantity body_vol energy method facet_torus_volume

gravity_method, full_gravity_method

Named method. Description: Gravitational energy, integral of p z^2/2 dxdy over a facet, where p is difference in adjacent body densities. Note: this method uses the gravitational constant as the modulus if invoked as full_gravity_method. Just gravity_method does not automatically use the gravitational constant. Element: facet. Parameters: none. Models: linear, quadratic, Lagrange. Ambient dimension: 3. Hessian: yes. Orientable: yes. Example datafile declaration:
quantity grav energy modulus 980*8.5 method gravity_method 

facet_area_u, density_facet_area_u

Named method. Description: Area of facet. In quadratic model, it is an upper bound of area, by the Schwarz Inequality. For the paranoid. Same as facet_area in linear model. Sets integral_order_2D to 6, since it doesn't work well with less. Using the density_facet_area_u name automatically incorporates the facet tension, but facet_area_u doesn't. Element: facet. Parameters: none. Models: linear, quadratic. Ambient dimension: any. Hessian: yes. Example datafile declaration:
quantity area_u energy method facet_area_u global

gap_energy

Named method. Description: Implementation of gap energy, which is designed to keep edges from short-cutting curved constraint surfaces. This method serves the same purpose as declaring a constraint convex. Automatically incorporates the gap_constant set in the datafile or by the k command. Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
quantity gappy energy method gap_energy global

metric_facet_area

Named method. Description: For a Riemannian metric, this is the area of a facet. Element: edge. Parameters: none. Models: linear,quadratic,simplex. Ambient dimension: any. Hessian: yes. Example datafile declaration:
metric
1+x^2 0 z
0 1+y^2 0
z 0 1+z^2
quantity mfa energy method metric_facet_area global

klein_area

Named method. Description: Facet area in Klein hyperbolic 3D space model. Does not depend on klein_metric being declared in the datafile. Vertices should be inside the unit sphere. Element: facet. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
quantity kleinarea energy method klein_area global

circle_willmore

Named method. Description: Alexander Bobenko's circle-based discrete Willmore energy, which is conformally invariant. At each vertex, energy is (sum of the angles between facet circumcircles) - 2*pi. More simply done as edge quantity, since angles at each end are the same. For edge e, if adjacent facet edge loops are a,e,d and b,c,-e, then circle angle beta for edge has
 cos(beta) = (<a,c><b,c>-<a,b><c,d>-<b,c><d,a>)/|a|/|b|/|c|/|d|
For now, assumes all vertices are faceted, and fully starred. Severe numerical difficulties: Not smooth when angle beta is zero, which is all too common. Set of zero angles should be codimension 2, which means generally avoided, but still crops up. Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
quantity bobenko energy method circle_willmore global

dirichlet_area

Named method. Description: Same as the facet_tension method, but the Hessian is modified to be guaranteed positive definite, after the scheme of Polthier and Pinkall [PP]. The energy is taken to be the Dirichlet integral of the perturbation from the current surface, which is exactly quadratic and positive definite. Hence the hessian command always works, but final convergence may be slow (no faster than regular iteration) since it is only an approximate Hessian. Also see the dirichlet command. Element: facet. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration:
quantity dirarea energy method dirichlet_area global

sobolev_area

Named method. Description: Same as the facet_tension method, but the Hessian is modified to be guaranteed positive definite, after the scheme of Renka and Neuberger. [RN]. Hence the hessian command always works, but final convergence may be slow (no faster than regular iteration) since it is only an approximate Hessian. Also see the sobolev command. Element: facet. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration:
quantity sobarea energy method sobolev_area global

pos_area_hess

Named method. Description: Same as the facet_area method, but the Hessian can be adjusted various ways by setting the variables fgagfa_coeff, gfa_2_coeff, gfagfa_coeff, and gfaafg_coeff. This will make sense if you look at the Dirichlet section of the Technical Reference chapter of the printed manual. The default values of the coefficients are -1, 1, -1, and 0 respectively. Element: facet. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration:
quantity parea energy method pos_area_hess global

spherical_area

Named method. Description: Area of the facet projected to unit sphere. The vertices of the facet are assumed to be on the unit sphere. Element: facet. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
constraint 1  formula: x^2 + y^2 + z^2 = 1
quantity spharea energy method spherical_area global

stokes2d

Named method. Description: Square of the Laplacian of z viewed as a function of (x,y). Meant for the calculation of two-dimensional Stokes flow of a fluid (i.e. slow steady-state flow where inertia is not significant) by having the Evolver surface be the graph of the velocity potential and minimizing the viscous dissipation, which is the square of the Laplacian of z. Boundary conditions are handled by declaring a vertex attribute "stokes_type" of type integer, and assigning each boundary vertex one of these values:
  • 0 - vertex is not on a wall; treat as if on a mirror symmetry plane.
  • 1 - vertex is on a slip wall.
  • 2 = vertex is on a nonslip wall; normal derivative of potential is zero.
Boundary values of z should be set to constants between 0 and 1 on various sections of boundary that represent walls. Element: vertex. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: yes. Example datafile declaration:
quantity dissip energy method stokes2d global
Note: Evolver creates a vertex attribute stokes_velocity for internal use.

stokes2d_laplacian

Named method. Description: The Laplacian of z viewed as a function of (x,y). This is auxiliary to the stokes2d method. It is the same Laplacian, unsquared, with the same boundary rules. Meant for calculating pressures and such after stokes2d energy has been minimized. Element: vertex. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: yes. Example datafile declaration:
quantity laplac info_only method stokes2d_laplacian global

Surface curvature functions


mean_curvature_integral

Named method. Description: Integral of signed scalar mean curvature of a 2D surface. The computation is exact, in the sense that for a polyhedral surface the mean curvature is concentrated on edges and singular there, but the total mean curvature for an edge is the edge length times its dihedral angle. Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration:
quantity mci energy method mean_curvature_integral
The method mean_curvature_integral_a does the same thing, but uses a numerical formulation which may be better behaved.

There is an obsolete use of mean_curvature_integral in the top of the datafile to indicate the integral of the mean curvature should be included as an energy, with syntax

  mean_curvature_integral: modulus
where modulus is the multiplier for the energy. The modulus winds up as the internal read-write variable mean_curvature_modulus.

sq_mean_curvature

Named method. Description: Integral of squared mean curvature of a surface. There are several methods implemented for calculating the integral of the squared mean curvature of a surface. The older methods sq_mean_curvature, eff_area_sq_mean_curvature, and normal_sq_mean_curvature, are now deprecated, since they don't have Hessians and the newer methods star_sq_mean_curvature, star_eff_area_sq_mean_curvature, star_normal_sq_mean_curvature, and my current favorite star_perp_sq_mean_curvature, do have Hessians and can now handle incomplete facet stars around vertices. But read the following for general remarks on squared curvature also.

The integral of squared mean curvature in the soapfilm model is calculated for this method as follows: Each vertex v has a star of facets around it of area A. The force F due to surface tension on the vertex is the gradient of area, Since each facet has 3 vertices, the area associated with v is A/3. Hence the average mean curvature at v is

h = (1/2)(F/(A/3)),
where the 1/2 factor comes from the "mean" part of "mean curvature". This vertex's contribution to the total integral is then
E = h2A/3 = (3/4)F2/A.
Philosophical note: The squared mean curvature on a triangulated surface is technically infinite, so some kind of approximation scheme is needed. The alternative to locating curvature at vertices is to locate it on the edges, where it really is, and average it over the neighboring facets. But this has the problem that a least area triangulated surface would have nonzero squared curvature, whereas in the vertex formulation it would have zero squared curvature.

Practical note: The above definition of squared mean curvature seems in practice to be subject to instablities. One is that sharp corners grow sharper rather than smoothing out. Another is that some facets want to get very large at the expense of their neighbors. Hence a couple of alternate definitions have been added.
Curvature at boundary: If the edge of the surface is a free boundary on a constraint, then the above calculation gives the proper curvature under the assumption the surface is continued by reflection across the constraint. This permits symmetric surfaces to be represented by one fundamental region. If the edge of the surface is a fixed edge or on a 1-dimensional boundary, then there is no way to calculate the curvature on a boundary vertex from knowledge of neighboring facets. For example, the rings of facets around the bases of a catenoid and a spherical cap may be identical. Therefore curvature is calculated only at interior vertices, and when the surface integral is done, area along the boundary is assigned to the nearest interior vertex. However, including IGNORE_FIXED or IGNORE_CONSTRAINTS in the method declaration will force the calculation of energy even at fixed points or ignoring constraints respectively.
If the parameter or vertex attribute h_zero is defined, then the value per vertex is the same as for the following method, eff_area_sq_mean_curvature.
Element: vertex. Parameters: IGNORE_CONSTRAINTS, IGNORE_FIXED. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:

quantity sqc energy method sq_mean_curvature global

eff_area_sq_mean_curvature

Named method. Description: Integral of squared mean curvature of a surface, with a slightly different definition from sq_mean_curvature or normal_sq_mean_curvature. The area around a vertex is taken to be the magnitude of the gradient of the volume. This is less than the true area, so makes a larger curvature. This also eliminates the spike instability, since a spike has more area gradient but the same volume gradient. Letting N be the volume gradient at vertex v,
h = (1/2)(F/N)),
and
E = h2 A/3 = (3/4)(F·F/N·N)A.
The facets of the surface must be consistently oriented for this to work, since the evolver needs an `inside' and `outside' of the surface to calculate the volume gradient. There are still possible instabilities where some facets grow at the expense of others.
If the parameter or vertex attribute h_zero is defined, then the value per vertex is
E = (h-h0)2 A/3 = (3/4)(F·N/N·N-2h0)2A.
This does not reduce to the non-h_zero formula when h_zero has the value zero, but is actually a pretty good formula in its own right (see star_perp_sq_mean_curvature .
If the vertex is on one or several constraints, the F and N are projected to the constraints, essentially making the constraints act as mirror symmetry planes.
WARNING: For some extreme shapes, Evolver may have problems detecting consistent local surface orientation. The assume_oriented toggle lets Evolver assume that the facets have been defined with consistent local orientation.
Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
quantity effsq energy method eff_area_sq_mean_curvature global

normal_sq_mean_curvature

Named method. Description: Integral of squared mean curvature of a surface, with a slightly different definition from sq_mean_curvature or eff_area_sq_mean_curvature. To alleviate the instability of eff_area_sq_mean_curvature, normal_sq_mean_curvature considers the area around the vertex to be the component of the volume gradient parallel to the mean curvature vector, rather than the magnitude of the volume gradient. Thus
h = (1/2)(F·F/N·F)
E = h2A/3 = (3/4)(F·F/N·F)2 A.
This is still not perfect, but is a lot better. WARNING: For some extreme shapes, Evolver may have problems detecting consistent local surface orientation. The assume_oriented toggle lets Evolver assume that the facets have been defined with consistent local orientation.
If the parameter or vertex attribute h_zero is defined, then the value per vertex is
E = (h-h0)2 A/3 = (3/4)(F·F/N·F - 2h0)2A
If the vertex is on one or several constraints, the F and N are projected to the constraints, essentially making the constraints act as mirror symmetry planes.
Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
quantity nsq energy method normal_sq_mean_curvature global

star_sq_mean_curvature

Named method. Description: Integral of squared mean curvature over a surface. This is a different implementation of sq_mean_curvature, and it has a Hessian. This method no longer requires a complete circle of vertices around a vertex; boundary edges are treated as if they are on mirror symmetry planes, which is usually true. The positive orientation of the surface is determined by the positive orientation of the first facet of the vertex's internal facet list. This method does not do prescribed mean curvature with the h_zero parameter.
Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration:
quantity starsq energy method star_sq_mean_curvature global

star_eff_area_sq_mean_curvature

Named method. Description: Integral of squared mean curvature over a surface. This is a different implementation of eff_area_sq_mean_curvature, and it has a Hessian. This method no longer requires a complete circle of vertices around a vertex; boundary edges are treated as if they are on mirror symmetry planes, which is usually true. The positive orientation of the surface is determined by the positive orientation of the first facet of the vertex's internal facet list. This method does not use the h_zero parameter.
Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration:
quantity seffsq energy method star_eff_area_sq_mean_curvature global

star_normal_sq_mean_curvature

Named method. Description: Integral of squared mean curvature over a surface. This is a different implementation of normal_sq_mean_curvature which is more suitable for parallel calculation and has a Hessian. This method no longer requires a complete circle of vertices around a vertex; boundary edges are treated as if they are on mirror symmetry planes, which is usually true. The positive orientation of the surface is determined by the positive orientation of the first facet of the vertex's internal facet list. This method can use the h_zero parameter or vertex attribute for prescribed mean curvature.
Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration:
quantity stnsq energy method star_normal_sq_mean_curvature global

star_perp_sq_mean_curvature

Named method. Description: Integral of squared mean curvature over a surface. This is my current favorite implementation of squared mean curvature. It is an implementation specifically designed to agree with the mean curvature computed as the gradient of area when normal motion is on (either the normal_motion toggle for 'g' iteration, or Hessian with hessian_normal). Thus if you get zero squared mean curvature with this method, then switch to area energy, the hessian will report exact convergence. Likewise if you do prescribed curvature and then convert to area minimization with a volume constraint. This method has a Hessian. This method does not require a complete circle of vertices around a vertex; boundary edges are treated as if they are on mirror symmetry planes, which is usually true. This method can use the h_zero parameter or vertex attribute for prescribed mean curvature. The actual formula for the energy at a vertex is
h = (1/2)(F·N/N·N)
E = (h-h0)2 A/3 = (3/4)(F·N/N·N - 2h0)2A
where F is the area gradient at the vertex, N is the volume gradient, and A is the area of the adjacent facets. If the vertex is on one or several constraints, the F and N are projected to the constraints, essentially making the constraints act as mirror symmetry planes. The positive orientation of the surface is determined by the positive orientation of the first facet of the vertex's internal facet list.
Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration:
quantity stnsq energy method star_perp_sq_mean_curvature global

gauss_curvature_integral

Named method. Description: This computes the total Gaussian curvature of a surface with boundary. The Gaussian curvature of a polyhedral surface may be defined at an interior vertex as the angle deficit of the adjacent angles. But as is well-known, total Gaussian curvature can be computed simply in terms of the boundary vertices, which is what is done here. The total Gaussian curvature is implemented as the total geodesic curvature around the boundary of the surface. The contribution of a boundary vertex is
E =  (\sum_i \theta_i) - pi.
For reasons due to the Evolver's internal architecture, the sum is actually broken up as a sum over facets, adding the vertex angle for each facet vertex on the boundary and subtracting pi for each boundary edge. The total over all boundary vertices is exactly equal to the total angle deficit of all interior vertices plus 2*pi*chi, where chi is the Euler characteristic of the surface. Boundary vertices are deemed to be those that are fixed or on a parametric boundary. Alternately, one may define a vertex extra attribute gauss_bdry_v and an edge extra attribute gauss_bdry_e and set them nonzero on the relevant vertices and edges; this overrides the fixed/boundary criterion. Element: facet. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
quantity gint energy method gauss_curvature_integral global

star_gauss_curvature

Named method. Computes the angle deficit around vertices to which this method is applied. The angle deficit is 2*pi minus the sum of all the adjacent angles of facets. No compensation is made for vertices on the boundary of a surface; you just get big deficits there. Deficits are counted as positive, following the convention for gaussian curvature. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
 quantity total_deficit energy method star_gauss_curvature global

sq_gauss_curvature

Named method. Description: Computes the integral of the squared Gaussian curvature. At each vertex, the Gaussian curvature is calculated as the angle defect divided by one third of the total area of the adjacent facets. This is then squared and weighted with one third of the area of the adjacent facets. This method works only on closed surfaces with no singularities due to the way it calculates the angle defect. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
quantity sqg energy method sq_gauss_curvature global

Simplex model methods


simplex_vector_integral

Named method. Description: Integral of a vectorfield over a (n-1)-dimensional simplicial facet in n-space. Vectorfield is dotted with normal of facet; actually the side vectors of the simplex and the integrand vector are formed into a determinant. Element: facet. Parameters: vector_integrand. Models: simplex. Ambient dimension: any. Hessian: no. Orientable: yes. Example datafile declaration, for 4-volume under a 3D surface in 4D:
quantity xvint energy method simplex_vector_integral
vector_integrand:
q1: 0
q2: 0
q3: 0
q4: x4

simplex_k_vector_integral

Named method. Description: Integral of a simple (n-k)-vector over an oriented k-dimensional simplicial facet in n-space. The vector integrand lists the components of each of the k vectors sequentially. Evaluation is done by forming a determinant whose first k rows are k vectors spanning the facet, and last (n-k) rows are vectors of the integrand. Element: facet. Parameters: k_vector_order, vector_integrand. Models: simplex. Ambient dimension: any. Hessian: yes. Orientable: yes. Example datafile declaration, for 3D surface in 5D:
quantity kvec energy method simplex_k_vector_integral
k_vector_order 3
vector_integrand:
q1: 0   // first vector
q2: 0
q3: 0
q4: 0
q5: x4
q6: 0   // second vector
q7: 0
q8: 0
q9: x3
q10: 0

edge_k_vector_integral

Named method. Description: Integral of a simple (n-k)-vector over an oriented k-dimensional simplicial edge in n-space. The vector integrand lists the components of each of the k vectors sequentially. Evaluation is done by forming a determinant whose first k rows are k vectors spanning the edge, and last (n-k) rows are vectors of the integrand. Element: edge. Parameters: k_vector_order, vector_integrand. Models: linear, quadratic, simplex. Ambient dimension: any. Hessian: yes. Orientable: yes. Example datafile declaration, for 3D edges of a 4D surface in 5D:
quantity kvec energy method edge_k_vector_integral
k_vector_order 3
vector_integrand:
q1: 0   // first vector
q2: 0
q3: 0
q4: 0
q5: x4
q6: 0   // second vector
q7: 0
q8: 0
q9: x3
q10: 0

knot_energy

Named method. Description: An ``electrostatic'' energy in which vertices are endowed with equal charges. Inverse power law of potential is adjustable via the global parameter `knot_power', default value 2 (which is not electrostatic, but the knot theorists like it). If the extra attribute `node_charge' is defined for vertices, then that value is used for the vertex charge. Use of this energy is not restricted to knots; it has been used to embed complicated network graphs in space. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration:
parameter knot_power  2     // the default
quantity knotten energy method knot_energy global

uniform_knot_energy or edge_knot_energy

Named method. Description: A knot energy where vertex charge is proportional to neighboring edge length. This simulates an electrostatic charge uniformly distributed along a wire. Inverse power law of potential is adjustable via the global parameter `knot_power' (default 2). Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
parameter knot_power  2     // the default
quantity knotten energy method uniform_knot_energy global

uniform_knot_energy_normalizer

Named method. Description: Supposed to approximate the part of uniform_knot_energy that is singular in the continuous limit. Element: vertex. Parameters: Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
parameter knot_power  2     // the default
quantity knottenorm energy method uniform_knot_energy global
			   method uniform_knot_energy_normalizer global

uniform_knot_normalizer1

Named method. Description: Calculates internal knot energy to normalize singular divergence of integral of uniform_knot_energy. Actually a synonym for uniform_knot_energy_normalizer. No gradient. Element: vertex. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
parameter knot_power  2     // the default
quantity knottenorm energy method uniform_knot_energy global
			   method uniform_knot_energy_normalizer1 global

uniform_knot_normalizer2

Named method. Description: Calculates internal knot energy to normalize singular divergence of integral of uniform_knot_energy a different way from uniform_knot_energy_normalizer. Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
parameter knot_power  2     // the default
quantity knottenorm energy method uniform_knot_energy global
			   method uniform_knot_energy_normalizer2 global

edge_edge_knot_energy

Named method. Description: Between pairs of edges, energy is inverse square power of distance between midpoints of edges. Can also be called just edge_knot_energy. See also edge_knot_energy_normalizer. (by John Sullivan) Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
quantity knotten energy method edge_edge_knot_energy global

edge_knot_energy_normalizer

Named method. Description: Calculates internal knot energy to normalize singular divergence of integral of edge_edge_knot_energy. Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
quantity knotten energy method edge_edge_knot_energy global
                        method edge_knot_energy_normalizer global

simon_knot_energy_normalizer

Named method. Description: Another normalization of edge_knot_energy, which I don't feel like deciphering right now. Element: edge. Parameters: none. Models: string linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
quantity kenergy energy method edge_knotenergy global
                   method simon_knot_energy_normalizer global

facet_knot_energy

Named method. Description: Charge on vertex is proportional to area of neighboring facets. Meant for knotted surfaces in 4D. Power law of potential is adjustable via the global parameter `knot_power'. See also facet_knot_energy_fix. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
parameter knot_power  2     // the default
quantity knotten energy method facet_knot_energy global

facet_knot_energy_fix

Named method. Description: Provides adjacent vertex correction to facet_knot_energy. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
parameter knot_power  2     // the default
quantity knotten energy method facet_knot_energy global
                        method facet_knot_energy_fix global

buck_knot_energy

Named method. Description: Energy between pair of edges given by formula suggested by Greg Buck. Power law of potential is adjustable via the global parameter `knot_power'. Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
parameter knot_power  2     // the default
quantity knotten energy method buck_knot_energy global

proj_knot_energy

Named method. Description: This energy is due to Gregory Buck. It tries to eliminate the need for a normalization term by projecting the energy to the normal to the curve. Its form is
   E_{e_1e_2} = {L_1L_2 \cos^p\theta\over |x_1 - x_2|^p}
where x_1,x_2 are the midpoints of the edges and \theta is the angle between the normal plane of edge e_1 and the vector x_1 - x_2. The default power is 2. Power law of potential is adjustable via the global parameter `knot_power'. Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
parameter knot_power  2     // the default
quantity knotten energy method proj_knot_energy global

circle_knot_energy

Named method. Description: This energy is due to Peter Doyle, who says it is equivalent in the continuous case to the insulating wire with power 2. Its form is
  E_{e_1e_2} = {L_1L_2 (1 - \cos\alpha)^2 \over |x_1 - x_2|^2},
where x_1,x_2 are the midpoints of the edges and \alpha is the angle between edge 1 and the circle through x_1 tangent to edge 2 at x_2. Only power 2 is implemented. Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
quantity knotten energy method circle_knot_energy global

sphere_knot_energy

Named method. Description: This is the 2D surface version of the circle energy. Its most general form is
  E_{f_1f_2} = { A_1A_2(1 - \cos\alpha)^p \over |x_1 - x_2|^q},
where A_1,A_2 are the facet areas, x_1,x_2 are the barycenters of the facets, and \alpha is the angle between f_1 and the sphere through x_1 tangent to f2 at x_2. The energy is conformally invariant for p = 1 and q = 4. For p=0 and q=1, one gets electrostatic energy for a uniform charge density. Note that facet self-energies are not included. For electrostatic energy, this is approximately 2.8A^{3/2} per facet. The powers p and q are Evolver variables surface_knot_power and surface_cos_power respectively. The defaults are p=1 and q=4. Element: facet. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
parameter surface_knot_power  1     // the default
parameter surface_cos_power  4     // the default
quantity knotten energy method sphere_knot_energy global

sin_knot_energy

Named method. Description: Another weird way to calculate a nonsingular energy between midpoints of pairs of edges. (by John Sullivan) Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
quantity knotten energy method sin_knot_energy global

curvature_binormal

Named method. Description: For string model. The energy evaluates to zero, but the force calculated is the mean curvature vector rotated to the binormal direction. Element: vertex. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
quantity curbi energy method curvature_binormal global

ddd_gamma_sq

Named method. Description: Third derivative of curve position as function of arclength, squared. Element: vertex. Parameters: none. Models: string, linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
quantity ddd energy method ddd_gamma_sq global

edge_min_knot_energy

Named method. Description: Between pairs of edges, energy is inverse square power of distance between closest points of edges.
    Energy = 1/d^2 * |e1||e2|
This should be roughly the same as edge_edge_knot_energy, but distances are calculated from edge midpoints there. This is not a smooth function, so we don't try to compute a gradient. DO NOT use as an energy; use just for info_only quantities. Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
quantity eminknot info_only method edge_min_knot_energy global

true_average_crossings

Named method. Description: Calculates the average crossing number of an edge with respect to all other edges, averaged over all projections. Knot stuff. No gradient, so use just in info_only quantities. Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
quantity true_cross info_only method true_average_crossings global

true_writhe

Named method. Description: For calculating the writhe of a link or knot. No gradient, so use just in info_only quantities. Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
quantity twrithe info_only method true_average_crossings global

twist

Named method. Description: Another average crossing number calculation. No gradient, so use just in info_only quantities. Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
quantity twister info_only method twist global

writhe

Named method. Description: An average crossing number calculation. This one does have a gradient. Suggested by Hermann Gluck. Programmed by John Sullivan. Between pairs of edges, energy is inverse cube power of distance between midpoints of edges, times triple product of edge vectors and distance vector.
     E = 1/d^3 * (e1,e2,d)
Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
quantity writhy energy method writhe global

curvature_function

Named method. Description: Calculates forces as function of mean and Gaussian curvatures at vertices. Function may be changed by user by altering teix.c. No energy, just forces. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
quantity curfun energy method curvature_function global

average_crossings

Named method. Description: To calculate the average crossing number in all projections of a knot. (by John Sullivan) Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
quantity across energy method average_crossings global

knot_thickness

Named method. Description: Calculates global radius of curvature at one vertex v, as the minimum radius of circle containing the vertex and the endpoints of any non-adjacent edge. Because of "min", this has no gradient, so should be used in info_only quantities. Element: vertex. Parameters: none. Models: linear. Ambient dimension: 3. Gradient: no. Hessian: no. Example datafile declaration:
quantity kthick info_only method knot_thickness global

knot_thickness_0

Named method. Description: Calculates global radius of curvature at one vertex, as Lp integral of radius of curvature of circle containing the vertex and the endpoints of edges not adjacent to the vertex. Integrand raised to -p power. The power p is taken from the global variable knot_power. No factor of length in integral. This method has a gradient. Element: vertex. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
quantity kthick info_only method knot_thickness_0 global

knot_thickness_p

Named method. Description: purpose: calculates global radius of curvature at one vertex v, as Lp integral of radius of curvature of v and endpoints of nonadjacent edges. Includes factors of length at v and w. This method has a gradient. The power p is taken from the global variable knot_power. Element: vertex. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
quantity kthick info_only method knot_thickness_p global

knot_thickness_p2

Named method. Description: Calculates the global radius of curvature at one vertex v, as Lp integral of r(v,w1,w2) over all vertices w. Here w1 and w2 are the two neighbors of vertex w. Includes factors of length at v and w. This has not been extended to allow open arcs (valence 1 vertices). This method does have a gradient. The power p is taken from the global variable knot_power. Element: vertex. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
quantity kthick info_only method knot_thickness_p2 global

knot_thickness2

Named method. Description: calculates global radius of curvature at one vertex v, as the minimum radius of circle containing the vertex and the neighbor vertices of any non-adjacent vertex. Because of "min", this has no gradient, so should be used in info_only quantities. Element: vertex. Parameters: none. Models: linear. Ambient dimension: 3. Gradient: no. Hessian: no. Example datafile declaration:
quantity kthick info_only method knot_thickness2 global

knot_local_thickness

Named method. Description: Calculates the radius of curvature at a vertex of the circle containing the vertex and its two neighbor vertices. Meant to investigate the radius at individual vertices. Element: vertex. Parameters: none. Models: linear. Ambient dimension: 3. Gradient: no. Hessian: no. Example datafile declaration:
quantity klocalthick info_only method knot_local_thickness global

Weird and miscellaneous


wulff_energy

Named method. Description: Method version of wulff energy. If Wulff filename is not given in top section of datafile, then the user will be prompted for it. Element: facet. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
wulff "crystal.wlf"
quantity wolf energy method wulff_energy global

linear_elastic

Named method. Description: To calculate the isotropic linear elastic strain energy for facets based on the Cauchy-Green strain matrix. Let S be Gram matrix of unstrained facet (dots of sides). Let Q be the inverse of S. Let F be Gram matrix of strained facet. Let C = (FQ-I)/2, the Cauchy-Green strain tensor. Let v be Poisson ratio. Then energy density is
(1/2/(1+v))(Tr(C^2) + v*(Tr C)^2/(1-(dim-1)*v))
Each facet has extra attribute poisson_ratio and extra attribute array form_factors[3] = {s11,s12,s22}, which are the entries in S. That is, s11 = dot(v2-v1,v2-v1), s12 = dot(v2-v1,v3-v1), and s22 = dot(v3-v1,v3-v1). If form_factor is not defined by the user, it will be created by Evolver, and the initial facet shape will be assumed to be unstrained. For a version of this method that gives compression zero energy, see relaxed_elastic_A. Element: facet. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: yes. Example datafile declaration:
quantity lastic energy method linear_elastic global

general_linear_elastic

Named method. Description: To calculate the nonisotropic linear elastic strain energy for facets. Let A be the linear transformation from the unstrained shape to the strained shape. Then the Cauchy-Green strain tensor is C = (ATA - I)/2. Let S1 and S2 be the sides of the unstrained facet. Let W1 and W2 be the transformed facet sides. Let F be the Gram matrix of strained facet. Define
S = [ S1 S2 ], Q = S-1
W = [ W1 W2 ] = AS
F = WTW = STATAS
Then
ATA = QTFQ
C = (QTFQ - I)/2
The energy density is
(1/2)Cij Kijkl Ckl
where Kijkl is the full tensor of elastic constants. By using symmetries, this can be reduced to
(1/2) [ C11 C22 C12 ] [ E1 E3 E4 ] [ C11 ]
[ E3 E2 E5 ] [ C22 ]
[ E4 E5 E6 ] [ C12 ]
Each facet has extra attribute elastic_coeff of size 6 containing { E1, E2, E3, E4, E5, E6 }, and extra attribute array elastic_basis of size 2x2 containing { {s11,s12},{s21,s22}}, which are the two sides of the unstrained facet. Note that the Ei are defined with respect to the original sides as defined by the form factors, so it is up to you to make sure everything works out right. Test carefully!!! The elastic_coeff attribute must be created and initialized by the user. Element: facet. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: yes. Example datafile declaration:
define facet attribute elastic_basis real[2][2]
define facet attribute elastic_coeff real[6]
quantity genlastic energy method general_linear_elastic global

linear_elastic_B

Named method. Description: A variation of the linear_elastic method. To calculate the linear elastic strain energy for facets based on the Cauchy-Green strain matrix. Let S be Gram matrix of unstrained facet (dots of sides). Let Q be the inverse of S. Let F be Gram matrix of strained facet. Let C = (FQ-I)/2, the Cauchy-Green strain tensor. Let v be Poisson ratio. Then energy density is
 (1/2/(1+v))(Tr(C^2) + v*(Tr C)^2/(1-(dim-1)*v))
Each facet has extra attribute poisson_ratio and each vertex has two extra coordinates, the coordinates of the unstrained surface in a plane. Hence the surface must be set up as five dimensional. There can also be a real-valued facet extra attribute LEBweight, which can be used to give a per-facet weighting of the energy. For a version of this method that gives compression zero energy, see relaxed_elastic. Element: facet. Parameters: none. Models: linear. Ambient dimension: 5. Hessian: yes. Example datafile declaration:
space_dimension 5
quantity lastic energy method linear_elastic_B global
relaxed_elastic_A,

relaxed_elastic1_A, relaxed_elastic2_A

Named method. Description: Calculates the linear elastic strain energy for facets based on the Cauchy-Green strain matrix, with compression counting for zero energy, simulating, say, plastic film. The effect is to permit wrinkling. Let S be the Gram matrix of unstrained facet (dots of sides). Let Q be the inverse of S. Let F be Gram matrix of strained facet. Let C = (FQ-I)/2, the Cauchy-Green strain tensor. Let v be Poisson ratio. Then the energy is
 (1/2/(1+v))(Tr(C^2) + v*(Tr C)^2/(1-(dim-1)*v))
Each facet has extra attribute poisson_ratio and extra attribute array form_factors[3] = {s11,s12,s22}, which are the entries in S. That is, s11 = dot(v2-v1,v2-v1), s12 = dot(v2-v1,v3-v1), and s22 = dot(v3-v1,v3-v1). If form_factor is not defined by the user, it will be created by Evolver, and the initial facet shape will be assumed to be unstrained. The compression is detected by doing an eigenvalue analysis of the strain tensor, and discarding any negative eigenvalues. Facets which are stressed in one or two dimensions can be separately counted by the relaxed_elastic1_A (one stress direction, and one wrinkle direction) and relaxed_elastic2_A (two stressed directions) methods, which are meant to be used in info_only mode. There can also be a real-valued facet extra attribute LEBweight, which can be used to give a per-facet weighting of the energy. For a sample datafile, see mylarcube.fe. For a version of this method that gives compression positive energy, see linear_elastic. Element: facet. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: yes. Example datafile declaration:
quantity lastic energy method relaxed_elastic_A global

relaxed_elastic, relaxed_elastic1, relaxed_elastic2

Named method. Description: A variation of the linear_elastic method. Calculates the linear elastic strain energy for facets based on the Cauchy-Green strain matrix, with compression counting for zero energy, simulating, say, plastic film. The effect is to permit wrinkling. Let S be Gram matrix of unstrained facet (dots of sides). Let Q be the inverse of S. Let F be Gram matrix of strained facet. Let C = (FQ-I)/2, the Cauchy-Green strain tensor. Let v be Poisson ratio. Then energy density is
 (1/2/(1+v))(Tr(C^2) + v*(Tr C)^2/(1-(dim-1)*v))
Each facet has extra attribute poisson_ratio and each vertex has two extra coordinates, the coordinates of the unstrained surface in a plane. Hence the surface must be set up as five dimensional. The compression is detected by doing an eigenvalue analysis of the strain tensor, and discarding any negative eigenvalues. The eigenvalues may be separately accessed by the relaxed_elastic1_A (lower eigenvalue) and relaxed_elastic2_A (higher eigenvalue) methods, which are meant to be used in info_only mode. There can also be a real-valued facet extra attribute LEBweight, which can be used to give a per-facet weighting of the energy. For a sample datafile, see mylarcube.fe. For a version of this method that gives compression zero energy, see linear_elastic_B. Element: facet. Parameters: none. Models: linear. Ambient dimension: 5. Hessian: yes. Example datafile declaration:
space_dimension 5
quantity lastic energy method relaxed_elastic global

dirichlet_elastic

Named method. Description: Calculate the Dirichlet elastic strain energy for facets, minimization of which gives conformal mapping. Let S be Gram matrix of unstrained facet (dots of sides). Let Q be the inverse of S. Let F be Gram matrix of strained facet. Let C = FQ, the linear deformation matrix. Then energy density is
Tr(CCT)
Each facet has an extra attribute array form_factors[3] = {s11,s12,s22}, which are the entries in S. That is, s11 = dot(v2-v1,v2-v1), s12 = dot(v2-v1,v3-v1), and s22 = dot(v3-v1,v3-v1). If form_factor is not defined by the user, it will be created by Evolver, and the initial facet shape will be assumed to be unstrained. Element: facet. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: yes. Example datafile declaration:
quantity dirich energy method dirichlet_elastic global

SVK_elastic

Named method. Description: SVK (Saint-Venant - Kirchhoff) potential. The facet energy is
lambda/2*(tr(E))^2+mu*(E:E) - (3 lambda + 2 mu) * alpha*theta*tr(E)
where E=(C-I)/2 is the Green-Lagrange Strain tensor, theta = T-T0 is the temperature deviation, and alpha is the thermal dilation coefficient. Needs real-valued facet attributes SVK_alpha, SVK_mu, SVK_lambda, and SVK_theta. Also needs the facet attribute form_factors, decribed in linear_elastic. Written by Dr. Rabah Bouzidi. Element: facet. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: yes. Example datafile declaration:
define facet attribute SVK_alpha real
define facet attribute SVK_lambda real
define facet attribute SVK_mu real
define facet attribute SVK_theta real
define facet attribute form_factors real[3]
quantity svk energy method SVK_elastic  global

neo_hookean

Named method. Contributed by Prof. Rabah Bouzidi. I don't seem to have the compact formula for this one. Needs neo_lambda, neo_mu, and form_factors. Element: facet. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration:
quantity bender energy method neo_hookean global

null_length

Named method. Description: Simply returns 0 for any edge. Useful in the string model with length_method_name when you don't want edge energy, but you still want to assign edges tension. Element: edge. Parameters: none. Models: any. Ambient dimension: any. Hessian: yes. Example datafile declaration:
  length_method_name "null_length"

null_area

Named method. Named method. Description: Simply returns 0 for any facet. Useful with area_method_name when you don't want area as energy, but you still want to assign edges tension. Element: edge. Parameters: none. Models: any. Ambient dimension: any. Hessian: yes. Example datafile declaration:
  area_method_name "null_area"

area_square

Named method. Description: Energy of a facet is the square of the facet area. Element: facet. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
quantity asquare energy method area_square global

carter_energy

Named method. Description: Craig Carter's energy.
Given bodies $B_1$ and $B_2$ in $R^3$, define the energy
    E = \int_{B_1}\int_{B_2} {1 \over |z_1 - z_2|^{p} } d^3 z_2 d^3 z_1
This reduces to
E = {1\over (3-p)(2-p)}\sum_{F_2\in\partial B_2}\sum_{F_1\in\partial B_1}
    N_1 \cdot N_2 \int_{F_2}\int_{F_1}{1\over |z_1 - z_2|^{p-2}}
    d^2 z_1 d^2 z_2.
And if we crudely approximate with centroids $\bar z_1$ and $\bar z_2$,
E = {1\over (3-p)(2-p)}\sum_{F_2\in\partial B_2}\sum_{F_1\in\partial B_1}
        {A_1 \cdot A_2 \over |\bar z_1 - \bar z_2|^{p-2}},
where $A_1$ and $A_2$ are unnormalized area vectors for the facets.
The power p is set by the variable carter_power (default 6).
Element: facet. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
parameter carter_power  6     // the default
quantity craig energy method carter_energy global

charge_gradient

Named method. Description: This energy is the gradient^2 of the knot_energy method, assuming the points are constrained to the unit sphere. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
parameter knot_power  2     // the default
quantity knotten energy method knot_energy global

johndust

Named method. Description: For all point pairs (meant to be on a sphere),
       E = (pi - asin(d/2))/d, 
where d is chord distance. For point packing problems on the sphere. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
constraint 1 formula: x^2+y^2+z^2 = 1
quantity jms energy method johndust global

stress_integral

Named method. Description: Hmm. Looks like this one calculates integrals of components of a stress tensor. The scalar_integrand value is set as an integer standing for which component to do (a kludge). See the function stress_integral in method3.c for details. Does not have a gradient, so should be used for just info_only quantities. Element: facet. Parameters: scalar_integrand. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration:
quantity stressy info_only method stress_integral global
scalar_integrand: 3

ackerman

Named method. Description: Not actually an energy, but a kludge to put inertia on vertices. Uses extra velocity coordinates to represent vertex in phase space. Invocation actually transfers computed forces from space coordinates to velocity coordinates, so forces become acceleration instead of velocity. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration:
quantity jeremy energy method ackerman global

laplacian_mean_curvature

Named method. Description: Calculates the velocity of a vertex as the Laplacian of the mean curvature of the surface, meant to model the surface diffusion of atoms in sintering. The mean curvature at each vertex is calculated as a scalar, in the same way as for area_normalized area gradient, i.e. area gradient dotted with volume gradient, divided by the area of the surrounding facets. Then finite differences are used to calculate the Laplacian of the mean curvature. This calculates velocity only; the energy is always 0. This method should only be used with fixed scale in the 'g' command.

The relative speed of vertices can be controlled by the vertex attribute lmc_mobility, which the user should declare if wanted. If the user wants to access the values of mean curvature the method finds, the user should define the vertex scalar attribute lmc_mean_curvature. This method conserves volume ideally, but you might want to put on volume constraints anyway due to numerical inaccuracies.

Warning: This method should only be used with a fixed 'g' scale factor. And for stability, the factor should be proportional to the fourth power of the shortest edge, since Laplacian of mean curvature is a fourth-derivative operator, something like 0.001*length^4. This can make for very slow evolution for highly refined surfaces.

Element: vertex. Parameters: none. Models: linear string and linear soapfilm. Ambient dimension: any. Hessian: no. Example datafile declaration:

  area_method_name "null_area"
  define facet attribute lmc_mobility real
  define facet attribute lmc_mean_curvature real
  quantity lmc energy method laplacian_mean_curvature global

Back to top of Surface Evolver documentation. Index. evolver-2.30c.dfsg/doc/twointor.htm0000644000175300017530000001003011410765113017564 0ustar hazelscthazelsct Surface Evolver Documentation - Torus Example

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Example: Torus partitioned into two cells (Kelvin's foam)

This example has a flat 3-torus (i.e. periodic boundary conditions) divided into two bodies. The unit cell is a unit cube, and the surface has the topology of Kelvin's partitioning of space into tetrakaidecahedra [TW], which was the least area partitioning of space into equal volumes known until recently [WP].

The datafile handles the wrapping of edges around the torus by specifying for each direction whether an edge wraps positively (+), negatively (-), or not at all (*).

Note the use of the keyword TORUS_FILLED in the datafile. This informs Evolver that one of the volume constraints is redundant, preventing a singular matrix when the time comes to enforce volume constraints. One could use just TORUS and only put on one volume constraint.

The display of a surface in a torus can be done several ways. The connected command (my favorite) makes each body show as a single unit. The clipped command shows the surface clipped to the fundamental parallelpiped. The raw_cells command shows the unedited surface.

The Weaire-Phelan structure [WP]. is in the datafile phelanc.fe. It has area 0.3% less than Kelvin's.
twointor The initial two-cell Kelvin shape. Note that due to periodidity, a single vertex or edge may appear multiple times in the image.

// twointor.fe
// Two Kelvin tetrakaidecahedra in a torus.

TORUS_FILLED   // signals that domain is a torus and bodies fill it.

periods
1.000000 0.000000 0.000000
0.000000 1.000000 0.000000
0.000000 0.000000 1.000000

vertices // values from another program
1  0.499733 0.015302 0.792314
2  0.270081 0.015548 0.500199
3  0.026251 0.264043 0.500458
4  0.755123 0.015258 0.499302
5  0.026509 0.499036 0.794636
6  0.500631 0.015486 0.293622
7  0.025918 0.750639 0.499952
8  0.499627 0.251759 0.087858
9  0.256701 0.499113 0.087842
10 0.026281 0.500286 0.292918
11 0.500693 0.765009 0.086526
12 0.770240 0.499837 0.087382

edges // with wraps in axis directions
1    1 2  * * *    
2    2 3  * * *
3    1 4  * * *
4    3 5  * * *
5    2 6  * * *
6    2 7  * - *
7    1 8  * * +
8    4 6  * * *
9    5 9  * * +
10   3 10 * * *
11   3 4  - * *
12   6 8  * * *
13   6 11 * - *
14   7 4  - + *
15   8 12 * * *
16   9 8  * * *
17   9 11 * * *
18   10 7 * * *
19   11 1 * + -
20   12 5 + * -
21   5 7  * * *
22  11 12 * * *
23  10 12 - * *
24   9 10 * * *

faces
1    1 2 4 9 16 -7 
2    -2 5 12 -16 24 -10 
3    -4 10 18 -21 
4    7 15 20 -4 11 -3 
5    -1 3 8 -5 
6    6 14 -11 -2 
7    5 13 -17 24 18 -6 
8    -12 13 19 7 
9    -16 17 22 -15 
10   -10 11 8 12 15 -23 
11   -21 9 17 19 1 6 
12   -14 -18 23 -22 -13 -8 
13   -24 -9 -20 -23 
14   -19 22 20 21 14 -3 

bodies
1    -1 -2 -3 -4 -5 9 7 11 -9 10 12 5 14 3 volume 0.500
2     2 -6 -7 8 -10 -12 -11 -13 1 13 -14 6 4 -8 volume 0.500

Doing some refining and iterating will show that the optimal shape is curved a bit.
Spinning ring example. Back to top of tutorial.
Back to top of Evolver documentation. Index. evolver-2.30c.dfsg/doc/news_01.htm0000644000175300017530000002027011410765113017162 0ustar hazelscthazelsct Surface Evolver Documentation - Newsletter 1

Surface Evolver Newsletter no. 1

Back to top of Surface Evolver documentation. Index.

		      Surface Evolver Newsletter Number 1
			      January 25, 1993

             Editor: Ken Brakke, brakke@geom.umn.edu

Contents:
  Introduction
  Announcement
  Bibliography
  Latest features

Introduction:

  The purpose of this Newsletter is to keep Surface Evolver users
  (and other interested parties) up to date and to promote interaction
  between those with like interests.  Contents will include:
     Latest features added to Evolver.
     Future plans for new features.
     Suggestions for new features, with the likelihood of their being
        added proportional to demand.
     Bibliography of papers using Evolver, hopefully with brief abstracts.
     Workshops, talks, etc. to be given.
     Descriptions of projects underway using Evolver.
     List of systems users have ported Evolver to.
     Anything else anybody would wish to contribute.

  Send contributions to brakke@geom.umn.edu.
  If you are not on the Newsletter mailing list and would like to be,
  send email to brakke@geom.umn.edu.

Announcement:

  The possibility has arisen of porting Evolver to a CM5, which
  is a massively parallel machine.  The main benefit would be in
  speeding up iteration with many thousands of facets.  If anyone
  has a project which might benefit, please let me know.  Even if
  you never plan to run on a CM5 yourself, we would like to be able
  to cite types of applications that could benefit when requesting
  CM5 time to do the port.   -- Ken Brakke


Bibliography:

K. A. Brakke, The Surface Evolver Manual, version 1.87.  Research
   Report GCG45, The Geometry Center, 1300 South Second Street,
   Minneapolis, MN 55454. October, 1992.

     Every so often, The Geometry Center prints and binds a bunch
     of copies of the manual.  This is the latest.  Of course, it
     is not as up to date as the version in the ftp package.

K. A. Brakke, "The Surface Evolver," Experimental Mathematics, vol. 1 (1992),
     no. 2, pp 141-165.

     A journal article giving a general description of the Evolver
     and some examples.  Good article to cite in your papers.

K. A. Brakke, "The Opaque Cube Problem," Am. Math. Monthly, vol. 99,
    no. 9 (November, 1992), pp 866-871.

     Uses the Evolver to compute the area of a proposed solution to
     the Opaque Cube Problem, which is to find the least area surface
     that intersects all lines through a cube.

K. A. Brakke, "Grain growth with the Surface Evolver,"
      Video Proceedings of the Workshop on Computational Crystal Growing, 
      J. E. Taylor, ed., American Mathematical Society, Providence RI, 1992.

      Evolver starts with 100 cells in a square and evolves by mean
      curvature until equilibrium.

M. Callahan, P. Concus and R. Finn, "Energy minimizing capillary surfaces 
      for exotic containers,"  Computing Optimal Geometries, 
      American Mathematical Society, Providence RI, 1991.

      Simulation of a Space Shuttle experiment done last year.
      See Science News for Aug. 22 1992 (I think) for a report 
      on the Shuttle experiment.

H. Mittelmann, "Symmetric capillary surfaces in a cube" and  
     "Symmetric capillary surfaces in a cube Part 2: Near the
      limit angle," preprints.

      Evolver does families of fixed-volume bodies inside a cube
      with various contact angles.

L. M. Racz, J. Szekely, and K. A. Brakke, "On some meniscus
      problems in materials processing," to appear in  Trans. Iron and
	    Steel Inst. vol. 33(1993) no. 2.

      General description of Evolver and some applications to 
      materials processing, such as liquid solder.

J. Tegart, "Three-dimensional fluid interfaces in cylindrical
      containers," AIAA paper AIAA-91-2174, 27th Joint Propulsion Conference,
      Sacramento, CA, June 1991.

      Engineer at Martin-Marietta uses Evolver to simulate liquid fuel
      shape in weightlessness for Shuttle small fuel tank.



Latest features:

Latest ftp version is 1.88a, Jan. 6, 1993.  For those of you who
haven't downloaded in a while, here's the history list from the manual
for the past year:

Version 1.76  March 20, 1992

     Autopopping and autochopping in string model for automatic
     evolution.

     Phase-dependent grain boundary energies.

     Approximate polyhedral curvature.

     Stability test for approximate curvature.

     Squared Gaussian curvature as part of energy only, not force.

     ``system'' command to execute shell commands.

     ``check_increase'' to guard against blowup during interation.

     ``effective_area'' to count only resistance to motion normal to surface.

      Runge-Kutta iteration.

Version 1.80 July 25, 1992

      Command and query language much extended.

      geomview interface added. (A MUST HAVE for SGI machines. geomview
	 available by ftp from geom.umn.edu.  Obsoletes MinneView.)

      Fixed area added as a constraint.

      Multiple viewing transforms can be specified in the datafile
      so one fundamental region of a symmetric surface can be
      displayed as the whole surface.

      Commands can be included at the end of the datafile, introduced
      by the keyword READ.

 Version 1.83 September 9, 1992

      Some alternate definitions of squared curvature added.
      Invoked by ``effective_area ON | OFF'' or 
      ``normal_curvature ON  | OFF''.

Version 1.84 September 10, 1992

      Shaded colors added to xgraph and cheygraph.

Version 1.85 September 29, 1992

      Restriction of motion to surface normal added. Toggle 
      ``normal_motion''.

      Squared mean curvature, gaussian curvature, and squared gaussian
	curvature extended to surfaces with boundary.
 
      Datafile element attribute ``bare'' added for vertices and edges 
      in soapfilm model so they won't generate erroneous warnings.

      Force calculation added for squared gaussian curvature, so it
      can be used in the energy.

      All prompts that require real value responses now accept
      arbitrary expressions.

 Version 1.86 October 19, 1992
 
      User-defined mobility added, both scalar and tensor forms.

      Default squared curvature works for 2-surfaces in $R^n$.

 Version 1.87 October 27, 1992

      ``close_show'' command added to close show window (the native
	graphics window, not geomview).

      Graphics command checks string for illegal characters
	before doing any transformations.

      Dihedral angles now work for 2-surfaces in any dimension.

      Permanent variable assignments may be made with ``\tt ::=''
      instead of ``:=''.  Such assignments will not be forgotten
      when  a new surface is begun.

      Conditional expressions of C form added: expr ? expr : expr.
      Useful for patching constraints  together.

Version 1.88    December 16, 1992

      ``SET BACKGROUND color'' command added for native graphics.

      View transformation generators and expressions added.

      Exact bounding box calculated for PostScript files.

Version 1.88a   January 6, 1993

      Default constraint_tolerance lowered from 1e-5 to 1e-12.

      Fixed bug in volume constraint calculation introduced in 1.88.

Version 1.88b   (not in ftp yet) 

      Postscript draws fixed and boundary edges in interior of surface.
      All internal graphics should be consistent in the special edges they
      draw (bare edges, triple edges, etc.).

      Viewing matrix can be read from datafile and will be dumped.
      Keyword VIEW_MATRIX

      Mod operator `%' added, and functions floor(), ceil().

      `rebody' command added to recalculate connected bodies
      after neck pinching and any other body disruption.

End of newsletter.

    


Back to top of Surface Evolver documentation. Index. evolver-2.30c.dfsg/doc/misc.htm0000644000175300017530000004573711410765113016660 0ustar hazelscthazelsct Surface Evolver Documentation: miscellaneous

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Miscellaneous terms

This file provides some entries for various keywords that are not used as name pointers elsewhere for ambiguity, incredible obscurity, or some other reason.

Alice

A keyword for a special purpose command not documented.

Area

As a keyword, area is a read-only attribute of a facet.

Area_fixed

An obsolete way of declaring the total area of the surface fixed in the datafile. Synonym: fixed_area. The preferred way of doing this now is to define a fixed named quantity in the datafile, such as
quantity the_area fixed = 2.3 global_method facet_area
This permits all the named quantity features to be used.

Attribute

As a keyword, "attribute" is used in the define command for element extra attributes.

Bare

As a keyword, "bare" is a vertex attribute or an edge attribute set by the user that tells the Evolver not to expect the vertex or edge to be attached to a facet.

Body

A body is a three dimensional geometric element. As a command keyword, "body" is an element name in element generators, either as the main element type, or as a subelement of a facet.

Burchard

A keyword for a special purpose command not documented.

Color

As a keyword, "color" is an attribute of edges and facets. For the permitted values, see colors.

Conducting_knot_energy

Datafile keyword that automatically creates an energy named quantity using the method knot_energy as a global method. May be followed in the datafile with a modulus value.

Insulating_knot_energy

Datafile keyword that automatically creates an energy named quantity using the method uniform_knot_energy as a global method. May be followed in the datafile with a modulus value.

Constraint

"Constraint" can be used in a general sense, but as a keyword it refers to level set constraints. "Constraint" and "constraints" are interchangeable.

Content

"Content" is used to mean volume (or area, in the string model) in constraint integrals.

Convex

A keyword used in declaring a constraint in the datafile that causes a gap energy to be included. The idea is to prevent straight edges and facets from short-cutting curved constraints with big gaps.

Density

A keyword referring either to the gravitational density of a body, or to the surface tension of a facet, or to the linear tension of an edge. In the latter two cases, "density" is synonymous with "tension".

Edge

In general, one of the basic geometric elements. As a keyword, it is used in element generators. Synonym: edges.

Efixed

Obsolete datafile attribute to make an edge fixed without fixing its endpoints. Keyword retained just for compatibility with old dump files.

Eprint

Function that prints an expression and returns the value. Syntax: eprint expr. Meant for debugging; probably an archaic leftover from when the command language was not as developed. Example: print sum(facet, eprint area) will print out all the facet areas and then the sum.

Facet

>

Fixed

Attribute that can be applied to vertices, edges, facets, or named quantities.

Formula

Datafile keyword used in level set constraints to introduce the function formula. Synonym: function

Gauss_curvature

Datafile keyword that automatically creates an energy named quantity using the method gauss_curvature_integral as a global method. May be followed in the datafile with a modulus value.

Tolerance

As a keyword, "tolerance" refers to a fixed quantity attribute that is used as the criterion for convergence. Uses target_tolerance as the default value.

Div_normal_curvature

Toggle to make sq_mean_curvature energy calculate the mean curvature by the divergence of the normal vectors at the vertices of a facet.

Force_pos_def

Toggle. If this is on during YSMP factoring of Hessian and the Hessian turns up indefinite, something will be added to the diagonal element to make it positive definite. Left over from some experiment probably.

H_inverse_metric

Toggle. Replaces force by Laplacian of force. For doing motion by Laplacian of mean curvature.

Modulus

A keyword that is an attribute of a named quantity or a method instance, which is a multiplier for the calculated value.

Opacity

Facet attribute for transparency on some geomview systems. Syntax: set facet opacity value where value is between 0 and 1. Actually, this just sets a global variable for all facets simultaneously. The value is passed to geomview in the alpha slot of the color.

Parameter

Refers to either a parametric boundary parameter, or a user-defined variable.

Parameter_file

In the top section of the datafile, a variable can be initialized with a set of values in a file with the syntax
PARAMETER name PARAMETER_FILE string
I forget exactly how it is all supposed to work.

Pressure

Usually means an attribute of fixed-volume bodies or fixed named quantities that is actually the Lagrange multiplier for the constraint. As body attribute in the datafile, establishes fixed pressure for the body. Also used rarely in the top section of the datafile to establish the ideal gas model.

Surface_energy

An obsolete way of including vector surface energy integrals in the datafile. The present method is to use a named quantity with the facet_vector_integral method.

Total

An obsolete syntax for the value of a named quantity (e.g. my_quant) is "total my_quant". The present syntax is "my_quant.value".

Valence

An attribute of a vertex, which is the number of incident edges, or an attribute of an edge, which is the number of incident facets, or an attribute of a facet, which is the number of edges on the facet.

Vertex

One of the basic geometric elements. As a keyword, it is used in element generators.

Utest

Runs a test to see if triangulation is Delaunay. Meant for higher dimensions and simplex model.

Stability_test

Command to find largest eigenvalue of mobility matrix. Don't really recall what this was for.

Tag

An obsolete facet attribute, once used to associate an arbitrary value with a facet. Now superseded by extra attributes.

Dynamic load libraries.

This feature hasn't been used much, but documentation is included here for completeness. Many Evolver features, such as level set constraints, parametric boundaries, named method integrands, and Riemannian metrics require user-defined functions of a set of arguments. The expressions for these functions are ordinarily stored as a parse tree and interpreted each time needed, which can be much slower that evaluating compiled expressions. There is a way to use a set of compiled functions specific to a datafile through a mechanism known as dynamic loading. Here a library of functions for a datafile is separately compiled, and then loaded at runtime when a the datafile is loaded. Currently, the Evolver only implements a dynamic loading mechanism found on many unix systems, whose presence can be tested by looking for the existence of the file /usr/include/dlfcn.h. If it exists, you can enable dynamic loading by including -DENABLE_DLL in the CFLAGS line in the Makefile. On some systems, you may need to include -ldl on the GRAPHLIB line also, to link Evolver with functions such as dlopen(). To create the library for a datafile, write a source file containing C code for the desired functions, compile it, and link it into a shared library. The function should be able to compute the value and the partial derivatives of the function, and its second partials if you are going to use any Hessian features. A sample source file for a 2-dimensional datafile:

#define FUNC_VALUE  1
#define FUNC_DERIV  2
#define FUNC_SECOND 3
#define MAXCOORD 4   /* must be same as in Evolver!! */
#define REAL double  /* long double if Evolver compiled with -DLONGDOUBLE */ 
struct dstack { REAL value; 
		REAL deriv[2*MAXCOORD];
                REAL second[2*MAXCOORD][2*MAXCOORD]; };


void func1 ( mode, x, s )
int mode; /* FUNC_VALUE, FUNC_DERIV, FUNC_SECOND */
REAL *x;  /* pointer to list of arguments */
struct dstack *s;  /* for return values */
{ REAL value;

  s->value = x[0] + x[1]*x[1];

  if ( mode == FUNC_VALUE ) return;

  /* first partials */
  s->deriv[0] = 1.0;
  s->deriv[1] = 2*x[1];

  if ( mode == FUNC_DERIV ) return;

  /* second partials */
  s->second[0][0] = 0.0;
  s->second[0][1] = 0.0;
  s->second[1][0] = 0.0;
  s->second[1][1] = 2.0;
   
  return;
}
Supposing the sourcefile name to be foo.c, compile and link on SGI systems (IRIX 5.0.1 or above) with
cc -c foo.c
ld -shared foo.o -o foo.so
Sun systems are the same, but with -s in place of -shared. For other systems, consult the ld documentation for the option to make a shared library or dynamic load library.

To use the functions in a datafile, include a line at the top of the datafile before any of the functions are used:

load_library "foo.so"
The current directory and the EVOLVERPATH will be searched for the library. Up to 10 libraries may be loaded. Afterwards, any of the functions may be invoked just by using their name, without an explicit argument list because the argument list is always implicit where these functions are legal. Examples, supposing func2 is also defined with one argument:
constraint 1
formula: func1

boundary 1 parameters 2
x1: func2
x2: 3*func2 + sin(p1)
It is up to you to make sure the number of arguments your function expects is the same as the number implicit in the use of the function. You do not need to explicitly declare your functions in the datafile. Any undefined identifier is checked to see if it is a dynamically loaded function.

NOTE: This implementation of dynamic loading is experimental, and the interface described here may change in the future.


__bhead_index

A body attribute used internally by Hessian routines.

__vhead_index

A vertex attribute used internally by Hessian routines.

form_factors

A facet extra attribute vector used by several elastic energy named methods: linear_elastic, neo_hookean, relaxed_elastic, relaxed_elastic_A, SVK_elastic, and dirichlet_elastic. The entries are the dot products of the sides of the relaxed facet. Due to symmetry, there are only three distinct dot products, so form_factors just has length 3. The entries are {s11,s12,s22}, where s11 = dot(v2-v1,v2-v1), s12 = dot(v2-v1,v3-v1), and s22 = dot(v3-v1,v3-v1). If form_factor is not defined by the user, it will be created by Evolver, and the initial facet shape will be assumed to be unstrained.

poisson_ratio

Facet extra attribute used by various elastic named methods: linear_elastic, linear_elastic_B, neo_hookean, relaxed_elastic, relaxed_elastic_A, SVK_elastic, and dirichlet_elastic. This is the two-dimensional isotropic poisson ratio.
Some physical surfaces have their lowest energy state when their mean curvature is some nonzero value. An example is a membrane made of parallel stacked molecules which have one end bigger than the other. Their curvature energy is thus proportional to (h-h0)^2, where h is the actual mean curvature and h0 is the equilibrium or intrinsic mean curvature. Several of the squared mean curvature named methods can use h0, either in the form of a variable h_zero or a real-valued vertex attribute h_zero. If the vertex attribute exists, it takes precedence. Methods using h_zero: Methods not using h_zero:

knot_power

Internal read-write variable used as exponent on some term in various knot energies. Named methods using knot_power are:

surface_knot_powerfacet_knot_energy and sphere_knot_energy named methods as the exponent of the denominator.

LEBweight>

Facet extra attribute used to weight individual facets in the named methods linear_elastic_B, named methods relaxed_elastic, named methods relaxed_elastic_A,

old_force_ribiere

Vertex extra attribute used internally by conjugate gradient mode.

parameter_1 A generic parameter used by a couple of named methods.
Back to top of Surface Evolver documentation. Index. evolver-2.30c.dfsg/doc/profiling.htm0000644000175300017530000000612211410765113017677 0ustar hazelscthazelsct Surface Evolver Documentation: Debugging

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Surface Evolver Execution Time Profiling


Coarse Profiling

Coarse timing with resolution of 0,01 second or so can be done with Evolver's clock variable. For example, to time iterations I typically do

   Enter command: quiet; start := clock; g 100; print start-clock; quiet off
which will print out the elapsed time in seconds. I do quiet here to suppress output to the screen, which can slow down things considerably.


Fine Profiling

Nanosecond resolution can be done with Evolver's cpu_counter variable.


Expression Evaluation Profiling

On systems where Evolver has cpu_counter available and Evolver has been compiled with the manifest constant PROF_EVALS defined, the expression evaluator inside Evolver keeps track of the clock cycles elapsed during each expression evaluation. These expressions include procedures, functions, constraint and boundary formulas, content integrands, energy integrands, quantity integrands, etc; everything that prints out as code in a dump file. The print profiling command will print the accumulated CPU cycles so far for each type of expression. The times are inclusive of any child functions or procedures. An example, from mound.fe after running "gogo":
Enter command: print profiling
Inclusive profiling counts:
                      Name            CPU Cycles
                              re           1,952,792
                            gogo         125,201,889
                           gogo2                   0
                           gogo3                   0
                           gogo4                   0
                           gogo5                   0

Constraint expressions
                    Constraint  Formula Cycles   Energy Cycles  Content Cycles
                             1         877,047       2,337,727               0
Note that hard-coded evaluations of area, volume, etc. do not show up here, except for their effect on overall elapsed time.

The command reset_profiling will set all the cycle values back to 0.


Execution sampling

Of course, if you are compiling Evolver yourself, you can use any type of profiling you would ordinarily use on a C program. i leave you to your own devices on this.


Back to top of Surface Evolver documentation. Index. evolver-2.30c.dfsg/doc/datafile.htm0000644000175300017530000020042511410765113017461 0ustar hazelscthazelsct Surface Evolver Documentation - Datafile format

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Evolver datafile format

The initial configuration of the surface is read from an ASCII datafile. See the cube or mound examples for samples. The datafile is organized into six parts: General datafile topics: In the syntax descriptions below, keywords will be in upper case. constexpr means a constant expression, and expr means any expression. n or k means an integer, which may be signed if it is being used as an oriented element label. Square brackets denote optional items. '|' means 'or'.

NOTE: Usually a formula can occur anyplace a number is legal (except element numbers, constraint numbers, etc). Some formulas are stored as formulas and re-evaluated at each use; examples include constraint, boundary, and quantity formulas. Others, such as vertex coordinates, are evaluated when the datafile is read, and only the numeric value stored. Thus if a user defines a vertex coordinate as 3*width, then changing the value of the variable width at runtime will not affect the vertex. If you are not clear on which interpretation applies in a certain spot, dump the datafile and look at the spot to see if a formula or number was dumped.


Include files

The standard C language method of including other files is available. The file name must be in double quotes. If the file is not in the current directory, EVOLVERPATH will be searched. Includes may be nested to 10 deep. Example:
	#include  "common.stuff"

Macros

Macros are text substitutions done by replacing an identifier by a string of characters before parsing. Macros are only defined in the datafile, and do not work from the command prompt. Simple macros (no parameters) may be defined as in C:
         #DEFINE  identifier  string
identifier must be an identifier without other special meaning to the parser. string is the rest of the logical line, not including comments. It will be substituted for identifier whenever identifier occurs as a token subsequently. Substitutions are re-scanned. No checks for recursiveness are made. There is a maximum length (currently 500 characters) on a macro definition. Note: macro identifiers are separate tokens, so if "-M" translates into "-2", this will be read as two tokens, not a signed number. The keyword keep_macros in the datafile will keep macro definitions active during runtime, until the next datafile is loaded.

Datafile top section

The datafile begins with optional definitions and specifications. These are definitions that need to be made before the geometric elements are defined, or for which command syntax is lacking or awkward. Other initializations can be made at the end of the datafile in the read section using the command language. Each line starts with a keyword. The order is immaterial, except for the usual rule that items must be defined before use. None of these are required if you are willing to accept various defaults. They are listed here in rough order of frequency of use; those in the first column you should know, those in the second column you might never use in a lifetime.

Definitions of variables

Variables may be defined in the datafile top section with this syntax:
      PARAMETER identifier = constexpr
This declares identifier to be a variable with the given initial value. The value may be changed at runtime with the A command, or by assignment. Variables may be used in any subsequent expression or constant expression. Changing variables defined here results in automatic recalculation of the surface when autorecalc is been toggled on. Hence only variables needed in other top section declarations should be defined here.

Level set constraint declarations

The format for declaring a level set constraint in the top section of the datafile is
CONSTRAINT n [GLOBAL] [CONVEX] [NONNEGATIVE] [NONPOSITIVE] [NONWALL]
FORMULA FUNCTION  expr
[ENERGY:
 E1: expr
 E2: expr
 E3: expr]
[CONTENT:
 C1: expr
 C2: expr
 C3: expr]
You may use EQUATION or FUNCTION as synonyms for FORMULA. This defines constraint number n, where n is a positive integer. The optional keyword GLOBAL means the constraint automatically applies to all vertices (but not automatically to edges or faces). GLOBAL constraints count in the number limit. If CONVEX is given, then an additional gap energy is attributed to edges on the constraint to prevent them from trying to short-circuit a convex boundary. NONWALL indicates this constraint is to be ignored in vertex and edge popping. If NONNEGATIVE or NONPOSITIVE is given, then this is a one-sided constraint, and all vertices will be forced to conform appropriately to the constraint at each iteration. The FORMULA expression defines the zero level set which is the actual constraint. It may be written as an equation, since '=' is parsed as a low-precedence minus sign. The formula may include any expressions whose values are known to the Evolver, given the particular vertex. Most commonly one just uses the coordinates (x,y,z) of the vertex, but one can use variables, quantity values, or vertex extra attributes. Using a vertex extra attribute is a good way to customize one formula to individual vertices. For example, if there were a vertex extra attribute called zfix, one could force vertices to individual z values with one constraint with the formula z = zfix, after of course assigning proper values to zfix for each vertex (be sure to fix up zfix after refining or otherwise creating vertices). Do not use '>' or '<' to indicate inequalities; use NONNEGATIVE or NONPOSITIVE. Conditional expressions, as in C language, are useful for defining constraints composed of several surfaces joined smoothly, such as a cylinder with hemispherical caps. Assignments to variables may be made at the start of expressions, mainly for the purpose of evaluating common subexpressions only once in the integrands. The syntax for such a compound expression is
    variable :=  expr, expr
The value of the expression is the value of the second expression.

The optional ENERGY section signifies that vertices or edges on the constraint are deemed to have an energy. In the soapfilm model, the next lines give components of a vectorfield that will be integrated along each edge on the constraint. In the string model, just one component is needed, which is evaluated at each vertex on the constraint. The main purpose of this is to permit facets entirely on the constraint to be omitted. Any energy they would have had should be included here. One use is to get prescribed contact angles at a constraint. This energy should also include gravitational potential energy due to omitted facets. Integrals are now also evaluated on fixed edges, which is a change from earlier versions of Evolver.

The optional CONTENT section signifies that vertices (string model ) or edges (soapfilm model) on the constraint contribute to the area or volume of bodies. If the part of a body boundary that is on a constraint is not defined by facets, then the body volume must get a contribution from a content integral. It is important to understand how the content is added to the body in order to get the signs right. The integral is evaluated along the positive direction of the edge. If the edge is positively oriented on a facet, and the facet is positively oriented on a body, then the integral is added to the body. This may wind up giving the opposite sign to the integrand from what you think may be natural. Always check a new datafile when you load it to be sure the integrals come out right.


Parameterized boundary declaration

A parameterized boundary may be declared in the top section of the datafile with the syntax
BOUNDARY n PARAMETERS k [CONVEX] 
X1: expr 
X2: expr
X3: expr
This defines boundary number n, where n is a positive integer and k is the number of parameters. If CONVEX is given, then an additional gap energy is attributed to edges on the boundary to prevent them from trying to short-circuit a convex boundary. The following lines have the functions for the coordinates in terms of the parameters P1 and maybe P2, P3,.... See the catenoid example. Energy and content integrals for boundaries are implemented, with the same syntax as for level set constraints.

Named quantities

The syntax for defining a named quantity in the data file is:

 QUANTITY name   ENERGY|FIXED=value|CONSERVED|INFO_ONLY [LAGRANGE_MULTIPLIER constexpr]
[TOLERANCE constexpr]  [MODULUS constexpr] methodlist | FUNCTION methodexpr

Here name is an identifier assigned by the user in order to refer to the quantity. Any quantities must be declared to be one of three types:

  • ENERGY quantities are added to the overall energy of the surface;
  • FIXED quantities that are constrained to a fixed target value;
  • CONSERVED quantities are like FIXED in that the motion is projected to conserve the quantity, but the actual value is not projected to a given value.
  • INFO_ONLY quantities whose values are merely reported to the user.
For fixed quantities, the optional Lagrange multiplier value supplies the initial value of the Lagrange multiplier (the "pressure" attribute of the quantity). It is meant for dump files, so on reloading no iteration need be done to have a valid Lagrange multiplier.

For fixed quantities, the tolerance attribute is used to judge convergence. A surface is deemed converged when the sum of all ratios of quantity discrepancies to tolerances is less than 1. This sum also includes bodies of fixed volume. If the tolerance is not set or is negative, the value of the variable target_tolerance is used, which has a default value of 0.0001.

Each quantity has a modulus, which is just a scalar multiplier of the whole quantity. A modulus of 0 will turn off an energy quantity. The default modulus is 1. The methodlist version of the quantity definition may contain one or more method instances. To incorporate a previously explicitly defined instance, include METHOD instancename. GLOBAL_METHOD may be used instead of METHOD to indicate the method applies to all elements of the appropriate type; it is equivalent to using GLOBAL in the method definition. To instantiate a method in the quantity definition, you essentially incorporate the instance definition, but without an instance name. Example of a quantity with one predefined method instance and one implicitly defined instance:

   method_instance qwerty method facet_scalar_integral
     scalar_integrand: x^2
   quantity foobar energy method qwerty method edge_scalar_integral
     scalar_integrand: y^3

Usually the second, implicit definition will be more convenient. Several method instances may be included in one methodlist (up to a current limit of 50), and their values are added together and multiplied by the quantity modulus to get the quantity value. The FUNCTION methodexpr variant defines the quantity as a function of previously defined method instances. Example:

   method_instance qwerty method facet_scalar_integral
     scalar_integrand: x^2
   quantity foobar energy function qwerty^3

Non-global quantities may be applied to elements individually by adding the quantity name to the datafile line defining an element. They may also be applied or unapplied at runtime with the set and unset commands. Orientable methods can be applied with negative orientation in the datafile by following the name with a dash. The orientation in a set command follows the orientation the element is generated with.

Methods applying to different types of elements may be combined in one quantity. If such a quantity is applied to an element, then all method instances of that quantity of the appropriate type are applied to the element. Original attachments of quantities are remembered, soIf an edge method is applied to a facet, then edges created from refining that facet will inherit the edge method.


Named method instances declaration

Method instances are usually defined as part of the definition of a named quantity, but there are circumstances where a quantity is composed of several method instances and the method instances need to be referred to individually; perhaps the user wants to know the values of the individual instances. The general syntax for defining an instance of a named method in a datafile is:
  METHOD_INSTANCE name METHOD methodname [MODULUS constexpr] 
   [ELEMENT_MODULUS attrname]  [GLOBAL] [parameters]
Here, name is a user-assigned name for referring to this particular instance. methodname is one of the pre-defined methods in Evolver. The modulus value multiplies the method value to give the instance value. The default modulus is 1. Individual elements may be given multipliers by specifying an extra attribute attrname for the type of element; the attribute must have been defined earlier. GLOBAL makes the method apply to all elements of the appropriate type. Non-global instances may be applied to elements individually by adding the instance name to the datafile line defining an element. They may also be applied or unapplied at runtime with the set and unset commands. Orientable methods can be applied with negative orientation in the datafile by following the name with a dash. The orientation in a set command follows the orientation the element is generated with.

Each method may have various parameters to specialize it to an instance. Currently the only parameters specified are:

SCALAR_INTEGRAND: expr
where expr is a scalar function of coordinates (and of tangent or normal vector components in edge_general_integral or facet_general_integral). Element attributes of the appropriate type element may also be used.

VECTOR_INTEGRAND:
Q1: expr
Q2: expr
Q3: expr
where the expressions are functions of the coordinates. Element attributes of the appropriate type element may also be used.

FORM_INTEGRAND:
Q1: expr
Q2: expr
Q3: expr
...
where the expressions are functions of the coordinates. Element attributes of the appropriate type element may also be used. When used in the facet_2form_integral method. The form components are listed in lexicographic order, i.e. in 4D the six components 12,13,14,23,24,34 would be listed as Q1 through Q6.

PARAMETER_1 constexpr
For specifying miscellaneous numeric parameters to certain methods.

K_FORM_ORDER constexpr
For methods that use differential k-forms, this specifies the value of k. Should occur before FORM_INTEGRAND when needed.

Surface dimension

The default dimension of the surface is 2. If not, it must be declared in the top section of the datafile. For a 1-dimensional surface (the string model), simply include the line
 STRING
The default dimension 2 soapfilm model is equivalent to using
 SOAPFILM
In general, the line
 SURFACE_DIMENSION n
defines the surface to have dimension n. Dimension over 2 is valid only in the simplex model. The surface dimension may be accessed at runtime through the read-only variable surface_dimension.

Space dimension

The default dimension of space is 3. Otherwise it must be declared in the top section of the datafile, with syntax
SPACE_DIMENSION n
The dimension must be at most the value of MAXCOORD in model.h, which is 4 in the distributed version. The space dimension may be accessed at runtime through the read-only variable space_dimension.

Extra attribute declarations

It is possible for the user to define extra attributes for elements, which may be single values or up to eight-dimensional arrays. If these attributes are to be included in the datafile, then the top section of the datafile must contain appropriate definitions. The definition syntax is the same as used by the define runtime command:
 DEFINE elementtype ATTRIBUTE name type [dim]...
where elementtype is vertex, edge, facet, or body, name is an identifier of your choice, type is REAL or INTEGER (internally, there is also a ULONG unsigned long type also), and dim is an optional expression for the vector dimension. There is no practical distinction between real and integer types at the moment, since everything is stored internally as reals. But there may be more datatypes added in the future. Extra attributes are inherited by elements of the same type generated by subdivision. The type may be followed by FUNCTION followed by a procedure in brackets to be evaluated whenever the value of the attribute is read; in the formula, self may be used to refer to the element in question to use its attributes, in particular to at some point assign a value to the attribute. The print command may be used to print attribute arrays or array slices in bracketed form. Examples:
  define edge attribute charlie real 
  define vertex attribute oldx real[3] 
  define facet attribute knots real[5][5][5] 
  define edge attribute bbb real function { self.bbb := self.x+self.y }
WARNING: there is a syntax ambiguity if you mean to define a stand-alone function in the top of the datafile and put it after an attribute declaration. You should define stand-alone functions before attributes, or separate them with some other kind of declaration.

Quadratic declaration

To declare that the datafile lists a surface in the quadratic model, the top section of the datafile should contain the line
QUADRATIC
The only effect on datafile syntax is that the edge section may list edge midpoint vertices.

Gravity constant declaration

The initial value of the gravitational constant may be set in the datafile with the syntax
  GRAVITY_CONSTANT value 
The default value is 1.

Torus model declaration

To declare periodic boundary conditions (i.e. make the domain a flat torus), include in the top section of the datafile the line
TORUS
All space dimensions will be periodic, with the period vectors given in the periods declaration. If the domain is completely filled by bodies with prescribed volumes, then the line
TORUS_FILLED
should be used instead to prevent degenerate volume constraints.

Torus periods

If periodic boundary conditions are used (the torus model) , the period vectors of the fundamental unit cell parallelpiped may be defined in the top section of the datafile. Default is the unit cube. The syntax is the keyword PERIODS followed by expressions for the components of each period vector:
PERIODS 
expr expr expr
expr expr expr
expr expr expr
The size of this matrix depends on the space dimension. Variables may be used in the expressions, so the fundamental domain may be changed interactively by assigning new values to the variables. Be sure to give a recalc command whenever you change such a variable, in order to get the period matrix re-evaluated.

Viewing matrix

The top section of the datafile may contain an initial viewing matrix:
VIEW_MATRIX
  constexpr constexpr constexpr constexpr
  constexpr constexpr constexpr constexpr
  constexpr constexpr constexpr constexpr
  constexpr constexpr constexpr constexpr
The matrix is in homogeneous coordinates with translations in the last column. The size of the matrix is one more than the space dimension. This matrix will be part of all dump files, so the view can be saved between sessions. This matrix is used and set by native screen graphics ('s' command) and only applies to internal graphics (Postscript, Xwindows, etc.) but not external graphics (geomview). The elements may be read or set at runtime by view_matrix[i][j], where the indices start at 1. In particular, one can write command scripts to save and reload particular view matrices; see saveview.cmd in the distribution package.

View transforms

For the display of several transformations of the surface simultaneously, a number of viewing transformation matrices may be given in the top section of the datafile:
VIEW_TRANSFORMS n 
COLOR color
SWAP_COLORS
  constexpr constexpr constexpr constexpr
  constexpr constexpr constexpr constexpr
  constexpr constexpr constexpr constexpr
  constexpr constexpr constexpr constexpr
    ...
The transforms apply to all graphics, internal and external, and are prior to the viewing matrix for internal graphics. The identity transform is always done, so it does not need to be specified. The number of matrices follows the keyword VIEW_TRANSFORMS. Each matrix is in homogeneous coordinates, with translation in the last column. The size of each matrix is one more than the space dimension. Individual matrices need no special separation; Evolver just goes on an expression reading frenzy until it has all the numbers it wants. Each matrix may be preceded by an optional color that applies to facets transformed by that matrix. The color applies to one transform only; it does not continue until the next color specification. If SWAP_COLORS is present instead, facet frontcolor and backcolor will be swapped when this matrix is applied. Transforms may be activated or deactivated interactively with the transforms toggle. The internal variable transform_count records the number of transforms, and the transform matrices are accessible at runtime as a three-dimensional matrix view_transforms[][][]. View transform generators are a more sophisticated way to control view transforms.

View transform generators

Listing all the view transforms is tedious and inflexible. An alternative is to list just a few matrices that can generate transforms. See the transform_expr command for instructions on entering the expression that generates the actual transforms. Special Note: in the torus model, the period translations are automatically added to the end of the list. So in the torus model, these are always available, even if you don't have view_transform_generators in the datafile. Syntax in the top of the datafile:
VIEW_TRANSFORM_GENERATORS n 
SWAP_COLORS
  constexpr constexpr constexpr constexpr
  constexpr constexpr constexpr constexpr
  constexpr constexpr constexpr constexpr
  constexpr constexpr constexpr constexpr
    ...
The number of matrices follows the keyword VIEW_TRANSFORM_GENERATORS. Each matrix is in homogeneous coordinates, with translation in the last column. The size of each matrix is one more than the space dimension. Individual matrices need no special separation; Evolver just goes on an expression reading frenzy until it has all the numbers it wants. If SWAP_COLORS is present, facet frontcolor and backcolor will be swapped when this matrix is applied. The internal variable transform_count records the number of transforms, and the transform matrices are accessible at runtime as a three-dimensional matrix view_transforms[][][].

Scale limit

To set an upper bound of valueon the gradient descent scale factor, include the line
   SCALE_LIMIT value
in the top section of the datafile. The upper bound can be changed at runtime with the m command, or by setting the scale_limit variable. If surface tension is the main energy, the scale_limit should be set to the inverse of the surface tension.

Functions and procedures

Usually stand-alone user-defined functions and procedures are defined in the read section of the datafile, but sometimes it is necessary to define them in the top section of the datafile so they may be used in other top section declarations. It is possible to define them in the top section with the same syntax as in the read section. Note this applies to the parameter-passing variety of functions and procedures, denoted by the leading keyword "function" or "procedure", and not command definitions like "gg := {...}". WARNING: there is a syntax ambiguity if you mean to define a stand-alone function in the top of the datafile and put it after an attribute declaration. You should define stand-alone functions before attributes, or separate them with some other kind of declaration.

Optimizing parameter

A variable may be made subject to optimization during iteration or hessian commands with the datafile declaration
      OPTIMIZING_PARAMETER identifier = constexpr PDELTA = constexpr PSCALE = constexpr
Such a variable joins the vertex coordinates as an independent variable during optimization. However, it differs from a coordinate in that gradients with respect to it are calculated numerically, rather than analytically. Thus it may be used anywhere a variable is permitted. Hessians with optimizing parameters are implemented. The optional pdelta value is the parameter difference to use in finite differences; the default value is 0.0001. The optional pscale value is a multiplier for the parameter's motion, to do "impedance matching" of the parameter to the surface energy. These attributes may be set on any parameter, for potential use as an optimizing parameter. At runtime, a parameter may be toggled to be optimizing or not with the FIX and UNFIX commands. That is, fix radius would make the radius variable non-optimizing (fixed value). Also, the pdelta and pscale attributes may be accessed at runtime, as in
height.pscale := 2*height.pscale
"Optimising_parameter" is a synonym.

Gap constant declaration

The initial value of the gap constant for gap energy may be set in the datafile with the syntax
  GAP_CONSTANT value 
The default value is 1. Synonym: spring_constant.

Symmetric content

The datafile keyword SYMMETRIC_CONTENT triggers the use of an alternate surface integral for calculating body volumes, namely the vectorfield (x,y,z)/3. It is useful if unmodelled sides of a body are radial from the origin, or if constraint content integrals (which is evaluated by an approximation) lead to asymmetric results on what should be a symmetric surface.

Keep original ids

The presence of the keyword
keep_originals
in the top of the datafile has the same effect as the -i command line option, which is to keep the id numbers internally the same as in the datafile, instead of renumbering them in the order they are read in.

Version

If a datafile contains features present only after a certain version of the Evolver, the datafile can contain a line of the form
evolver_version "2.10"
This will generate a version error message if the current version is earlier, or just a syntax error if run on an Evolver version earlier than 2.10.

Constraint tolerance

This is datafile declaration of the tolerance within which a vertex is deemed to satisfy a level-set constraint. Default is 1e-12. Syntax:
  CONSTRAINT_TOLERANCE const_expr 
Sets the value of the internal variable constraint_tolerance.

Symmetry group declaration

To declare that the domain is the quotient space of a symmetry group, the top section of the datafile must contain a line of the form
SYMMETRY_GROUP "name" 
"name" is a double-quoted name that is matched against the list of defined symmetry groups.

Length method

This item, length_method_name, specifies the name of the pre-defined method to use as the method to compute edge length in place of the default edge_length method. It is optional. Developed so circular arcs can be used in two-dimensional foams. Current reasonable methods are circular_arc_length and spherical_arc_length. Usage implies converting to everything_quantities mode. Syntax:
length_method_name quoted_method_name
For example,
string
space_dimension 2
length_method_name "circular_arc_length"

Area method

This item, area_method_name, specifies the name of the pre-defined method to use as the method to compute facet areas in place of the default edge_area method in the string model or facet_area method in the soapfilm model. It is optional. Developed so circular arcs can be used in two-dimensional foams. Current reasonable methods are circular_arc_area and spherical_arc_area. Synonymous with volume_method_name in the string model. Usage implies converting to everything_quantities mode. Syntax:
area_method_name quoted_method_name
For example,
string
space_dimension 2
area_method_name "circular_arc_area"

Volume method

This item, volume_method_name, specifies the name of the pre-defined method to use as the method to compute body volumes in place of the default edge_area method in the string model or facet_volume method in the soapfilm model. It is optional. Developed so circular arcs can be used in two-dimensional foams. Synonymous with area_method_name in the string model. Usage implies converting to everything_quantities mode. Syntax:
volume_method_name quoted_method_name
For example,
string
space_dimension 2
volume_method_name "circular_arc_area"

Hessian special normal vector

When hessian_special_normal is on, hessian commands use a special vectorfield for the direction of the perturbation, rather than the usual surface normal. The vectorfield is specified in the format
HESSIAN_SPECIAL_NORMAL_VECTOR
c1: expr
c2: expr
c3: expr
One can use vertex attributes in the expressions. Beware that hessian_special_normal also applies to the normal calculated by the vertexnormal attribute and the normal used by regular vertex averaging.

Simplex model declaration

To declare that the datafile lists a surface in the simplex model, the top section ot the datafile should contain the line
SIMPLEX_REPRESENTATION
The main effect on the datafile is that faces are defined by oriented vertex lists rather than edge lists.

Dynamic load library

To load a dynamic library of compiled functions, the syntax is
 LOAD_LIBRARY "filename"
where the double-quoted filename is the library. The current directory and the EVOLVERPATH will be searched for the library.

Crystalline integrands

To declare that surface area energy should be calculated with a crystalline integrand, the top section of the datafile should contain a line of the form
 WULFF "filename"
The double-quoted filename (with path) refers to a file giving the Wulff vectors of the integrand. The format of the file is one Wulff vector per line with its components in ASCII decimal format separated by spaces. The first blank line ends the specification. Some special integrands can be used by giving a special name in place of the file name. Currently, these are "hemisphere" for a Wulff shape that is an upper unit hemisphere, and "lens" for two unit spherical caps of thickness 1/2 glued together on a horizontal plane. These two don't need separate files.

Phase file declaration

To declare that the surface tension of an edge or facet depends on the phases of its adjacent facets or bodies, the top section of the datafile should contain a line of the form
 PHASEFILE "filename" 
The information is read from an ASCII file, whose name is given in a double-quoted string. The first line of the file has the number of different phases. Each line after consists of two phase numbers and the surface tension between them. Lines not starting with a pair of numbers are taken to be comments. If a pair of phases is not mentioned, the surface tension between them is taken to be 1.0. Facets in the string model or bodies in the soapfilm model can be labelled with phases with the PHASE n phrase in the datafile.

Ideal gas model

A line in the top section of the datafile of the form
PRESSURE constexpr
specifies that bodies are compressible and the ambient pressure is the value of constexpr. The default is that bodies with given volume are not compressible.

Interpolation of parameters

To use interpolation instead of extrapolation in calculating the parameters of edge midpoints during refining, use the keyword
   INTERP_BDRY_PARAM
This should be done only if there are no periodic parameters.

Everything_quantities

Keyword in top section of the datafile. Causes all areas, volumes, etc. to be converted to named quantities and methods. Equivalent to the command line option -q, or the convert_to_quantities command.

Mobility declaration

A mobility matrix may be defined in the top section of the datafile by the syntax
MOBILITY_TENSOR
expr expr expr
expr expr expr
expr expr expr
or
MOBILITY expr
The first form gives the full mobility matrix, and the second form gives the matrix as a scalar multiple of the identity matrix. The formulas are evaluated at each vertex at each iteration, and so formulas may depend on vertex position and any vertex attributes.

Metric declaration

A Riemannian metric on the ambient space may be declared in the top section of the datafile with the syntax
METRIC
expr expr expr
expr expr expr
expr expr expr
or
CONFORMAL_METRIC expr
or
KLEIN_METRIC
The keyword METRIC is followed by the N^2 components of the metric tensor, where N is the dimension of space. The components do not have to obey any particular line layout; they may be all on one line, or each on its own line, or any combination. It is up to the user to maintain symmetry. A conformal metric is a scalar multiple of the identity matrix, and only the multiple need be given. A conformal metric will run about twice as fast. The Klein metric is a built-in metric for hyperbolic n-space modelled on the unit disk or ball.

Merit factor

If the keyword MERIT_FACTOR is present, then the i command will print the ratio total_area^3/total_volume^2, which measures the efficiency of area enclosing volume. A holdover from the early days of trying to beat Kelvin's partition of space.

Squared mean curvature

To add an energy of squared mean curvature, include a line in the top of the datafile
  SQUARED_CURVATURE modulus
The modulus is a multiplier for the energy, and is available at runtime in the read-write variable sq_curvature_modulus. This is the original squared mean curvature energy; later versions are in the squared curvature named methods.

In the string model, the power of the curvature is controlled by the internal read-write variable curvature_power.


Squared Gaussian curvature

To add an energy of squared Gaussian curvature, include a line in the top of the datafile
  SQUARED_GAUSSIAN_CURVATURE modulus
The modulus is a multiplier for the energy. Synonyms: square_gaussian_curvature, sqgauss

Zoom_vertex

Datafile keyword setting the current zoom vertex. Used in dump files after a zoom command has been given.

Zoom_radius

Datafile keyword setting the current zoom radius. Used in dump files after a zoom command has been given.

Suppress_warning

Datafile keyword instructing Evolver not to print a certain warning. Syntax:
  SUPPRESS_WARNING number
where number is the number of the warning. Meant to suppress irritating warning messages that you know are irrelevant. Warnings can be restored with the syntax
  UNSUPPRESS_WARNING number


Element lists

The lists of geometric elements follow a general format. Each element is defined on one line. The first entry on a line is the element number. Numbering need not be consecutive, and may omit numbers, but be aware that internally elements will be renumbered in order. The original number in the datafile is accessible as the original attribute of an element. After the element number comes the basic defining data, followed by optional attributes in arbitrary order. Besides the particular attributes for each element type listed below, one may specify values for any extra attributes defined earlier. The syntax is attribute name followed by the appropriate number of values. Also an arbitrary number of named quantities or method instances may be listed. These add method values for this element to the named quantity. The named quantity or instance must have been declared in the top section of the datafile.

Vertex list

The datafile vertex list is started by the keyword VERTICES at the start of a line. It is followed by lines with one vertex specification per line. If the vertex is not on a parametric boundary, the syntax is
k   x y ... [FIXED] [CONSTRAINT c1 c2 ...]  [BARE]
            [quantityname ...] [methodname ...]
The syntax for a vertex on a parametric boundary is
k p1 [p2 ...]  BOUNDARY b [FIXED] [BARE] [quantityname ...] 
   [methodname ...] 
Here k is the vertex number, a positive integer. Vertices do not need to be listed in order, and there may be gaps in the numbering. However, if they are not in consecutive order, then the numbering in dump files will be different. x y ... are constant expressions for coordinates. In the parametric boundary format, the boundary parameter values are given instead of the coordinates. If FIXED is given, then the vertex never moves, except possibly for an initial projection to constraints. If CONSTRAINT is given, then one or more constraint numbers must follow. You can list as many constraints as you want, as long as those that apply exactly at any time are consistent and independent. The given coordinates need not lie exactly on the constraints; they will be projected onto them. A vertex on a parametric boundary cannot also be on a constraint.

The BARE attribute is just an instruction to the checking routines that this vertex is not supposed to have an adjacent facet in the soapfilm model, so spurious warnings will not be generated. This is useful when you want to show bare wires or outline fundamental domains.

An arbitrary number of named quantities or method instances may be listed. These add method values for this element to the named quantity. The named quantity or instance must have been declared in the top section of the datafile.

The list vertices command prints the datafile format listing of vertices.


Edge list

The datafile edge list follows the vertex list, and is started by the keyword EDGES at the start of a line. It is followed by lines with one edge specification per line in this format (linespliced here):
k v1 v2 [midv] [s1 s2 s3] [WRAP w] [FIXED] [BOUNDARY b] \
   [CONSTRAINTS c1 [c2 ...]] [TENSION constexpr] [COLOR n] \
     [BARE]  [quantityname ...] [methodname ...] 
Here k is the edge number, with numbering following the same rules as for vertices. v1 and v2 are the numbers of the tail and head vertices of the edge. In the quadratic model, the edge midpoint may be listed as a third vertex midv (otherwise a midpoint vertex will be created). In the torus model, there must follow signs s1 s2 s3 indicating how the edge wraps around each unit cell direction: + for once positive, * for none, and - for once negative. In non-torus symmetry groups, each edge should have a WRAP symmetry group element encoded as an integer. FIXED means that all vertices and edges resulting from subdividing this edge will have the FIXED attribute; it does not mean that the endpoints will be automatically fixed. Likewise the BOUNDARY and CONSTRAINT attributes will be inherited by all edges and vertices derived from this edge. If a constraint has energy or content integrands, these will be done for this edge. IMPORTANT: If a constraint number is given as negative, the edge energy and content integrals will be done in the opposite orientation. In the string model, the default tension is 1, and in the soapfilm model, the default tension is 0. However, edges may be given nonzero tension in the soapfilm model, and they will contribute to the energy.

If the simplex model is in effect, edges are one less dimension than facets and given by an ordered list of vertices. Only edges on constraints with integrals need be listed.

The BARE attribute is just an instruction to the checking routines that this ede is not supposed to have an adjacent facet in the soapfilm model, so spurious warnings will not be generated. This is useful when you want to show bare wires or outline fundamental domains.

An arbitrary number of named quantities or method instances may be listed. These add method values for this element to the must have been declared in the top section of the datafile. If the quantity or instance has orientation-dependent methods, the name may be followed by a dash to reverse the applied orientation.

The list edges command prints the datafile format listing of edges.


Face list

The datafile face list follows the edge list, and is started by the keyword FACES at the start of a line. It is followed by lines with one facets specification per line in this format:
  k   e1 e2 ...  [FIXED] [TENSION constexpr] [BOUNDARY b] \
  [CONSTRAINTS c1 [c2 ...]]     [NODISPLAY]   \
   [COLOR n]} [FRONTCOLOR n] [BACKCOLOR n] \
   [PHASE n] [quantityname ...] [methodname ...] 
Here k is the face number, with numbering following the same rules as for vertices. There follows a list of oriented edge numbers in counterclockwise order around the face. A negative edge number means the opposite orientation of the edge from that defined in the edge list. The head of the last edge must be the tail of the first edge (except if you're being tricky in the string model). There is no limit on the number of edges. The face will be automatically subdivided into triangles if it has more than three edges in the soapfilm model. The TENSION (synonym: DENSITY) value is the energy per unit area (the surface tension) of the facet; the default is 1. Density 0 facets exert no force, and can be useful to define volumes or in displays. Fractional density is useful for prescribed contact angles. NODISPLAY prevents the facet from being displayed. The COLOR attribute applies to both sides of a facet; FRONTCOLOR applies to the positive side (edges going counterclockwise) and BACKCOLOR to the negative side. The PHASE number is used in the string model to determine the surface tension of edges between facets of different phases, if phases are used.

If the simplex model is in effect, the edge list should be replaced by an oriented list of vertex numbers.

An arbitrary number of named quantities or method instances may be listed. These add method values for this element to the must have been declared in the top section of the datafile. If the quantity or instance has orientation-dependent methods, the name may be followed by a dash to reverse the applied orientation.

The faces section is optional in the string model.

The list facets command prints the datafile format listing of facets.


Body list

The datafile body list follows the face list, and is started by the keyword BODIES at the start of a line. It is followed by lines with one body specification per line in this format:
k  f1 f2 f3 .... [VOLUME constexpr] [VOLCONST constexpr] [ACTUAL_VOLUME constexpr] \
[PRESSURE p] [DENSITY constexpr] [PHASE ] 
Here k is the body number, and f1 f2 f3 ... is an unordered list of signed facet numbers. Positive sign indicates that the facet normal (as given by the right-hand rule from the edge order in the facet list) is outward from the body and negative means the normal is inward. Giving a VOLUME value constexpr means the body has a volume constraint, unless the ideal gas model is in effect, in which case constexpr is the volume at the ambient pressure. VOLCONST is a value added to the volume; it is useful when the volume calculation from facet and edge integrals differs from the true volume by a constant amount, as may happen in the torus model. ACTUAL_VOLUME is a number that can be specified in the rare circumstances where the torus volume volconst calculation gives the wrong answer; volconst will be adjusted to give this volume of the body. Giving a PRESSURE value means that the body is deemed to have a constant internal pressure; this is useful for prescribed mean curvature problems. It is incompatible with prescribed volume. Giving a DENSITY value means that gravitational potential energy will be included.

To endow a facet with VOLUME, PRESSURE, or DENSITY attributes in the string model, define a body with just the one facet.

The PHASE number is used in the soapfilm model to determine the surface tension of facets between bodies of different phases, if phases are used.

The BODIES section is optional.

The list bodies command prints the datafile format listing of bodies.


READ section

The final section of the datafile may contain commands. These commands are read and executed immediately, just as if they had been entered at the command prompt. Encountering the keyword READ in the datafile causes the Evolver to switch from datafile mode to command mode and read the rest of the datafile as command input. This feature is useful for automatic initialization of the surface with refining, iteration, defining your own commands, etc. The READ section is optional. Example:
  bodies
  1   1 2 3 4 5 6 volume 1

  read

  // automatically do this when datafile is loaded
  refine edge where on_constraint 1

  // typical evolution
  gogo := { g 5; r; g 10; r; g 20 }

The list bottominfo command prints the READ section that would be printed in a dump file.
Back to top of Surface Evolver documentation. Index. evolver-2.30c.dfsg/doc/news_14.htm0000644000175300017530000000771211410765113017174 0ustar hazelscthazelsct Surface Evolver Documentation - Newsletter 12

Surface Evolver Newsletter no. 14

Back to top of Surface Evolver documentation.




                    Surface Evolver Newsletter Number 14
                              August 15, 1996

                   Editor: Ken Brakke, brakke@geom.umn.edu

Contents
  Version 2.01
  New features and changes
  Tetrahedralization
  grad/postdoc opening wanting Evolver experience

Version 2.01
  Surface Evolver version 2.01 is now available at
  http://www.geom.umn.edu/locate/evolver
  or by anonymous ftp from geom.umn.edu in /pub/software/evolver.
  This version is mostly bug fixes, but with a couple new features
  as listed below.

  There are new Mac 68K and Power PC versions in Evolver68K.sea.Hqx
  and EvolverPPC.sea.Hqx, respectively.  This is the first native
  Power PC version, and the first 68K update in a year.

New features and changes

  There is a new no_refine attribute for edges and facets.  It
  prevents edges from being subdivided by the 'r' command.
  Useful for large flat fixed surfaces or outlines.

  You can create new elements on the fly with new_vertex, new_edge,
  new_face, and new_body.  

  The V command has been tweaked to work better on constrained vertices.
  Also, there is a new command vertex_average to do individual
  vertex averaging on the vertices of your choice.

  Command output can be appended to a file using the >> redirection
  symbol, as in    { g 100; u; g 20 } >> "logfile"

  Equiangulation has been implemented for surfaces made of 3D
  simplices.

  The knot_energy method now uses "node_charge" vertex attribute
  for calculating its electrostatic-type energy.  Useful for
  spreading network graphs out in 3D.

  There is an experimental implementation dynamic link libraries for 
  functions used in constraints, boundaries, integrands, etc.
  This can replace interpreted functions with compiled functions
  for greater speed.
 
  DOS, Windows versions leaves alphanumeric escape sequences alone
  in quoted strings, so path names don't get mangled.
  
  The variables transform_expr and transform_count can be printed,
  so you can see the current state if you are using multiple
  image transforms.

Tetrahedralization.

  Some users are interested in constructing tetrahedral meshes
  of the inside of an Evolver surface, e.g. for doing stress
  analysis on solidified solder.  I've put together a set of
  programs that does this, which those interested may get by
  anonymous ftp from geom.umn.edu as /priv/brakke/tetra.tar.

Research Opportunity wanting Evolver experience:


	Project:  3-D Imaging of Flexible Polyurethane Foam

	   -  Advisers:  Xiaodaong Zhang and Professor Chris Macosko
		         (Chemical Engineering and Material Science)

	   -  Goals:     To image foam using laser confocal microscopy and 
	 	         other methods and use existing software 
			 (such as Koros, Surface Evolver) or create 
			 algorithms to size foam cells
	
	*  Graduate assistant or Postdoctoral salary level

	*  Potential for industrial interaction

	*  Desired qualifications:
	   -  Strong math skills (particularly geometry)
	   -  Works well with computers (programming not necessary)
	   -  Experience with image processing and UNIX
              (desirable but not necessary)

	*  For further information, please contact Xiaodong Zhang
           (send resume to apply):
	     Dept. of Chemical Engineering and Materials Science
	     University of Minnesota
	     151 Admundson Hall
	     421 Washington Avenue SE
	     Minneapolis, MN 55455
	     Phone:  625-8573
	     E-mail:  zhan0063@maroon.tc.umn.edu 



Back to top of Surface Evolver documentation. evolver-2.30c.dfsg/doc/column.fe0000644000175300017530000000723711410765113017015 0ustar hazelscthazelsct// column.fe // Example of calculating forces exerted by a // column of liquid solder in shape of skewed catenoid. // All units cgs parameter RAD = 0.05 // ring radius parameter ZH = 0.08 // total height parameter SHIFT = 0.025 // shift #define SG 8 // specific gravity of solder #define TENS 460 // surface tension of solder #define GR 980 // acceleration of gravity gravity_constant GR BOUNDARY 1 PARAMETERS 1 X1: RAD*cos(P1) X2: RAD*sin(P1) + SHIFT X3: ZH CONTENT // used to compensate for missing top facets c1: 0 c2: ZH*x c3: 0 ENERGY // used to compensate for gravitational energy under top facets e1: 0 e2: SG*GR*ZH^2/2*x e3: 0 BOUNDARY 2 PARAMETERS 1 X1: RAD*cos(P1) X2: RAD*sin(P1) X3: 0 vertices // given in terms of boundary parameter 1 0.00 boundary 1 fixed 2 pi/3 boundary 1 fixed 3 2*pi/3 boundary 1 fixed 4 pi boundary 1 fixed 5 4*pi/3 boundary 1 fixed 6 5*pi/3 boundary 1 fixed 7 0.00 boundary 2 fixed 8 pi/3 boundary 2 fixed 9 2*pi/3 boundary 2 fixed 10 pi boundary 2 fixed 11 4*pi/3 boundary 2 fixed 12 5*pi/3 boundary 2 fixed edges 1 1 2 boundary 1 fixed 2 2 3 boundary 1 fixed 3 3 4 boundary 1 fixed 4 4 5 boundary 1 fixed 5 5 6 boundary 1 fixed 6 6 1 boundary 1 fixed 7 7 8 boundary 2 fixed 8 8 9 boundary 2 fixed 9 9 10 boundary 2 fixed 10 10 11 boundary 2 fixed 11 11 12 boundary 2 fixed 12 12 7 boundary 2 fixed 13 1 7 14 2 8 15 3 9 16 4 10 17 5 11 18 6 12 faces 1 -1 13 7 -14 density TENS 2 -2 14 8 -15 density TENS 3 -3 15 9 -16 density TENS 4 -4 16 10 -17 density TENS 5 -5 17 11 -18 density TENS 6 -6 18 12 -13 density TENS bodies 1 1 2 3 4 5 6 volume 0.00045 density SG read // horizontal force on upper pad by central differences dy := .0001 do_yforce := { oldshift := shift; shift := shift + dy; set vertex y y+dy*z/zh; // uniform shear recalc; energy1 := total_energy - body[1].pressure*(body[1].volume - body[1].target); oldshift := shift; shift := shift - 2*dy; set vertex y y-2*dy*z/zh; // uniform shear recalc; energy2 := total_energy - body[1].pressure*(body[1].volume - body[1].target); yforce := -(energy1-energy2)/2/dy; printf "restoring force: %20.15f\n",yforce; // restore everything oldshift := shift; shift := shift + dy; set vertex y y+dy*z/zh; // uniform shear recalc; } // vertical force on upper pad by central differences. dz := .0001 do_zforce := { oldzh := zh; zh := zh + dz; set vertex z z+dz*z/oldzh; recalc; // uniform stretch energy1 := total_energy - body[1].pressure*(body[1].volume - body[1].target); oldzh := zh; zh := zh - 2*dz; set vertex z z-2*dz*z/oldzh; recalc; // uniform stretch energy2 := total_energy - body[1].pressure*(body[1].volume - body[1].target); zforce := -(energy1-energy2)/2/dz; printf "vertical force: %20.15f\n",zforce; // restore everything oldzh := zh; zh := zh + dz; set vertex z z+dz*z/oldzh; recalc; // uniform stretch } // Sample evolution and force calculation gogo := { u; g 5; r; g 5 ; r; g 5; hessian; hessian; do_yforce; do_zforce; } evolver-2.30c.dfsg/doc/iterate.htm0000644000175300017530000003237011410765113017347 0ustar hazelscthazelsct Surface Evolver Documentation: iteration

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Gradient Descent Iteration

Basic surface evolution is done by gradient descent iteration. Relevant topics are:

Gradient descent iteration step

Each g command iteration does the following:
  • Calculates the force vector at each vertex as the gradient of the total energy.
  • Calculates the gradients at each vertex of constrained body volumes and named quantities. Also calculates the multiple of gradient motion needed to restore target values of the constraints.
  • Projects the forces to be tangent to level set constraints, volume constraints (pressure is calculated here), and fixed quantity constraints.
  • If any mobility options are in effect, the proper force to velocity conversion is done.
  • If the normal_motion toggle is on, then velocities are projected to the surface normal.
  • Does the conjugate gradient projection, if conjugate gradient is in effect.
  • The current vertex positions are saved.
  • If the optimizing mode is on, then trial motions are made to find the optimal scale factor.
  • The scale factor is multiplied by the value of the scale_scale internal variable. (Useful only if you want to muck around with modifying the optimal scale factor for some strange reason.)
  • Each vertex is moved by the scale factor times the velocity, plus the volume and fixed quantity restoring motions. If runge_kutta is in effect, then a fourth-order Runge-Kutta step is done instead of a simple Euler step.
  • All level set constraints are enforced. Vertices violating an inequality or equality constraint are projected to the constraint (Newton's method). Several projection steps may be needed, until the violation is less that a certain tolerance or a certain number of steps are done (which generates a warning message). The default constraint tolerance is 1e-12, but it can be set with the CONSTRAINT_TOLERANCE option in the datafile, or setting the constraint_tolerance variable.
  • If jiggling is on, the surface is randomly perturbed.
  • If autopop or autochop are on, then the appropriate edge deletion or division is done.
  • New energies, volumes, and quantities are calculated.
  • If check_increase is on and the surface energy has increased, then the vertices are restored to their old values.
  • If estimating is on, then a linear estimate of the energy change is printed. This is calculated from the velocities, gradients, and scale factor.
  • The number of iterations left, the new area, energy, and scale factor are printed.
  • If a graphics display is active and set to autodisplay, then graphics are redrawn.

Scale factor

Once a direction of motion is found, the direction must be multiplied by a scale factor to compute the actual motion. If one interprets the direction of motion as a velocity (as in motion by mean curvature), then the scale factor becomes a time step. The scale factor may be fixed with the m command, or it may be in optimizing mode. The default is to start in optimizing mode.

Optimizing scale

In using gradient descent to seek a minimum energy, one finds a direction of motion and does a line search along that direction to find the minimum energy. Evolver will do that in optimizing scale mode. The line search consists of halving or doubling the current scale factor until an energy minimum is bracketed; then quadratic interpolation is used to estimate the optimum scale. Optimizing scale is the default; it also may be turned on with the m command or the optimizing command.

For safety, there is an upper bound to the scale; it defaults to 1 but may be changed with the optimizing command. There is also a lower bound; if Evolver gets a scale below 1e-12 of the scale bound when attempting to find a minimum, it gives up and just uses scale 0. Scale 0 is not a null operation since it still projects to constraints, if they are not exactly satisfied.

In general, a good scale factor depends on the type of energy being minimized and the level of refinement. However, for minimizing area, when the triangulation is well-behaved and area normalization is off, the best scale factor is usually around 0.2, independent of refinement. In optimizing mode, a scale factor getting small, say below 0.01, indicates triangulation problems. Too large a fixed scale factor will show up as total energy increasing. If you have motion by area normalization ON use a small scale factor, like 0.001, until you get a feel for what works.

If check_increase is toggled on, then the motion is not done if it would increase energy. But be aware that energy sometimes may have to increase in order to satisfy constraints.


Conjugate gradient

"Conjugate gradient" is a method of accelerating gradient descent. In ordinary gradient descent, one uses the gradient of energy to find the steepest downhill direction, then moves along that line to the minimum energy in that direction. Hence successive steps are at right angles. However, this can be very inefficient, as you can spend a lot of time zigzagging across an energy "valley" without making much progress "downstream". With conjugate gradient, the search direction is chosen to be in a "conjugate" direction to the previous direction. For a mathematical explanation, see any decent book in numerical analysis, such as [P]. In practice, the conjugate gradient method remembers a cumulative "history vector", which it combines with the ordinary gradient to figure out the conjugate gradient direction. The upshot is that conjugate gradient can converge much faster than ordinary gradient descent.

Conjugate gradient can be toggled with the U command, or with the conj_grad toggle. It should always be used with optimizing scale.

Notes: The conjugate gradient method is designed for quadratic energy functions. As long as the energy function is nearly quadratic, as it should be near an energy minimum, conjugate gradient works well. Otherwise, it may misbehave, either by taking too big steps or by getting stalled. Both effects are due to the history vector being misleading. To prevent too big steps, one should iterate without conjugate gradient for a few steps whenever significant changes are made to the surface (refining, changing a constraint, etc.). On the other hand, if it looks like conjugate gradient is converging, it may have simply become confused by its own history. See the catenoid example for a case in point. A danger signal is the scale factor going to zero. If you are suspicious, toggle conjugate gradient off and on ("U 2" does nicely) to erase the history vector and start over.


Diffusion

The Evolver can simulate the real-life phenomenon of gas diffusion between neighboring bubbles. This diffusion is driven by the pressure difference across a surface. This is invoked by the keyword DIFFUSION in the first part of the datafile, followed by the value of the diffusion constant. The amount diffused across a facet during an iteration is calculated as scale*diffusion_constant*facet_area*pressure_difference. The scale factor is included as the time step of an iteration. The amount is added to or subtracted from the prescribed volumes of the bodies on either side of the facet. The diffusion constant can be accessible at runtime through the read-write internal variable diffusion_coeff.

If you want finer control over the rate of diffusion across various surfaces, you can define the edge_diffusion edge attribute in the string model or the facet_diffusion facet attribute in the soapfilm model and give individual values for edges or facets as you desire. If the attribute is defined, then its value is used instead of the global diffusion constant.


Stability

The timestep of an iteration should not be so large as to amplify perturbations of the surface. Short wavelength perturbations are most prone to amplification. This section contains a sketch of the stability characteristics of the various mobility modes, enough to let the user relate the maximum timestep to the minimum facet or edge size. Two examples are discussed: a zigzag string and a nearly flat surface with equilateral triangulation. Effective area is not included, as it is an insignificant correction for nearly flat surfaces. The general moral of this section is that the maximum time step in iteration is limited by the length of the shortest edge or the area of the smallest facet, except in one case.

Zigzag string

Let the amplitude of the perturbation about the midline be Y and the edge length L. Then the force on a vertex is F = -4Y/L for small Y. Let the timestep (the Evolver scale factor) be \Delta t. Let V be the vertex velocity. Then the critical timestep for amplification of the perturbation is given by V\Delta t = -2Y, or \Delta t = -2Y/V.

Vertex mobility. Here V = F, so \Delta t = L/2.

Area normalization. Here the vertex star has length 2L, so V = F/L and \Delta t = L^2/2.

Approximate curvature. It turns out that the zigzag is an eigenvector of the mobility matrix M for the largest eigenvalue 3/L, so V = 3F/L and \Delta t = L^2/6. This is a major disadvantage of approximate curvature. If perturbation instability is the limitation on the timestep, it will take three times as many iterations as with area normalization to do the same evolution.

Perturbed sheet with equilateral triangulation.

Consider a plane surface triangulated with equilateral triangles of area A. The perturbation consists of a tiling of hexagonal dimples with their centers of height Y above their peripheries. The force at a central vertex turns out to be -sqrt(3)Y and the force at a peripheral vertex sqrt(3)Y/2.

Vertex mobility. The critical time step is given by
(\sqrt(3)Y + sqrt(3)Y/2)\Delta t = 2Y,
so \Delta t = 4/3*sqrt(3). Note that this is independent of the triangle size. This is consistent with experience in evolving with optimizing scale factor, where the optimum time step is in the range 0.2 - 0.3 indenpendent of triangle size. This is a definite advantage of this version of mobility, since different parts of the surface can have different size triangulations and one size time step can work for all.

Area normalization. The star area of each vertex is 6A, so the velocities become -sqrt(3)Y/2A and sqrt(3)Y/4A, and the critical time step \Delta t = 4/6*sqrt(3)A. Hence on a surface with varying size triangles, the timestep is limited by the area of the smallest triangles.

Approximate area. This force turns out to be an eigenvector of the mobility matrix with eigenvalue 2/A. Hence the velocity is four times that of the area normalization, and the critical time step four times shorter.


Back to top of Surface Evolver documentation. Index. evolver-2.30c.dfsg/doc/toggle.htm0000644000175300017530000013111211410765113017165 0ustar hazelscthazelsct Surface Evolver Documentation: toggle commands

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Toggle commands

There are a large number of Surface Evolver features that can be turned on or off with simple "toggle" commands. The general syntax is "togglename ON" or "togglename OFF", with just "togglename" being a synonym for "togglename ON". The toggle names below have brief descriptions of their actions in the ON state. Toggles will usually print their previous state. A togglename used in an arithmetic expression is the current value of the toggle, 0 for OFF and 1 for ON. The current value of a toggle may be found by "print togglename".

All toggles are reset when a surface is loaded. Default values are OFF unless otherwise noted.

These full-word toggle commands are not to be confused with various single-letter toggle commands, which always change the state. All single-letter toggles have full word toggle equivalents.


Toggles

AMBIENT_PRESSURE

Toggles ideal gas mode with ambient pressure outside bodies. The external pressure can be set with the pressure phrase in the top of the datafile, or at runtime with the p command, e.g. p 10.

APPROXIMATE_CURVATURE

Evolver toggle command. Uses polyhedral curvature (linear interpolation over facets for metric) for mean curvature vector. Actually establishes the inner product for forms or vectors to be integration over facets of euclidean inner products of linear interpolation of values at vertices. Synonyms: APPROX_CURV, APPROX_CURVATURE.

AREA_NORMALIZATION

Evolver toggle command. Convert the force on vertex to a mean curvature velocity vector by dividing the force by the area associated to the vertex, which is one-third of the area of its adjacent vertices. The string model divides by one-half of the sum of the lengths of adjacent edges. Useful in doing grain growth simulations.

ASSUME_ORIENTED

Evolver toggle command. Tells squared mean curvature routines that they can assume the surface is locally consistently oriented. Significant only for extreme shapes.

AUGMENTED_HESSIAN

Evolver toggle command. Solves constrained Hessians by putting the body and quantity constraint gradients in an augmented matrix with the Hessian, and using sparse matrix techniques to factor. Vastly speeds things up when there are thousands of sparse constraints, as in a foam. The default state is unset (prints as a value of -1), in which case augmentation is used for 50 or more constraints, but not for less.

AUTOCHOP

Evolver toggle command. Evolver toggle command. Do automatic refining of long edges each iteration. Use "autochop_length := expr" to set autochop length. Each iteration, any edge that projected to become longer than the cutoff is bisected. If any bisections are done, the motion calculation is redone. The autochop length may be accessed by the read/write internal variable autochop_length; but note that simply assigning a value to autochop_length does not toggle autochop on.

AUTODISPLAY

Evolver toggle command. Toggles automatic redrawing of graphics whenever the surface changes. Default ON. Same function as the 'D' command.

AUTOPOP

Evolver toggle command. Toggles automatic deletion of short edges and popping of improper vertices each iteration. Before each iteration, any edge projected to shorten to under the critical length is deleted by identifying its endpoints. The critical length is calculated as L = sqrt(2*dt), where dt is the time step or scale factor. Hence this should be used only with a fixed scale, not optimizing scale factor. The critical length is chosen so that instabilities do not arise in motion by mean curvature. If any edges are deleted, then vertices are examined for improper vertices as in the 'o' command. Useful in string model.

Autopop is also implemented for small facets as of Evolver version 2.30. The critical area is calculated as sqrt(2*dt)*perimeter/2, where perimeter is the sum of the lengths of the three sides of the facet.

See also the immediate_autopop and autopop_quartic toggles.


AUTOPOP_QUARTIC

Toggle. Modifies the autopop mode. The critical length for edges is set to 2*sqrt(sqrt(dt)) and the critical area for facets is 2*sqrt(sqrt(dt))*perimeter/2; meant for quantities such as laplacian_mean_curvature where velocity is proportional to fourth derivative of surface.

AUTORECALC

Evolver toggle command. Toggles automatic recalculation of the surface whenever adjustable parameters or energy quantity moduli are changed. Default is OFF.

BACKCULL

Evolver toggle command. Prevents display of facets with normal away from viewer. May have different effects in different graphics displays. For example, to see the inside back of a body only, "set frontcolor clear" alone works in 2D displays, but needs backcull also for direct 3D.

BEZIER_BASIS

Evolver toggle command. When Evolver is using the Lagrange model for geometric elements, this toggle replaces the Lagrange interpolation polynomials (which pass through the control points) with Bezier basis polynomials (which do not pass through interior control points, but have positive values, which guarantees the edge or facet is within the convex hull of the control points). This is experimental at the moment, and not all features such as graphing or refinement have been suitably adjusted.

BIG_ENDIAN

Evolver toggle command. Controls the order of bytes in binary_printf numerical output. Big-endian is most significant byte first. To change to little-endian, use little_endian, not "little_endian off".

BLAS_FLAG

Evolver toggle command. Toggles using BLAS versions of some matrix routines, if the Evolver program has been compiled with the -DBLAS option and linked with some BLAS library. For developer use only at the moment.

BOUNDARY_CURVATURE

Evolver toggle command. When doing integrals of mean curvature or squared curvature, the curvature of a boundary vertex cannot be defined by its neighbor vertices, so the area of the boundary vertex star instead is counted with an adjacent interior vertex.

BREAK_AFTER_WARNING

Causes Evolver to cease execution of commands and return to command prompt after any warning message. Same effect as option -y.

BUNCH_KAUFMAN

Evolver toggle command. Toggles Bunch-Kaufman factoring of the Hessian in the alternative minimal degree factoring method (ysmp off). This factors the Hessian as LBL^T where L is lower triangular with ones on the diagonal, and B is block diagonal, with 1x1 or 2x2 blocks. Supposed to be more stable when factoring indefinite Hessians.

CHECK_INCREASE

Evolver toggle command. Toggles checking for increase of energy in an iteration step. If energy increases, then the step is undone and any iteration loop is halted. Meant for early detection of instabilities and other problems causing the surface to misbehave. Useful in doing a multiple iteration with a fixed scale. Also applies to the hessian command. Caution: there are circumstances where an energy increase is appropriate, for example when there are volume or quantity constraints and conforming to the constraints means an energy increase initially.

CIRCULAR_ARC_DRAW

If on, then in quadratic string mode, an edge is drawn as a circular arc (actually 16 subsegments) through the endpoints and midpoint, instead of a quadratic spline.

CLIP_VIEW

Toggles use of clipping planes in graphics display.

CLIPPED

Evolver toggle command. Sets torus model display to clip to the fundamental region. Not an on-off toggle. 3-way toggle with raw_cells and connected. Synonym: clipped_cells. The origin (lower left back corner) of the clip parallelogram can be set by setting entries in the display_origin vector.

COLORMAP

Evolver toggle command. Use colormap from file in certain graphics output. See the P command. Use COLORFILE := "filename" to set file.

CONF_EDGE

Evolver toggle command. Calculation of squared curvature by fitting sphere to edge and adjacent vertices (conformal curvature).

CONJ_GRAD

Evolver toggle command. Use conjugate gradient method in g command. See also U command and RIBIERE.

CONNECTED

Evolver toggle command. Sets torus model display to do each body as a connected, wrapped surface. Not an on-off toggle. 3-way toggle with clipped and raw_cells. Synonym: connected_cells.

CONVERT_TO_QUANTITIES

Evolver toggle command. This will do an automatic conversion of old-style energies to new-style named quantities. This has the same effect as the -q command line option, but can be done from the Evolver command prompt. Useful when hessian complains about not being able to do a type of energy. A few energies don't convert yet. It is my intention that this will be the default sometime in the near future, if it can be made sufficiently fast and reliable.

DETURCK

Evolver toggle command. Motion by unit velocity along normal, instead of by curvature vector.

DIFFUSION

Evolver toggle command. Activates diffusion step each g command.

DIRICHLET_MODE

When the facet_area method is being used to calculate areas in hessian commands, this toggles using an approximate facet_area hessian that is positive definite. This permits hessian iteration to make big steps in a far-from-minimal surface without fear of blowing up. However, since it is only an approximate hessian, final convergence to the minimum can be slow. Linear model only. Does convert_to_quantities implicitly. Another variant of this is triggered by sobolev_mode.

EFFECTIVE_AREA

Evolver toggle command. In area normalization, the resistance factor to motion is taken to be only the projection of the vertex star area perpendicular to the motion. If squared mean curvature is being calculated, this projected area is used in calculating the curvature.

ESTIMATE

Evolver toggle command. Activates estimation of energy decrease in each gradient descent step (g command). For each "g" iteration, it prints the estimated and actual change in energy. The estimate is computed by the inner product of energy gradient with actual motion. Useful only for a fixed scale factor much less than optimizing, so linear approximation is good. The internal variable estimated_change records the estimated value.

FACET_COLORS

Evolver toggle command. Enables coloring of facets in certain graphics interfaces (e.g. xgraph). If off, facet color is white. Default on.

FORCE_DELETION

Evolver toggle command. In the soapfilm model, overrides the refusal of the delete command to delete edges or facets when that would create two edges with the same endpoints. Sometimes it is necessary to have such edges, for example in pinching off necks. But usually it is a bad idea. Also see star_finagling. Default is off.

FULL_BOUNDING_BOX

Evolver toggle command. Causes bounding box in PostScript output to be the full window, rather than the actual extent of the surface within the window. Default off.

FUNCTION_QUANTITY_SPARSE

Evolver toggle command. For named quantities defined as functions of named methods, this toggles the use of sparse matrices in calculating hessians.

GRAVITY

Evolver toggle command. Includes the gravitational energy of bodies with density in total energy. The gravitational constant can be set in the top of the datafile with "gravity_constant = value", or with the runtime command "G value".

GV_BINARY

Evolver toggle command. Toggles sending data to geomview in binary format, which is faster than ascii. Default is binary on SGI, ascii on other systems, since there have been problems with binary format on some systems. Ascii is also useful for debugging.

HESSIAN_DIFF

Evolver toggle command. Toggles computing the hessian matrix by finite differences. Used only for debugging, since it is very slow.

HESSIAN_DOUBLE_NORMAL

Evolver toggle command. When hessian_normal is also on, and the space dimension is even, then the normal vector components in the last half of the dimensions are copies of the components in the first half. Sounds weird, huh? But it is useful in calculating the stability of cylindrically symmetric surfaces using a string model in 4D.

HESSIAN_NORMAL

Evolver toggle command. Constrains Hessian to move each vertex perpendicular to the surface. This eliminates all the fiddly sideways movement of vertices that makes convergence difficult. Perpendicular is usually defined as the volume gradient, but at triple (and higher) junction lines it is a subspace perpendicular to the line, and singular points where several triple lines meet have full degrees of freedom. See hessian_normal_one to alter this behavior.

HESSIAN_NORMAL_ONE

Evolver toggle command. If this and hessian_normal are on, then the normal at any point will be one dimensional. This is meant for soap films with Plateau borders, where there are triple junctions with tangent films. Ordinary hessian_normal permits lateral movement of such triple junctions, but hessian_normal_one does not. Valid only for string model in 2D and soapfilm model in 3D. The normal vector is computed as the eigenvector of the largest eigenvalue of the sum of the normal projection matrices of all edges or facets adjoining a vertex.

HESSIAN_NORMAL_PERP

Evolver toggle command. If this is on, then the Hessian linear_metric uses only the component of the normal perpendicular to the facet or edge. This raises eigenvalues slightly.

HESSIAN_QUIET

Evolver toggle command. Toggles suppression of printing of information during hessian calculations. Default is on.

HESSIAN_SPECIAL_NORMAL

Evolver toggle command. When hessian_normal is on, this toggles using a special vectorfield for the direction of the perturbation, rather than the usual surface normal. The vectorfield is specified in the hessian_special_normal_vector section of the datafile header. Beware that hessian_special_normal also applies to the normal calculated by the vertexnormal attribute and the normal used by regular vertex averaging.

HOMOTHETY

Evolver toggle command. Adjust total volume of all bodies to fixed value after each iteration by uniformly scaling entire surface.

IMMEDIATE_AUTOPOP

Evolver toggle command. Modifies the autopop mode. Causes deletion of a short edge or small facet immediately upon detection, before proceeding with further detection of small edges or facets. Original behavior was to do all detection before any elimination, which could cause bad results if a lot of edges got short simultaneously. Default off for backward compatibility, but you should probably turn it on.

INTERP_BDRY_PARAM

Evolver toggle command. For edges on parametric boundaries, calculate the parameter values of new vertices (introduced by refining) by interpolating parameter values, rather than extrapolating from one endpoint. Useful only when parameters are not periodic.

INTERP_NORMALS

Evolver toggle command. Display using interpolated vertex normals for shading for those graphics interfaces that support it.

ITDEBUG

Evolver toggle command. Prints some debugging information during a 'g' step. For gurus only.

JIGGLE

Evolver toggle command. Toggles jiggling on every iteration.

KRAYNIKPOPEDGE

Toggles edge-popping mode (O or pop commands) in which poppable edges look for adjacent facets of different edge_pop_attribute values to split off from the original edge; failing that it reverts to the regular mode of popping the edge. This is meant to give the user greater control on how edge popping is done. It is up to the user to declare the edge_pop_attribute integer facet attribute and assign values.

KRAYNIKPOPVERTEX

Toggles 3D vertex popping mode in which a poppable vertex is examined to see if it is a special configuration of six edges and 9 facets. If it is, a special pop is done that is much nicer than the default pop.

KUSNER

Evolver toggle command. Calculation of squared curvature by edge formula rather than vertex formula.

LABELFLAG

Evolver toggle command. When on, the postscript command will print element labels in the postscript file. Synonym for ps_labelflag.

LITTLE_ENDIAN

Evolver toggle command. Controls the order of bytes in binary_printf numerical output. Little-endian is least significant byte first. To change to big-endian, use big_endian, not "little_endian off".

LINEAR_METRIC

Evolver toggle command. Eigenvalues and eigenvectors of the Hessian are defined with respect to a metric. This command toggles a metric that imitates the smooth surface natural metric of L_2 integration on the surface. Use with hessian_normal to get eigenvalues and eigenvectors similar to those on smooth surfaces.

MEMDEBUG

Evolver toggle command. When ON, the 'c' command prints full memory usage statistics on some systems. Also, each allocation or freeing of memory is printed. Causes heap checking to be done on some systems at each memory operation.

METIS_FACTOR

Evolver toggle command. Computes and uses an ordering for Hessian factoring using the METIS library of Karypis and Kumar, if this library has been compiled into the Evolver.

METRIC_CONVERT

Evolver toggle command. If a Riemannian metric is defined, whether to use the metric to do gradient form to vector conversions. Synonym: metric_conversion.

NORMAL_CURVATURE

Evolver toggle command. Calculation of squared curvature by taking area of vertex to be the component of the volume gradient parallel to the mean curvature vector.

NORMAL_MOTION

Evolver toggle command. Projects motion to surface normal, defined as the volume gradient. May be useful with squared curvature if vertices tend to slither sideways into ugly patterns.

OLD_AREA

Evolver toggle command. In the string model with area normalization, at a triple vertex Evolver normally tries to calculate the motion so that Von Neumann's Law will be obeyed, that is, the rate of area change is proportional to the number of sides of a cell. If old_area is ON, then motion is calculated simply by dividing force by star area.

PINNING

Evolver toggle command. Check for vertices that can't move because adjacent vertices are not on same constraint when they could be. Obscure.

POP_DISJOIN

Evolver toggle command. Changes the behavior of popping edges and vertices to act like merging Plateau borders, i.e. produce disjoined films instead of films joined with cross-facets. In the edge case, if four facets meet along an edge and two opposite bodies are the same body, then popping the edge will join the bodies if pop_disjoin is in effect. In the vertex case, if the vertex has one body as an annulus around it, then the vertex will be separated into two vertices so the annulus becomes a continuous disk. This is all done regardless of the angles at which facets meet. Applies to pop, o, and O commands.

POP_ENJOIN

Evolver toggle command. Changes the behavior of popping vertices in the soapfilm model so that when two distinct cones are detected meeting at a common vertex, the popping result is a widening of the cone vertex into a neck rather than a disjoining of the cones. meet. Applies to pop and o commands.

POP_TO_EDGE

Evolver toggle command. The non-minimal cone over a triangular prism frame can pop in two ways. If this toggle is on, then popping to an edge rather that a facet will be done. Default off.

POP_TO_FACE

Evolver toggle command. The non-minimal cone over a triangular prism frame can pop in two ways. If this toggle is on, then popping to a facet rather that an edge will be done. Default off.

PS_CROSSINGFLAG

Evolver toggle command. When on and the string model is in effect, the postscript command will show background edges with a break where foreground edges pass in front.

DEBUG

Evolver toggle command. Print YACC debug trace of parsing of commands.

PS_GRIDFLAG

Evolver toggle command. If toggled on, the postscript command will show all edges of displayed facets, instead of just those satisfying the current edge show condition.

PS_LABELFLAG

Evolver toggle command. When on, the postscript command will print element labels in the postscript file. Synonym for labelflag.

PS_COLORFLAG

Evolver toggle command. When on, the postscript command will do color.

QUANTITIES_ONLY

Evolver toggle command. Inactivates all energies except named quantities. Meant for programmer's debugging use.

QUIET

Evolver toggle command. Suppresses all normal output messages automatically generated by commands. Good while running scripts, or for loading datafiles with long read sections. Explicit output from print, printf, and list commands will still appear, as will prompts for user input. Applies to redirected output as well as console output. An error or user interrupting a command (i.e. with CTRL-C) will turn QUIET off, for sanity.

QUIETGO

Evolver toggle command. Suppresses only g iteration step output.

QUIETLOAD

Evolver toggle command. Suppresses echoing of files being read in. This applies to the read section at the end of the datafile and any files read in with the read command. This toggle does not get reset at the start of a new datafile. This toggle can be set with the -Q command line option, to suppress echoing in the first datafile loaded. Default is OFF.

POST_PROJECT

Evolver toggle command. Introduces extra projections to volume and fixed quantity constraints each g iteration. If convergence fails after 10 iterations, you will get a warning message, repeated iterations will stop, and the internal variable iteration_counter will be negative.

RAW_CELLS

Evolver toggle command. Sets torus model display for plain, unwrapped facets. Not an on-off toggle; 3-way toggle with clippedand connected.

RGB_COLORS

Evolver toggle command. Toggles graphics to use user-specified red-green-blue components of color for elements rather than the color attribute indexing the pre-defined 16 color palette. The individual element rgb values are in element extra attributes: ergb for edges, frgb for facets, and fbrgb for facet backcolor. It is up to the user to define these attributes; if they are not defined, then they are not used and do not take up space. If frgb is defined but not fbrgb, then frgb is used for both front and back color. The attributes are real of dimension 3 or 4; if 4 dimensional, the fourth component is passed to the graphics system as the alpha value, but probably won't have any effect. The value range is 0 to 1. Be sure to initialize the rgb attributes, or else you will get an all-black surface. The attribute definitions to use are:
   define edge attribute ergb real[3]
   define facet attribute frgb real[3]
   define facet attribute fbrgb real[3]

RIBIERE

Evolver toggle command. Makes the conjugate gradient method use the Polak-Ribiere version instead of Fletcher-Reeves. (The toggle doesn't turn on conjugate gradient.) Polak-Ribiere seems to recover much better from stalling. Ribiere is the default mode.

RUNGE_KUTTA

Evolver toggle command. Use Runge-Kutta method in a g iteration step (fixed scale factor only).

SELF_SIMILAR

Evolver toggle command. If squared mean curvature energy is being used, this scales the velocity toward a self-similar motion. Applies only when the old top-of-datafile "squared_curvature" declaration is used, or the sqcurve named method. The global read-write variable self_sim_coeff is used as a multiplier.

SHADING

Evolver toggle command. Toggles facet shading in certain graphics interfaces (xgraph, psgraph). Darkness of facet depends on angle of normal from vertical, simulating a light source above surface. Default is ON.

SHOW_ALL_QUANTITIES

Evolver toggle command. By default, only explicitly user-defined named quantities are shown by the Q or v commands. If show_all_quantities is on, then all internal quantities created by option -q or by doing convert_to_quantities are also shown.

SHOW_INNER

Evolver toggle command. Display interior facets, those on 2 bodies.

SHOW_OUTER

Evolver toggle command. Display outer facets, those on 0 or 1 body.

SLICE_VIEW

Toggles use of slice view in graphics display.

SMOOTH_GRAPH

Evolver toggle command. In string quadratic and Lagrange model, causes edges to be plotted with many subdivisions for smooth look. In soapfilm Lagrange model, causes edges and facets to be plotted with 8-fold subdivision rather than Lagrange order subdivision. Is not implemented in quadratic soapfilm model. Default off.

SOBOLEV_MODE

Evolver toggle command. When the facet_area method is being used to calculate areas in hessian commands, this toggles using an approximate facet_area hessian that is positive definite. This permits hessian iteration to make big steps in a far-from-minimal surface without fear of blowing up. However, since it is only an approximate hessian, final convergence to the minimum can be slow. Linear model only. Does convert_to_quantities implicitly. Another variant of this is triggered by dirichlet_mode.

SPARSE_CONSTRAINTS

Evolver toggle command. Toggles using sparse matrix techniques to accumulate and handle body and quantity gradients in iteration and hessian commands. Now the default.

SQUARED_GRADIENT

Evolver toggle command. In hessian_seek, use minimizing the square of the gradient of the energy as the objective rather than minimizing the energy.

STAR_FINAGLING

Evolver toggle command. In the soapfilm model, the delete command for edges or facets normally will not do the deletion if it would result in the creation of two edges with the same endpoints. Some simple configurations that cause this are detected and handled automatically, namely a "star" configuration in which there are three facets forming a triangle adjacent to the edge being deleted. Such a star is automatically removed by deleting one of its internal edges before deleting the original edge. But sometimes there are more complicated configurations that such unstarring won't handle, and then Evolver will not delete the edge unless the force_deletion toggle is on. An alternative is to first refine the edges that would have the common endpoints, and this is what the star_finagling toggle enables. Default off.

THICKEN

Evolver toggle command. Whether to display differently colored sides of a facet separated by thickness. Default on. This helps prevent weird striping due to limited resolution of depth buffers.

TORUS_FILLED

Evolver toggle command. Whether Evolver should treat one fixed volume as not fixed, for the purpose of avoiding redundant constraints in the case of a torus space being completely filled with bodies of fixed volume, for example a periodic foam.

TRANSFORMS

Evolver toggle command. Toggles graphing multiple images of the surface, according to the view transforms defined in the datafile, or according to the current transform expression applied to the view transform generators defined in the datafile.

VIEW_4D

Evolver toggle command. Toggles sending 4D information to geomview.

VERBOSE

Evolver toggle command. Toggles printing of progress messages during surface modification commands such as refine, delete, notch, edgeswap, o, O.

VISIBILITY_TEST

Evolver toggle command. Toggles an occluded-triangle test for graphics output that uses the Painter's Algorithm to produce 2D output (PostScript, Xwindows). This can greatly reduce the size of a PostScript file, but inspect the output since the implementation of the algorithm may have flaws.

VOLGRADS_EVERY

Evolver toggle command. Toggles recalculating volume constraint gradients every projection step during constraint enforcement. Good for stiff problems.

YSMP

Evolver toggle command. Toggles between Yale Sparse Matrix Package routines for factoring hessians, and my own minimal degree factoring. Default is YSMP off.

ZENER_DRAG

Evolver toggle command. Toggles Zener drag feature, in which the velocity of the surface is reduced by a magnitude given by the variable zener_coeff, and the velocity is set to zero if it is smaller than zener_coeff.
Back to top of Surface Evolver documentation. Index. evolver-2.30c.dfsg/doc/news_15.htm0000644000175300017530000001351411410765113017172 0ustar hazelscthazelsct Surface Evolver Documentation - Newsletter 12

Surface Evolver Newsletter no. 15

Back to top of Surface Evolver documentation. Back to top of Surface Evolver documentation.


                    Surface Evolver Newsletter Number 15
                              August 10, 1998

                   Editor: Ken Brakke, brakke@susqu.edu

Contents
  Version 2.10b
  New features and changes
  Geometry Center shutdown
  Evolver web page
  Geomview bug


Version 2.10b
  Surface Evolver Version 2.10 is now available.  It's been a while
since the last version, 2.01, so I bumped the version number up to
2.10; actually a few more changes in the last month bring the version
number to 2.10b.  No major changes, just bug fixes and some minor new features.
The Windows NT/95/98 version has much improved OpenGL graphics.
The Mac version is still 2.01, since I don't have access to a Mac
with a compiler.

New features and changes

   Most of the changes have been bug fixes, tweaks, and special purpse
   features for various users.  Here are general interest updates:

   In the datafile, making an edge FIXED no longer fixes its endpoints.
   This is compatible with how fixed facets and run-time fixing edges 
   have always worked.  If you relied on fixed edges making their
   endpoints fixed in any datafiles, and didn't explicitly fix the
   endpoints, you should revise your datafiles.  One cheap way to
   do that is to put a command "foreach edge ee where fixed do fix
   ee.vertex" in the READ section at the end of the datafile.  Dump
   files do not need to be fixed.

   The "load" command now terminates the command containing it, since
   technically the command was wiped out along with the old surface.
   "Load" is useful as the last command in a script, or in a script
   used as stdin by redirection.

   Window NT/95/98 version using OpenGL has much better graphics.
   Can rotate, translate, or zoom with left mouse button, pick elements
   with right button, even do cross-eyed stereo.
   Type 'h' in the graphics window for a command summary.
   Also made catenoid icon for the program.

   C-style assignment operators +=, -=, *=, /= work where reasonable.

   Level set constraint formulas and parameterized boundary formulas
   may contain vertex extra attributes, which can be very useful
   in customizing formulas to vertices, for example keeping each
   vertex at its own z level.

   Parameterized boundaries can have energy and content integrals,
   in the same way level set constraints have had.

   Command output redirection to a file using >> for append, >>>
    for overwrite.

   A variable can be toggled between optimizing or non-optimizing at run
   time with "unfix varname" and "fix varname" respectively.

   "keep_macros" in datafile header keeps macros active after datafile.

   Command line option -i will keep element ids the same as in the
   datafile, rather than renumbering consecutively as is the default.

   If you want to reorder elements in the internal lists (the way
   elements are listed by, say, 'list vertices', you can define
   the extra attributes vertex_order_key, edge_order_key,
   facet_order_key, body_order_key, facetedge_order_key, give them
   all appropriate values, and then give the command reorder_storage.
   See reorder.cmd in the distribution fe directory.

   'renumber_all' renumbers elements in internal list order.

   Read-only internal variable 'random' for random numbers
   uniformly distributed between 0 and 1.

   Added warnings about using keywords as identifiers in the datafile.
   The datafile will still run, but your commands will misinterpret
   those identifiers.

   All keywords are in the on-line help.  Do 'help "keyword"' if you
   want to check on a potential keyword.  'help' also recognizes
   identifiers defined by the user in the current surface.  For
   testing in scripts, there is a function is_defined(stringexpr)
   that returns 1 if a name is already in use, 0 if not.

   In the datafile, making an edge FIXED no longer fixes its endpoints.
   This is compatible with how fixed facets and fixing edges at run
   time have always worked.

   For named methods that logically depend on the orientation of the
   element (i.e. facet_vector_integral, etc.), the relative orientation
   of the element when the method is applied is recorded. Default is
   positive in the datafile, unless a '-' is added after the name of
   a method or quantity applied to an individual element.


Geometry Center shutdown
  The Geometry Center, which has sponsored much of my Surface Evolver
work, will be shutting down in August.  The Geometry Center web site
and ftp site should continue to exist for at least a year.  My email
address brakke@geom.umn.edu should continue to work.  However, my
preferred email address is now brakke@susqu.edu, and the home Evolver
web site is http://www.susqu.edu/facstaff/b/brakke/evolver.

Evolver web page
  My Evolver home page http://www.susqu.edu/facstaff/b/brakke/evolver
has downloads, papers, and many example datafiles.  The examples have
various degrees of commentary, but they generally have at least a
sample evolution script.

Geomview bug
  Geomview version 1.6 has a bug in picking that prevents it from
reporting the correct elements to Evolver.  This bug has been 
fixed in geomview patch 1.6p7, which unfortunately hasn't been
posted to the geomview site.  See the Evolver home page
http://www.susqu.edu/facstaff/b/brakke/evolver for more.


Back to top of Surface Evolver documentation. evolver-2.30c.dfsg/doc/news_16.htm0000644000175300017530000000515511410765113017175 0ustar hazelscthazelsct Surface Evolver Documentation - Newsletter 16

Surface Evolver Newsletter no. 16

Back to top of Surface Evolver documentation.

                    Surface Evolver Newsletter Number 16
                               August 18, 1999

                       by Ken Brakke, brakke@susqu.edu

Contents
  Version 2.14
  New examples
  New features and changes

Version 2.14

  Version 2.14 is now available at
  http://www.susqu.edu/facstaff/b/brakke/evolver/evolver.htm.
  It is also available from the Geometry Center, geom.umn.edu,
  but I don't know how long that site will be available.
  This is the first publicly announced version since 2.10c a year
  ago, although version 2.13 was unofficially posted in May.

  There is finally a current Mac version for the PowerPC.

Examples
  
  A gallery of triply periodic minimal surfaces (many new) is at
  http://www.susqu.edu/facstaff/b/brakke/examples/periodic/periodic.html.
  This page is the result of joint work with Alan Schoen.

  There are three examples of liquid solder models, each with a
  series of datafiles and commentary:

      Ball Grid Array joint (simple column between circular pads)
      http://www.susqu.edu/facstaff/b/brakke/evolver/bga/bga.htm

      Gull Wing Lead (curved lead above rectangular pad)
      http://www.susqu.edu/facstaff/b/brakke/evolver/gullwing/gullwing.html

      Tombstone (chip pulled upright by solder tension)
      http://www.susqu.edu/facstaff/b/brakke/tombstone/tomb.htm

New features and changes

    Almost all of the changes are bug fixes, internal improvements,
    and specialized new features. A couple are of general interest:

    hessian_normal is now defaults to ON.

    Put in automatic convert_to_quantities; still a place or two
    where can't convert on the fly.  Command line option -a-
    disables.

    Added "verbose" toggle command for action messages from
    pop edge, pop vertex, delete, notch, refine, dissolve, and edgeswap.

    The "dissolve" command will now dissolve facets on bodies in the
    soapfilm model, and edges on facets in the string model.

    Edges have frontbody and backbody attributes in the string model.

    There is a chdir command to change the working directory. 

    Element extra attributes can be declared with code to evaluate their
    values.

Back to top of Surface Evolver documentation. evolver-2.30c.dfsg/doc/news_13.htm0000644000175300017530000001110611410765113017163 0ustar hazelscthazelsct Surface Evolver Documentation - Newsletter 12

Surface Evolver Newsletter no. 13

Back to top of Surface Evolver documentation.




                    Surface Evolver Newsletter Number 13
                              April 30, 1996

                   Editor: Ken Brakke, brakke@geom.umn.edu

Contents
  Version 2.00 released
  New Features
  Minor backward compatibilities
  Request for web links
  Bibliography

Version 2.00 released

  Version 2.00 is now available from geom.umn.edu by ftp, or from
  http://www.geom.umn.edu/software/download/evolver/evolver.html, or from 
  my own home page, http://www.susqu.edu/facstaff/b/brakke/evolver/evolver.htm.
  The version number is 2.00 mostly because the last version was 1.99.
  No big rewrite, but there are some major new features.

  Anybody who happened to get a pre-release version 2.00, dated before
  April 30, should get the official release, as there have been a few
  bug fixes.

New features

  There are now versions for Windows-95 and Windows-NT.

  Much of the manual has been translated into HTML form, which
  can be browsed with standard Web browsers.  I've only tested
  it with Netscape, and I don't make any guarantees about how
  other browsers will do. But it's mostly just basic text.  No
  pictures or fancy graphics yet.

  The Evolver has a "help" command which will print extracts
  from the HTML files, based on keyword.

  Error and warning messages are numbered.  Someday there may be
  helpful documentation of all the error messages, but for now the
  numbers are useful to me to pinpoint the exact error.
  
  For precision freaks, higher-order Lagrange elements have been
  added (LINEAR is Lagrange order 1, QUADRATIC is Lagrange order 2).

  Also for precision freaks, if you compile with -DLONGDOUBLE then
  all floating point calculations will be in your system's
  long double type, which may provide up to 30 significant digits.

  There is a "postscript" command that prints postscript files 
  without asking you any questions.  Various toggles control the
  options.

  The distribution has a bunch of sample command files.  One is
  "vrml.cmd", which writes a VRML file suitable for your web site.

  The "interp_bdry_param" toggle causes boundary parameters to be
  interpolated rather than extrapolated when refining an edge.

  The 'X' command shows the dictionary of extra attributes, including
  those used internally.

  There is a "return" command for exiting the current command, or causing
  a subcommand to return to its parent command.

  When using the -q option (which converts everything to named quantities),
  only the user's explicitly defined quantities are shown by the 'A' and
  'v' commands.  The "show_all_quantities" toggle will cause those commands
  to list all quantities.
  
Minor backward compatibilities

  Fixed facets, edges, and vertices are now included when calculating 
  energies and areas.

  Evolver now enforces in the datafile the rule that all variable names 
  must be at least two characters.

  The term "variable_parameter", introduced in version 1.99, has been
  replaced by the more descriptive term "optimizing_parameter".

  There have been some changes in the internal workings of the 'g'
  command that might cause slightly different numerical values. 

Request for web links

  If you have web pages relevant to the Surface Evolver, please let
  me know.  One site with several Evolver examples involving liquid
  solder is http://www.ctcms.nist.gov/programs/solder/.

Bibliography

  Xavier Michalet and David Bensimon, "Observation of stable
  shapes and conformal diffusion in genus 2 vesicles",
  Science, vol. 269 (4 Aug 1995), 666-668.
  Tests hypothesis that some cell membranes are shaped by
  an elastic bending energy (i.e. squared mean curvature)
  by comparing Evolver surfaces to actual vesicles.

  R. Phelan, D. Weaire, and K. Brakke, "Computation of equilibrium
  foam structures using the Surface Evolver", Experimental Mathematics,
  vol. 4 no. 3 (1995), 181-192.  Discusses foam structures that beat
  Kelvin's tetrakaidecahedral foam, including the now-famous
  Weaire-Phelan structure.  Also describes the stability of the 
  structures as liquid is added to form Plateau borders.  Color pictures.


Back to top of Surface Evolver documentation. evolver-2.30c.dfsg/doc/syntax.htm0000644000175300017530000017672111410765113017251 0ustar hazelscthazelsct Surface Evolver Documentation - general syntax

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Surface Evolver syntax

Both the datafile and user commands follow a common general syntax described in this file, with a few differences as noted.

Lexical format

For those who know about such things, the datafile and commands are read with a lexical analyzer generated by the lex program. The specification is in datafile.lex. Commands are further parsed by a yacc-generated parser.

In parsing an expression, the longest legal expression is used. This permits coordinates to be specified by several consecutive expressions with no special separators.


Comments

Comments may be enclosed in /* */ pairs (as in C) and may span lines. // indicates the rest of the line is a comment, as in C++.

Lines and line splicing

The file is made up of lines. Line breaks are significant. The next physical line can be spliced onto the current line by having \ be the last character of the current line. Line splicing is not effective in // comments. Blank lines and comment lines may be placed freely anywhere. The various combinations of CR and NL that various computer systems use are all recognized

Case

Case is not significant in the datafile. In commands, case is only significant for single-letter commands.

Whitespace

In the datafile, whitespace consists of spaces, tabs, commas, colons, and semicolons. So it's fine if you want to use commas to separate coordinate values, and colons to prettify constraint definitions. In commands, whitespace consists of spaces and tabs. CTRL-Z is also whitespace, for the benefit of files imported from DOS.

Identifiers

Identifiers follow standard C rules (composed of alphanumeric characters and '_' with the leading character not a digit) and must not be keywords. Identifiers are used for macro names (in the datafile) and user-defined variables and commands. Identifiers must have at least two characters, since single characters can be confused with commands. To find out if a name is already in use as a keyword or user-defined name interactively, use help command. In scripts, one can use the is_defined function, which has the syntax
 is_defined(stringexpr)
The stringexpr must be a quoted string or other string expression. The return value is 0 if the name is undefined, 1 if defined. This function is evaluated at run-time, but variables in the whole command are parsed before the command is executed, so a command like if is_defined("newvar") then newvar := 1 else newvar := 2 will give newvar the value 1 even if this is its first appearance. A better way in scripts to test is to use the define command to define the variable without initialization, and then test to see if it has the default value, i.e. 0 for a numeric variable and a sizeof 0 for a string variable.

Numbers

Constant values may be in any of the usual forms. This includes integers, fixed point, and scientific notation such as
      2    -3    .5    23.    5e-10    +0.7D2
Hexadecimal integers starting with 0x, as in 0x12Af, are also accepted, as are binary numbers such as 11001b, indicated by a trailing 'b'. Color names are interpreted as integers.

Colors

The colors of edges and facets are recorded as integers in the range -1 through 15. How these integers translate to colors on the screen is determined by how Evolver's graphics drivers are written. The following synonyms are supplied, and it is hoped that the graphics drivers will be written to display these correctly:
-1 CLEAR
0 BLACK
1 BLUE
2 GREEN
3 CYAN
4 RED
5 MAGENTA
6 BROWN
7 LIGHTGRAY
8 DARKGRAY
9 LIGHTBLUE
10 LIGHTGREEN
11 LIGHTCYAN
12 LIGHTRED
13 LIGHTMAGENTA
14 YELLOW
15 WHITE
The special color value CLEAR (-1) makes a facet transparent. "Transparent" is a synonym for clear.

These tokens are simply translated to integer values wherever they occur, so these are reserved words. Edge and facet colors may be set in the datafile or by the set command.


Arithmetic expressions

Arithmetic expressions evaluate to real numbers. Boolean expressions are a subclass, with zero as false and nonzero as true; true results evaluate as 1. Ordinary algebraic notation is used.

Constant expressions

Constant expressions are evaluated when parsed. They are denoted by constexpr in various syntax definitions. They occur mostly in the datafile. Although they may contain variables, changing the variable value after parsing has no effect. Variable expressions (denoted by expr in syntax definitions) are recorded as parse trees and are re-evaluated each time needed.

Atomic values

The following evaluate to single numbers in expressions:
number
An integer, real, or hexadecimal constant. Hex numbers begin with 0x.
pi
Mathematical constant, ratio of circle circumference to radius.
x1,x2,...; x,y,z,w
Depending on context, space coordinates, vertex coordinates, edge vector components, or facet normal components.
p1,p2,...
Parameters of vertices on boundaries.
G
Current gravitational constant for calculating gravitational energy.
user-defined variables
Arbitrary variables defined in the datafile or at runtime.
Indexed array variables.
User-defined multidimensional arrays of reals or integers, and some internally defined arrays.
internal variables
Special pre-defined variables.
element attributes
Things like vertex coordinates, edge length facet area, colors.
named quantity attributes
Including modulus, target, value, pressure.
method instance attributes
Including modulus, value.
toggle name
Any toggle command name may be used as a Boolean variable in an expression (full word toggles, not single letters). But beware the ambiguity in interpreting a toggle as a command or a value. You may have to force the toggle to be interpreted as a value. "ad := autodisplay" sets ad as a command synonym for autodisplay, while "ad := (autodisplay)" records the current boolean value.

String expressions

A string expression evaluates to a string of characters. At present, the only ways to produce strings are:
  • double-quoted string literals, e.g. "this is a string". The following standard C escape sequences are recognized:
    • \n newline
    • \r carriage return
    • \t tab
    • \b backspace
    • \q double-quote mark
    • \c the character c elsewise
    In DOS, MS-Windows, or Windows NT paths, use / as the directory separator, since \ is an escape character. DOS and Windows have always accepted / as a directory separator.
  • successive double-quoted strings, which are concatenated into one string when read.
  • string variables, either internal like datafilename, or user-defined.
  • output from sprintf.

Arithmetic operators

These are the arithmetic operators that may appear in expressions:
+,-,*,/
Usual real arithmetic. NOTE: A '+' or '-' preceded by whitespace and followed by a number is taken to be a signed number. Thus "3 - 5" and "3-5" are single expressions, but "3 -5" is not. This is for convenience in separating multiple expressions listed on the same line for vertex coordinates, metric components, etc. in the datafile.

idiv
Integer divide. Rounds toward zero, then does integer division. Ex: 7 idiv 2 is 3; -3.5 idiv 2.1 is -1; -3 idiv 2 is -1.

%, mod
Real arithmetic modulus, x % y = x - floor(x/y)*y.

imod
Integer arithmetic modulus, x imod y = floor(x) - floor(floor(x)/floor(y))*floor(y).

=
NOT an assignment operator. Treated as low-precedence '-' so level-set constraint formulas like x^2 + y^2 - R^2 may be written naturally as x^2 + y^2 = R^2.

(,)
Parentheses for grouping and functional notation.

^,**
Raise to real power.

? :
Conditional expression, as in the C language. x ? y : z evaluates to y if x is nonzero and to z if x is zero.


Boolean operators

Boolean expressions are a subclass of arithmetic expressions, with zero as false and nonzero as true; true results evaluate as 1.
==,>,<,>=,<=,!= Numerical or boolean comparison.
NOT, ! Boolean NOT
AND, && Boolean AND, with short-circuit evaluation.
OR, || Boolean OR, with short-circuit evaluation.

Operator precedences

Here is the order of operator precedence, listed from high to low with equal precedence on same line, with associativity.
Operator Associativity
^,** left associative
unary - right associative
*,/,%,IMOD,IDIV left associative
+,- left associative
on_boundary
on_constraint
hit_constraint
on_quantity
on_method_instance
nonassociative
==,>,<,>=,<=,!= right associative
NOT, ! right associative
AND, && left associative
OR, || left associative
? : left associative
= left associative

Math functions

These are the math functions available in expressions. They take one argument in parentheses, except where noted.
abs(x)
Absolute value.
sqr(x)
Square.
sqrt(x)
Square root. Argument must be nonnegative.
sin(x),cos(x),tan(x)
Trig functions, argument in radians.
acos(x),asin(x),atan(x)
Inverse trig functions (acos, asin arguments clamped to [-1,1]).
atan2(y,x)
Inverse tangent, range -pi to pi.
log(x) ,exp(x)
Natural log, exponentiation base e.
sinh(x),cosh(x),tanh(x)
Hyperbolic functions.
asinh(x),acosh(x),atanh(x)
Inverse hyperbolic functions.
pow(x,y)
Raise x to real power y; x may be negative if y is an integer.
ceil(x),floor(x)
Round up or down to integer.
minimum(a,b),maximum(a,b)
Extreme of two arguments.
ellipticE(x),ellipticK(x)
Complete elliptic functions.
incompleteEllipticE(phi,m),incompleteEllipticF(phi,m)
Incomplete elliptic functions of parameters (phi,m); agrees with Mathematica definition.
matrix_multiply(A,B,C)
Built-in matrix multiplication of arrays. Stores A*B in C. A, B, and C must be stand-alone 1 or 2 dimensional array variables with appropriately matching dimensions; in particular, you cannot get a scalar product of vectors with this, unless you declare A to be 1xN, B to be Nx1, and C to be 1x1. C must be different from A and B. Does not return a value. Also doesn't work with array attributes yet.
matrix_inverse(A,B)
Built-in matrix inversion of an array. Stores the inverse of A in B. A and B must be stand-alone 2 dimensional array variables with matching dimensions. B can be the same as A. Does not return a value. Doesn't work with array attributes yet.
matrix_determinant(A)
Built-in matrix determinant of a square array. Has function syntax, so it returns the value of the determinant. Doesn't work with array attributes yet.
wrap_inverse(w)
Inverse of symmetry group element w, in numerical representation. Useful only if a symmetry group has been declared in the datafile.
wrap_compose(w1,w2)
Composition of symmetry group elements w1, w2, in numerical representation. Useful only if a symmetry group has been declared in the datafile.
usrn
user-defined functions

Miscellaneous functions

IS_DEFINED

To find out if a name is already in use as a keyword or user-defined name, use the is_defined function, which has the syntax
 IS_DEFINED(stringexpr)
The stringexpr must be a quoted string or other string expression. The return value is 0 if the name is undefined, 1 if defined. This function is evaluated at run-time, but variables in the whole command are parsed before the command is executed, so a command like if is_defined("newvar") then newvar := 1 else newvar := 2 will give newvar the value 1 even if this is its first appearance. A better way in scripts to test is to use the define command to define the variable without initialization, and then test to see if it has the default value, i.e. 0 for a numeric variable and a sizeof 0 for a string variable.

SIZEOF

Returns the number of entries in an array or array extra attribute. Can also be applied to a string or string variable to get the number of characters in the string. Syntax:
  SIZEOF(name)
  SIZEOF(string)
In the first form, name is the name of the array or extra attribute, not in quotes.

VALID_BOUNDARY

Boolean function. Returns 1 or 0 depending on whether a parametric boundary with the given number exists (note that the name of a named boundary is internally interpreted as a number). Syntax:
 VALID_BOUNDARY(expression)
One use is in looping through all parametric boundaries, in conjunction with the high_boundary internal variable. For example,
  for ( bnum := 1 ; bnum <= high_boundary ; bnum += 1 )
    if valid_boundary(bnum) then
       printf "Boundary %d has %d vertices on it\n",bnum,
          sum(vertex,on_boundary bnum);

VALID_CONSTRAINT

Boolean function. Returns 1 or 0 depending on whether a level-set constraint with the given number exists (note that the name of a named constraint is internally interpreted as a number). Syntax:
 VALID_CONSTRAINT(expression)
One use is in looping through all level-set constraints, in conjunction with the high_constraint internal variable. For example,
  for ( cnum := 1 ; cnum <= high_constraint ; cnum += 1 )
    if valid_constraint(cnum) then
       printf "Constraint %d has %d vertices on it\n",cnum,
          sum(vertex,on_constraint cnum);

VALID_ELEMENT

Boolean function. Returns 1 or 0 depending on whether an element of a given index exists. Syntax:
 VALID_ELEMENT(indexed_element)
Examples:
   if valid_element(edge[12]) then refine edge[12]
   if valid_element(body[2]) then set body[2].facet color red

User-defined built-in functions

User-defined functions can be defined in C in userfunc.c, so you have to be compiling your own Evolver if you want to use these. They are meant for situations where expression interpretation is too slow, or functions such as elliptic integrals are wanted. Currently, they are automatically functions of the coordinates. Do not give any arguments in the expression; for example "(usr1 + usr3)/usr10".

Aggregate functions

The maximum, minimum, sum, or average of an expression over a set of elements may be done with aggregate functions. The syntax is
   aggregate(generator,expr)
where aggregate is max, min, sum, or avg, and generator is an element generator. Example: this prints the total area of all green facets:
   print sum(facet where color == green, area)
"Count" is an aggregate function that gives the number of elements generated, regardless of the expression.

Element attribute values in expressions

The value of any element attribute may be used in an expression. The attribute name alone may be used if there is a default element active. Example:
   foreach facet do print area
Otherwise the attribute is referred to as a field of the element:
   foreach facet ff do print ff.area
Indexed extra attributes need an index in square brackets:
   foreach facet ff do print ff.something[3]
Index values are rounded down to integer values. Indexing starts with 1.

Most attributes are numerical or boolean, but some are element generators.

The following attributes are available (for details, see elements):

X1,X2,...,X,Y,Z,W
Coordinates of vertices, components of edge vector or facet normal
P1,P2
Parameters for boundaries
ID
Element identifying number
OID
Oriented element identifying number
ORIGINAL
Number of parent datafile element
COLOR
Integer representing color of facet (color of front if back different) or edge
FRONTCOLOR
Color of front of facet
BACKCOLOR
Color of back of facet
VALENCE
Number of edges on a vertex, facets on edge, edges on a facet, or facets, or on a body
AREA
Area of facet
LENGTH
Length of edge
VOLUME
Actual volume of body
TARGET
Target fixed volume of body
VOLCONST
Constant added to calculated volume of body
DENSITY
Density of edge, facet, or body
DIHEDRAL
Dihedral angle of facets on an edge
ORIENTATION
Sign for oriented integrals of edges or facets
ON_CONSTRAINT n
True if element on given constraint
HIT_CONSTRAINT n
True if element is exacty on a constraint. Useful for one-sided constraints.
ON_BOUNDARY n
True if element on given boundary
ON_QUANTITY quantityname
True if the given named quantity applies to the element.
ON_METHOD_INSTANCE instancename
True if the given method instance applies to the element.
WRAP
numerical edge wrap in torus model or other quotient space
quantity_name
contribution of an element to a named quantity
instance_name
contribution of an element to a named method instance
extra_attribute[expr]
name of user-defined extra attribute, with subscript if needed.
VERTEX
Generator of edge or facet vertices.
MIDV
Id of edge midpoint in quadratic model.
EDGE
Generator of edges attached to vertex, or edges around a facet.
FACET
Generator of facets around a vertex or an edge.
BODY
Generator of bodies that a facet is on.

Variables

The Evolver command language has its own version of the user-defined variables common to most programming languages. Variables are typed according to the types of the values assigned to them: numeric or string. Users may define numeric variables either by variable declarations in the datafile, or both types by assigning a value to an identifier in a command. A variable may be subjected to optimization by declaring it an optimizing_parameter in the datafile.

Arrays

It is possible to define multidimensional arrays of integers or reals with the syntax
   DEFINE variablename  REAL|INTEGER [expr]...
This syntax works both in the datafile header and at the command prompt. If the array already exists, it will be resized, with old elements kept as far as possible. Do not resize with a different number of dimensions. Note that array indexing starts at 1. A size of 0 is legal, and useful if you are not using an array at the moment and want to free storage space. There is runtime checking of array bounds. Example:
  define fvalues integer[10][4]
  define basecoord real[10][space_dimension]
Identifier names declared local can be used in array declarations in procedures and functions; dynamic sizes can be used, but static sizes may be a bit faster since memory doesn't need to be dynamically allocated.

In the top of the datafile, arrays may be initialized with nested bracket initializers following the definition. For example:

  define qwerty integer[4] = { 23, 45, 12, 2 }
  define vmat real[3][2] = {{1,2},{3,4},{5,6}}
Initializers need not be complete; missing values will be zero.

Array elements may be used and assigned as is usual for programming languages:

  fvalues[3][4] := 234;
  if basecoord[3][1] > 2 then print "hello world!\n";
The print command may be used to print whole arrays or array slices in bracketed form. Example:
   print fvalues
   print fvalues[4]
There are some basic whole-array operations that permit arrays on the left side of an assignment statement:
   array := scalar
   array := array 
   array := scalar * array
   array := array + array
   array := array - array
   array := array * array
Here "array" on both sides of the assignment means a single whole array; not an array-producing expression or array slice. But "scalar" can be any expression that evaluates to a single value. For multiplication, the arrays must be two-dimensional with properly matching sizes. These operations also apply to element attributes that are arrays.

There is also a matrix_inverse procedure.

For one-dimensional arrays, the infix operator dot_product gives the sum of the product of corresponding entries. Example:

   print vertex[1].__velocity dot_product facet[3].__facet_normal

Internal variables

These are pre-defined names for some of Evolver's internal variables. They may be used in the same way as user-defined variables, except the values of read-only variables may not be changed by the user.
ambient_pressure_value
Internal read-write variable. Value of the external pressure in the ideal gas model.
background
Internal read-write variable. Background color used in certain screen graphics (xgraph and Windows, at the moment).
body_count
Internal read-only variable. Number of bodies.
breakflag
Internal read-write variable. When set to a non-zero value, causes the command interpreter to abort and return to the command prompt. Software equivalent of hitting the keyboard interrupt (typically CTRL-C). The break doesn't happen immediately, but at a certain point in the interpreter loop when it periodically checks for user interrupt. Meant for bailing out of nested commands, since return only breaks out of the current procedure.
body_dissolve_count
Internal read-only variable. Number of bodies dissolved by dissolve command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts.
brightness
Internal read-only variable. Median gray level used in PostScript output and screen graphics.
check_count
Internal read-only variable. Number of errors found by the most recent C command.
clock
Internal read-only variable. Total elapsed Evolver execution time in seconds. Reads system process elapsed time, which often has a fairly coarse resolution of 0.01 seconds. For nanosecond timing, see cpu_counter.
constraint_tolerance
Internal read-write variable. When vertices are projected to level-set constraints, projection by Newton's method is repeated until the level-set function is smaller than constraint_tolerance. Default value 1e-12.
cpu_counter
Internal read-only variable. Processor cycle counter, available only on systems I know how to access this (x86 for now). Gives the number of CPU cycles since the system booted. Note that this is wall clock time, not process time. Also note that it resets to zero when a computer hibernates, so it is not guaranteed to be monotone increasing during the life of a process! Also, multiple processors in a machine may not have identical CPU counters, so the Evolver process should be assigned to a particular processor (in Windows, this may be done in Task Manager by setting the process "affinity" in its properties). For process elapsed time, see clock.
datafilename
Internal read-only variable. String containing name of current datafile.
delete_count
Internal read-only variable. Sum of edge_delete_count and facet_delete_count. Kept for backwards compatibility.
display_origin
Internal read-write vector. For a torus mode surface, if clipped mode is in effect, the center of the clip box is set with this array, whose dimension is the dimension of the ambient space. This array does not exist by default, it has to be created by the user. Details.
delete_count
Internal read-only variable. Sum of edge_delete_count and facet_delete_count. Kept for backwards compatibility.
dissolve_count
Internal read-only variable. Sum of vertex_dissolve_count edge_dissolve_count, facet_dissolve_count, and body_dissolve_count. Kept for backwards compatibility.
date_and_time
Internal read-only variable. String containing current date and time.
edge_count
Internal read-only variable. Number of edges.
edge_delete_count
Internal read-only variable. Number of edges deleted by delete command. This does not count the secondary edge deletions caused by deleting an edge. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts.
edge_dissolve_count
Internal read-only variable. Number of edges dissolved by dissolve command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts.
edge_refine_count
Internal read-only variable. Number of edges refined by refine edges command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts.
edge_pop_count
Internal read-only variable. Number of edges popd by pop edges, o, or O, commands. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts.
edgeswap_count
Internal read-only variable. Number of edges swapped by edgeswap command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts.
eigenneg
Internal read-only variable. Number of negative eigenvalues in last Hessian factoring.
eigenpos
Internal read-only variable. Number of positive eigenvalues in last Hessian factoring.
eigenvalues[]
Internal read-only array. Contains the list of eigenvalues produced by the ritz command. Example: print eigenvalues[2]
eigenzero
Internal read-only variable. Number of zero eigenvalues in last Hessian factoring.
equi_count
Internal read-only variable. Number of edges flipped by equiangulation. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts.
estimated_change
Internal read-only variable. Estimated change of energy during last iteration with estimate option in effect.
facet_count
Internal read-only variable. Number of facets.
facetedge_count
Internal read-only variable. Number of facetedges.
facet_delete_count
Internal read-only variable. Number of facets deleted by delete command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts.
facet_dissolve_count
Internal read-only variable. Number of facets dissolved by dissolve command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts.
facet_refine_count
Internal read-only variable. Number of facets refined by refine facets command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts.
fix_count
Internal read-only variable. Number of elements fixed by fix command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts.
gravity_constant
Internal read-write variable. Value of the gravitational constant, which can also be set by the G command.
hessian_epsilon
Internal read-write variable. Magnitude regarded as zero by hessian command when factoring the Hessian matrix. If a zero appears on the diagonal at the pivot during factoring, a check is made to see if the rest of the row is zero. If it is, then a 1 is placed on the diagonal; else an error is reported.
hessian_slant_cutoff
Internal read-write variable. Hessian commands treat vertices whose normal vector is nearly perpendicular to constraints as fixed. hessian_slant_cutoff is the cosine of angle. Works on vertices with one degree of freedom in hessian_normal mode. Default value 0.
high_boundary
Internal read-only variable giving the number of the highest parametric boundary defined. Remember that the name of a named boundary is internally interpreted as a number, and that boundary may be referred to by that number. Useful in iterating through all boundaries, for example
  for ( bnum := 1 ; bnum <= high_boundary ; bnum += 1 )
    if valid_boundary(bnum) then
       printf "Boundary %d has %d vertices on it\n",bnum,
          sum(vertex,on_boundary bnum);
high_constraint
Internal read-only variable giving the number of the highest level-set constraint. Remember that the name of a named constraint is internally interpreted as a number, and that constraint may be referred to by that number. Useful in iterating through all constraints, for example
  for ( cnum := 1 ; cnum <= high_constraint ; cnum += 1 )
    if valid_constraint(cnum) then
       printf "Constraint %d has %d vertices on it\n",cnum,
          sum(vertex,on_constraint cnum);
instance_name.modulus
Internal read-write variable. Modulus of a named method instance.
instance_name.value
Internal read-only variable. Value of a named method instance.
inverse_periods[expr][expr]
Internal read-only variable. Inverse matrix of the torus_periods matrix. Uses 1-based indexes. Useful for normalizing vertex coordinates to period basis.
integral_order
Internal read-write variable. Order of polynomial done by 1D and 2D Gaussian integration. Much better to set 1D and 2D separately. Synonym: integration_order
integral_order_1d
Internal read-write variable. Order of polynomial done by 1D Gaussian integration. Synonym: integration_order_1D
integral_order_2d
Internal read-write variable. Order of polynomial done by 2D Gaussian integration. Synonym: integration_order_2D
iteration_counter
Internal read-only variable. Loop count of last loop in a command.
jiggle_temperature
Internal read-only variable. Current temperature for jiggling.
lagrange_order
Internal read-only variable. Order of Lagrange model. Set with the lagrange n command.
last_eigenvalue
Internal read-only variable. Eigenvalue from last saddle or ritz command.
last_error
Internal read-write variable. Has error number of last error message.
last_hessian_scale
Internal read-only variable. Stepsize from last saddle or hessian_seek command.
linear_metric_mix
Internal read-write variable. Fraction of linear interpolation in Hessian metric.
memory_arena
Internal read-only variable. Total memory allocated to the program's heap. Available only on SGI and Win32 versions.
memory_used
Internal read-only variable. Total memory used in the program's heap. Available only on SGI and Win32 versions.
notch_count
Internal read-only variable. Number of edges notched in last notch command.
pickvnum
Internal read-write variable. Number of last vertex picked in geomview.
pickenum
Internal read-write variable. Number of last edge picked in geomview.
pickfnum
Internal read-write variable. Number of last facet picked in geomview.
pop_count
Internal read-only variable. Sum of vertex_pop_count and edge_pop_count. Kept for backwards compatibility.
pop_edge_to_tri_count
Internal read-only variable. Number of edges flipped to triangles by the pop_edge_to_tri command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts.
pop_quad_to_quad_count
Internal read-only variable. Number of quadrilaterals flipped by the pop_quad_to_quad command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts.
pop_tri_to_edge_count
Internal read-only variable. Number of triangles flipped to edges by the pop_tri_to_edge command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts.
quadratic_metric_mix
Internal read-write variable. Fraction of linear interpolation in Hessian metric in the quadratic model. Not very useful.
quantity_name.modulus
Internal read-write variable. Modulus of a named quantity.
quantity_name.pressure
Internal read-only variable. Lagrange multiplier for a constrained named quantity.
quantity_name.target
Internal read-write variable. Constrained value of a named quantity.
quantity_name.value
Internal read-only variable. Value of a named quantity.
random
Internal read-only variable. Random number between 0 and 1.
random_seed
Internal read-write variable. Seed for random number generator, used for example in jiggling or in finding random initial vectors in various Hessian eigenvector algorithms. Defaults to 1 at start of datafile.
refine_count
Internal read-only variable. Number of elements refined in last refine command.
rotation_order
Internal read-write variable. Order of rotation group for symmetry groups rotate and flip_rotate.
scale
Internal read-write variable. Current scale factor for multiplying the force to get the motion in the 'g' command.
scale_scale
Internal read-write variable. Multiplier for the scale factor used in the 'g' command. Default value 1.
scrollbuffersize
Internal read-write variable. The command window scroll buffer size on Windows machines. Meant for non-NT Windows that don't have menu properties option for this.
self_sim_coeff
Internal read-write variable. Used as magnitude coefficent when self_similar is toggled on.
simplex_representation
Internal read-only variable. Whether the simplex model is in effect.
space_dimension
Internal read-only variable. Dimension of ambient space.
sq_curvature_modulus
Internal read-write variable. Multiplier for the squared_curvature energy when it is declared the old way in the top of the datafile.
string_curve_tolerance
Internal read-write variable.

In the quadratic model, the smoothness of graphing of curved quadratic edges can be controlled with the internal variable string_curve_tolerance, which is the desired angle in degrees between successive graphed segments making up the edge.

surface_dimension
Internal read-only variable. Dimension of surface.
symmetry_group
Internal read-only variable. Whether any symmetry group is active (Boolean).
t1_edgeswap_count
Internal read-only variable. Number of edges swapped by t1_edgeswap command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts.
target_tolerance
Internal read-write variable. When volume constraints or named quantity constraints are enforced, Newton's method is repeated until the total deviation from target values is less than target_tolerance. Default value 1e-4.
temperature
Internal read-only variable. Amplitude for jigglinge.
thickness
Internal read-only variable. Thickness for thickened surfaces in graphics output in P command. Used when facet frontcolor and backcolor are different. Default value 0.001 times the maximum linear dimension of the surface. If you get backside color showing through, increase the thickness.
torus
Internal read-only variable. Whether the torus model is in effect. (Boolean).
torus_periods[expr][expr]
Internal read-only variable. Current values of the period vectors in the torus model. Torus_periods[i][j] is component j of vector i. Uses 1 based indexes. For changing the torus_periods, define the periods in the datafile with variables, and alter the variables.
total_area
Internal read-only variable. Total area of the surface in soapfilm or simplex models.
total_energy
Internal read-only variable. Total energy of the surface.
total_length
Internal read-only variable. Total length of the surface in the string model.
total_time
Internal read-only variable. Elapsed evolution time in the form of total scale factors.
transform_count
Internal read-only variable. Number of image transforms active, as generated by the transform_expr command.
fix_count
Internal read-only variable. Number of elements unfixed by unfix command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts.
vertex_count
Internal read-only variable. Number of vertices.
vertex_dissolve_count
Internal read-only variable. Number of vertices dissolved by dissolve command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts.
vertex_pop_count
Internal read-only variable. Number of vertices popped by pop or o command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts.
where_count
Internal read-only variable. Number of items satisfying last where condition. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts.
window_aspect_ratio
Internal read-write variable. The ratio of the the vertical to horizontal dimensions of the display window. If set, this locks the aspect ratio to the given value. The window may be resized with the mouse, but the aspect ratio will stay the same. The unset value of window_aspect_ratio is 0; setting window_aspect_ratio to 0 will unlock the aspect ratio. Applies also to the PostScript bounding box, if full_bounding_box is on. Currently implemented only in Evolver with GLUT graphics. Caveat: the window doesn't always fully follow the mouse; just keep trying.

Back to top of Surface Evolver documentation. Index. evolver-2.30c.dfsg/doc/square-e2.gif0000644000175300017530000006523611410765113017502 0ustar hazelscthazelsctGIF89ad   $$$''('('(''((',,,//0/0//000//0/000/444778787788877878887;;;??@?@??@@@??@?@@@?CCCGGHGHGGHHHGGHGHHHGKKKOOPOPOOPPPOOPOPPPOSTTWWXWXWWXXXWWXWX[[[__`_`__```__`_```_dddgghghgghhhgghghhhgkkkoopopoopppoopopppotttwxwwxxxwwxwxxxw{{{!!B Image generated by AFPL Ghostscript BETA RELEASE (device=ppmraw) ,dURJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJթG5RIH)e+Vdb .](RJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*TRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ%F1j("E-‚ ,X` &L8 "L Q  @*A*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ@UTRJ*UTRJ*UTRE"9RQHtɲ-ZtҥK"N0a N @aC 2dؐaC =$IrBB 6@¥O>zӧO>},2$A%( :uԩSN:uԩSN1qNTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UT:("E)hHdhŊ.V`D Nq! $H A $XA;vرcljlfM5mڴYf͚5w"E)(]2(ɓ'O>}ɓO<})ЇKyɔ)SL2eʔ)SL2EDSNZSN:uԩSN"ԩS RJ*UTRJ*UTRJ*UTRJ*UTRJ*U'd0AB *\P!?C, 0` B"t!(;$9dȐ!C :А!C%:t萡C `(RH"ERH"Ed)R:',qғaMO:yӧO}ӧ?82e*TPɔ)SL2e*ԉ5L:eꔩSN PN:DSN:u*UTRJ*UTRJ*UTRJ*UTRJ*UTR'B *TPB *TPeXdIQB)(H)HH="tD:DСCs Rt!C):dȐ!C5 H"*."E)R$K"YIR$Jrd%K̠ȓO>}ɓ'O25*TQB 5 (L2eʔ!L:eʔSN2eꔩS}NDSN:u)( TRJ*UTRJ*UTRJ*UTRJ*UTRJ*kh*TPB *TPBʠȒ%Kw$HHER(H)Z"Cp("D!C:d!C#СCNh1d!E :dȐ!C 1EG"Iq)RH"E)RH"aI%K,YD(COl1(/N:uԩSN:uP:ԩS6EөSȨRJ*UTRJ@UTRJ*UTRJ*UTRJ*Up2uԩ> *T ,Ydɒ%K,YBJ$E"ERHQ$E,Q("C"!D"t!B!dȐC :tB!C 2"E$RHHER( +"E4)R;',Y'K,udɒ%K, 9QP,(]2eʔ)SLHcʔ)SP&ԩSN:u&kP:u ;N:! QJ*UTRJ*UTRJ*UTRJ*UTR *UT2)SL2R(BYdɒ%K,Ydɒ p)R("E Ȑ"E )JP +"D!B@Ȑ!B:tP w2dȐ"C I"E)RI"ETXiRH2dɒ%K,Ydɒ%K,YGD6R@GQL2%ʔQL}ƔSL2e*7:uԩ; :u S\:ԩSNj@ UTRJ*UTRJ*UTRJ*UTRJ*UTRESL2u0YВ%K,Ydɒ%KeI"E"=#P"ERH!E},!D!B:T̡C2d!BH8tȐ"C !c"E")R("E"9RJH" cɒ%K,Ydɒ%KzpD0D"ɔ)SL2eʔ)SLB)SL2e)SLYIPDBԩSNԩSN:u*5TRJ*UTRJ*UTRJ*UTRJ*UTRJU(N:u*# 4pх%K,Ydɒ%K,YPH)CRH!Er#C:DC 2dȐ!C 2E!C2dEERIQ$EER("V"EjB%K,Y '2$ҧSN}2| }L2eʔ)SL2 !SL2e)SL2eSHtSNBu*SP:u*TJ*UTRJ*UTRJ*UTRJ*UTRJ*UNHRN@cɒ%K,Yd ")HHERdH!ERd(ByӧOdPdɒ%K,1!6,Yd$I$E RH!C)2H"CЇC9dСC "d!C j#"CHDR("EERȑ"Er(ԩSN:uԩSN:u%PB)'O< qDYe SDBu *TP=q)TB*U)6.\t©;:uԩSRJ*UTRJ*UTRJ*UTRJ*UTRJ*U]'O}N1.,Ydɒ%K,Yd('Jh@L E)2dHCRdȐ"E5cСC 2dСC2tCNt9AF;d0p"E)RHldIQ:uԩSN:u)KB}*'OB}j$$H% SB)TN* SP:uJTRB RT\ )TP:uPTRJ*UTRJ*UTRJ*UTRJ*UTREk>})>I!bɒ%K,Ydɒ%K,Y5,!#d2H"EC2dСC:t!Dn l$Y'H"E)RH"Qa%KdSN:uԩSByS(O<(>Idʔ)S@ibBPz SN: S.6J U*Tb,BE8:uԩSRJ*UTRJ*UTRJ*UTRJ*UTRJAmɓON|K,Ydɒ%K,Yt'6 )RH"Bd$#jHBI `dǎ;,HP vJp"E)RH"EQH"E@%K&Yd ʉ>:uԩSNIx*ԧPBr"ILyaȔ)SL2eʔO}PCRP:)TH U*6%ؤBU)PuKSN9J*UTRJ*UTRJ*UTRJ*UTRJ*UT%biO>y:铧OXdɒ%K,YdNC"H"E)RH"E)RD ر 2dRdȐ"E)RH!(II)RH"E)RH"EIK,Ydi$K}@$SNSO )SL)SL2eʔ)SLIvp MPBUbS*B J**TTܠJ * *UTJ*UTRJ*UTRJ*UTRJ*UTRA(B,}iP>Yʐǒ%K,Ydҝ "Md E)RH"E)RH"E$QAF"C )2H!E )RH!C}8rDHG"=)RH"=) ,Yd$K"Y&N:`)T(ERNRɔ)SL2"SL2eʔ)SčB"0*C`pႊ B U*TP *TN" TRJ*UTRJ*UTRJ*UTTJ*UTRJU)mBS5U>}RE,Ydɒ; E)R>)RH"E)RH"E;RȐ"E)RtH!C IQ$BD)RHE)H YTRI,UTR%E8NDAѝl1eʔ)SL2eCL2eʔ)JjT)PJEETTJ*.qTTPBTPB U*BRJ*UTRJ*UTRJ*UTRJ*UTRJ*UBN ƧO)O2Ddɒ%Kw2(4)RH"H"E)RH"E)R"#Ew,QȐ"E )Rd"C)r"H&RH"))RHT%K,YdɒJ,Y\ 2eʔ)SL2eʔ) L2eʔ!( v1 )aPB*U*7;RJ*ա/ؤJ  tB *TJJ*MT@tD*UTRJ*UTRJ*UTRJ*UTRJ*UT9)T()I>y:ʓ'O.v:ujUTRJ*UTRJ*UTRJ*UTRJ*UTRAPB⩇O>y  Kd0)RH"EiR w)RH"E)R)Hя$ )RdȐ"ER")HQ$ETH)R$H E$K,ITR%K,AEB eN dʔ)Sl1PBI zRʩTRD)@(PER%*UTȸp&* :uԩPRJ*UTRJ*UTRJ*UTRJ*UTRJ*4X ğ/_<O5ɀ(H"Yd)RHPH"E)RH"y )RH#E 2H"E )2@ ERIQH")tBJH"ELJ,UT%KA@cM'O2( *TPB"$ j ;"D+%2!+ vqSN Hpa̎T&!#e:uꔩSLȠJ*UTRJ*UTRJ*UTRJ*UTRJ*UNȐA$KɓO>)rϤH&E)RH*Ed"E)RH"EI(RIQ$Eǐ!ERHC;8rIQ$E")( )E ,Ud%Kwlp'O*TP` (P@AP ɔ*UP"E $@ȩSN:uꔄS\P u ,"RPҩSN:e %UTRJ*UTRJ*UTRJ*UTRJ*UTR@>.6SP8 Dd)RH"Y)$d)RH"EPH"E"AR E Rd!ExȑH"ERIQ$EN$)R$`,YdҝvɓN<ѩ0T(OB *TP,%!)SLD'TRB U0.T¢)2:uԩSk֜:US$TPBUJTB:u);TRJ*UTRJ*UTRJ*UTRJ*UTRJ#,y)'2 SNJ(AH,E)R$K 5 H"E)GQ$E)RHPN(RȐ"E)'ңHER(H")tEHp@Xd΍ ;x䩓'OP2ؠ)RH*Et"E)R#"E)("E)Ő"E)2凢H")(H)(#**"ePDE$}uißP< *TPw2ذdj SL@BTRGNP*PL e]N:u JSNb!$ASP N:*UTRJ*UTRJ*UTRJ*UTRJ*UT O:UХN8iD6*EtF}"IT)R$!I)RdNG)RH"EERHQ7 )RdBERIQ$EErIQG"%i E<}SO>}S+B 'Ka$@dԚL!ATڸp1%ҩSP: !SL eʔ(SȀtT(N:uT eHP1 SF` TRJ*UTRJ*UTRJ*UTRJ*UTR/tD=:iԉ.2d)H$J"eH {)RIH)R("G,2Ȑ])(H"))"Bex}%+2eʔSL)2`֘2e)SL2eNH$ (I@I"D 2d Ԑ'TOBIIr"!Hǔ!VLeʔ)dTRJ*UTRJ*UTRJ*UTRJ*UTRJ<өOtԩS=:Ej``$J(ER$K(UR!;PAB)$XPcǎ?! lXH"Y$)ҤH&R'K,qԉ5yh)֘2e)SLZS@6Z9e)SL:eʒV$GžPD2*TPB"*T(S!%T(OB} E(ɘBbS4 aʔ)S4PJ*UTRJ*UTRJ*UTRJ*UTRJ*U xT)$E5<iSNdtdIOK,ER$J&M2 "E0RH"E)R$BkЬY& O"Y$)$J(EI%J&EN:Yĉ'N8qA΃72էO>#*aʔ)SN2*HЩSH't *T(SB"5jT(RLq /B}S$ *T(0JNL*UTRJ*UTRJ*UTRJ*UTRJ*UTZ542*PEFKFPX2e SLsPL U*/:uԩS'K:@(PB DN,YdS>y iPP,y *ԉG `R$LBF*UTRJ*UTRJ*UTRJ*UTRJ*UT !/2xIQHԩSNzDDR$CXdɒ%K,IQHE)RH"Mbɒ%K,pRH"Qd)R$C"uRN:u℗P,e*QB)/ 2eJS,aPC2*)2.:uԉSN:u)R(B S=,Y'KdSPBd)ãPn` :ec UTRJ*UTRJ*UTRJ*UTRJ*UTR v SOkdtBӥN:uR$K2ܱdɒ%K,YdH"E)H"Eғ%K,YdIQ+,E)Hl餩%K,EAHPBT(SL"BԜ2e"ִ'PL *)BeS(E,PPIRJ*UTRJ*UTRJ*UTRJ*UTRJ&eզN:yхЎN,uԩS;4cɒ%K,Ydɒ%EDRIQH"ERI!J,Id%KpD)%K,9ɒ&K:qqLPB EPB EH;*2e)EN)TRB TP2DԩSN:u)SN:u2t 6m&K,ad.4B} ΧPB)ɝP`j C('؀J*UTRJ*UTRJ*UTRJ*UTRJ*UHF(KNS>':uiSN2pǒ%K,Ydɒ%K,@(R$H")z)+,YdI%K,Yd!K"I N"u $RPB ЉQB IH>X2eʔ>P*TPB e*)RBөSN:uԩSN:u2gJv N0Yd%LNPB B5 `H@(UTRJ*UTRJ*UTRJ*UTRJ*UTReF$RBCK2DRN"9K,Ydɒ%K,YdE"1)R$8 ,Yd%K,Ydɒ;PIN8&TPB *T' H* @1eJQs'IP2e*TPBSN:qdS$8]d!8w2i` (,a&K,uKP> T04:h FE$X ,TRJ*UTRP*UTRJ*UTRJ*UTRJU BX AKpNQ@O'C]\!P8@Ȓ%K,Ydɒ%KVTDb)RH(dɒ%K,Ydɒ%K,`%ҝd)TPF 5*TP'  @DF#k@&TPB8B EN"b䄊 6cɒ%K,ȇK8K$K4Y„&`Bu)O>% %TPR(P?RJ*UTRJ*UTRJ*UTRJ*UTJ*UH e)15N ( 'vQ*TP;j@D 8Xdɒ%K,Y2$R$E"hcɒI"Ydɒ%K,Idɒ  N*TPB *TPX1% ] XPlԈfG(SB *TPPʁ'J\p!K,Ydɒ%K,Yr"RH*Xd..2 %N:eN(OB} 剆PBY@hFBYH*UTRJ*UTRJ*UTRJ*UTRJ*UTuAꓥ @*T(K铧PB%K,r";,Ydɒ /"Ec%K,Ydɒ%K,YtH`PPB *TPB9a OF)SPB *TPB  ̇Xdɒ%K,Y4ɒI,Y(H"pǒ%K,Yt PX))> *"/` jPH  UTRJ*UTRJ*UTRJ*UTRJ*UTR PePB@(TPB}T# 8.mڤI&88f8%K2'%K,Ydɒ;mb.p"J!(T$c*TQB$Љr a)SHcT'B U*TPB HH(I`Œ%K*Ydɒ%K,Yd4EPF,Ydɒ%K,D=N Gʉ8tƝP$UM;BRJ*UTRJ*UTRJ*UTRJ*UTRJ FP5 EK<} )ԉP2dN0Y 8j@K# @sS'N:ybSPL  PX @@`㔩Sx c*)S6 *TPB  8ed) #,Ydɒ%K,Ydɒ%+B"E(RHp2Xdɒ%K,Ydɒ%Gd\ (8:u BEI2X@ B9HJ*UTRJ*UTRJ*UTRJ*UTRJ*U L X@(T 2X SO4&K,uɒN,rA\dHNN:uԩSN:u!PB T(Bb㄄ )SH)P$ *TPB$H.u qR$JPdɒJ,MTi%K~$D)RHE’%K,Ydɒ%K,Y…L:aĩSPxD,9*UTRJ*UTRJ*UTRJ*UTRJ*UTR! @RK!t“OBy *ԟ ,YdN,miS#'$ԩSN:uڴSN:uŝPJ *TPB2D CƩS@*@SL*TPBjsK,uDFH,Et'ÝI,Ydɒ%K,HEj)R#,Ydɒ%K,YdɅKBtԩ'NLt:Ǔ'O$脥tTRJ*UTRJ*UTRJ*UTRJ*UTRJ@Q#P6X!B hFOd&K,YdM3$EN:uԩSN:uԩ+B T(SB"$ę. cʈSpԈ*(QJuiTP"%%RN,u `H"YR>Xd)%K,Y$RH"E(RHTXdɒ%K,Ydɒ +&MʠS'N:qJ'RJ:y!15jPJ*UTRJ*UTRJ*UTRJ*UTRJ*Ut)+S#T H *ԧ < ƅ&K8YdN\ (=tԩSN:uԩS'8'B *QBu"†!SkrT#c*PBPPpN:YdӉ6"Y4)H bɒ%K,YD$EERQH")%K,Ydɒ%V,EdOH:u NzNx$ *UTRJ*UTRJ*UTRJ*UTRJ*UT”)]L!w ULOj( '/.:uR'K"eTPBq#RN:uĩSN2t**)SBqb)S)kxT(RH"EꉕPdN,uR'26"ER$K&E E,QH"E)H"EPB,Yhɒ%K/DRH`\tRH.:uœ'O'T!%TRJ*UTRJ*UTRJ*UTRJ*UTRJHLAaw*T(_B%TO>bS'K8E@(T(SB  p:uԩSN:e *TP"YŒ)S I蔩S9@ *RB"5TmNR'K,udN2(T)H"M4)RIm2dR5))HER(RHp2(dɒ%K.XɒH,ҩSNBӉ>>IYsORJ*UTRJ*UTRJ*UTRJ*UTRJ*U 2e̊I8PBeJюP<} FL0iӀP(SB ԨP:S'M:urT(SP)S>A)SlhBPJugO="@ԩN:YdS'K,XR$K*ER$K&E%05"E)R$E"E)RHEc’%KX)H"E)N:e:iSH"y2$cRKvJ*URJ*UTRJ*UTRJ*UTRJ*UTA@ʔ)S{2ҧ@'B !O'By ʓO`¤CPB**T(SJ  N>:qEHPHʔ)SNA)SH r@ dp'M,uR'K:ucҤH"URH"Yd)"P2)RH"A(RH"E)ehcIH"Ed)R>:uN:9SO}Nx#*UTRJ*UTRJ*UTRJ*UURJ*UTRu SL2!K @*T(K , TOBYʠLNH"U*T(SB e*ԨPNION`NPdʔSN`$)9F Ɣ)SL2eʔ5+jШIQd4YdN:uJ,ERH,EiR0920!Î$dC!I֐Y Hj@pGR$I,Ed)R$K"ѩS'8P2u OUxR"TRJ*UTRJ*UTRJ*UTRJ*UTRJL2e*L}a#$B MP<} B4Ξ;ws'PB &'Ze)SLYRBњ HPň)SN2eT. XsS'O*%%R'K,udS0"UTiRH,M2FH6YE")RI"3dҘ9DȎBjdH(IɒH"Yd ǚN" SN'(ѩS>2PJ*UTRJ*UTRJ*UTRJ*UTRJ*UѣG=$@1e TFŽPBI@(kHO>h@ (P @ a (p@ I\cj SN:eʔ'„ e)CN:yS IYd%> *ER$KbE5"=ZA>)RH"HERH"E)R(ɎB2ɒH"YRH ]tSN:E`)C'O"Tb*UTRJ*UTRJ*UTRJ*UTRJ*UT)RHSt x@ѥϩSNI $)SL2e)SL2e )S$D:u թN;wTܰdԚN2eT5 * ;>uӧN>ɁN'K2(d)R&*@FQ$EER(( ȑ"GERI"EERHIw"ER$K"Щ O:uԩω*k:usS;TRJ*UTRJ*UTRJ*UTRJ*UTR N/:u).I##@ A ԩSL:f)('2eʔ)RL2*)Sd@Bu)T"A#Ҩ6 |2Д)57L2eI )'/I}ɓ'a%G?R)H")(= )RH E)H"E")RHH (R#H`ϤH"ED NItꄩSNPvS);RJ*UTRJ*UTRJ*UTRJ*UTRJSքөS15E8Pdʅ#N:u) NYb)SL2eʔ)S$9uSP* Ԙ2uʔ> L2 /B} *TA'uS'O,A F<ER(HERIQ$E@QdH"C5(rH#EERHQ$E$QH"w"ERd)R$K6S'L:u2SNNH*UTRJ*UTRJ*UTRJ*UTRJ*UT q%FH:RlpFBPHsԩS:uTpL2Eʔ)S:Xs թHI@ !!':e)S@2eF;< SO2(ӧN>u'I]N 8(R$E)(H"aȐ"CGH)RHHH"EePiH,EやC"ʠN0q)>(J*UTRJ*UTRJ*UTRJ*UTRJ*UuԩSBAq*TPBuG@DVvHCSt:u)Kl H * kL2eʔ)SS@˜P<*TO> e)N:uJd$Y$I%K D) p"ERIQH")E2H!E Aȑ"EERE)RH"E,R$K"E)R$@ GE>}D+ja*UTRJ*UTRJ*UTRJ*UTRJ*UTRu`ͩSN:uʒTD+dHtBE v  8H!.IP' *T(OB *ԉ>.\IJ(PB**S*B U* jdʔ)O 2eʔ)SL2eʔ)C6@p%K"Yd)J,UdHE)RH"ER!%"E")JD"E)BH!E2E)pG"E)RH"E)"ФH"E) ,Yd)ωP<}'O6X  UTRJ*UTRJ*UTRJ*UTRJ*UTR%'6PB *T;6J *T@$&ȘJ*UT(0 T$DBNB2eʔ)S>(2eʔ)SL2e$. Nx' A,YdR$K,YdR "E)H"E) (")DȂ"C RdH"C )2H!CP(R$$"E)RH"E)RȐ"Ydɒ%.,Ydɒ%KXjxS$tSOU*UTRJ*UTRJ*UTRJ*UTRJ*UT ЩSS*M.PBPB%)TPBuP* `)SL2eʔ)SLaƔ)SPP@ IQH:uԩӤU(dRJ*Y'R$E"9)R$E"9)R$+@1dH2dlc,!A P#É?H"E)RHQ &bɒ%K,Ydɒ%K2 ӆO>}TRJ*UTRJ*UTRJ*UTRJ*UTRJ P: wPY!*nPB )P: *TP9r,llcʔ)SL2eÌ)OfllH'OB S })ԧOԩSN:uԩSN bɒ5"EDhMN"E)TL }:d!B:d!C != (2"C!RdȐ"E )RFd`%K,Ydɒ%KdO>}d;TRJ*UTRJ*UTRJ*UTRJ*UTRSN:uԩ)*"cđHPB MN:ȩSP:u թP$QH \|1(ԧPByS(OB:ѩSN:uԩSN:yK'QB k(Z($E9RCڀ8tСC:d!C2THBaH!E):dH!E )RdHQ=$ܱ#$H!Ȓ%K,YDF >}S(BLRJ*UTRJ*UTRJ*UTRJ*UTRJP:uԩH œ:өSq'TtB ĝSP:% )l@cɔ)S6Ș2eR^< ӧP>$!RN:uԩS'O:u N4B"E)RH"E )ȑ"w2e!C2D!B:d "DhGC !RdH!E )RdH"#I)"@%K,Y%Dd"YdR5}SO>IJ*UTRJ*UTRJ*UTRJ*UTRJ*UTpqԩST\lBu #P:J.ڔri MPq:L2uʔ)Sl1eʔ)S:ɐDQO>}DN:uԩSNxI (ph"G )("EERHQ$ERv2dȐ` "d!B:DСC "d!B RtH"ERdȐ!Ew@4Z( ,Ydɒ%Kuq…Bpdh*;I2"a*UTRJ*UTTJ*UTRJ*UTRJ*UTRS@AuԩS\:ԩSNj‚.@@I#Ȕ)SL2eʔ)SL c)SL2ej4| S(:u$'N0B%K,Y"#R hERH"EERHH50t!CXsСC :d!D@!tCɠ"E):Ȑ"E IQ#Edɒ%K,Ydɒ%B\\pF 1eԚTRJ*UTRJ*UTRJ*UTRJ*URJF uԩSNZSN:u#]tHʔ)SL2eʔ)SLAƔ)SL2eʔ)SLIa̧;5:# PXdɒ%K,Y)H eHH)R(R$E)Ɛ"C 2!B 2dC@!B"D E ):dH"EJ$H")R%K,Ydɒ%K,Ydɑ L:e)SQJ*UTRJ*UTRJ*UTRJ*UTRJ*Uh 9uԩSNBJ©SOBu)/.H:EjuL2uʔ)SLB)SL2ej)SL2eSdq !K,YdɒN,YdiS;"ED(EERI"E)R!C 2!(;2t萡C:$ C "D!B'RtH"C2tG5R(RV,Ydɒ%K,Ydɒ%-5B)SN!B*UTRJ*UTRJ*UTRJ*UTRJ*UTDHN:)*N:u )SN:uԩSĀСȔ)L2 D"SL2eʔ)St!M H%K,YdɒN,YdI%(P"M)%C(("E")rH; :dȐC sСC:D B"t!B iȐ!E )RH>ER(R$E"cɒ%K,Ydɒ%KVj *R(N:uJPTRJ*UTRJ*UTRJ*UTRJ*UTRJSnSԩS48u թH :uԩSN:uP,2 !SL%J $C(PBqӟ d,aɒ%K,Ydɒ%Kw2D)R$J"G"E)RH.;2dȐ!C2GBC"d! w"t!B"th E R(!#?")HQ$EERDK,Ydɒ%K*TPt)S RJ*UTRJ*UTRJ*UTRJ*UTRJ*E,:u kN:cөSPT:uԩSN:uԩSNB@ GB e(RFq'Ozdɒ%K,Yd N,e0)ҤH"MP;)("G$9tȐ!E 2dHѡ6 "d!CVv"D!B2D!B;RdH!Ew,8RHH)I"B Ydɒ%K,i*TPB DSN!pJ*UTRJ*UTRJ*UTRJ*UTRJ*UT5rԩSBy)\x9uԩS,!t ( @`B :D"*TPB e*TPH ϧO}B2uԩ4+yɓ'EXdɒ%K,Y"RH"Y)RH"@H#Eݑ`Ȑ!C :tСCxB!C:DC :DC$E)R(R$E"ERK,YdFPB *TPB)#TRJ*UTRJ*UTRJ*UTRJ*UTRJթkN:uԩSBaSN:eԩSN:e)S92eʔ)4Ȑ2*PB2*T /<}'O}Ӛ y2Yd҉;"E)RH"E)RHw2RD2dȐ"C2dȐ!CH tC"t!B"D!B cG ))"E*IQ$E")RB *TPB *TPB *UTRJ*UTRJ*UTRJ*UTRJ*UTҩSN2b$ԩSN:FKL:eԩSPdʔ)SL2e'(2*TPVxS'O>}ɓ'O,bɒ%(F"E)H,E)H"E@! } 2dСC 2tH!E 2F"t!B"D!B"ԧ9RI2kȬ1 4eʬI# I}ɓ'OBy)ɉH@D)RH"E)RH"E)B2d0dȀ 2`@ٱcǎN؁$H A $H8'@qbĈ=\r˕,XteETRJ*TRJ*UTRJ*UTRJ*UTRJ*Up FSN:uԩSN: (L2bĔ)SL2eʔ)SL2eʔ'('Ν;pܑ#9w I$I$4P!D 2`Ȑ!C ,dpD'@8A℉]p٢+]trgѢE5ZI#ENRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ@UTRJ*UT:@C?@ (P$ @ @P"N8.V`Ŋ.Vt!HQE")RѢHTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTR 8'J AD,V`b.V`HQ#G=r)UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*UTRJ*U;evolver-2.30c.dfsg/doc/news_12.htm0000644000175300017530000001265011410765113017167 0ustar hazelscthazelsct Surface Evolver Documentation - Newsletter 12

Surface Evolver Newsletter no. 12

Back to top of Surface Evolver documentation.




                    Surface Evolver Newsletter Number 12
                              July 19, 1995

                   Editor: Ken Brakke, brakke@geom.umn.edu

Contents
  Version 1.99 ready
  New features

Version 1.99 ready.
  Surface Evolver version 1.99 is now available from geom.umn.edu
  in /pub/software/evolver/evolver.tar.Z.  There are also new DOS and 
  Mac versions.  The DOS version is in evolver.zip.
  The Mac version is in Evolver.sea.Hqx.  This was compiled for
  use with a math coprocessor and will not run on a Power Mac.
  There is a non-coprocessor version in Evolver.no68881.sea.Hqx
  which will run on the Power Mac I have access to, although it has
  been reported to crash others. Any ambitious volunteers to figure
  this out will be welcomed.
 
  For those of you into Hessians and eigenvalues, there is a little
  tutorial in eigentut.txt.  This is a plain text file, about 10 pages.


New features.

  Storage used by element structures has been cut down by only
  allocating storage for features actually used.

  The limit on the number of constraints has been raised to 128,
  and can easily go much higher if somebody needs it.

  Body "volume" technically should be a read-only value, the
  actual computed volume.  "target" should be used for setting
  the volume constraint, as in "set body[1] target 2.34", 
  although "set body[1] volume 2.34" will still work, although
  with a warning.

  "volconst" has been added as a read-write body attribute.
  This is a constant added to the calculated volume.  Also
  applies to named quantities.

  The command "convert_to_quantities" will do an automatic
  conversion of old-style energies to new-style named quantities.
  This has the same effect as the -q command line option, but
  can be done from the Evolver command prompt.  Useful when
  "hessian" complains about not being able to do a type of energy.
  A few energies don't convert yet.  It is my intention that
  this will be the default in the near future, probably in 
  version 2.00.

  "edgeswap" command will force an equiangulation-type switch
  on designated edges, as in "edgeswap edge[23]" or
  "edgeswap edge where color == red".  "edgeswap edge" will
  swap all edges, if you like to see weird surfaces.

  A "variable_parameter" is a parameter whose value will be varied
  during optimization.  Just use "variable_parameter" instead of
  "parameter" in the top of the datafile.  Be aware that energy
  derivatives with respect to variable parameters are done
  numerically, not symbolically.  Won't work with Hessian yet.

  For the torus mode, torus translations have been automatically
  added as view_transform_generators after any appearing in the
  datafile.  So multiple copies of the fundamental domain can
  been seen with
      transform_expr "abc"

  Command P can now take a number after it, so "P 8" starts geomview
  without having to bring up the "P" menu.

  "saddle" and "hessian_seek" commands takes maximum stepsize, 
  e.g. "saddle 0.5".

  "move" command will continue in direction of previous motion,
  whether from gradient or hessian.  Takes stepsize, e.g. "move 0.5".
  Negative move is not a perfect undo.

  Toggles print previous value along with new value.

  Can now make saddle or hessian_seek minimize squared gradient
  instead of energy.  Use hessian_menu toggle G.

  There is now a "bottominfo" command that prints out the bottom
  part of a dumpfile.  Useful for generating new datafiles from old.

  You can redefine single-letter commands with " x :::= command".
  Restore with "x :::= ".  Redefs undone at start of surface.
  Redef not retroactive to single letter commands used in
  definition of commands. Letter redefed at execution, not parsing.
  Redefed letter used in command will revert to standard meaning
  when letter unredefed. Meant for analogous replacements,
  like special r.  "r :::= { hooke_length := hooke_length/2; r}"
  will work right first time, but not second. Use single quotes
  around letter to get old meaning, i.e. 'r'.
  Should only use to disable commands.

  hessian_menu has option Z to do subspace iteration for multiple
  eigenvectors (same as "ritz" command), and you can pick
  eigenvectors for motion with option X.

  Read-only variable "last_eigenvalue" stores eigenvalue found
  in saddle command, or first eigenvalue found by ritz.
  Read-only variable "last_hessian_scale" stores stepsize used
  by most recent saddle or hessian_seek command.

  Compilation option -DSDIM=3 will hard-wire space dimension 3 into
  the code, permitting more compiler optimization.  But such
  an Evolver cannot handle any other space dimension.

  The eigenprobe command has an alternate form, eigenprobe(value,iters),
  that will do shifted inverse power iteration to find the eigenvalue
  nearest the given value. iters is the limit on the number of iterations.
  The eigenvalue is stored in last_eigenvalue, and the eigenvector
  can be used via the "move" command.
  

Back to top of Surface Evolver documentation. evolver-2.30c.dfsg/doc/news_09.htm0000644000175300017530000001060711410765113017175 0ustar hazelscthazelsct Surface Evolver Documentation - Newsletter 9

Surface Evolver Newsletter no. 9

Back to top of Surface Evolver documentation.


                      Surface Evolver Newsletter Number 9
                              September 30, 1994

                    Editor: Ken Brakke, brakke@geom.umn.edu

Version 1.96 is now available.

New features:

   Indexing can be done on element generators in commands.  That is, you
   can say "list vertex[23]" instead of "list vertex where id==23".
   The new syntax avoids searching through the entire element list for just
   one element. You can also apply indices to component elements, as
   in "foreach edge ee do print ee.vertex[1].id".  Component elements are
   accessed by linear search through a list, but the list is usually short.
   Remember indices start at 1.

   Named quantities can be composed of arbitrary functions of method instances,
   instead of just sums.  The keyword "function" indicates this.  Example:
   method_instance inst_1 method edge_length global
   method_instance inst_2 method vertex_scalar_integral global
   scalar_integrand x^2-y*z
   quantity comp energy function inst_1^2 + sin(inst_2)

   Named quantities may refer to element attributes in expressions for
   integrands. This can give more discriminating control over quantities
   on different elements, by using facet densities in integrands for
   example.  This feature is especially useful with `extra' attributes,
   which are added completely under the user's control.

   Hessian: Newton's Method can greatly accelerate convergence to a minimum,
   but users of the "hessian" command know that one has to be extremely
   close to the minimum before the command works.  This is largely due
   to motions tangential to the surface as the triangulation rearranges itself.
   There is now a toggle "hessian_normal" that constrains motion to be along
   the surface normal (volume gradient, to be precise).  Points without
   a well-defined normal (such as along triple junctions or at tetrahedral
   points) are not constrained.

   More Hessian: Some experimental stuff using the Hessian matrix can be
   accessed with the command "hessian_menu".  This brings up a menu of
   various things.  Some of the more interesting: choice E will find the
   lowest eigenvalue and corresponding eigenvector.  Useful at a saddle
   point.  After E, use choice S to move along eigenvector direction to minimum
   energy.  Choice P finds the number of eigenvalues above and below
   a value.

   The command "saddle" will seek lowest energy along the lowest eigenvalue's
   eigenvector, without the need to go into hessian_menu.

   Conjugate gradient now does the Polak-Ribiere variety of the algorithm as
   the default.  Seems to work much better than the previous Fletcher-Reeves
   variety, especially in automatically restarting itself when it gets in
   trouble.

   Users can send geomview commands through Evolver.  Syntax:
   geomview "command"
   Example:
   geomview "(transform mound mound Camera rotate 1 0 0)"
   Useful in scripts to have geomview save frames for a movie.

   The gravity-setting 'G' command now takes a numerical argument instead of 
   a repetition count.  So "G 5" sets gravity to 5 instead of producing 5
   gravity prompts.

   The name of the current datafile can be referred to in commands as
   `datafilename' wherever a string can be used.  See next item.

   sprintf, printf now accept string arguments, but a string pointer is pushed
   as 8 bytes on the calling stack to C's sprintf, so %s in format string should 
   be followed by %0.0s to dispose of the second half.  Example:  
   printf "This is %s%0.0s with total energy %f\n",datafilename,total_energy

   The print command also accepts strings. Example: print datafilename

   'Extra' attributes now inherited for same type element.  That is, edges
   formed from subdividing an edge will inherit its extra attributes.

   Added 'jiggle' toggle command, and jiggle_temperature internal variable.

   Added total_time internal variable. Settable also.


End of newsletter 9.


Back to top of Surface Evolver documentation. evolver-2.30c.dfsg/doc/intro.htm0000644000175300017530000001161311410765113017042 0ustar hazelscthazelsct Surface Evolver Documentation

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Surface Evolver Overview

General description

The Surface Evolver is an interactive program for the study of surfaces shaped by surface tension and other energies, and subject to various constraints. A surface is implemented as a simplicial complex, that is, a union of triangles. The user defines an initial surface in a datafile. The Evolver evolves the surface toward minimal energy by a gradient descent method. The aim can be to find a minimal energy surface, or to model the process of evolution by mean curvature, which was studied in [B1] for surface tension energy in the context of varifolds and geometric measure theory. The energy in the Evolver can be a combination of surface tension, gravitational energy, squared mean curvature, user-defined surface integrals, or knot energies. The Evolver can handle arbitrary topology (as seen in real soap bubble clusters), volume constraints, boundary constraints, boundary contact angles, prescribed mean curvature, crystalline integrands, gravity, and constraints expressed as surface integrals. The surface can be in an ambient space of arbitrary dimension, which can have a Riemannian metric, and the ambient space can be a quotient space under a group action. The user can interactively modify the surface to change its properties or to keep the evolution well-behaved. The Evolver was written for one and two dimensional surfaces, but it can do higher dimensional surfaces with some restrictions on the features available. Graphical output is available as screen graphics and in several file formats, including PostScript.

The Surface Evolver program is freely available and is in use by a number of researchers. Some of the applications of the Evolver so far include modelling the shape of fuel in rocket tanks in low gravity [Te], calculating areas for the Opaque Cube Problem [B4], computing capillary surfaces in cubes [MH] and in exotic containers [C], simulating grain growth [FT] [WM], studying grain boundaries pinned by inclusions, finding partitions of space more efficient than Kelvin's tetrakaidecahedra [WP] [KS1], foam rheology [KR1] [KR2], sphere eversion [FS], modelling the shape of molten solder on microcircuits [RSB], studying polymer chain packing, modelling cell membranes [MB], and classifying minimal surface singularities.

The strength of the Surface Evolver program is in the breadth of problems it handles, rather than optimal treatment of some specific problem. It is under continuing development, and users are invited to suggest new features.

This manual contains full operational details. A journal article description of the Evolver appeared in [B2].

Portability

The Evolver is written in portable C and has been run on several systems: Sun, Silicon Graphics, HP, Dec, MS-DOS, Macintosh, and various MS-Windows versions. It is meant to be easily portable to any system that has C.

Acknowledgements

The Evolver was written as part of the Geometry Supercomputing Project (later The Geometry Center, now defunct), sponsored by the National Science Foundation, the Department of Energy, Minnesota Technology, Inc., and the University of Minnesota. The program is available free of charge.


Back to top of Surface Evolver documentation. Index.
Author's home page. evolver-2.30c.dfsg/doc/news_03.htm0000644000175300017530000002777411410765113017204 0ustar hazelscthazelsct Surface Evolver Documentation - Newsletter 3

Surface Evolver Newsletter no. 3

Back to top of Surface Evolver documentation.


		      Surface Evolver Newsletter Number 3
			      April 3, 1993

                    Editor: Ken Brakke, brakke@geom.umn.edu

Contents:
  A question
  DOS 32-bit version
  Version 1.90 
  Some examples of using new language features

A question:

Is anybody out there still using Minneview?  That was the precursor
to geomview.  Any objections if I drop Minneview support from the
Evolver?

DOS 32-bit version:

I have made a DOS version of the Surface Evolver using a 
32-bit compiler and DOS extender, the DJGPP package from 
the Free Software Foundation.  It requires at least a 386
with math coprocessor, and will use any extended memory it
finds.  Also VGA graphics. It is available for anonymous ftp 
from geom.umn.edu as pub/evolver.zip.  This contains the binary, 
the extender, and a short documentation file.  Version 1.90 for
DOS may not be posted for ftp for a day or two after this newsletter
goes out, so check the date when you get it.

This is my first venture into DOS extenders, and I've tested it
on just my own machine, so there may be some glitches.  However,
it is vastly superior to any 16 bit version.  I do not intend to
support a 16-bit version any more.

Version 1.90:

Now available for ftp.

Version 1.90 new features:

Named quantities and methods.
These are an effort to provide a more systematic way of adding new
energies and constraints.  A "method" is a way of calculating a scalar
value from some particular type of element (vertex, edge, facet, body).
A "quantity" is the sum total of various methods applied to various
elements, although usually just one method is involved. Any quantities
may be declared to be one of three types: 
1) energy quantities are added to the overall energy of the surface; 
2) fixed quantities that are constrained to a fixed target value (by a 
single Newton step at each iteration); and
3) information quantities whose values are merely reported to the user.
Each quantity has a "modulus", which is just a scalar multiplier of
the whole quantity. A modulus of 0 will turn off an energy quantity.
Adding a new method involves writing C routines to calculate the value
and the gradient as a function of vertex coordinates, and adding a
structure to the method name  array in quantity.c.  Currently implemented 
methods include edge_length, facet_area, mean_curvature_integral (first 
power, not square), gauss_curvature_integral (first power),  hooke_energy 
(for edges as springs with an equilibrium length), and various knot energies.
See the Datafile chapter for details on how to set these up. The sample
datafile  knotty.fe contains some examples. Quantity values can be seen 
with the Q or A command, and the A command can be used to change the 
target value of a fixed quantity, the modulus of a quantity, or some 
parameters associated to various methods.  NOTE: named quantities and
methods are still under development, and the syntax and usage may
change as I get more experience with them.  But they are the way I
intend to add all new energies and quantity constraints to the Evolver.  
In particular, the `AREA_FIXED' feature should be regarded as obsolete,
and you should use something like
`quantity surface_area fixed = 6 modulus 1 global_method facet_area'
in the top of your datafile. This may seem a little long-winded,
but that's the price of generality.

Conjugate gradient ON/OFF state saved in dump file.
Note that conjugate gradient history vector is not saved.

Notching and "dihedral" attribute apply to vertices in the string model.

FOREACH iterator added to the command language to iterate over elements.
See examples below.

LOAD command added. Syntax: LOAD ``filename''. Useful
for starting new surfaces, especially in scripts.

PRINTF command added for formatted printing. Syntax:
      PRINTF "format string",expr,expr,...
expr is floating point, so use %f or %g formats.  Everything is passed
to the C printf functions, so you can use all its precision and
format options on %f and %g. See examples below.

String variables have been added.  Can be used where quoted strings needed.  
Can be assigned to.  SPRINTF is a version of PRINTF giving string output.
NOTE: Piping and `system' command work only with quoted strings or
string variables.

A view_transformation matrix in the datafile may be preceded by "color n" 
to give that transform a color (overrides any facet color).

In queries, element attribute ``oid'' added, which returns a 
signed version of id.  Useful if you want to use the command language
to make a special version of a datafile.  See example below.

Many knot energies added.  Also a ``hooke energy'' that keeps edges near 
a uniform length.  These were due to a Knot Workshop held at the
Geometry Center in March.  It is a popular thing these days to 
put an "electric charge" on a knot and let it repel itself into
a nice smooth curve.  Some people even evolver knotted 2-spheres
in 4-space!

PostScript output now optionally includes color.  When making a
PostScript file from the P menu, you will be asked if you want
to include color.  Including color makes the file longer, but
the files should still print on monochrome postscript devices.

On NeXT version, -t option added to command line arguments (i.e. when
starting evolver) to give terminal interface instead of menu and
dialog boxes.


Some examples of using new language features

The Evolver command language continues to grow by accretion, and it
looks like it's headed towards a full programming language.  It has
variables, expressions, subroutines, conditionals, and iteration constructs.
But not subroutine arguments, local variables, structures, or arrays.
Variables are either floating point, string, or subroutine names.
There are no integer variables, so you can use only floating point
formats in printf.  But the %g format will print whole number floats
as integers.  Remember to use at least two-letter names for variables,
as single letters tend to be interpreted as commands.

One feature different from ordinary C is the presence of iterators
over geometric elements.  These occur wherever an element type
(vertices, edges, facets, bodies, singular or plural) appears in a 
command.  Attributes of the iteration element may be used later in the
command.  The iteration element may be named (useful in nested iterations),
in which case the name must be used when the attribute is referenced.
Element types used as attributes will just generate the elements 
associated with the parent element, so if ff is a facet, then
ff.vertex will generate its three vertices.  Also, each iterator
can take a "where" clause to limit the elements included.

Here are some examples.  Since the language expects one command per
line, line splicing is needed for long commands.  Hence I tend to
break long commands up into several little named commands.
The best way to use these longer commands is to put their definitions
at the end of your datafile in a "read" section, or have them in a
separate file you can read with the "read" command.


1. A script to evolve and regularly save dumps in different files:

nn := 1
while nn < 100 do { g 100; ff := sprintf "stage%g.dmp",nn; dump ff; nn:=nn+1}


2. Your own iterate command to print out what you want, like energy
change each step:

olde := total_energy;
gg := { g; printf "Change: %10.5g\n",total_energy-olde; olde:=total_energy}


3. A command to print a file listing vertices and edges (happens to
be the OFF format for geomview):

aa:=foreach vertex vv do { printf "%f %f %f \n",x,y,z }
bb:=foreach facet ff do { printf "3 ";\
       foreach ff.vertex do printf "%g ",id-1; printf "\n"}
do_off:= {printf "OFF\n%g %g %g\n",vertex_count,facet_count,edge_count;aa;bb}

This prints to stdout, so you would use it with redirection:

Enter command: do_off | "cat > test.off"


4. A command to duplicate the fundamental region of a symmetric surface
across a plane of mirror symmetry. 

// Producing datafile surface duplicated across a mirror.
// This is set up to do constraint 1 only.
// Mirrored constraints given x numbers.
// Resulting datafile needs mirrored constraints added by hand.
// Usage: mm | "cat > filename.fe"

// Mirror plane aa*x + bb*y + cc*z = dd
aa := 1; bb := 0; cc := 0; dd := 0
// You have to set these to the proper coefficients for constraint 1
// before doing mm.

ma := foreach vertex do { printf "%g  %g %g %g ",id,x,y,z;  \
           if fixed then printf "fixed "; \
	   if on_constraint 1 then printf "constraint 1 " ;\
	   if on_constraint 2 then printf "constraint 2 " ;\
	   if on_constraint 3 then printf "constraint 3 " ;\
	   if on_constraint 4 then printf "constraint 4 " ;\
	   if on_constraint 5 then printf "constraint 5 " ;\
	   printf "\n";\
	   }
mb := { voff := vertex_count }
mc := foreach vertex do if not on_constraint 1 then { \
  	   lam := 2*(dd-aa*x-bb*y-cc*z)/(aa*aa+bb*bb+cc*cc);\
           printf "%g  %g %g %g ",id+voff,x+lam*aa,y+lam*bb,z+lam*cc;  \
           if fixed then printf "fixed "; \
	   if on_constraint 1 then printf "constraint 1x " ;\
	   if on_constraint 2 then printf "constraint 2x " ;\
	   if on_constraint 3 then printf "constraint 3x " ;\
	   if on_constraint 4 then printf "constraint 4x " ;\
	   if on_constraint 5 then printf "constraint 5x " ;\
	   printf "\n";\
	   }
md := foreach edge ee do {\
	   printf "%g   ",id; \
	   foreach ee.vertex do printf "%g ",oid; \
           if ee.fixed then printf "fixed "; \
	   if ee.on_constraint 1 then printf "constraint 1 " ;\
	   if ee.on_constraint 2 then printf "constraint 2 " ;\
	   if ee.on_constraint 3 then printf "constraint 3 " ;\
	   if ee.on_constraint 4 then printf "constraint 4 " ;\
	   if ee.on_constraint 5 then printf "constraint 5 " ;\
	   printf "\n";\
	   }
me := { eoff := edge_count }
mf := foreach edge ee do if not on_constraint 1 then {\
	   printf "%g   ",id+eoff; \
	   foreach ee.vertex do { \
	   if on_constraint 1 then { printf "%g ",oid;} \
	   else { printf "%g ", id+voff;} };   \
           if ee.fixed then printf "fixed "; \
	   if ee.on_constraint 1 then printf "constraint 1x " ;\
	   if ee.on_constraint 2 then printf "constraint 2x " ;\
	   if ee.on_constraint 3 then printf "constraint 3x " ;\
	   if ee.on_constraint 4 then printf "constraint 4x " ;\
	   if ee.on_constraint 5 then printf "constraint 5x " ;\
	   printf "\n";\
	   }
mg := foreach facet ff do {\
	   printf "%g   ",id; \
	   foreach ff.edge do printf "%g ",oid;\
           if ff.fixed then printf "fixed "; \
	   if ff.on_constraint 1 then printf "constraint 1 " ;\
	   if ff.on_constraint 2 then printf "constraint 2 " ;\
	   if ff.on_constraint 3 then printf "constraint 3 " ;\
	   if ff.on_constraint 4 then printf "constraint 4 " ;\
	   if ff.on_constraint 5 then printf "constraint 5 " ;\
	   printf "\n";\
	   }
mh := {foff := facet_count}
mi := foreach facet ff do if not on_constraint 1 then  {\
	   printf "%g   ",id+foff; \
	   foreach ff.edge do \
	     { if on_constraint 1 then { printf "%g ",id;} \
	       else printf "%g ",(oid<0?-(id+eoff):id+eoff); }; \
           if ff.fixed then printf "fixed "; \
	   if ff.on_constraint 1 then printf "constraint 1x " ;\
	   if ff.on_constraint 2 then printf "constraint 2x " ;\
	   if ff.on_constraint 3 then printf "constraint 3x " ;\
	   if ff.on_constraint 4 then printf "constraint 4x " ;\
	   if ff.on_constraint 5 then printf "constraint 5x " ;\
	   printf "\n";\
	   }
mm := { list topinfo; \
	printf "\nVertices\n"; ma;mb;mc;\
	printf "\nEdges\n"; md;me;mf;\
	printf "\nFaces\n"; mg;mh;mi;\
	}


End of Evolver Newsletter 3.


Back to top of Surface Evolver documentation. evolver-2.30c.dfsg/doc/news_04.htm0000644000175300017530000001066111410765113017170 0ustar hazelscthazelsct Surface Evolver Documentation - Newsletter 4

Surface Evolver Newsletter no. 4

Back to top of Surface Evolver documentation.


		      Surface Evolver Newsletter Number 4
			      May 28, 1993

                    Editor: Ken Brakke, brakke@geom.umn.edu

Contents:

New features
Bibliography

Version 1.91 now available. New features:

      The two sides of a facets can have different colors. 'COLOR'
      applies to both sides, 'FRONTCOLOR' and 'BACKCOLOR'
      to different sides, according to the orientation inherited
      from the datafile. Example:
	"set facet backcolor red"

      Attributes of individual named elements can be set inside
      loops, i.e. "foreach facet ff do set ff color red"
      does the same as "set facet color red".

      To reduce need for explicit line-splicing on long commands,
      the parser now keeps track of depth of brace and parenthesis
      nesting, and will call for more input if a line ends inside nest.
      So if you want to type a multiline command, start with '{'
      and end with '}' many lines later.  Also does auto line-splicing 
      if certain tokens are last token in line (such as '+'), but it
      is wiser to enclose with braces rather than count on this.  

      Queries can run through edges and facets adjacent to
      a vertex, and facets of a body, as in "list vertices vv
      where max(vv.facet,color==red) > 0".

      New attributes: Valence attribute has been extended to vertices
      and bodies.  Vertex valence is the number of edges on
      a vertex.  Body valence is the number of facets on the
      body (edges in the string model). Example:
      "list vertices where valence == 4"

      Surface area can be minimized by minimizing Dirichlet integral,
      according to a scheme of Polthier and Pinkall.  Command 'dirichlet'.
      Not a replacement for other evolutions.

      Using a command from the history list now echoes it.

      Improved NeXT terminal interface.  -u option for no graphics,
      -t for terminal and graphics.

      Command assignment fixed to assign only one command.
      so "ggg := g; g" will be the same as "{ggg := g}; g"
      and not "ggg := {g;g}".

      Every time a command changed a global variable, the surface
      was being recalculated.  This slowed down scripts immensely.
      So now the only variables that cause recalculation are
      1) adjustable parameters defined in the datafile
      2) quantity moduli and parameters
      You can turn off even this with "autorecalc off".

      "view_4d" command to toggle sending full 4D coordinates
      to geomview.  Default is OFF.


Bibliography:

      Alice Underwood, "Constructing barriers to minimal surfaces from
      polyhedral data," Ph.D. thesis, Princeton, 1993.

         When the Evolver reaches a minimum area for a given triangulation
      of a surface, can you guarantee that there is a smooth minimal
      surface nearby?  This thesis proves some sufficient conditions.
      You have to be able to "blow" on the surface and still have it
      stable.  The triangulation must be well-behaved.  Unfortunately,
      the estimates are far from the best possible (I hope!), and one
      needs on the order of 10^7 to apply the proof.

      Ken Brakke, "Stability of a toroidal bubble in a rotating tank."

	 This is the final report of an Evolver project I did for Lockheed.
      A tank of liquid helium is rotating in weightlessness.  The tank
      has a rod along the axis of rotation.  The tank is partially full,
      and the bubble is supposed to form an axisymmetric torus so the center
      of mass is on the axis.  The questions are: what is the rotation rate
      needed to force a spherical bubble to form a torus, and what is the
      minimum rotation rate at which the torus is stable against breaking 
      up into a sphere?  This is not an official publication of anybody,
      but copies are available to anybody interested.

      Hans Mittelmann, "Symmetric Capillary Surfaces in a Cube",
      Math. Comp. Simul. 35 (1993), 139-152.  (reference update)


End of Evolver Newsletter 4


Back to top of Surface Evolver documentation. evolver-2.30c.dfsg/doc/default.html0000644000175300017530000000566311410765113017517 0ustar hazelscthazelsct Surface Evolver Documentation

The Surface Evolver

Kenneth A. Brakke
Mathematics Department
Susquehanna University
Selinsgrove, PA, 17870
brakke@susqu.edu

with past support from
The Geometry Center
University of Minnesota
Minneapolis, MN 55454

Version 2.26, August 20, 2005


Contents

This HTML version of the Evolver documentation contains most of what the printed manual has, with the main exception of sections with a lot of mathematical formulas, such as the more advanced examples and the Techical Reference chapter. Not all possible links are in yet, but you can consult the Index to find something.

Web home page

My Web home page is http://www.susqu.edu/brakke/. Evolver-related material and links will be posted there. In particular, there are many examples of surfaces.
Back to top of Surface Evolver documentation.

Author's home page. evolver-2.30c.dfsg/doc/square-e6.gif0000644000175300017530000006015611410765113017502 0ustar hazelscthazelsctGIF89ad   ###''('(''(((''('(,,,//0/0//000//0/000/444778787788877878887;;;??@?@??@@@??@?@@@?CCCGGHGHGGHHHGGHGHHHGKKKOOPOPOOPPPOOPOPPPOTTTWWXWXWWXXXWXXXW\\\__`_`__```__`_```_cccgghghgghhhgghghhhgkkkoopopoopppoopopppossswwxwxwwxxxwwxwxxxw{{{!!B Image generated by AFPL Ghostscript BETA RELEASE (device=ppmraw) ,dWZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZj*U1 ?4ZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZj*VZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjU(<9r*SR切#,fhjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZi9ԪUVZjժUVZjժUVZjժUVZjUVZjժUVZjժUVZjժUVZjժUVZ*,!剅RNJJU)OxP=ZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVa#G1TREJ 9XHhժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVt&*HJ TRU**R8S"T(@ÒMVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժU@rB*UTʑ/9X,iժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVIRCEBTJJ'`T"TEE e*UTENhQQŁ#TRJ f\iժUVZjժUVZjժUVZjժUVZʓ,3<\82 >}qĉ'38pK)RJ"UTRHRC)RH Eԧj>jժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZj"T*ETRJC!'HIRRJ,ET)Lv8AN#@NHrERUVZjժUVZT#I$-c)*T5H"E)RpV8ydժUVZj*VZjժUVZjժUVZjժUVZjժUVZjժUp")R"E)HC)RD)RH"E,U&L$I)Ԑ*IŘPB *T(RDQ''4 %*TPB   OHDE$Ir$)J,aT%K*Y 5$x"E)Rfx"EJ)R<͠jH"ETVZjժUVZjժUVZjժUVZjժUVZjժUVZjU)Ix@RH8D)RH}d PD"EH"E)RmpR S%L0UTDI$5$O*TPB $RJ*F×PB *T(O"E 4IH$FUdRJ*U/>JATH"% Ȣj>-T)RZjժUVZjժUVZjժUVZjժUVs)RH"E'H88H:Î>@}爧*|H"-HJAT*YDI!R%L0 I#I$I$I$I\$ĉ'8yӧ @qT(RBACI4H$I$K$UhѢ#!a„#$IHE$IDCEB"D!B"D!ByC#II$I$I$-RRJ(ṠeѡE-Jg$5^$I$ɒ$I$I`'QBETO@JjժUVZjժUVZjժUVZjժUVZ $RH"d )RHړ"O@ 'PG TPH"E*T(IG P#IR%K,U BH҅E-a J0H#I$I <y'PH*E $YTɒJjHТE-s JNH$I$I.TIR*A"D!B"D!B9H$I$Ij<đ$I"$Q"9-ZhѢEJHE.H$I$I$}qR)%Or ET(O9JZjժUVZjժUVZjժUVZjժUVY!,xɓ y$IFRm|ꓧ?xzUVZjժUVZjժUVZjժUVZjժU/.Hɓ'O<*S'O\,JO@y*D"p Oy'O<"j<} L.$R IĢE=ZhѢEf"I$K$Y8I$IyC!B"T$I$I$I$I$IB),:hѢE-Zh(Iz$ #/j$Y$iQJ0a$“'O-ƒȑ^<ȪUVZjժUVZjժUVZjժUVZjժ^ ԧO>y QJxɓ'Of`$K*$I$I3@Z"I-jUIdI$I&I'!B"DI$I$I$I$Iė8%)ТE\hѢE.8z$iG$q"I$I8TT&<} 'O$8H<{$IS(JZjժUVZjժUVZjժUVZjժUVeI'OjhqJ$M$I$I@"D!BAH$i$I$IÑ8|B!/,ZhѢE9H"I$-D$I&Lɓ'OxzDgI$I!B I$I$MTÇ1|!B"˜E-Zt("I$=$)H$$IP%L*a (O"DhI$Qԇ#rÇO'A"D!BrxhshѢ1R$=HҢEIR!IR0UJ?}sC>(Ir9,$I$jç>}xCOB}"Dxy H"I$EI I<|Ӈ>jTxrH@jy#GӇO>|飦!B"D!Bᐂ$I$=(HTHRJ0U„ O}Ӈ*@j铇O<}铇ό1"D!B"DJ)T# `HIZυE*aT S%LHiOH"E*ŨP< 5 O9xPeժUVZjժUVZjժUVZjժUVZ'9ȑȓ&O<85B *T(BIxSJ0a„I$I$I"D!B"B <}ᓧO>}Q@GӇ>}'>}0"DOB}Ӈ |!thL*~yHU*U„&L*US H")F´PP2>Y*VZjժUVZjժUVZjժUVZjժUpD'O|Ç>GTÇO>}ӧO>}9!?C!BxR9"ɡC:H3TRJ0UT )R")RH}i*T(O!SYjժUVZjժUVZjժUVZjժUVZ@'O@*8YtP%"D!B O>|ç>|>}'O>|'B}"D!By0φ/9:t"E!“…8SJ0ɐц)RH"EJPHԁ.yÇFBj>ç<|ӧ>jX"DB">nbqC!Bt!0m$)Θ#,X`@RH"5(RR jTNS <DZjժUVZjժUVZjժUVZjժUVaɓ'L|Ç O<}çO>yaǏB"D}aQC!ZtСCQ &L0a„I1-Z萓/:!B"J<}'O>y>!D!rçO>y#!B"Gg!B:ТCJF$L1m´I&L0(㤔$'*hEJ=_B *TNxIVZjժUVZjժUVZjժUVZjժUzɓ'C,ᓧ<}B!B>|ᓧ!H"!Bm$ӧO'N!ʁСC!B(PHG`„ &L0ajsSRJ¤F1Hʓ')T8pA'O9Yj*VZjժUVZjժUVZjժUVZ $O<0ɓ'O}ç>"P|çO>}\!ByyO>}CȐC:tСCND'L0a„ I)UTRJ!A`*UU.x9ĪUVZjժUVZjժUVZjժUVZjժ NED!BiPç>|3&!B"D>}'!B<:tСC)$0a„&L\x$IERJ:UT9)UTR̘IM BZjժUVZjժUVZjժUVZjժUV9I:}(%d ,iGҧfTTR\&OThѢEHT >}A !By>|@!B"D(υ<}Ӈm"4O#}ӧO#U!"ѡC:thH$؄ &L0I*&O.4*UTR<RuCOJ*UʃBdժUVZjժUVZjժUVZjժUVZT8NxI% RH"UO/Nh@TRJ**E Bt4"8ӧO>}x@D_:"D)IZ$&L}!BÇO"!B"ta >yPB^$ӧO@}3!D9t"DH$ x0a„ !T%RJ”CN|F!!BC!<ӇBЧO>C9"&ˢC:C $Iy &OJɓ'O)E*URJUԑAxDG4tĪUVZjժUV\jժUVZjժUVZjժU%| (P>*SP%J"UT)Rjh"UR TT)JɤSCjrZCӧ?}т}QSPB"D!B"}ӧO>} BD!09!:h $Iz$iL4ajsē'O}!Bɱ!D8hE<`„ S Iӣ> RJRCRJ"U)UHUQA990usD'-ʁ"7@ ԧO> B >Xr"D!B "D!BGM/ӧO>}ӧ>BЌCZ"I$-&L[xɓ'Oqh GÑ*m‰*| EVZjժUVZjժUVZjժUVZjժUSOJ,*PJ*EJ RH*UT;TT*Eg&$yXthQ1!'P>ӧ@C!B"D!B"DH)JӧO>}ӧOxR2D!B :t-IR0E*ɓ'OA)R*BITR*E8@RO_fjժUVZjժUVZjժUVZjժUVZ O>RS(P@}% )54J*UTJ}xN$ EƌAthQ1'PA !B"D!<^x"Ƌ/^.8ҧO>}ӧOB}B!Dq"D9,HE$'OQI R*J@O,<}Q*U }ӧOB}!B!"DPC!H#I"I <ř)34HU*UTJ#,ڐ"FR>R—0ZjժUVZjժUVZ UVZjժUVC(P%*(PB 'q"ET-iHQ@J*UTĢ )I,HqH69:DC> O>x. =x}ӧO10"D!B :tȉI$-$)E,XS'R#'UTRJUITRE*4TJM<֤cժUVZjժUVZjժUVZjժUVZjU1T (P(qARH"ERg"ԢTR OT#F*UϡCN'P?} }C!B!"4СC$=HңE"Iq OJR‰ 9rTQI*UHy J*UH@ωET\ *VZjժUVZjժUVZjժUVZjժUlT"ԧPxv)E)RHA)RTQ >TRJ JTRT*)ѧO>}S !BG}C"xH#I$I$IH"EJ́^N*J)r Uɑ PӇLB=PjժUVZjժUVZjժUVZjժUVZ5 @ }ӧO'B!Brr(I$-h %A#4*URJ*8SO>P%4` UVZjժUVZjժUVZjժUVZjժU.*T(P@T"On"ET%H"Eԧ1*\p"G*}JPU8"ԩRO>QJ"DU<{<3 DP@JӧO>.!d!B "KIZ$\<]ɓ$.@TRJE@8˧O JM  PЌOx}B!B "DP/Ax$i"xt'LԩRB‚1H"&L9 S(L3m 7_fcժUVZjժUVZjժUVZjժUWZj*b@} Pxv'B4Hp33diP>G@ (P@D'AlPRJ"@B"P*yNhQRRC!B\O<{< "D(P B"DH͆1}<"D"B"D |Tɓ'Lr'hШ҇)RH"E)RcX`„ &I0 *ETi)}i='O)ŧOB'5B0a„)OH"ET8NT@>" 0}I(PJTYjժUVZjժUVZjժUVZjժUVӧP2` )ԧA3`*GO>}S E@ysӧO>} UB&L0a„I 'H"E)RH %*}:r'(PxH9IO3*}ꓧO^| >}#0a„ &L0T)RH‰'RHyC|X ԧ>%0PsēVZjժUVZjժUVZjժUVZjժU<PO\p"ɓ'OG>}U &L0a„ &L0a„ H"E)RDEJU < C!BQM>} F$I$-H#3A$Q)RH"e )RHEJ-)JQ1cXTK%! &L0R(P@yqSYjժUVZjժUVZjժUVZjժUV'O~ Ӈ1hh"I9zr'RJ"U:4Hä'x*UdR%K(y hѢ_*Ud%K,Y%,ȑ#%}<,ZD(1jЌQ>}ӧ)R2C<&U$ɒ%K*IJE-$K(UdRJ*UZ@8qyD?J`jժUVZjժUVZjժUVZjժUVZU@M6y @OmTRJxAI,YT%K*UdR 5ji!,YdJ,UdR*qXx&OG.AUVZjժUVZjժUVZjժUVZjժU=Xxɓ %0a„ &LcJ,d%KXhѢE-ѢE.TI%KH$I$I$шI'.q0*TO\&0a$L0m´ &L0a„ K}T,ZhѢE-ZhѢE-Z$I $I$I$I$I$OJxI҅C2ZjժUVZjժUVZjժUVZjժUV9ɓ&O  ' رH @~ Rؑ#&N'L0a„ &LI L> *ԧPB} Ӣc}:R'E.<:hѢCr|!4 !B"D!B"P9"I#I{P$I$I&O0a"ªUVZjժUVZjժUVZjժUVZj*9-ZhѡEZthѢC=ˢGIZH#I$-GqТC-:ТCyC!BP!B"D.9"D!B,H$i$I$I$$B D)RYjժUVZjժUVZjժUWZjժUVZ @69H#I$=$GIr$<-ZhѢEr!!B"DRDP"D!B RE$-$I .4yIҌ1HcժUVZjժUVZjժUVZjժUVZj1H"E)REI$I$I$I$Iz&!B"D!5)%"D(!BC!D!:DHMCBhѢC9HZ$iF$Q!iEII$-HҢG(YТE@ !DB!B !D!Drx!D!B"D!|$I$I$IUxɓ'O"D!B"D!BH"I$5HR#I$A "D!D"D!BU !B"D!B"D!BjTɓ8mĉ'N8qrHVZjժUVZjժUVZjժUVZjժU< EjO A body attribute used internally by Hessian routines. <-------------------- __vhead_index ----------------------------> A vertex attribute used internally by Hessian routines. <-------------------- A ----------------------------> Single letter main command. Lists the current values of variables and named quantity values, moduli, and targets. Only explicitly user-defined named quantities are shown unless show_all_quantities is toggled on. You are allowed you to enter new values (except quantity values). The new value is entered as the number of the variable (from the list) and the new value. Exit by hitting RETURN on a blank line. All changes that can be made here can also be made with assignment commands. <-------------------- a ----------------------------> Single letter main command. Toggles area normalization of vertex forces and other gradients, to model motion by mean curvature. Meant to be used with a fixed scale factor. Be sure you have a small enough scale factor or else things tend to blow up. Reduce the scale factor temporarily after refinement, since triangle areas are cut by a factor of 4 but the old creases remain. When this option is ON, there is an optional check that can be made for facets that move too much. This is done by computing the ratio of the length of the normal change to the length of the old normal. If this exceeds the user-specified value, then all vertices are restored to their previous position. The user should reduce the motion scale factor and iterate again. <-------------------- abort ----------------------------> abort Main prompt command. Causes immediate termination of the executing command and returns to the command prompt. Meant for stopping execution of a command when an error condition is found. There will be an error message output, giving the file and line number where the abort occurred, but it is still wise to have a script or procedure or function print an error message using errprintf before doing the abort command, so the user knows why. If used at a subcommand prompt, the abort command will return to the prompt on the same subcommand level. <-------------------- abs ----------------------------> abs(x) : Absolute value. <-------------------- ackerman ----------------------------> Named method. Description: Not actually an energy, but a kludge to put inertia on vertices. Uses extra velocity coordinates to represent vertex in phase space. Invocation actually transfers computed forces from space coordinates to velocity coordinates, so forces become acceleration instead of velocity. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: quantity jeremy energy method ackerman global <-------------------- acos ----------------------------> acos(x),asin(x),atan(x): Inverse trig functions (acos, asin arguments clamped to [-1,1]). Results in radians. <-------------------- acosh ----------------------------> asinh(x),acosh(x),atanh(x): Inverse hyperbolic functions. <-------------------- actual_volume ----------------------------> Body datafile attribute. Actual_volume is a number that can be specified in the datafile definition of a body in the rare circumstances where the torus volume volconst calculation gives the wrong answer; volconst will be adjusted to give this volume of the body. <-------------------- addload ----------------------------> addload Main prompt command. Loads a new datafile without deleting the current surface, permitting the simultaneous loading of multiple copies of the same datafile or different datafiles. Syntax: ADDLOAD string where string is either a sting literal in double quotes, or a string variable name such as datafilename. Elements in the new datafile are re-numbered to not conflict with existing elements. This is actually the same as the default behavior of Evolver when loading a single datafile. Thus the -i command line option or the keep_originals keyword is not obeyed for the new datafile. The read section of the new datafile is not executed; this permits a datafile to use the addload commnand in its read section to load more copies of itself. The loading script is responsible for all initialization that would ordinarily be done in the read section of the new datafile. Declarations in the top section of the new datafile will overwrite any existing declarations. This is usually not a problem when loading multiple copies of the same datafile, but requires attention when loading different datafiles. For example, numbered constraints are a bad idea; use named constraints instead. See the sample datafile addload_example.fe for an example of how to load and distinguish between multiple copies of the same surface. <-------------------- aggregate functions ----------------------------> <-------------------- avg ----------------------------> <-------------------- sum ----------------------------> <-------------------- min ----------------------------> <-------------------- max ----------------------------> <-------------------- count ----------------------------> Aggregate functions give a result from a specified set of geometric elements. The maximum, minimum, sum, or average of an expression over a set of elements may be done with aggregate functions. The syntax is aggregate(generator,expr) where aggregate is max, min, sum, or avg, and generator is an element generator. Example: this prints the total area of all green facets: print sum(facet where color == green, area) "Count" is an aggregate function that gives the number of elements generated, regardless of the expression. <-------------------- aggregate commands ---------------------------> Certain commands (fix, unfix, set, unset, list, delete, dissolve, refine, edgeswap, foreach, vertex_average, equiangulate, pop, t1_edgeswap, reverse_orientation, etc.) act on sets of elements. By default, the set is all elements of the appropriate type, but the set may be narrowed by use of a "where" clause. Examples: list vertices delete edges where length < 0.001 set edge[23] color red set vertex[2].facets color green <---------------- MPI aggregate commands --------(MPI Evolver)------> When MPI Evolver executes an aggregate command (fix, unfix, set, unset, histogram, list, delete, dissolve, refine, edgeswap, foreach, vertex_average, equiangulate, pop, t1_edgeswap, reverse_orientation, etc.) or aggregate function (sum, min, max, avg, count) from the main prompt, the set of elements used in each task includes only elements belonging to that task, not imported elements. However, when used in a parallel_exec or task_exec command, an aggregate will include all elements, native and imported. <-------------------- alice ----------------------------> A keyword for a special purpose command not documented. <-------------------- ambient_pressure ----------------------------> Toggles ideal gas mode with ambient pressure outside bodies. The external pressure can be set with the pressure phrase in the top of the datafile, or at runtime with the command "p 10" for example, to set the external pressure to 10. <-------------------- ambient_pressure_value ----------------------------> Internal read-write variable. Value of the external pressure in the ideal gas model. <-------------------- boolean operators ----------------------------> <-------------------- and ----------------------------> <-------------------- or ----------------------------> <-------------------- not ----------------------------> <-------------------- greater than ----------------------------> <-------------------- less than ----------------------------> <-------------------- equal ----------------------------> <-------------------- not equal ----------------------------> Boolean operator. Boolean expressions are a subclass of arithmetic expressions, with zero as false and nonzero as true; true results evaluate as 1. The Boolean operators available are: ==,>,<,>=,<=,!= Numerical comparison. NOT, ! Boolean NOT. AND, && Boolean AND, with short-circuit evaluation. OR, || Boolean OR, with short-circuit evaluation. <-------------------- approx_curv ----------------------------> Evolver toggle command. Uses polyhedral curvature (linear interpolation over facets for metric) for mean curvature vector. Actually establishes the inner product for forms or vectors to be integration over facets of euclidean inner products of linear interpolation of values at vertices. Synonyms: APPROX_CURV, APPROX_CURVATURE. <-------------------- approx_curvature ----------------------------> Evolver toggle command. Uses polyhedral curvature (linear interpolation over facets for metric) for mean curvature vector. Actually establishes the inner product for forms or vectors to be integration over facets of euclidean inner products of linear interpolation of values at vertices. Synonyms: APPROX_CURV, APPROX_CURVATURE. <-------------------- approximate_curvature ----------------------------> volver toggle command. Uses polyhedral curvature (linear interpolation over facets for metric) for mean curvature vector. Actually establishes the inner product for forms or vectors to be integration over facets of euclidean inner products of linear interpolation of values at vertices. Synonyms: APPROX_CURV, APPROX_CURVATURE. <-------------------- area ----------------------------> As a keyword, "area" is a read-only attribute of facets. Examples: print facet[4].area histogram(facet where color == blue, area) Area is normally the Euclidean area as calculated from vertex coordinates (using Gaussian integration in quadratic and lagrange mode), but it is be altered when there is a metric or an area_method_name. <-------------------- area_normalization ----------------------------> Toggle for a type of surface mobility. In motion by mean curvature, the resistance to motion is really due to the surfaces, not the vertices. One way to approximate this is to say the resistance to motion of a vertex is proportional to the area associated with the vertex. So this mode counts the area of a vertex as equal to 1/3 of the area of the star of facets around it (or 1/2 the length of the star of edges in the string model). This is easy to calculate, since it is a local calculation for each vertex. The result is that velocity = force/area, which is a much better approximation to motion by mean curvature than the default of velocity = force. This mode is not restricted to motion by mean curvature. Area normalization can be toggled with the "a" command or the "area_normalizaton" toggle. <-------------------- area_fixed ----------------------------> <-------------------- fixed_area -------------------------> An obsolete way of declaring the total area of the surface fixed in the datafile. Synonym: fixed_area. The preferred way of doing this now is to define a fixed named quantity in the datafile, such as quantity the_area fixed = 2.3 global_method facet_area This permits all the named quantity features to be used. <-------------------- area_method_name ----------------------------> This top-of-datafile item, area_method_name, specifies the name of the pre-defined method to be used as the method to compute facet areas in place of the default edge_area method in the string model or facet_area method in the soapfilm model. It is optional. Developed so circular arcs can be used in two-dimensional foams. Current reasonable methods are circular_arc_area and spherical_arc_area. Synonymous with volume_method_name in the string model. Usage implies converting to everything_quantities mode. Datafile syntax: area_method_name quoted_method_name For example, string space_dimension 2 area_method_name "circular_arc_area" length_method_name "circular_arc_length" <-------------------- area_normalization ----------------------------> Evolver toggle command. Converts the force on vertex to a mean curvature velocity vector by dividing the force by the area associated to the vertex, which is one-third of the area of its adjacent vertices. The string model divides by one-half of the sum of the lengths of adjacent edges. Useful in doing grain growth simulations. <-------------------- area_square ----------------------------> Named method. Description: Energy of a facet is the square of the facet area. Element: facet. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: quantity asquare energy method area_square global <-------------------- areaweed ----------------------------> Main prompt command. For deleting facets with less than a given area. Syntax: AREAWEED expr Same as 'w' command, except does not need interactive response. Also same as "delete facets where area < expr". Examples: areaweed 0.001 areaweed 2*min(facet,area) <-------------------- arrays ----------------------------> It is possible to define multidimensional arrays of integers or reals with the syntax DEFINE variablename REAL|INTEGER [expr]... This syntax works both in the datafile header and at the command prompt. If the array already exists, it will be resized, with old elements kept as far as possible. Do not resize with a different number of dimensions. Note that array indexing starts at 1. A size of 0 is legal, and useful if you are not using an array at the moment and want to free storage space. There is runtime checking of array bounds. Example: define fvalues integer[10][4] define basecoord real[10][space_dimension] Identifier names declared local can be used in array declarations in procedures and functions; dynamic sizes can be used, but static sizes may be a bit faster since memory doesn't need to be dynamically allocated. In the top of the datafile, arrays may be initialized with nested bracket initializers following the definition. For example: define qwerty integer[4] = { 23, 45, 12, 2 } define vmat real[3][2] = {{1,2},{3,4},{5,6}} Initializers need not be complete; missing values will be zero. Array elements may be used and assigned as is usual for programming languages: fvalues[3][4] := 234; if basecoord[3][1] > 2 then print "hello world!\n"; The print command may be used to print whole arrays or array slices in bracketed form. Example: print fvalues print fvalues[4] There are some basic whole-array operations that permit arrays on the left side of an assignment statement: array := scalar array := array array := scalar * array array := array + array array := array - array array := array * array Here "array" on both sides of the assignment means a single whole array; not an array-producing expression or array slice. But "scalar" can be any expression that evaluates to a single value. For multiplication, the arrays must be two-dimensional with properly matching sizes. These operations also apply to element attributes that are arrays. There is also a matrix_inverse procedure. <-------------------- arrayslice ----------------------------> A special syntax for the "print" command lets one print various dimensioned slices of multi-dimensional arrays. Syntax: PRINT arrayslice The arrayslice option takes an array name or a partially indexed array name. If more than one element results, the slice is printed in nested curly braces. The arrayslice can also be that of an array attribute of an element. Examples: define parts real[3][2][3]; print parts; print parts[3][2]; <-------------------- arrow keys ----------------------------> Graphics mode command keys. Move image in appropriate direction. May be prefixed by a real number, which is multiple of thirds of screen width to move. Default move is 1/12 screen width. May not work on all terminals. Arrow keys alone may work in the graphics window, again depending on the system. <-------------------- asin ----------------------------> acos(x),asin(x),atan(x): Inverse trig functions (acos, asin arguments clamped to [-1,1]). Result in radians. <-------------------- asinh ----------------------------> asinh(x),acosh(x),atanh(x): Inverse hyperbolic functions. <-------------------- assignment ----------------------------> The assignment operator := can be used to assign numeric and non-numeric values to various entities. Note that ':=' is used for assignment, not '='. The C-style arithmetic assignments +=, -=, *=, and /= work for numerical variables. For example, val += 2 is equivalent to val := val + 2. These also work in other assignment situations where I thought they made sense, such as attribute assignment. Possible assignments: > User-defined commands, Ex: gogo := {g 100; r; g 100} > User-defined variables, Ex: foo := 2.3 > Writable internal variables, Ex: scale := 0.1 > Named quantity modulus, target, and volconst. Syntax: quantityname.modulus := expr quantityname.target := expr quantityname.volconst := expr > Method instance modulus. Syntax: instancename.modulus := expr <-------------------- assume_oriented ----------------------------> Evolver toggle command. Tells squared mean curvature routines that they can assume the surface is locally consistently oriented. Significant only for extreme shapes. <-------------------- atan ----------------------------> acos(x),asin(x),atan(x): Inverse trig functions (acos, asin arguments clamped to [-1,1]). <-------------------- atan2 ----------------------------> atan2(y,x): Inverse tangent, range -pi to pi. <-------------------- atanh ----------------------------> asinh(x),acosh(x),atanh(x): Inverse hyperbolic functions. <-------------------- atomic values ----------------------------> The following evaluate to single numbers in expressions: number: An integer, real, hexadecimal, or binary constant. Hex numbers begin with 0x; binary numbers have a trailing 'b'. pi: Mathematical constant, ratio of circle circumference to radius. x1,x2,...; x,y,z,w : Depending on context, space coordinates, vertex coordinates, edge vector components, or facet normal components. p1,p2,...: Parameters of vertices on boundaries. G : Current gravitational constant for calculating gravitational energy. User-defined variables : Arbitrary variables defined in the datafile or at runtime. Indexed array variables. Internal variables : Special pre-defined variables. Element attributes : Things like vertex coordinates, edge length facet area, colors. Array attributes must be fully indexed. Named quantity attributes : Including modulus, target, value, pressure. Method instance attributes : Including modulus, value. Toggle name : Any toggle command name may be used as a Boolean variable in an expression (full word toggles, not single letters). But beware the ambiguity in interpreting a toggle as a command or a value. You may have to use parentheses to force the toggle to be interpreted as a value. Thus "ad := autodisplay" sets ad as a command synonym for autodisplay, while "ad := (autodisplay)" records the current boolean value. <-------------------- attribute ----------------------------> As a keyword, "attribute" is used in the define command for element extra attributes, for example, define facet attribute myattrname real define vertex attribute oldx real[3] This syntax works both in the top of the datafile and at runtime. For a list of pre-defined attributes, see "attributes". <-------------------- attributes ----------------------------> The surface is defined in terms of its geometric elements of each dimension. Each element has its own set of attributes. Some may be set by the user; others are set internally but may be queried by the user. It is also possible to dynamically define extra attributes for any type of element, which may be single values or vectors of values. Attribute values can be specified in the datafile, and queried with commands. The following attributes are pre-defined for all types of elements: ID: Element identifying number OID: Oriented element identifying number ORIGINAL: Number of parent datafile element ON_QUANTITY quantityname: True if the given named quantity applies to the element. ON_METHOD_INSTANCE instancename: True if the given method instance applies to the element. For other attributes, see specific types of elements: vertices, edges, facets, bodies, facet-edges. A list of the attributes stored within element data structures can be printed with the "list attributes" command. <-------------------- augmented_hessian ----------------------------> Evolver toggle command. Solves constrained Hessians by putting the body and quantity constraint gradients in an augmented matrix with the Hessian, and using sparse matrix techniques to factor. Vastly speeds things up when there are thousands of sparse constraints, as in a foam. The default state is unset (prints as a value of -1), in which case augmentation is used for 50 or more constraints, but not for less. <-------------------- autochop ----------------------------> Evolver toggle command. Do automatic refining of long edges each iteration. Use "autochop_length := expr" to set autochop length. Each iteration, any edge that is projected to become longer than the cutoff is bisected. If any bisections are done, the motion calculation is redone. The autochop length may be accessed by the read/write internal variable autochop_length; but note that simply assigning a value to autochop_length does not toggle autochop on. <-------------------- autochop_length ----------------------------> Evolver internal read-write variable that autochop mode uses as the critical length; longer edges will be refined. <-------------------- autodisplay ----------------------------> Evolver toggle command. Toggles automatic redrawing of graphics whenever the surface changes. Default ON. Same function as the 'D' command. <-------------------- autopop ----------------------------> Evolver toggle command. Toggles automatic deletion of short edges and popping of improper vertices each iteration. Before each iteration, any edge projected to shorten to under the critical length is deleted by identifying its endpoints. The critical length is calculated as L = sqrt(2*dt), where dt is the time step or scale factor. Hence this should be used only with a fixed scale, not optimizing scale factor. The critical length is chosen so that instabilities do not arise in motion by mean curvature in the string model. If any edges are deleted, then vertices are examined for improper vertices as in the 'o' command. Useful in the string model. Autopop is also implemented for small facets as of Evolver version 2.30. The critical area is calculated as sqrt(2*dt)*perimeter/2, where perimeter is the sum of the lengths of the three sides of the facet. See also the immediate_autopop and autopop_quartic toggles. <-------------------- autopop_quartic ----------------------------> Toggle. Modifies the autopop mode. The critical length for edges is set to 2*sqrt(sqrt(dt)) and the critical area for facets is set to 2*sqrt(sqrt(dt))*perimeter/2; meant for quantities such as laplacian_mean_curvature where velocity is proportional to fourth derivative of surface. <-------------------- autorecalc ----------------------------> Evolver toggle command. Toggles automatic recalculation of the surface whenever adjustable parameters or energy quantity moduli are changed. Default is OFF. Alternatively, the user can use the "recalc" command to force recalculation if the default recalculations are not timely. <-------------------- average_crossings ----------------------------> Named method. Description: To calculate the average crossing number in all projections of a knot. (by John Sullivan) Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: quantity across energy method average_crossings global <-------------------- axial_point ----------------------------> Vertex read-write attribute. Certain symmetry groups (e.g. cubocta or rotate) have axes of rotation that are invariant under some non-identity group element. A vertex on such an axis must be labeled in the datafile with the attribute axial_point, since these vertices pose special problems for the wrap algorithms. If you are only using a subgroup of the full group, then you only need to label vertices on the axes of the subgroup. The net wrap around a facet containing an axial point need not be the identity. Edges out of an axial point must have the axial point at their tail, and must have zero wrap. Facets including an axial point must have the axial point at the tail of the first edge in the facet. It is your responsibility to use constraints to guarantee the vertex remains on the axis. <-------------------- b ----------------------------> Single letter main command. Permits user to interactively change body prescribed volumes or pressures, depending on which is prescribed. Prints old value for each body and prompts for new. Graphics mode command. Toggles display of bounding box. Useful for visualizing orientation. In the native graphics window, the 'o' key does the same thing. <-------------------- B ----------------------------> Graphics mode command. Toggles display of facets on boundaries or equality level-set constraints (as opposed to one-sided constraints). <-------------------- backbody ----------------------------> Facet read-write attribute. The id of the body of which the facet is on the negatively oriented boundary. As a read attribute, the value is 0 if there is no such body. As a write attribute, it can be used to assign facets to bodies. Examples: newb := new_body; set facet[1] frontbody newb; set facet backbody newb where id == 2 or id == 4; print facet[4].backbody Backbody also works for adding edges to a facet in the string model, but the added edge must be attach to one end of the edge arc, or close the arc. See also "frontbody". <-------------------- backcolor ----------------------------> Facet read-write attribute. Color of negative side of facet for graphics. Default is white. Set also when the "color" attribute is set. Datafile example: Faces 1 1 2 3 frontcolor green backcolor red Command examples: list facets where backcolor == red set facet[3] backcolor green set facet backcolor red where area > 2 If frontcolor and backcolor of a facet are different, the native graphics display may draw the facet as a double layer. The "thickness" variable controls the distance between layers. If thickness is too small, the frontcolor and backcolor may both show in a confused manner. <-------------------- backcull ----------------------------> Evolver toggle command. Prevents display of facets with normal away from viewer. May have different effects in different graphics displays. For example, to see the inside back of a body only, "set frontcolor clear" alone works in 2D displays, but needs backcull also for direct 3D. <-------------------- background ----------------------------> Internal read-write variable. Background color used in certain screen graphics (xgraph and Windows, at the moment). <-------------------- bare ----------------------------> As a keyword, "bare" is a vertex attribute or an edge attribute set by the user that tells the Evolver not to expect the vertex or edge to be attached to a facet. Useful in a datafile to prevent a lot of warning messages. <-------------------- bare edge ----------------------------> Edge read-write attribute. Declaring an edge "bare" indicates that an edge does not have an adjacent facet (soapfilm model). Best declared in the datafile, by adding the keyword bare to the line defining an edge. Useful in avoiding warning messages. Bare edges are useful to show wires, frameworks, outlines, etc. in graphics. Example: list edge where bare <-------------------- bare vertex ----------------------------> Vertex read-write attribute. Declaring a vertex "bare" says that a vertex does not have an adjacent edge (string model) or an adjacent facet (soapfilm model). Useful in avoiding warning messages. A vertex may be declared bare in the vertices section of the datafile by adding the keyword bare to the line defining the vertex. Example: list vertex where bare <-------------------- bezier_basis ----------------------------> Evolver toggle command. When Evolver is using the Lagrange model for geometric elements, this toggle replaces the Lagrange interpolation polynomials (which pass through the control points) with Bezier basis polynomials (which do not pass through interior control points, but have positive values, which guarantees the edge or facet is within the convex hull of the control points). This is experimental at the moment, and not all features such as graphing or refinement have been suitably adjusted. <-------------------- big_endian ----------------------------> Evolver toggle command. Controls the order of bytes in binary_printf numerical output. Big-endian is most significant byte first. To change to little-endian, use little_endian, not "little_endian off". <-------------------- binary numbers ----------------------------> Integer values may be given in binary notation, such as 11001b, indicated by a trailing 'b'. <-------------------- binary_off_file ----------------------------> Main prompt command. Produces one frame file for my 3D movie program evmovie (see http://www.susqu.edu/brakke/evmovie/evmovie-doc.html for more details). Syntax: binary_off_file string where string is the name of the output file, either a double-quoted string, a string variable, or a string-generating expression (typically using sprintf). <-------------------- binary_printf ----------------------------> Main prompt command. For printing formatted binary output to files. Syntax: BINARY_PRINTF string,expr,expr,... Prints to standard output using a binary interpretation of the standard C formats: %c one byte %d two byte integer %ld four byte integer %f four byte float %lf eight byte float %s string, without the trailing null non-format characters are copied verbatim as single bytes. The byte order for numbers can be set with the big_endian or little_endian toggles. NOTE: Either big_endian or little_endian must be set for binary_printf to work! The format string can be a string variable or a quoted string. There is a limit of 1000 characters on the format string, otherwise there is no limit on the number of arguments. Meant to be use with redirection to a file. In Microsoft Windows, the output file type is temporarily changed from TEXT to BINARY so newline bytes don't get converted. Example: binary_printf "%ld%ld%ld",vertex_count,edge_count,facet_count >>"out.bin" <-------------------- colors ----------------------------> <-------------------- black ----------------------------> <-------------------- blue ----------------------------> <-------------------- green ----------------------------> <-------------------- red ----------------------------> <-------------------- cyan ----------------------------> <-------------------- magenta ----------------------------> <-------------------- brown ----------------------------> <-------------------- lightgray ----------------------------> <-------------------- lightgrey ----------------------------> <-------------------- grey ----------------------------> <-------------------- gray ----------------------------> <-------------------- darkgray ----------------------------> <-------------------- darkgrey ----------------------------> <-------------------- lightblue ----------------------------> <-------------------- lightgreen ----------------------------> <-------------------- clear ----------------------------> <-------------------- transparent ----------------------------> <-------------------- lightred ----------------------------> <-------------------- lightcyan ----------------------------> <-------------------- yellow ----------------------------> <-------------------- lightmagenta ----------------------------> <-------------------- white ----------------------------> The colors of edges and facets are recorded as integers in the range -1 through 15. How these integers translate to colors on the screen is determined by how Evolver's graphics drivers are written. The following synonyms are supplied, and it is hoped that the graphics drivers will be written to display these correctly: -1 CLEAR 0 BLACK 1 BLUE 2 GREEN 3 CYAN 4 RED 5 MAGENTA 6 BROWN 7 LIGHTGRAY 8 DARKGRAY 9 LIGHTBLUE 10 LIGHTGREEN 11 LIGHTCYAN 12 LIGHTRED 13 LIGHTMAGENTA 14 YELLOW 15 WHITE The special color value CLEAR (-1) makes a facet transparent. "Transparent" is a synonym for clear. These tokens are simply translated to integer values wherever they occur, so these are reserved words. Edge and facet colors may be set in the datafile or by the set command. See "rgb_colors" for more detailed control of colors. <-------------------- blas_flag ----------------------------> Evolver toggle command. Toggles using BLAS versions of some matrix routines, if the Evolver program has been compiled with the -DBLAS option and linked with some BLAS library. For developer use only at the moment. <-------------------- bodies ----------------------------> <-------------------- body ----------------------------> A "body" is an Evolver element type representing a full-dimensional region of space. Bodies are not triangulated. Rather, they are determined by their boundary facets (or edges in 2D). These facets are used for calculating body volume and gravitational energy. Only those facets needed for correct calculation need be given. In the string model, usually a body corresponds to one facet. Bodies of the original surface are defined in the "bodies" section of the datafile; for datafile syntax, see "bodies section". Attributes: facets id density volume target volfixed actual_volume volconst pressure phase extra attributes As a command keyword, "body" is an element name in element generators, either as the main element type, or as a subelement of a facet. Examples: foreach body bb do printf "Body %d has volume %f\n",bb.id,bb.volume; list facet[1].bodies Bodies may be created with the "newbody" command, and destroyed with the "dissolve" command. <-------------------- bodies section ----------------------------> The datafile body list follows the face list, and is started by the keyword BODIES at the start of a line. It is followed by lines with one body specification per line in this format: k f1 f2 f3 .... [VOLUME constexpr] [VOLCONST constexpr] [ACTUAL_VOLUME constexpr] [PRESSURE p] [DENSITY constexpr] [PHASE] Here k is the body number, and f1 f2 f3 ... is an unordered list of signed facet numbers. Positive sign indicates that the facet normal (as given by the right-hand rule from the edge order in the facet list) is outward from the body and negative means the normal is inward. Giving a VOLUME value constexpr means the body has a volume constraint, unless the ideal gas model is in effect, in which case constexpr is the volume at the ambient pressure. VOLCONST is a value added to the volume; it is useful when the volume calculation from facet and edge integrals differs from the true volume by a constant amount, as may happen in the torus model. ACTUAL_VOLUME is a number that can be specified in the rare circumstances where the torus volume volconst calculation gives the wrong answer; volconst will be adjusted to give this volume of the body. Giving a PRESSURE value means that the body is deemed to have a constant internal pressure; this is useful for prescribed mean curvature problems. It is incompatible with prescribed volume. Giving a DENSITY value means that gravitational potential energy will be included. To endow a facet with VOLUME, PRESSURE, or DENSITY attributes in the string model, define a body with just the one facet. The PHASE number is used in the soapfilm model to determine the surface tension of facets between bodies of different phases, if phases are used. The BODIES section is optional. The list bodies command prints the datafile format listing of bodies. <-------------------- body density ----------------------------> Body read-write attribute. Density used for gravitational potential energy. It can be set in the bodies section of the datafile, or with the set command, or by assignment. Command examples: print body[2].density set body density 3 body[2].density := 5 <-------------------- body facets ----------------------------> Body read-only attribute. Generates facets bounding a body, with proper facet orientation with respect to the body. Examples: list body[1].facets set body[2] facet frontcolor red <-------------------- body phase ----------------------------> Body read-write attribute. For determining facet tension in soapfilm model, if a phase file is used. <-------------------- body pressure ----------------------------> Body read-write attribute. If a body has a prescribed volume, this is a read-only attribute, which is the Lagrange multiplier for the volume constraint. If a body is given a prescribed pressure, then there is an energy term equal to pressure times volume. A body cannot have a prescribed volume and a prescribed pressure at the same time. Prescribed volume or pressure can be set in the bodies section of the datafile. If pressure is prescribed, then the value can be changed interactively with the b command, the set command, or by assignment. Examples: print body[2].pressure body[2].pressure := 1.3 set body[2] pressure 1.3 <-------------------- body volconst ----------------------------> Body read-write attribute. A constant added to the calculated volume. Useful for correcting for omitted parts of body boundaries. Also used internally as a correction in the torus model , which will use the target volume to calculate volconst internally. In the torus model, the target volume should be set within 1/12 of a torus volume of the actual volume for each body, so the correct volconst can be computed. Each volconst will be adjusted proportionately when the volume of a fundamental torus domain is change by changing the period formulas. Volconst can be set in the datafile bodies section, or interactively by the set command or by assignment. Examples: print body[1].volconst set body[2] volconst 1.2 body[2].volconst := 1.2 It is best to avoid using volconst except in the torus model. Rather, use edge content integrals so that the proper adjustments will be made if the boundary of the surface is moved, or rebody is done. <-------------------- body volume ----------------------------> Body read-only attribute. Actual volume of a body. This is the sum of three parts, in the soapfilm model: 1. An integral over the facets bounding the body. This is \int z dx dy normally, but \int (x dy dz + y dz dx + z dx dy)/3 if SYMMETRIC_CONTENT is in effect. 2. Any constraint content edge integrals applying to the body. 3. The body's volconst attribute. In the string model, the parts are 1. An integral over the edges bounding the body's facet. This is \int -y dx. 2. Any constraint content vertex integrals applying to the body. 3. The body's volconst attribute. Body volumes can be displayed with the v command, or with standard attribute syntax. Example: print body[1].volume foreach body where volume > 2 do print id <-------------------- body_count ----------------------------> Internal read-only variable. Number of bodies. <-------------------- body_dissolve_count ----------------------------> Internal read-only variable. Number of bodies dissolved by dissolve command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- body_metis ----------------------------> Main prompt command. Partitions the set of bodies into n parts using the METIS library of Karypis and Kumar, if this library has been compiled into the Evolver. The partition number of each body is left in its extra attribute bpart (which will be created if it does not already exist). BODY_METIS works only in the soapfilm model; for the string model, partition facets using metis or kmetis. BODY_METIS uses the PMETIS algorithm. Meant for experiments in partitioning the surface for multiprocessors. Syntax: BODY_METIS n Example: body_metis 50; for each body bb do set bb.facet frontcolor (bb.bpart imod 15)+1; <-------------------- bottominfo ----------------------------> "list bottominfo" prints what would be dumped in the "read" section at the end of a dumpfile: command definitions and various toggle states. <-------------------- boundaries ----------------------------> <-------------------- boundary ----------------------------> Parametric "boundary" curves and surfaces can be used for locating vertices. Vertex locations may be given in terms of parameters on a parameterized curve or surface. Such curves or surfaces are called "boundaries" in Evolver terminology, since they are usually used as boundary curves of surfaces, for example a soap film on a wire loop could have the wire implemented as a boundary. Vertices, edges, and facets may be deemed to lie in a boundary. For a vertex, this means that the fundamental parameters of the vertex are the parameters of the boundary, and its coordinates are calculated from these. Vertices on boundaries may move during iteration, unless declared fixed. See cat.fe for an example. Boundaries are defined in the top section of the datafile. Vertices on boundaries are listed in the datafile with their parameter values instead of their coordinates, with "boundary n" appended to each such vertex definition. Edges and faces on boundaries are defined as usual, but with "boundary n" appended to each definition. So the datafile has lines like these: boundary 1 parameters 1 x1: cos(p1) x2: sin(p1) x3: 0.75 ... Vertices 1 0.0 boundary 1 2 pi/3 boundary 1 ... Edges 1 1 2 boundary 1 ... Putting an edge on a boundary means that vertices created on that edge will be on the boundary. An edge on a boundary must have at least one endpoint on the boundary, for use in extrapolating the boundary parameters of any created vertices. Extrapolating instead of interpolating midpoint parameters solves the problem of wrap-arounds on a boundary such as a circle or cylinder. However if you do want interpolation, you can use the keyword INTERP_BDRY_PARAM in the top of the datafile, or use the toggle command interp_bdry_param. Interpolation requires that both endpoints of an edge be on the same boundary, which cannot happen where edges on different boundaries meet. To handle that case, it is possible to add extra boundary information to a vertex by declaring two particular vertex extra attributes, extra_boundary and extra_boundary_param: interp_bdry_param define vertex attribute extra_boundary integer define vertex attribute extra_boundary_param real[1] Then declare attribute values on key vertices, for example vertices 1 0.00 boundary 1 fixed extra_boundary 2 extra_boundary_param 2*pi If the extra_boundary attribute is not set on a vertex when wanted, Evolver will silently fall back on interpolation. Putting a face on a boundary means that all edges and vertices created from refining the face will be on the boundary. In this case, the boundary should have two parameters (or whatever the dimension of the surface is). This is good for getting a surface to conform to a known parametric shape. Edges on boundaries have energy and content integrals like level-set constraints edges, but they are internally implemented as named quantities. Whether an element is on a particular boundary can be queried with the on_boundary Boolean attribute. Elements can be removed from boundaries with the unset command, but they cannot be set on boundaries. A typical use of unset is to define an initial surface using a 2-parameter boundary, refine a couple of times, then unset. Examples: list vertex where on_boundary 2 unset vertex boundary 1 where on_boundary 1 unset edge boundary 1 unset facet boundary 1 It does not hurt to unset an element not on the boundary. Vertex parameters can be accessed in expressions as the attribute p1 (and p2,... for further parameters). Vertex parameters can be changed with the set command. Example: print vertex[5].p1 set vertex p1 p1+.1 where id < 4 vertex[2].p1 := 3 It is not an error to access the parameters of a vertex not on a boundary as long as some vertex is on a boundary (so that space is allocated in the vertex structure for parameters). A general guideline is to use constraints for two-dimensional walls and boundaries for one-dimensional wires. If you are using a boundary wire, you can probably declare the vertices and edges on the boundary to be FIXED. Then the boundary becomes just a guide for refining the boundary edges. NOTE: A vertex on a boundary cannot also have constraints. <-------------------- boundary declaration ----------------------------> Parameterized boundary declaration A parameterized boundary may be declared in the top section of the datafile with the syntax BOUNDARY n PARAMETERS k [CONVEX] X1: expr X2: expr X3: expr [ENERGY: E1: expr E2: expr E3: expr] [CONTENT: C1: expr C2: expr C3: expr] This defines boundary number n, where n is a positive integer and k is the number of parameters. If CONVEX is given, then an additional gap energy is attributed to edges on the boundary to prevent them from trying to short-circuit a convex boundary. The following lines have the functions for the coordinates in terms of the parameters P1 and maybe P2, P3,.... See the catenoid example. Optional energy and content integrals for boundaries are implemented, with the same syntax as for level set constraints. <-------------------- boundary_curvature ----------------------------> Evolver toggle command. When doing integrals of mean curvature or squared curvature, the curvature of a boundary vertex cannot be defined by its neighbor vertices, so the area of the boundary vertex star instead is counted with an adjacent interior vertex. <-------------------- bounding box ----------------------------> b : Graphics mode command. Toggles display of bounding box. Useful for visualizing orientation. In the native graphics window, the 'o' key does the same thing. <-------------------- break ----------------------------> Command syntax for exiting loops. Syntax: BREAK BREAK n The first form exits the innermost current loop. The second form exits n loops. Note: Commands with repetition counts do not qualify as loops. Example: foreach vertex do { print x; if y < 0 then break; print z } <-------------------- break after warning option ----------------------------> <-------------------- option -y ----------------------------> Command line option -y causes Evolver to cease execution of commands and return to the command prompt after any warning message. Same effect as break_after_warning runtime toggle. <-------------------- break_after_warning ----------------------------> Runtime toggle command. Causes Evolver to cease execution of commands and return to command prompt after any warning message. Same effect as command line option -y. <-------------------- breakflag ----------------------------> Internal read-write variable. When set to a non-zero value, causes the command interpreter to abort and return to the command prompt. Software equivalent of hitting the keyboard interrupt (typically CTRL-C). The break doesn't happen immediately, but at a certain point in the interpreter loop when it periodically checks for user interrupt. Meant for bailing out of nested commands, since "return" only breaks out of the current procedure. <-------------------- breakpoint ----------------------------> breakpoint Main prompt command. The user may set a breakpoint in an already loaded script with the breakpoint command. The syntax is BREAKPOINT scriptname linenumber where scriptname is the name of the function or procedure and linenumber is the line number in the file where the breakpoint is to be set. There must be executable code on the line, or you will get an error. linenumber may be an expression. Breakpoints may be unset individually with UNSET BREAKPOINT scriptname linenumber or as a group with UNSET BREAKPOINTS When a breakpoint is reached, Evolver will enter into a subcommand prompt, at which the user may enter any Evolver commands (although some commands, such as load would be very unwise). To exit from the subcommand prompt, use q or exit or quit. <-------------------- brightness ----------------------------> Internal read-only variable. Median gray level used in PostScript output and screen graphics. <-------------------- buck_knot_energy ----------------------------> Named method. Description: Energy between pair of edges given by formula suggested by Greg Buck. Power law of potential is adjustable via the global parameter `knot_power'. Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: parameter knot_power 2 // the default quantity knotten energy method buck_knot_energy global <-------------------- bugs ----------------------------> Bug reports should be submitted by email to brakke@susqu.edu. Please include the Evolver version number, a description of the problem, the initial data file, and the sequence of commands necessary to reproduce the problem. <-------------------- bunch_kauffman ----------------------------> <-------------------- bunch_kaufman ----------------------------> Evolver toggle command. Toggles Bunch-Kaufman factoring of the Hessian in the alternative minimal degree factoring method (ysmp off). This factors the Hessian as LBL^T where L is lower triangular with ones on the diagonal, and B is block diagonal, with 1x1 or 2x2 blocks. Supposed to be more stable when factoring indefinite Hessians. <-------------------- burchard ----------------------------> A keyword for a special purpose command not documented. <-------------------- bye ----------------------------> <-------------------- exit ----------------------------> <-------------------- quit ----------------------------> Main prompt command. Exits Evolver or starts new datafile. Same as `q' command. "quit", "bye", and "exit" are synonyms. <-------------------- c ----------------------------> Single letter main command. Prints count of elements and memory used. The memory is just the total of the element structures. On some systems, enabling the "verbose" will print more complete statistics on total memory usage. Synonym: counts. Graphics mode command. Rotate clockwise about center of screen, default 6 degrees. Integer prefix is taken as multiples of 6 degrees, and a decimal prefix is taken as degrees for the rotation. Examples: `15c' does 90 degree rotation, `15.0c' does 15 degree rotation. <-------------------- C ----------------------------> Single letter main command. Runs various internal consistency checks. Synonym: check. If no problems, just prints "Checks completed." The number of errors found is stored in the variable check_count. The checks are: 1. Element list integrity - checks that data structures are intact. 2. Facet-edge check - that if a facet adjoins an edge, then the edge adjoins the facet, and that the three edges around a facet link up. 3. Facet-body check - whether adjacent facets have the same body on the same side. 4. Collapsed elements - check if endpoints of an edge are the same, and whether neighboring facets share more than one edge and two vertices. Graphics mode command. Rotate counterclockwise about center of screen, default 6 degrees. Integer prefix is taken as multiples of 6 degrees, and a decimal prefix is taken as degrees for the rotation. Examples: `15C' does 90 degree rotation, `15.0C' does 15 degree rotation. <-------------------- carter_energy ----------------------------> Named method. Description: Craig Carter's energy. pre> Given bodies $B_1$ and $B_2$ in $R^3$, define the energy E = \int_{B_1}\int_{B_2} {1 \over |z_1 - z_2|^{p} } d^3 z_2 d^3 z_1 This reduces to E = {1\over (3-p)(2-p)}\sum_{F_2\in\partial B_2}\sum_{F_1\in\partial B_1} N_1 \cdot N_2 \int_{F_2}\int_{F_1}{1\over |z_1 - z_2|^{p-2}} d^2 z_1 d^2 z_2. And if we crudely approximate with centroids $\bar z_1$ and $\bar z_2$, E = {1\over (3-p)(2-p)}\sum_{F_2\in\partial B_2}\sum_{F_1\in\partial B_1} {A_1 \cdot A_2 \over |\bar z_1 - \bar z_2|^{p-2}}, where $A_1$ and $A_2$ are unnormalized area vectors for the facets. The power p is set by the variable carter_power (default 6). Element: facet. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: parameter carter_power 6 // the default quantity craig energy method carter_energy global <-------------------- carter_power ----------------------------> carter_power Internal read-write variable for the denominator power in the formula for the named method carter_energy. Default value 6. <-------------------- case ----------------------------> Case is not significant in the datafile. In commands, case is only significant for single-letter commands. <-------------------- catenoid example ----------------------------> The catenoid is the minimal surface formed between two rings not too far apart. In cylindrical coordinates, its equation is r = (1/a)cosh(az). In cat.fe, both the upper and lower rings are given as one-parameter boundary wires. The separation and radius are parameters, so you can change them during a run with the A command. The initial radius given is the minimum for which a catenoid can exist for the given separation of the rings. To get a stable catenoid, you will have to increase this value. However, if you do run with the original value, you can watch the neck pinch out. The initial surface consists of six rectangles forming a cylinder between the two circles. The vertices on the boundaries are fixed, elsewise they would slide along the boundary to short-cut the curvature; two diameters is shorter than one circumference. The boundary edges are fixed so that vertices arising from subdividing the edges are likewise fixed. The initial catenoid skeleton, with vertices and edges numbered. Here is the catenoid datafile: // cat.fe // Evolver data for catenoid. PARAMETER RMAX = 1.5088795 // minimum radius for height PARAMETER ZMAX = 1.0 boundary 1 parameters 1 // upper ring x1: RMAX * cos(p1) x2: RMAX * sin(p1) x3: ZMAX boundary 2 parameters 1 // lower ring x1: RMAX * cos(p1) x2: RMAX * sin(p1) x3: -ZMAX vertices // given in terms of boundary parameter 1 0.00 boundary 1 fixed 2 pi/3 boundary 1 fixed 3 2*pi/3 boundary 1 fixed 4 pi boundary 1 fixed 5 4*pi/3 boundary 1 fixed 6 5*pi/3 boundary 1 fixed 7 0.00 boundary 2 fixed 8 pi/3 boundary 2 fixed 9 2*pi/3 boundary 2 fixed 10 pi boundary 2 fixed 11 4*pi/3 boundary 2 fixed 12 5*pi/3 boundary 2 fixed edges 1 1 2 boundary 1 fixed 2 2 3 boundary 1 fixed 3 3 4 boundary 1 fixed 4 4 5 boundary 1 fixed 5 5 6 boundary 1 fixed 6 6 1 boundary 1 fixed 7 7 8 boundary 2 fixed 8 8 9 boundary 2 fixed 9 9 10 boundary 2 fixed 10 10 11 boundary 2 fixed 11 11 12 boundary 2 fixed 12 12 7 boundary 2 fixed 13 1 7 14 2 8 15 3 9 16 4 10 17 5 11 18 6 12 faces 1 1 14 -7 -13 2 2 15 -8 -14 3 3 16 -9 -15 4 4 17 -10 -16 5 5 18 -11 -17 6 6 13 -12 -18 The parameter in a boundary definition is always P1 (and P2 in a two-parameter boundary). The Evolver can handle periodic parameterizations, as is done in this example. Try this sequence of commands (displaying at your convenience): r (refine to get a crude, but workable, triangulation) u (equiangulation makes much better triangulation) g 120 (takes this many iterations for neck to collapse) t 0.05 (collapse neck to single vertex by eliminating all edges shorter than 0.05) o (split neck vertex to separate top and bottom surfaces) g (spikes collapse) The catenoid shows some of the subtleties of evolution. Suppose the initial radius is set to RMAX = 1.0 and the initial height to ZMAX = 0.55 (these are pre-set in catman.fe). Fifty iterations with optimizing scale factor result in an area of 6.458483. At this point, each iteration is reducing the area by only .0000001, the triangles are all nearly equilateral, everything looks nice, and the innocent user might conclude the surface is very near its minimum. But this is really a saddle point of energy. Further iteration shows that the area change per iteration bottoms out about iteration 70, and by iteration 300 the area is down to 6.4336. The triangulation really wants to twist around so that there are edges following the lines of curvature, which are vertical meridians and horizontal circles. Hence the optimum triangulation appears to be rectangles with diagonals. The evolution can be speeded up by turning on the conjugate gradient method with the U command. With catman.fe, try the script "r; u; U; g 70". For conjugate gradient cognoscenti, the saddle point demonstrates the difference between the Fletcher-Reeves and Polak-Ribiere versions of conjugate gradient. The saddle point seems to confuse the Fletcher-Reeves version (which used to be the default). However, the Polak-Ribiere version (the current default) has little problem. The U toggles conjugate gradient on and off, and ribiere toggles the Polak-Ribiere version. With Fletcher-Reeves conjugate gradient in effect, the saddle point is reached at iteration 17 and area starts decreasing again until iteration 30, when it reaches 6.4486. But then iteration stalls out, and the conjugate gradient mode has to be turned off and on to erase the history vector. Once restarted, another 20 iterations will get the area down to 6.4334. In Polak-Ribiere mode, no restart is necessary. Exercise for the reader: Get the Surface Evolver to display an unstable catenoid by declaring the catenoid facets to be the boundary of a body, and adjusting the body volume with the b command to get zero pressure. See the sample datafile catbody.fe. <-------------------- ceil ----------------------------> ceil(x),floor(x): Round up or down to integer. <-------------------- central_symmetry ----------------------------> This is the order 2 symmetry group of inversion through the origin, X -> -X. Datafile declaration: symmetry_group "central_symmetry" Group element encoding: 0 for identity, 1 for inversion. <-------------------- charge_gradient ----------------------------> Named method. Description: This energy is the gradient^2 of the knot_energy method, assuming the points are constrained to the unit sphere. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: parameter knot_power 2 // the default quantity knotten energy method knot_energy global <-------------------- chdir ----------------------------> Main prompt command. Changes the current directory, used for searching for files before EVOLVERPATH is used. Syntax: CHDIR stringexpr In MS-Windows, use a front slash '/' or a double backslash '\\' instead of a single backslash as the path character. Example: chdir "/usr/smith/project" <-------------------- check ----------------------------> Main command. Runs various internal consistency checks. Synonym: C. If no problems, just prints "Checks completed." The number of errors found is stored in the variable check_count. The checks are: 1. Element list integrity - checks that data structures are intact. 2. Facet-edge check - that if a facet adjoins an edge, then the edge adjoins the facet, and that the three edges around a facet link up. 3. Facet-body check - whether adjacent facets have the same body on the same side. 4. Collapsed elements - check if endpoints of an edge are the same, and whether neighboring facets share more than one edge and two vertices. <-------------------- check_count ----------------------------> Internal read-only variable. Number of errors found by the most recent "C" or "check" command. <-------------------- check_increase ----------------------------> Evolver toggle command. Toggles checking for increase of energy in an iteration step. If energy increases, then the step is undone and any iteration loop is halted. Meant for early detection of instabilities and other problems causing the surface to misbehave. Useful in doing a multiple iteration with a fixed scale. Also applies to the hessian command. Caution: there are circumstances where an energy increase is appropriate, for example when there are volume or quantity constraints and conforming to the constraints means an energy increase initially. <-------------------- checking ----------------------------> Checking your datafile You should always check your initial datafile to be sure it is doing exactly what you want. It is easy to get signs on integrands wrong, or apply quantities to the wrong elements. When you load the initial datafile, the initial energy, body volumes, and quantities values should be exactly what you expect, either from hand calculation or from another datafile you trust. In particular, when using constraint integrals to replace omitted facets, I suggest you make a separate datafile with facets instead of integrals just for checking the agreement between the two. With the named methods and quantities feature, it is possible to get very detailed information on where numbers are coming from. If you give the "convert_to_quantities" command, every energy, volume, and constraint integrand will be internally converted to named methods and quantities (although the user interface for all remains the same). These internal quantities are ordinarily not displayed by the 'v' or 'Q' commands, but if you do "show_all_quantities" then they will be displayed. Further, 'Q' will show all the component method instances also. For an example, consider the following output: Enter command: convert_to_quantities Enter command: show_all_quantities Enter command: Q Quantities and instances: (showing internal quantities also; to suppress, do "show_all_quantities off") 1. default_length 64.2842712474619 info_only quantity modulus 1.00000000000000 2. default_area 4.00000000000000 energy quantity modulus 1.00000000000000 3. constraint_1_energy -0.342020143325669 energy quantity modulus 1.00000000000000 4. constraint_2_energy -0.342020143325669 energy quantity modulus 1.00000000000000 5. body_1_vol 1.00000000000000 fixed quantity target 1.00000000000000 modulus 1.00000000000000 body_1_vol_meth 0.000000000000000 method instance modulus 1.00000000000000 body_1_con_2_meth 1.00000000000000 method instance modulus 1.00000000000000 6. gravity_quant 0.000000000000000 energy quantity modulus 0.000000000000000 Here's a detailed explanation of the output of the Q command above: default_length - total edge length, using the edge_length method. This would be the default energy in the string model, and I guess it really doesn't need to exist here. But it's an info_only quantity, which means it is only evaluated when somebody asks to know its value. default_area - the default energy in the soapfilm model, and included in the energy here, as indicated by "energy quantity" at the right. constraint_1_energy - the energy integral of constraint 1, using the edge_vector_integral method applied to all edges on constraint 1. constraint_2_energy - the energy integral of constraint 2, using the edge_vector_integral method applied to all edges on constraint 2. body_1_vol - the volume of body 1, as a sum of several method instances. body_1_vol_meth is the facet_vector_integral of (0,0,z) over all the facets on the body. body_con_2_meth is the integral of the constraint 2 content integrand over all edges on facets of body 1 which are edges on constraint 2. gravity_quant - the total gravitational energy of all bodies with assigned densities. This quantity is always present even if you don't have any bodies, or don't have any body densities. But you'll notice the modulus is 0, which means its evaluation is skipped, so the presence of this quantity doesn't harm anything. You can find the quantity or method contribution of single elements by using the quantity or method name as an attribute of elements. Using a quantity name really means summing over all its constituent methods that apply to the element. For example, in plates_column, Enter command: foreach edge ee where on_constraint 2 do printf "%d %f\n",id, ee.body_1_con_2_meth 5 0.000000 6 0.000000 7 1.000000 8 0.000000 Enter command: foreach edge where constraint_1_energy != 0 do print constraint_1_energy -0.342020143325669 <-------------------- circle_knot_energy ----------------------------> Named method. Description: This energy is due to Peter Doyle, who says it is equivalent in the continuous case to the insulating wire with power 2. Its form is E_{e_1e_2} = {L_1L_2 (1 - \cos\alpha)^2 \over |x_1 - x_2|^2}, where x_1,x_2 are the midpoints of the edges and \alpha is the angle between edge 1 and the circle through x_1 tangent to edge 2 at x_2. Only power 2 is implemented. Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: quantity knotten energy method circle_knot_energy global <-------------------- circle_willmore ----------------------------> circle_willmore Named method. Description: Alexander Bobenko's circle-based discrete Willmore energy, which is conformally invariant. At each vertex, energy is (sum of the angles between facet circumcircles) - 2*pi. More simply done as edge quantity, since angles at each end are the same. For edge e, if adjacent facet edge loops are a,e,d and b,c,-e, then circle angle beta for edge has cos(beta) = (--)/|a|/|b|/|c|/|d| For now, assumes all vertices are faceted, and fully starred. Severe numerical difficulties: Not smooth when angle beta is zero, which is all too common. Set of zero angles should be codimension 2, which means generally avoided, but still crops up. Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: quantity bobenko energy method circle_willmore global <-------------------- circular_arc_area ----------------------------> Named method. Description: Area between an edge and the y axis, with the edge modelled as a circular arc through three points. Useful in the quadratic model; in other models it is the same as edge_area. It is particularly meant to be used as area_method_name. Element: edge. Parameters: none. Models: quadratic. Ambient dimension: 2. Orientable: yes. Hessian: yes. Example datafile declaration: quantity arcarea energy method circular_arc_area global <-------------------- circular_arc_draw ----------------------------> If on, then in quadratic string mode, an edge is drawn as a circular arc (actually 16 subsegments) through the endpoints and midpoint, instead of a quadratic spline. Meant to be used when circular_arc_length and circular_arc_area are being used. <-------------------- circular_arc_length ----------------------------> Named method. Description: Edge length, modelling the edge as a circular arc through three points, hence useful only in the quadratic model. If not in the quadratic model, it evaluates as the edge_length method. The presence of this quantity has the side effect of automatically toggling circular_arc_draw, causing edges to display as circular arcs in the quadratic model. Particularly meant for length_method_name. Element: edge. Parameters: none. Models: quadratic. Ambient dimension: any. Hessian: yes. Example datafile declaration: quantity arclen energy method circular_arc_length global <-------------------- clipped ----------------------------> <-------------------- clipped_cells ----------------------------> Evolver toggle command. Sets torus model display to clip to the fundamental region. Not an on-off toggle. 3-way toggle with raw_cells and connected. Synonym: clipped_cells. <-------------------- clip_view ----------------------------> <-------------------- clip_coeff ----------------------------> clip_view Main prompt command that toggles user-defined clipping planes. It is possible to have the graphics display clip the surface with multiple clipping planes. A clipping plane is defined by a plane of the form ax + by + cz = d. The visible volume is ax + by + cz <= d. Up to 10 clipping planes may be stored in the array clip_coeff[][], with the first plane coefficients a,b,c,d stored in clip_coeff[1][1] through clip_coeff[1][4], etc. The user does not have to create clip_coeff[][], and Evolver will initialize it with a default vertical plane through the middle of the surface. To use clip view, first set the coefficients of however many clip planes you want and then use the clip_view toggle. For example, to get a vertical clipping plane parallel to the y and z axes and a little in front of them: clip_coeff[1] := 1; clip_coeff[2] := 0; clip_coeff[3] := 0; clip_coeff[4] := .2; clip_view; With OpenGL graphics, the first clip plane plane can be varied interactively by hitting the 'l' key (lower case L) in the graphics window and dragging the mouse horizontally. The 'k' key will make mouse dragging change the orientation of the clip plane. Hit 'r' or 'c' or 't' to get back to another mouse mode. The 'L' key will end clip_view. Clip_view works separately, and after, torus model viewing modes such as clipped and connected, so it is no problem to have them together. In case clip_view and slice_view are both in effect, slice_view operates instead of clip_view. <-------------------- clock ----------------------------> Internal read-only variable. Total elapsed Evolver execution time in seconds. Reads system process elapsed time, which often has a fairly coarse resolution of 0.01 seconds. For nanosecond timing, see cpu_counter. <-------------------- close_show ----------------------------> Main prompt command. Closes the native graphics window started by the "s" or "show" commands. Does not affect geomview version. Synonym: show_off. <-------------------- color ----------------------------> As a keyword, "color" is a read-write attribute of edges and facets. For the permitted values, see "colors". <-------------------- colorfile ----------------------------> Internal read-write variable. Name of colormap file used in certain graphics output. See the P command. Use COLORFILE := "filename" to set file. <-------------------- colormap ----------------------------> Evolver toggle command. Use colormap from file in certain graphics output. See the P command. Use COLORFILE := "filename" to set file. <-------------------- column example ----------------------------> Example: Column of liquid solder Here we have a tiny drop of liquid solder that bridges between two parallel, horizontal planes at z = 0 and z = ZH. On each plane there is a circular pad that the solder perfectly wets, and the solder is perfectly nonwetting off the pads. This would be just a catenoid problem with fixed volume, except that the pads are offset, and it is desired to find out what aligning force the solder exerts. The surface is defined the same way as in the catenoid example, except the lower boundary ring has a shift variable "SHIFT" in it to provide an offset in the y direction. This makes the shift adjustable at run time. Since the top and bottom facets of the body are not included, the constant volume they account for is provided by content integrals around the upper boundary, and the gravitational energy is provided by an energy integral. One could use the volconst attribute of the body instead for the volume, but then one would have to reset that every time ZH changed. The interesting part of this example is the calculation of the forces. One could incrementally shift the pad, minimize the energy at each shift, and numerically differentiate the energy to get the force. Or one could set up integrals to calculate the force directly. But the simplest method is to use the Principle of Virtual Work by shifting the pad, recalculating the energy without re-evolving, and correcting for the volume change. Re-evolution is not necessary because when a surface is at an equilibrium, then by definition any perturbation that respects constraints does not change the energy to first order. To adjust for changes in constraints such as volume, the Lagrange multipliers (pressure for the volume constraint) tell how much the energy changes for given change in the constraints: DE = L*DC where DE is the energy change, L is the row vector of Lagrange multipliers and DC is the column vector of constraint value changes. Therefore, the adjusted energy after a change in a parameter is E_adj = E_raw - L*DC where E_raw is the actual energy and DC is the vector of differences of constraint values from target values. The commands do_yforce and do_zforce in the datafile do central difference calculations of the forces on the top pad, and put the surface back to where it was originally. Note that the perturbations are made smoothly, i.e. the shear varies linearly from bottom to top. This is not absolutely necessary, but it gives a smoother perturbation and hence a bit more accuracy. The initial column skeleton, with vertices and edges numbered. // column.fe // Example of calculating forces exerted by a // column of liquid solder in shape of skewed catenoid. // All units cgs parameter RAD = 0.05 // ring radius parameter ZH = 0.08 // total height parameter SHIFT = 0.025 // shift #define SG 8 // specific gravity of solder #define TENS 460 // surface tension of solder #define GR 980 // acceleration of gravity gravity_constant GR BOUNDARY 1 PARAMETERS 1 X1: RAD*cos(P1) X2: RAD*sin(P1) + SHIFT X3: ZH CONTENT // used to compensate for missing top facets c1: 0 c2: -ZH*x c3: 0 ENERGY // used to compensate for gravitational energy under top facets e1: 0 e2: GR*ZH^2/2*x e3: 0 BOUNDARY 2 PARAMETERS 1 X1: RAD*cos(P1) X2: RAD*sin(P1) X3: 0 vertices // given in terms of boundary parameter 1 0.00 boundary 1 fixed 2 pi/3 boundary 1 fixed 3 2*pi/3 boundary 1 fixed 4 pi boundary 1 fixed 5 4*pi/3 boundary 1 fixed 6 5*pi/3 boundary 1 fixed 7 0.00 boundary 2 fixed 8 pi/3 boundary 2 fixed 9 2*pi/3 boundary 2 fixed 10 pi boundary 2 fixed 11 4*pi/3 boundary 2 fixed 12 5*pi/3 boundary 2 fixed edges 1 1 2 boundary 1 fixed 2 2 3 boundary 1 fixed 3 3 4 boundary 1 fixed 4 4 5 boundary 1 fixed 5 5 6 boundary 1 fixed 6 6 1 boundary 1 fixed 7 7 8 boundary 2 fixed 8 8 9 boundary 2 fixed 9 9 10 boundary 2 fixed 10 10 11 boundary 2 fixed 11 11 12 boundary 2 fixed 12 12 7 boundary 2 fixed 13 1 7 14 2 8 15 3 9 16 4 10 17 5 11 18 6 12 faces 1 1 14 -7 -13 density TENS 2 2 15 -8 -14 density TENS 3 3 16 -9 -15 density TENS 4 4 17 -10 -16 density TENS 5 5 18 -11 -17 density TENS 6 6 13 -12 -18 density TENS bodies 1 -1 -2 -3 -4 -5 -6 volume 0.00045 density SG read // horizontal force on upper pad by central differences dy := .0001 do_yforce := { oldshift := shift; shift := shift + dy; set vertex y y+dy*z/zh; // uniform shear recalc; energy1 := total_energy - body[1].pressure*(body[1].volume - body[1].target); oldshift := shift; shift := shift - 2*dy; set vertex y y-2*dy*z/zh; // uniform shear recalc; energy2 := total_energy - body[1].pressure*(body[1].volume - body[1].target); yforce := -(energy1-energy2)/2/dy; printf "restoring force: %20.15f\n",yforce; // restore everything oldshift := shift; shift := shift + dy; set vertex y y+dy*z/zh; // uniform shear recalc; } // vertical force on upper pad by central differences. dz := .0001 do_zforce := { oldzh := zh; zh := zh + dz; set vertex z z+dz*z/oldzh; recalc; // uniform stretch energy1 := total_energy - body[1].pressure*(body[1].volume - body[1].target); oldzh := zh; zh := zh - 2*dz; set vertex z z-2*dz*z/oldzh; recalc; // uniform stretch energy2 := total_energy - body[1].pressure*(body[1].volume - body[1].target); zforce := -(energy1-energy2)/2/dz; printf "vertical force: %20.15f\n",zforce; // restore everything oldzh := zh; zh := zh + dz; set vertex z z+dz*z/oldzh; recalc; // uniform stretch } <-------------------- combinatorics ----------------------------> Surface representation and combinatorics: All surfaces are simplicial complexes made of the basic elements: vertices, edges, and facets. The Evolver has three different ways of representing the combinatorics of the surface, depending on the dimension of the surface. Any of these may be used in any ambient space dimension at least as great at the surface dimension. String model for dimension 1 surface. Soapfilm model (default) for dimension 2 surface. Simplex model for dimension 3 or higher surface. <-------------------- command definition ----------------------------> User-defined commands: Users may define their own commands with the syntax identifier := command The shortest complete command on the right side is used. Thus "gg := g 10; u" would give gg the same value as "gg := g 10". It is wise and strongly advised to use braces to enclose the command on the right side so the parser can tell it's a command and not an expression. Also multiline commands then don't need linesplicing. Do not try to redefine single-letter commands this way; use :::=. Example: gg := {g 10; u} <-------------------- command repetition ----------------------------> ertain types of commands can be repeated a number of times by following the command with an integer. Be sure to leave a space between a single-letter command and the expression lest your command be interpreted as one identifier. To avoid several types of confusion, only certain types of commands are repeatable: Single letter commands that don't have optional arguments (K,k,l,t,j,m,n,p,w,y,P,M,G have optional arguments), Compound commands in braces, User-defined command names. Redefined single-letter commands. Examples: g 10 U 2 { g 20; u; V; u } 20 myc := { g 20; V }; myc 10 <-------------------- command separator ----------------------------> <-------------------- semicolon ----------------------------> Several commands on the same line or within a compound command must be separated by a semicolon. A semicolon is not needed after the last command, but won't hurt. Do not use a semicolon after the first command in an IF THEN ELSE command. Do use a semicolon to separate a compound command from the next. Example: g 10; r; g 10; u myc := { print 5; g 10; if qwer < foo then print 3 else { print 2; print 4; }; aa := 23 } <-------------------- comments ----------------------------> Comments may be enclosed in /* */ pairs (as in C) and may span lines. // indicates the rest of the line is a comment, as in C++. <-------------------- compound commands ----------------------------> Curly braces group a list of commands into one command. The commands are separated by semicolons. A semicolon is needed after a compound command within a compound command to separate it from following commands (note this is different from the C language). Do not use a semicolon after the first command in an IF THEN ELSE command. An empty compound command {} is legal. Examples: if ( foo > 4 ) then { g;g;u; } else print 4; while ( xx < 2 ) do { g; xx := xx + 1 } aa := { g 5; foreach vertex vv do { printf "id: %g coord: %f %f %f\n",id,x,y,z; count := count + 1; }; // note semicolon here! printf "done.\n" } <-------------------- compressibility energy ----------------------------> If the ideal gas mode is in effect (set by the PRESSURE keyword in the datafile), then each body contributes an energy E = P*V_0*ln(V/V_0) where P is the ambient pressure, V_0 is the target volume of the body, and V is the actual volume. To account for work done against the ambeint pressure, each body also makes a negative contribution of E = -P*V. The ambient pressure can be set in the datafile or with the p command. This energy is calculated only for bodies given a target volume. <-------------------- conditional ----------------------------> A c onditional expression has the same syntax as in the C language: expr1 ? expr2 : expr3 evaluates to expr2 if expr1 is nonzero and to expr3 if expr1 is zero. <-------------------- conducting_knot_energy ----------------------------> Datafile keyword that automatically creates an energy named quantity using the method knot_energy as a global method. May be followed in the datafile with a modulus value. <-------------------- conf_edge ----------------------------> Evolver toggle command. Calculation of squared curvature by fitting sphere to edge and adjacent vertices (conformal curvature). Only applies to square_curvature is declared in the top of the datafile, not to the squared curvature named methods. <-------------------- conformal_metric ----------------------------> The ambient space can be endowed with a conformal Riemannian metric by putting the keyword CONFORMAL_METRIC in the datafile followed by a formula for the conformal factor, i.e. the multiple of the identity matrix that gives the metric. Only one coordinate patch is allowed, but the quotient space feature makes this quite flexible. Edges and facets are linear in coordinates, they are not geodesic. The metric is used solely to calculate lengths and areas. It is not used for volume. To get a volume constraint on a body, you will have to define your own named quantity constraint. See quadm.fe for an example of a metric. Example datafile declaration: CONFORMAL_METRIC 1/(1 - x^2 - y^2 - z^2) <-------------------- conj_grad ----------------------------> Evolver toggle command. Use conjugate gradient method in g command. Synonym for "U" command. See "conjugate gradient" for explanation of the method. <-------------------- conjugate gradient ----------------------------> "Conjugate gradient" is a method of accelerating gradient descent. In ordinary gradient descent, one uses the gradient of energy to find the steepest downhill direction, then moves along that line to the minimum energy in that direction. Hence successive steps are at right angles. However, this can be very inefficient, as you can spend a lot of time zigzagging across an energy "valley" without making much progress "downstream". With conjugate gradient, the search direction is chosen to be in a "conjugate" direction to the previous direction. For a mathematical explanation, see any decent book in numerical analysis, such as [P]. In practice, the conjugate gradient method remembers a cumulative "history vector", which it combines with the ordinary gradient to figure out the conjugate gradient direction. The upshot is that conjugate gradient can converge much faster than ordinary gradient descent. Conjugate gradient can be toggled with the "U" command, or with the "conj_grad" toggle. It should always be used with optimizing scale. Notes: The conjugate gradient method is designed for quadratic energy functions. As long as the energy function is nearly quadratic, as it should be near an energy minimum, conjugate gradient works well. Otherwise, it may misbehave, either by taking too big steps or by getting stalled. Both effects are due to the history vector being misleading. To prevent too big steps, one should iterate without conjugate gradient for a few steps whenever significant changes are made to the surface (refining, changing a constraint, etc.). On the other hand, if it looks like conjugate gradient is converging, it may have simply become confused by its own history. See the catenoid example for a case in point. A danger signal is the scale factor going to zero. If you are suspicious, toggle conjugate gradient off and on ("U 2" does nicely) to erase the history vector and start over. See the "ribiere" toggle for switching between the default Fletcher-Reeves version and the Ribiere version of conjugate gradient. <-------------------- connected ----------------------------> <-------------------- connected_cells ----------------------------> Evolver toggle command. Sets torus model display to do each body as a connected, wrapped surface. Not an on-off toggle. 3-way toggle with "clipped" and "raw_cells". Synonym: "connected_cells". <-------------------- conserved ----------------------------> <-------------------- conserved quantity ----------------------------> A type of named quantity, along with energy, fixed, and info_only. A "conserved" named quantity is like a fixed quantity, but the value is irrelevant. The quantity gradient is used to eliminate a degree of freedom in motion. Rarely used, but useful to eliminate rotational degrees of freedom, for example. Will not work with optimizing parameters, since they do gradients by numerical differences. <-------------------- constant expressions ----------------------------> <-------------------- constexpr ----------------------------> Constant expressions are evaluated when parsed. They are denoted by constexpr in various syntax definitions. They occur mostly in the datafile. Although they may contain variables, changing the variable value after parsing has no effect. Variable expressions (denoted by expr in syntax definitions) are recorded as parse trees and are re-evaluated each time needed. <-------------------- constraint ----------------------------> <-------------------- constraints ----------------------------> "Constraint" can be used in a general sense, but as a keyword it refers to level set constraints. "Constraint" and "constraints" are interchangeable in syntax. See "level set constraints" for more, and "constraint decl" for datafile syntax. <-------------------- constraint declaration ----------------------------> The format for declaring a level set constraint in the top section of the datafile is CONSTRAINT n [GLOBAL] [CONVEX] [NONNEGATIVE] [NONPOSITIVE] [NONWALL] FORMULA FUNCTION expr [ENERGY: E1: expr E2: expr E3: expr] [CONTENT: C1: expr C2: expr C3: expr] The same syntax also works at runtime, but it is wise to enclose the entire definition in curly braces to guarantee Evolver reads the definition as a single unit and is not confused by line breaks. You may use EQUATION or FUNCTION as synonyms for FORMULA. This defines constraint number n, where n is a positive integer. The optional keyword GLOBAL means the constraint automatically applies to all vertices (but not automatically to edges or faces). GLOBAL constraints count in the number limit. If CONVEX is given, then an additional gap energy is attributed to edges on the constraint to prevent them from trying to short-circuit a convex boundary. NONWALL indicates this constraint is to be ignored in vertex and edge popping. If NONNEGATIVE or NONPOSITIVE is given, then this is a one-sided constraint, and all vertices will be forced to conform appropriately to the constraint at each iteration. The FORMULA expression defines the zero level set which is the actual constraint. It may be written as an equation, since '=' is parsed as a low-precedence minus sign. The formula may include any expressions whose values are known to the Evolver, given the particular vertex. Most commonly one just uses the coordinates (x,y,z) of the vertex, but one can use variables, quantity values, or vertex extra attributes. Using a vertex extra attribute is a good way to customize one formula to individual vertices. For example, if there were a vertex extra attribute called zfix, one could force vertices to individual z values with one constraint with the formula z = zfix, after of course assigning proper values to zfix for each vertex (be sure to fix up zfix after refining or otherwise creating vertices). Do not use '>' or '<' to indicate inequalities; use NONNEGATIVE or NONPOSITIVE. Conditional expressions, as in C language, are useful for defining constraints composed of several surfaces joined smoothly, such as a cylinder with hemispherical caps. Assignments to variables may be made at the start of expressions, mainly for the purpose of evaluating common subexpressions only once in the integrands. The syntax for such a compound expression is variable := expr, expr The value of the expression is the value of the second expression. The optional ENERGY section signifies that vertices or edges on the constraint are deemed to have an energy. In the soapfilm model, the next lines give components of a vectorfield that will be integrated along each edge on the constraint. In the string model, just one component is needed, which is evaluated at each vertex on the constraint. The main purpose of this is to permit facets entirely on the constraint to be omitted. Any energy they would have had should be included here. One use is to get prescribed contact angles at a constraint. This energy should also include gravitational potential energy due to omitted facets. Integrals are now also evaluated on fixed edges, which is a change from earlier versions of Evolver. The optional CONTENT section signifies that vertices (string model ) or edges (soapfilm model) on the constraint contribute to the area or volume of bodies. If the part of a body boundary that is on a constraint is not defined by facets, then the body volume must get a contribution from a content integral. It is important to understand how the content is added to the body in order to get the signs right. The integral is evaluated along the positive direction of the edge. If the edge is positively oriented on a facet, and the facet is positively oriented on a body, then the integral is added to the body. This may wind up giving the opposite sign to the integrand from what you think may be natural. Always check a new datafile when you load it to be sure the integrals come out right. <-------------------- constraint energy integrals ----------------------------> An edge on a level-set constraint may have an energy given by integrating a vectorfield F over the oriented edge: E = \int_{edge} F . dl. The integrand is defined in the constraint declaration in the datafile. The integral uses the innate orientation of the edge, but if the orientation attribute of the edge is negative, the value is negated. This is useful for prescribed contact angles on walls (in place of wall facets with equivalent tension) and for gravitational potential energy that would otherwise require facets in the constraint. The mound example illustrates this. See "constraint decl" for syntax. <-------------------- constraint_tolerance ----------------------------> Internal read-write variable. When vertices are projected to level-set constraints, projection by Newton's method is repeated until the level-set function is smaller than constraint_tolerance. Default value 1e-12. There is also a top-of-datafile declaration with the syntax CONSTRAINT_TOLERANCE constexpr <-------------------- content ----------------------------> "Content" is used to mean volume (or area, in the string model) in constraint integrals. See "constraint decl". <-------------------- continue ----------------------------> Command syntax for skipping the rest of the body of the current loop, and going to the next iteration of the loop. Syntax: CONTINUE CONTINUE n where n is a positive integer. The second form exits the innermost n-1 loops, and skips to the loop control of the nth innermost loop. Note: Commands with repetition counts do not qualify as loops. Example: foreach vertex vv do { foreach vv.edge do { print length; if length < .4 then continue 2; } } <-------------------- control structures ----------------------------> Control structures The following control structures are available in the Evolver commmand language: if ... then ... else do ... while .... while .... do ... for foreach break continue return <-------------------- convert_to_quantities ----------------------------> Evolver toggle command. This will do an automatic conversion of old-style energies to new-style named quantities. This has the same effect as the -q command line option, but can be done from the Evolver command prompt. A few energies don't convert yet. It is my intention that this will be the default sometime in the near future, if it can be made sufficiently fast and reliable. <-------------------- convex ----------------------------> <-------------------- convex constraint ----------------------------> A keyword used in declaring a constraint in the datafile that causes a gap energy to be included. The idea is to prevent straight edges and facets from short-cutting curved constraints with big gaps. See "gap energy". A constraint may be declared CONVEX, in which case edges in the constraint have an energy associated with them that is proportional to the area between the straight edge and the curved wall. This energy (referred to as "gap energy") is meant to compensate for the tendency for flat facets meeting a curved wall to minimize their area by lengthening some edges on the wall and shortening others, with the net effect of increasing the net gap between the edges and the wall. See tankex.fe for an example. <-------------------- coordinates ----------------------------> Vertex read-write attribute. The coordinates of a vertex are its location in space. By default, these are Euclidean coordinates, but they may represent any coordinate system if the user defines appropriate length, area, volume, etc. integrals. But graphics always treat the coordinates as Euclidean. The individual coordinates may be referred to as x,y,z,w or x1,x2,x3,... or x[1], x[2], x[3],... In the vertices section of the datafile, vertices of the original surface have their coordinates given unless they are on a parametric boundary. Vertices on parametric boundaries have their coordinates calculated from their parameter values. Coordinates may be read or modified with the command language. Examples: foreach vertex do printf "%g %f %f %f\n",id,x,y,z set vertex z z+.1*x <-------------------- corona_state --------(MPI Evolver)------> corona_state Internal read-write variable for MPI Evolver. The "corona" of a task's piece of surface is the set of elements imported from other tasks, so the task has sufficient knowlegde to do its work. The possible corona_state values are 0 (no corona), 1 (import neighbor edges and vertices), or 2 (import neighbor vertices, edges, and facets). Setting this variable changes the corona state. It is a very bad idea to set it to 0. <-------------------- cos ----------------------------> sin(x),cos(x),tan(x): Trig functions, argument in radians. <-------------------- cosh ----------------------------> sinh(x),cosh(x),tanh(x): Hyperbolic functions. <-------------------- cpu_counter ----------------------------> Internal read-only variable. Processor cycle counter, available only on systems where I know how to access this (x86 for now). Gives the number of CPU cycles since the system booted. Note that this is wall clock time, not process time. Also note that it resets to zero when a notebook computer hibernates, so it is not guaranteed to be monotone increasing during the life of a process! <-------------------- ps_crossingflag ----------------------------> Evolver toggle command. When "on" and the string model is in effect, postscript output will show background edges with a break where foreground edges pass in front. <-------------------- crossings ----------------------------> The interactive PostScript command "P 3" asks about several options. "Do crossings?" is asked if the surface is 1-dimensional (the string model) and the dimension of space is at least 3. If you reply 'y', a 3D effect will be created by plotting edges back to front, with each edge plotted first as a thick white line and then as a thin black line. This creates a broken back line and continuous foreground line at each crossing. Default 'n'. The postscript command uses the crossingflag toggle to control this. <-------------------- crystalline energy ----------------------------> The Evolver can model energies of crystalline surfaces. These energies are proportional to the area of a facet, but they also depend on the direction of the normal. The energy is given by the largest dot product of the surface normal with a set of vectors known as the Wulff vectors. Surface area can be regarded as a crystalline integrand whose Wulff vectors are the unit sphere. See the datafile section on Wulff vectors for more. A surface has either crystalline energy or surface tension, not both. Use is not recommended since nonsmoothness makes Evolver work poorly. Example datafile: crystal.fe <-------------------- cube example ----------------------------> Example: Cube evolving into a sphere. A sample datafile cube.fe comes with Evolver. The initial surface is a unit cube. The surface bounds one body, and the body is constrained to have volume 1. There is no gravity or any other force besides surface tension. Hence the minimal energy surface will turn out to be a sphere. This example illustrates the basic datafile format and some basic commands. The initial cube skeleton, with vertices and edges numbered. This is the datafile that specifies the initial unit cube: // cube.fe // Evolver data for cube of prescribed volume. vertices /* given by coordinates */ 1 0.0 0.0 0.0 2 1.0 0.0 0.0 3 1.0 1.0 0.0 4 0.0 1.0 0.0 5 0.0 0.0 1.0 6 1.0 0.0 1.0 7 1.0 1.0 1.0 8 0.0 1.0 1.0 edges /* given by endpoints */ 1 1 2 2 2 3 3 3 4 4 4 1 5 5 6 6 6 7 7 7 8 8 8 5 9 1 5 10 2 6 11 3 7 12 4 8 faces /* given by oriented edge loop */ 1 1 10 -5 -9 2 2 11 -6 -10 3 3 12 -7 -11 4 4 9 -8 -12 5 5 6 7 8 6 -4 -3 -2 -1 bodies /* one body, defined by its oriented faces */ 1 1 2 3 4 5 6 volume 1 // end of cube.fe The datafile is organized in lines, with one geometric element defined per line. Vertices must be defined first, then edges, then faces, then bodies. Each element is numbered for later reference in the datafile. Comments are delimited by /* to begin and */ to close as in C, or from // until the end of the line as in C++. Case is not significant, and all input is made lower-case immediately. Hence error messages about your datafiles will refer to items in lower case, even when you typed them in upper case. The datafile syntax is based on keywords. The keywords VERTICES, EDGES, FACES, and BODIES signal the start of the respective sections. Note that the faces are not necessarily triangles (which is why they are called FACES and not FACETS). Any non-triangular face will be automatically triangulated by putting a vertex at its center and putting in edges to each of the original vertices. Faces don't have to be planar. Note that a minus sign on an edge means that the edge is traversed in the opposite direction from that defined for it in the EDGES section. A face's oriented normal is defined by the usual right hand rule. The cube faces all have outward normals, so they all are positive in the body list. In defining a body, the boundary faces must have outward normals. If a face as defined has an inward normal, it must be listed with a minus sign. That the body is constrained to have a volume of 1 is indicated by the keyword VOLUME after the body definition, with the value of the volume following. Any attributes or properties an element has are given on the same line after its definition. Start Evolver and load the datafile with the command line evolver cube.fe You should get a prompt Enter command: Give the command s to show the surface. You should see a square divided into four triangles by diagonals. This is the front side of the cube; you are looking in along the positive x-axis, with the z axis vertical and the positive y axis to the right. On most systems, you can manipulate the displayed surface with the mouse: dragging the mouse over the surface with the left button down rotates the surface; you can change to "zoom" mode by hitting the z key, to "translate" by hitting t, to "spin" by hitting c, and back to "rotate" by hitting r. Hit the 'h' key with the mouse focus in the graphics window to get a summary of the possibilities. You can also give graphics commands at the graphics command prompt; this is good for precise control. The graphics command prompt is Graphics command: It takes strings of letters, each letter making a viewing transformation on the surface: The most used ones are r rotate right by 6 degrees l rotate left by 6 degrees u rotate up by 6 degrees d rotate down by 6 degrees R reset to original position q quit back to main command prompt Try typing rrdd to get an oblique view of the cube. Any transformations you make will remain in effect the next time you show the surface. Now do q to get back to the main prompt. If you are using geomview for graphics, do command P option 8 to get a display, or just "P 8" for short. Geomview takes a couple of seconds to initialize. You can manipulate the geomview display as usual independently of the Evolver. Evolver will automatically update the image whenever the surface changes. Now do some iterations. Give the command "g 5" to do 5 iterations. You should get this: 5. area: 5.11442065156005 energy: 5.11442065156005 scale: 0.186828 4. area: 5.11237323810972 energy: 5.11237323810972 scale: 0.21885 3. area: 5.11249312304592 energy: 5.11249312304592 scale: 0.204012 2. area: 5.11249312772740 energy: 5.11249312772740 scale: 0.204386 1. area: 5.11249312772740 energy: 5.11249312772740 scale: 0 Enter command: Note that after each iteration a line is printed with the iterations countdown, area, energy, and current scale factor. By default, the Evolver seeks the optimal scale factor to minimize energy. At first, there are large motions, and the volume constraint may not be exactly satisfied. There may be an energy increase due to the volume constraint taking hold. At the end, the scale is 0 because the surface has converged as well as it can at this coarse a triangulation. (Different systems may not give a zero scale here due to numerics.) Volume constraints are not exactly enforced, but each iteration tries to bring the volume closer to the target. Here that results in increases in area. You can find the current volumes with the v command: Body target volume actual volume pressure 1 1.000000000000000 0.999999779366360 3.408026016427987 The pressure in the last column is actually the Lagrange multiplier for the volume constraint. Now let's refine the triangulation with the r command. This subdivides each facet into four smaller similar facets. The printout here gives the counts of the geometric elements and the memory they take: Vertices: 50 Edges: 144 Facets: 96 Facetedges: 288 Memory: 27554 Iterate another 10 times: 10. area: 4.908899804670224 energy: 4.908899804670224 scale: 0.268161 9. area: 4.909526310166165 energy: 4.909526310166165 scale: 0.204016 8. area: 4.909119925577212 energy: 4.909119925577212 scale: 0.286541 7. area: 4.908360229118204 energy: 4.908360229118204 scale: 0.304668 6. area: 4.907421919968726 energy: 4.907421919968726 scale: 0.373881 5. area: 4.906763705259419 energy: 4.906763705259419 scale: 0.261395 4. area: 4.906032256943935 energy: 4.906032256943935 scale: 0.46086 3. area: 4.905484754688263 energy: 4.905484754688263 scale: 0.238871 2. area: 4.904915540917190 energy: 4.904915540917190 scale: 0.545873 1. area: 4.904475138593070 energy: 4.904475138593070 scale: 0.227156 You can continue iterating and refining as long as you have time and memory. Eventually, you will want to quit. So give the q command. You get Enter new datafile name (none to continue, q to quit): You can start a new surface by entering a datafile name (it can be the same one you just did, to start over), or continue with the present surface by hitting ENTER with no name (in case you pressed q by accident, or suddenly you remember something you didn't do), or you can really quit with another q. <-------------------- cubocta ----------------------------> <-------------------- cubocta symmetry group ----------------------------> The defined surface can be treated as the fundamental region of a larger surface with a symmetry. See "symmetry group". Cubocta is the full symmetry group of the cube. It can be viewed as all permutations and sign changes of (x,y,z). Datafile declaration: symmetry_group "cubocta" Group element encoding: wrap & {1,2,4} give the sign changes for x,y,z; (wrap&24)/8 is the power of the (xyz) permutation cycle; (wrap&32)/32 tells whether to then swap x,y. (By John Sullivan; source in quotient.c under name pgcube) <-------------------- curvature_binormal ----------------------------> Named method. Description: For string model. The energy evaluates to zero, but the force calculated is the mean curvature vector rotated to the binormal direction. Element: vertex. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: quantity curbi energy method curvature_binormal global <-------------------- curvature_function ----------------------------> Named method. Description: Calculates forces as function of mean and Gaussian curvatures at vertices. Function may be changed by user by altering teix.c. No energy, just forces. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: quantity curfun energy method curvature_function global <-------------------- curvature_power ----------------------------> Internal read-write variable that sets the power of curvature used in the string model when squared_curvature is declared in the top of the datafile or the sqcurve_string named method is used. <-------------------- d ----------------------------> Single letter main command. Dumps data to ASCII file in same format as initial data file. You will be prompted for a filename. An empty reponse will use the default dump name, which is the datafile name with a ".dmp" extension. Same as the "dump" command, except the dump command requires the filename as part of the command. Useful for checking your input is being read correctly, for saving current configuration, and for debugging. Graphics mode command. Tip down. Rotates image other way, default 6 degrees. An integer prefix indicates how many 6 degree rotations to do, and a decimal prefix indicates an angle in degrees. Example: `15d' does 90 degree rotation, `15.0d' does 15 degree rotation. <-------------------- D ----------------------------> Single letter main command. Toggles updating graphics every iteration or other surface change. Default is to display. Status can also be changed or queried with the autodisplay toggle. <-------------------- datafilename ----------------------------> Internal read-only variable. String containing name of current datafile. It is the datafile name as given by the user, without any path or extension added by the file finding routine. <-------------------- date_and_time ----------------------------> Internal read-only variable. String containing current date and time, as provided by the operating system. <-------------------- ddd_gamma_sq ----------------------------> Named method. Description: Third derivative of curve position as function of arclength, squared. Element: vertex. Parameters: none. Models: string, linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: quantity ddd energy method ddd_gamma_sq global <-------------------- debug ----------------------------> Evolver toggle command. Print YACC debug trace of parsing of commands. Don't do this! <-------------------- debug option ----------------------------> <-------------------- option -d ----------------------------> Command line option -d : Prints YACC debugging trace as datafile is parsed. Not too useful to end users. <-------------------- default symmetry ----------------------------> By default, the domain of a surface is Euclidean space. A symmetric surface can be done this way if its fundamental domain is bounded by mirror planes. Each mirror plane should be implemented as a linear level set constraint. <-------------------- define ----------------------------> Introduces syntax for defining various things in the top of the datafile: variables, arrays, and extra attributes of elements. "Define" can also be used at runtime to additionally define level set constraints, boundaries, named methods, and named quantities. The syntax for defining single variables is DEFINE variable type where type is REAL, INTEGER, or STRING. Note that this way of declaring a variable does not take an initial value; thus it is a way of making sure a variable is defined without overwriting an existing value of the variable. The syntax for defining arrays and extra attributes is the same as in the top of the datafile; for constraints, boundaries, named quantities, and method instances, it is the same as in the top of the datafile except the word "define" comes first. Multi-line definitions should be enclosed in brackets and terminated with a semicolon. Or they can be enclosed in quotes and fed to the exec command. Of course, using exec means the parser doesn't know about the define until the exec is executed, so you cannot use the defined item in commands until then. It is legal to re-define an existing array or array extra attribute with different dimensions (but the same number of dimensions); data will be preserved as best as possible in the resized array. An array may be given the dimension 0 to free its memory allocation. Examples: define fudge_factor real define pqlist real[imax][jmax] define edge attribute charlie real define vertex attribute oldx real[3] define facet attribute knots real[5][5][5] { define constraint frontcon formula z = 0 energy: e1: -y/2 e2: x/2 e3: 0; } exec "define boundary newboundary parameters 1 x: sin(p1) y: cos(p1) z: 3" exec "define quantity qarea info_only method facet_area global" <-------------------- delete ----------------------------> Main prompt command. For collapsing and removing edges or facets. Syntax: DELETE generator Deletes edges by shrinking the edge to zero length (as in the tiny edge weed command t) and facets by eliminating one edge of the facet. Facet edges will be tried for elimination in shortest to longest order. Edges will not be deleted if both endpoints are fixed, or both endpoints have different constraints or boundaries from the edge. In the soapfilm model, DELETE will also fail if it would create two edges with the same endpoints, unless the force_deletion toggle is on; also see star_finagling. DELETE maintains the continuity and connectedness of the surface, as opposed to DISSOLVE. Examples: delete edges where length < 0.01 and valence == 2 delete facets where area < 0.0001 <-------------------- delete_count ----------------------------> Internal read-only variable. Sum of edge_delete_count and facet_delete_count. Kept for backwards compatibility. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- delete_text ----------------------------> delete_text Main prompt command to delete a text string from the graphics display. Syntax: delete_text(text_id) where text_id is the value returned by the call to display_text that created the string. <-------------------- density ----------------------------> An element attribute referring either to the gravitational density of a body, or to the surface tension of a facet, or to the linear tension of an edge. In the latter two cases, "density" is synonymous with "tension". <-------------------- density_edge_length ----------------------------> Named method. Description: Length of edge, multiplied by the edge density. Quadratic model uses Gaussian quadrature of order integral_order_1D. Element: edge. Parameters: none. Models: linear, quadratic, Lagrange. Ambient dimension: any. Hessian: yes. Example datafile declaration: quantity len energy method density_edge_length global <-------------------- density_facet_area ----------------------------> Named method. Description: Area of facet, multiplied by its density. Otherwise same as facet_area. Element: Parameters: Models: linear, quadratic, Lagrange, simplex. Ambient dimension: any. Hessian: yes. Example datafile declaration: quantity farea energy method density_facet_area global <-------------------- density_facet_area_u ----------------------------> <-------------------- facet_area_u ----------------------------> Named method. Description: Area of facet. In quadratic model, it is an upper bound of area, by the Schwarz Inequality. For the paranoid. Same as facet_area in linear model. Sets integral_order_2D to 6, since it doesn't work well with less. Using the density_facet_area_u name automatically incorporates the facet tension, but facet_area_u doesn't. Element: facet. Parameters: none. Models: linear, quadratic. Ambient dimension: any. Hessian: yes. Example datafile declaration: quantity area_u energy method facet_area_u global <-------------------- deturck ----------------------------> Evolver toggle command. Motion by unit velocity along normal, instead of by curvature vector. <-------------------- diffusion ----------------------------> <-------------------- edge_diffusion ----------------------------> <-------------------- facet_diffusion ----------------------------> The Evolver can simulate the real-life phenomenon of gas diffusion between neighboring bubbles. This diffusion is driven by the pressure difference across a surface. This is invoked by the keyword DIFFUSION in the first part of the datafile, followed by the value of the diffusion constant. The amount diffused across a facet during an iteration is calculated as scale*diffusion_constant*facet_area*pressure_difference. The scale factor is included as the time step of an iteration. The amount is added to or subtracted from the prescribed volumes of the bodies on either side of the facet. If you want finer control over the rate of diffusion across various surfaces, you can define the edge_diffusion edge attribute in the string model or the facet_diffusion facet attribute in the soapfilm model and give individual values for edges or facets as you desire. If the attribute is defined, then its value is used instead of the global diffusion constant. Diffusion can be toggled at runtime with the "diffusion" toggle. <-------------------- diffusion_coeff ----------------------------> diffusion_coeff Internal read-write variable controlling the rate of diffusion of volume across surfaces when "diffusion" is in effect. If you want finer control over the rate of diffusion across various surfaces, you can define the edge_diffusion edge attribute in the string model or the facet_diffusion facet attribute in the soapfilm model and give individual values for edges or facets as you desire. If the attribute is defined, then its value is used instead of the global diffusion constant. <-------------------- dihedral ----------------------------> Vertex read-only attribute in the string model. This is the angle from straightness of two edges at a vertex. If there are less than two edges, the value is 0. If two or more edges, the value is 2*asin(F/2), where F is the magnitude of the net force on the vertex, assuming each edge has tension 1. Upper limit clamped to pi. Edge read-only attribute in the soapfilm model. The angle in radians between the normals of two facets on an edge. Zero if there are not exactly two facets. This attribute is not stored, but recalculated each time it is used. <-------------------- dihedral_hooke ----------------------------> Named method. Description: Energy of an edge is edge length times square of angle between normals of adjacent facets. Actually, energy = (1 - cos(angle))*length. Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration: quantity bender energy method dihedral_hooke global <-------------------- dirichlet ----------------------------> Main prompt command. Does one iteration of minimizing the Dirichlet integral of the surface. The current surface is the domain, and the Dirichlet integral is of the map from the current surface to the next. This is according to a scheme of Konrad Polthier and Ulrich Pinkall [PP]. At minimum Dirichlet integral, the area is minimized also. Works only on area with fixed boundary; no volume constraints or anything else. Seems to converge very slowly near minimum, so not a substitute for other iteration methods. But if you have just a simple soap film far, far from the minimum, then this method can make a big first step. DIRICHLET_SEEK will do an energy-minimizing search in the direction. <-------------------- dirichlet_area ----------------------------> Named method. Description: Same as the facet_tension method, but the Hessian is modified to be guaranteed positive definite, after the scheme of Polthier and Pinkall [PP]. The energy is taken to be the Dirichlet integral of the perturbation from the current surface, which is exactly quadratic and positive definite. Hence the hessian command always works, but final convergence may be slow (no faster than regular iteration) since it is only an approximate Hessian. Also see the dirichlet command. Element: facet. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration: quantity dirarea energy method dirichlet_area global <-------------------- dirichlet_elastic ----------------------------> dirichlet_elastic Named method. Description: Calculate the Dirichlet elastic strain energy for facets, minimization of which gives conformal mapping. Let S be Gram matrix of unstrained facet (dots of sides). Let Q be the inverse of S. Let F be Gram matrix of strained facet. Let C = FQ, the linear deformation matrix. Then energy density is Tr(CC^T) Each facet has an extra attribute array form_factors[3] = {s11,s12,s22}, which are the entries in S. That is, s11 = dot(v2-v1,v2-v1), s12 = dot(v2-v1,v3-v1), and s22 = dot(v3-v1,v3-v1). If form_factor is not defined by the user, it will be created by Evolver, and the initial facet shape will be assumed to be unstrained. Element: facet. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: yes. Example datafile declaration: quantity dirich energy method dirichlet_elastic global <-------------------- dirichlet_mode ----------------------------> When the facet_area method is being used to calculate areas in hessian commands, this toggles using an approximate facet_area hessian that is positive definite. This permits hessian iteration to make big steps in a far-from-minimal surface without fear of blowing up. However, since it is only an approximate hessian, final convergence to the minimum can be slow. Linear model only. Does convert_to_quantities implicitly. Another variant of this is triggered by sobolev_mode. <-------------------- dirichlet_seek ----------------------------> Main prompt command. Calculates a motion as in the DIRICHLET command, but uses this as a direction of motion instead of as the motion itself. DIRICHLET_SEEK then uses a line-search along this direction to find a minimum of energy. <-------------------- display_origin ----------------------------> Display_origin For a torus mode surface, if clipped mode is in effect, the center of the clip box is set with the display_origin[] array whose dimension is the dimension of the ambient space. This array does not exist by default, it has to be created by the user in the top of the datafile with the syntax display_origin x y z where x y z are the coordinates for the desired center of the clip box. At runtime, the array elements may be changed as normal: display_origin[2] := 0.5 Changing display_origin will automatically cause the graphics to re-display. <-------------------- display_periods ----------------------------> The displayed parallelogram unit cell can be different from the actual unit cell if you put an array called display_periods in the top of the datafile, in addition to the regular periods. For a string model example, parameter shear = 1 torus_filled periods 4 0 shear 4 display_periods 4 0 0 4 This will always display a square, no matter how much the actual unit cell is sheared. This feature works well for shears; it may not work nicely for other kinds of deformation. Display_periods works better for the string model than the soapfilm model. For the soapfilm model, it seems to do horizontal shears best, but it can't cope with large shears, so if your shear gets too large, I advise resetting your fundamental region to less shear, say with the unshear command in unshear.cmd. <-------------------- display_text ----------------------------> display_text Main prompt command. Causes the display of simple text on the graphics display. Currently implemented for OpenGL and PostScript graphics. Syntax: text_id := display_text(x,y,string) The x,y coordinates of the start of the string are in window units, i.e. the window coordinates run from (0,0) in the lower left to (1,1) in the upper right. The return value should be saved in a variable in case you want to delete the text later with delete_text(text_id); even if you don't want to delete it, you must have something on the left of the assignment for syntax purposes. No font size control or font type or color implemented. Meant for captioning images, for example a timer in frames of a movie. <-------------------- dissolve ----------------------------> Main prompt command. Removes elements from the surface without closing the gap left. Syntax: DISSOLVE generator The effect is the same as if the line for the element were erased from a datafile. Hence no element will be dissolved that is used by a higher dimensional element. (There are three exceptions: dissolving an edge on a facet in the string model, and dissolving a facet on one body or with both adjacent bodies the same in the soapfilm model.) Thus "dissolve edges; dissolve vertices" is safe because only unused edges and vertices will be dissolved. No error messages are generated by doing this. Good for poking holes in a surface. Examples: dissolve facets where original == 2; dissolve edges; dissolve vertices Thus "dissolve edges; dissolve vertices" is safe because only unused edges and vertices will be dissolved. No error messages are generated by doing this. <-------------------- dissolve_count ----------------------------> Internal read-only variable. Sum of vertex_dissolve_count, edge_dissolve_count, facet_dissolve_count, and body_dissolve_count. Kept for backwards compatibility. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- div_normal_curvature ----------------------------> Toggle to make sq_mean_curvature energy calculate the mean curvature by the divergence of the normal vectors at the vertices of a facet. <-------------------- do ----------------------------> Command syntax for post-test iteration loop. Syntax: DO command WHILE expr where expr is true if nonzero. Parentheses around expr are not needed, but do not hurt. Example: do { oldenergy := total_energy; g 10 } while (oldenergy-total_energy < 1e-6) <-------------------- dodecahedron symmetry group ----------------------------> The defined surface can be treated as the fundamental region of a larger surface with a symmetry. See "symmetry group". "Dodecahedron" is the symmetry group of translations of hyperbolic 3 space tiled with right-angled dodecahedra. The elements of the group are represented as integers. There are 32 generators of the group so each generator is represented by five bits. Under this scheme any element that is the composition of up to five generators can be represented. If you want to use this group, you'll have to check out the source code in dodecgroup.c, since somebody else wrote this group and I don't feel like figuring it all out right now. Datafile declaration: Klein_metric symmetry_group "dodecahedron" <------------------------- dot_product ----------------------------> Binary operator for vectors (1-dimensional arrays). Used in infix position. Example: print vertex[1].__x dot_product vertex[2].__x <------------------------- dump ----------------------------> Main prompt command. Dumps current surface to named file in datafile format. Syntax: DUMP filename filename is a string. With no filename, dumps to the default dump file, which is the current datafile name with ".dmp" extension. Same as the 'd' command, except 'd' requires a response from the user for the filename. Examples: dump "foo.dmp" dump sprintf "%s.%g.dmp",datafilename,counter <-------------------- dump_memlist ----------------------------> dump_memlist Main prompt command. Lists the currently allocated memory blocks. For my own use in debugging memory problems. <-------------------- dynamic load library ----------------------------> Many Evolver features, such as level set constraints, parametric boundaries, named method integrands, and Riemannian metrics require user-defined functions of a set of arguments. The expressions for these functions are ordinarily stored as a parse tree and interpreted each time needed, which can be much slower that evaluating compiled expressions. There is a way to use a set of compiled functions specific to a datafile through a mechanism known as dynamic loading. Here a library of functions for a datafile is separately compiled, and then loaded at runtime when a the datafile is loaded. Currently, the Evolver only implements a dynamic loading mechanism found on many unix systems, whose presence can be tested by looking for the existence of the file /usr/include/dlfcn.h. If it exists, you can enable dynamic loading by including -DENABLE_DLL in the CFLAGS line in the Makefile. On some systems, you may need to include -ldl on the GRAPHLIB line also, to link Evolver with functions such as dlopen(). To create the library for a datafile, write a source file containing C code for the desired functions, compile it, and link it into a shared library. The function should be able to compute the value and the partial derivatives of the function, and its second partials if you are going to use any Hessian features. A sample source file for a 2-dimensional datafile: #define FUNC_VALUE 1 #define FUNC_DERIV 2 #define FUNC_SECOND 3 #define MAXCOORD 4 /* must be same as in Evolver!! */ #define REAL double /* long double if Evolver compiled with -DLONGDOUBLE */ struct dstack { REAL value; REAL deriv[2*MAXCOORD]; REAL second[2*MAXCOORD][2*MAXCOORD]; }; void func1 ( mode, x, s ) int mode; /* FUNC_VALUE, FUNC_DERIV, FUNC_SECOND */ REAL *x; /* pointer to list of arguments */ struct dstack *s; /* for return values */ { REAL value; s->value = x[0] + x[1]*x[1]; if ( mode == FUNC_VALUE ) return; /* first partials */ s->deriv[0] = 1.0; s->deriv[1] = 2*x[1]; if ( mode == FUNC_DERIV ) return; /* second partials */ s->second[0][0] = 0.0; s->second[0][1] = 0.0; s->second[1][0] = 0.0; s->second[1][1] = 2.0; return; } Supposing the sourcefile name to be foo.c, compile and link on SGI systems (IRIX 5.0.1 or above) with cc -c foo.c ld -shared foo.o -o foo.so Sun systems are the same, but with -s in place of -shared. For other systems, consult the ld documentation for the option to make a shared library or dynamic load library. To use the functions in a datafile, include a line at the top of the datafile before any of the functions are used: load_library "foo.so" The current directory and the EVOLVERPATH will be searched for the library. Up to 10 libraries may be loaded. Afterwards, any of the functions may be invoked just by using their name, without an explicit argument list because the argument list is always implicit where these functions are legal. Examples, supposing func2 is also defined with one argument: constraint 1 formula: func1 boundary 1 parameters 2 x1: func2 x2: 3*func2 + sin(p1) It is up to you to make sure the number of arguments your function expects is the same as the number implicit in the use of the function. You do not need to explicitly declare your functions in the datafile. Any undefined identifier is checked to see if it is a dynamically loaded function. NOTE: This implementation of dynamic loading is experimental, and the interface described here may change in the future. <-------------------- e ----------------------------> Single letter main command. Extrapolates total energy to infinite refinement if at least two r commands have been done. Uses last energy values at three successive levels of refinement, and uses a power law fit for the error. For best results, use only the r command to refine, and iterate to complete convergence at each level of refinement. Synonym: extrapolate. Graphics mode command. Toggle showing all the facet edges. <-------------------- __e_constraint_list ----------------------------> This read-only attribute gives access to the list of constraints an edge is on. __e_constraint_list[1] is the number of constraints in the list, followed by the numbers of the constraints. Note that for named constraints, the internally assigned numbers are used. <-------------------- echo option ----------------------------> <-------------------- option -e ----------------------------> Command line option -e : Echo input. Meant for echoing commands of piped input to screen so the user can follow what is going on in case Evolver is being controlled by another process. <-------------------- edge ----------------------------> In general, one of the basic geometric elements. As a keyword, it is used in element generators. Synonym: edges. Examples: list edges where on_constraint 2 set edge[3] color red list facet[2].edge[2] <-------------------- edge boundary ----------------------------> Edge read-write attribute. If an edge is on a parametric boundary, then any edges and vertices generated from the edge will inherit the boundary. By default, new vertex parameter values are calculated by extrapolating from one end of the edge. This avoids wrap-around problems that would arise from interpolating parameter values. But if the interp_bdry_param toggle is on, then interpolation is used. The status of whether an edge is on a boundary can be queried with the Boolean attribute on_boundary. Edges can be unset from boundaries, and set on them (but care is needed to do this properly). Examples: list edges where on_boundary 1 unset edges boundary 2 <-------------------- edge color ----------------------------> Edge read-write attribute. Color for graphics. The default color is black. Color may be set in the datafile, or with the set command. In geomview, the edge color will show up only for edges satisfying the show edge condition, and then they will have to compete with the edges geomview draws, unless you turn off geomview's drawing of edges with "ae" in the geomview window. Examples: set edge color red where length > 1 show edge where color != black <-------------------- edge constraints ----------------------------> Edge read-write attribute. An edge may be put on a level set constraint. For such an edge, any vertices and edges generated by refining the edge will inherit the constraint. An edge may be put on constraints in the edges section of the datafile by listing the constraint numbers after the keyword constraint on the line defining the edge. Putting an edge on a constraint does not put its existing vertices on the constraint. In commands, the status of an edge can be read with the "on_constraint" attribute. The status can be changed with the set or unset commands. Examples: list edge where on_constraint 2 set edge constraint 1 where id == 4 or id == 6 unset edge constraint 3 <-------------------- edge density ----------------------------> <-------------------- edge tension ----------------------------> Edge read-write attribute. "Density" and "tension" are synonyms. Energy per unit length of edge. Default 1 in string model, 0 in soapfilm model. The tension may be modified in the datafile edges section by adding "tension value" to the line defining the edge. The tension may be modified with the set command. Examples: set edge tension .5 where id < 10 loghistogram(edge,density) <-------------------- edge facets ----------------------------> Edge read-only attribute. Generates facets attached to an edge, in order around the edge when meaningful, with facet orientation agreeing with edge orientation. Examples: list edge[2].facets foreach edge ee do print max(ee.facets,area) <-------------------- edge length ----------------------------> Edge read-only attribute. Length of the edge. Examples: histogram(edge where on_constraint 1, length) print edge[3].length <-------------------- edge noncontent ----------------------------> Edge read-write attribute. When set, indicates this facet should not be used in volume calculations in the soapfilm model or facet area calculations in the string model. Useful, for example, if you want to have edges be part of a body boundary for display purposes, but want to use constraint integrands for greater accuracy in volume calculations. Example: set edge noncontent where on_constraint 1 <-------------------- edge orientation ----------------------------> Edge read-write attribute. Controls the sign of oriented integrals on an edge. Value +1 or -1. Useful when triangulation manipulations create an edge going the wrong way. Example: set edge[2] orientation -1 <-------------------- edge tangent ----------------------------> Edge read-only attribute. The components of the edge vector in the linear model can be accessed as edge attributes x,y,z or x1,x2,x3,.... In a command, the vector between edge endpoints is used in quadratic model or lagrange model. But when used in an integral, the tangent is evaluated at the Gaussian integration points. Not defined in the simplex model. Example to list nearly vertical edges: list edges where z^2 > 10*(x^2 + y^2) <-------------------- edge valence ----------------------------> Edge read-only attribute. The valence of an edge is the number of facets adjacent to it. Examples: list edges where valence == 1 refine edge where valence != 2 <-------------------- edge vertices ----------------------------> Edge vertices Edge read-only attribute. Acts as a generator for the two endpoints in the linear and quadratic models, and for all vertices on an edge in the Lagrange and simplex models. Example: list edge[2].vertices list edge ee where ee.vertex[1].on_constraint 1 <-------------------- edge_area ----------------------------> Named method. Description: For calculating the area of a body in the string model. Implemented as the exact integral of -y dx over the edge. Valid for torus model, but not general symmetry groups. You may have to set the quantity volconst attribute in the torus model, since the area calculation is ambiguous up to one torus area. Element: edge. Parameters: none. Models: linear, quadratic, Lagrange. Ambient dimension: 2. Hessian: yes. Example datafile declaration: quantity cell1_area fixed = 1.3 method edge_area <-------------------- edge_count ----------------------------> Internal read-only variable. Number of edges in the total surface. <-------------------- edge_delete_count ----------------------------> Internal read-only variable. Number of edges deleted by delete command. This does not count the secondary edge deletions caused by deleting an edge. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- edge_dissolve_count ----------------------------> Internal read-only variable. Number of edges dissolved by dissolve command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- edge_divide ----------------------------> Main command, synonym of 'l' (lower-case 'L'). Subdivides long edges, creating new facets as necessary. Syntax: edge_divide expr You will be prompted for a cutoff edge length, if you don't give a value with the command. Existing edges longer than the cutoff will be divided once only. Newly created edges will not be divided. Hence there may be some long edges left afterward. If you enter h, you will get a histogram of edge lengths. If you hit RETURN with no value, nothing will be done. It is much better to use the refine command r than to subdivide all edges. This command does not respect the no_refine attribute. <-------------------- edge_edge_knot_energy ----------------------------> Named method. Description: Between pairs of edges, energy is inverse square power of distance between midpoints of edges. Can also be called just edge_knot_energy. See also edge_knot_energy_normalizer. (by John Sullivan) Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: quantity knotten energy method edge_edge_knot_energy global <-------------------- edge_general_integral ----------------------------> Named method. Description: Integral of a scalar function of position and tangent over an edge. The components of the tangent vector are represented by continuing the coordinate indices. That is, in 3D the position coordinates are x1,x2,x3 and the tangent components are x4,x5,x6. For proper behavior, the integrand should be homogeneous of degree 1 in the tangent components. Uses Gaussian quadrature of order integral_order_1D. Element: edge. Parameters: scalar_integrand. Models: linear, quadratic, Lagrange. Ambient dimension: any. Hessian: yes. Example datafile declaration: the edge length in 3D could be calculated with this quantity: quantity arclength energy method edge_general_integral scalar_integrand: sqrt(x4^2 + x5^2 + x6^2) <-------------------- edge_k_vector_integral ----------------------------> Named method. Description: Integral of a simple (n-k)-vector over an oriented k-dimensional simplicial edge in n-space. The vector integrand lists the components of each of the k vectors sequentially. Evaluation is done by forming a determinant whose first k rows are k vectors spanning the edge, and last (n-k) rows are vectors of the integrand. Element: edge. Parameters: k_vector_order, vector_integrand. Models: linear, quadratic, simplex. Ambient dimension: any. Hessian: yes. Orientable: yes. Example datafile declaration, for 3D edges of a 4D surface in 5D: quantity kvec energy method edge_k_vector_integral k_vector_order 3 vector_integrand: q1: 0 // first vector q2: 0 q3: 0 q4: 0 q5: x4 q6: 0 // second vector q7: 0 q8: 0 q9: x3 q10: 0 <-------------------- edge_knot_energy ----------------------------> Named method. Description: Between pairs of edges, energy is inverse square power of distance between midpoints of edges. Can also be called just edge_knot_energy. See also edge_knot_energy_normalizer. (by John Sullivan) Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: quantity knotten energy method edge_edge_knot_energy global <-------------------- edge_knot_energy_normalizer ----------------------------> Named method. Description: Calculates internal knot energy to normalize singular divergence of integral of edge_edge_knot_energy. Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: quantity knotten energy method edge_edge_knot_energy global method edge_knot_energy_normalizer global <-------------------- edge_length ----------------------------> <-------------------- edge_tension ----------------------------> Named method. Description: Length of edge. Quadratic or Lagrange model uses Gaussian quadrature of order integral_order_1D. edge_length and edge_tension are synonyms. Element: edge. Parameters: none. Models: linear, quadratic, Lagrange. Ambient dimension: any. Hessian: yes. Example datafile declaration: quantity len energy method edge_length global <-------------------- edge_merge ----------------------------> Main prompt command. Merges two edges into one in a side-by-side fashion. Meant for joining together surfaces that bump into each other. Should not be used on edges already connected by a facet, but merging edges that already have a common endpoint(s) is fine. Syntax: edge_merge(integer,integer) Note the arguments are signed integer ids for the elements, not element generators. The tails of the edges are merged, and so are the heads. Orientation is important. Example: edge_merge(3,-12) <-------------------- edge_min_knot_energy ----------------------------> Named method. Description: Between pairs of edges, energy is inverse square power of distance between closest points of edges: Energy = 1/d^2 * |e1||e2| This should be roughly the same as edge_edge_knot_energy, but distances are calculated from edge midpoints there. This is not a smooth function, so we don't try to compute a gradient. DO NOT use as an energy; use just for info_only quantities. Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: quantity eminknot info_only method edge_min_knot_energy global <-------------------- edge_pop_count ----------------------------> Internal read-only variable. Number of edges popd by pop edges, o, or O, commands. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- edge_refine_count ----------------------------> Internal read-only variable. Number of edges refined by refine edges command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- edge_scalar_integral ----------------------------> Named method. Description: Integral of a scalar function over arclength. Uses Gaussian quadrature of order integral_order_1D. Element: Parameters: Models: linear, quadratic, Lagrange. Ambient dimension: any. Hessian: yes. Type: edge. Parameters: scalar_integrand. Example datafile declaration: quantity edge_sint energy method edge_scalar_integral scalar_integrand: x^2 - 3*y + 4 <-------------------- edge_torus_area ----------------------------> Named method. Description: For 2D torus string model body area calculations. Contains adjustments for torus wraps. You may have to set the quantity volconst attribute in the torus model, since the area calculation is ambiguous up to one torus area. Element: edge. Parameters: none. Models: torus; string; linear,quadratic,Lagrange. Ambient dimension: 2. Hessian: no. Example datafile declaration: quantity cell_area fixed = 1.3 method edge_torus_area <-------------------- edge_vector_integral ----------------------------> Named method. Description: Integral of a vectorfield over an oriented edge. Uses Gaussian quadrature of order integral_order_1D. Element: edge. Parameters: vector_integrand. Models: linear, quadratic, Lagrange. Ambient dimension: any. Hessian: yes. Orientable: yes. Example datafile declaration: quantity edge_vint energy method edge_vector_integral vector_integrand: q1: 0 q2: 0 q3: z^2/2 <-------------------- edges ----------------------------> An edge is a one-dimensional geometric element. In the linear model, an edge is an oriented line segment between a tail vertex and a head vertex. In the quadratic model, an edge is defined by quadratic intepolation of two endpoints and a midpoint. In the lagrange model, an edge is defined by the appropriate order interpolation with the edge vertices. In the string model, edges carry a default surface tension energy proportional to their length. Edges may also carry energy by being on level set constraints in the soapfilm model, or by having named quantity energies applied to them. The edges of the original surface are defined in the edges section of the datafile. Attributes: > id > oid > original > length > density or tension > fixed > constraints > on_constraint > __e_constraint_list > boundary > on_boundary > wrap > color > noncontent > bare > no_refine > orientation > vertices > midv > facets > valence > backbody > frontbody > dihedral > tangent vector > quantities > on_quantity > on_method_instance > extra attributes <-------------------- edges section ----------------------------> The datafile edge list follows the vertex list, and is started by the keyword EDGES at the start of a line. It is followed by lines with one edge specification per line in this format (linespliced here): k v1 v2 [midv] [s1 s2 s3] [WRAP w] [FIXED] [BOUNDARY b] \ [CONSTRAINTS c1 [c2 ...]] [TENSION constexpr] [COLOR n] \ [BARE] [quantityname ...] [methodname ...] Here k is the edge number, with numbering following the same rules as for vertices. v1 and v2 are the numbers of the tail and head vertices of the edge. In the quadratic model, the edge midpoint may be listed as a third vertex midv (otherwise a midpoint vertex will be created). In the torus model, there must follow signs s1 s2 s3 indicating how the edge wraps around each unit cell direction: + for once positive, * for none, and - for once negative. In non-torus symmetry groups, each edge should have a WRAP symmetry group element encoded as an integer. FIXED means that all vertices and edges resulting from subdividing this edge will have the FIXED attribute; it does not mean that the endpoints will be automatically fixed. Likewise the BOUNDARY and CONSTRAINT attributes will be inherited by all edges and vertices derived from this edge. If a constraint has energy or content integrands, these will be done for this edge. IMPORTANT: If a constraint number is given as negative, the edge energy and content integrals will be done in the opposite orientation. In the string model, the default tension is 1, and in the soapfilm model, the default tension is 0. However, edges may be given nonzero tension in the soapfilm model, and they will contribute to the energy. If the simplex model is in effect, edges are one less dimension than facets and given by an ordered list of vertices. Only edges on constraints with integrals need be listed. The BARE attribute is just an instruction to the checking routines that this ede is not supposed to have an adjacent facet in the soapfilm model, so spurious warnings will not be generated. This is useful when you want to show bare wires or outline fundamental domains. An arbitrary number of named quantities or method instances may be listed. These add method values for this element to the must have been declared in the top section of the datafile. If the quantity or instance has orientation-dependent methods, the name may be followed by a dash to reverse the applied orientation. The list edges command prints the datafile format listing of edges. <-------------------- edgeswap ----------------------------> Main prompt command. For changing the endpoints of edges. Syntax: EDGESWAP edgegenerator If any of the qualifying edges are diagonals of quadrilaterals, they are flipped in the same way as in equiangulation, regardless of whether equiangularity is improved. "edgeswap edge" will try to swap all edges, and is not recommended, unless you like weird things. Various conditions will prevent an edge from being swapped: > The edge is fixed. > There are not exactly two facets adjacent to the edge. > The adjacent facets do not have equal density. > The adjacent facets are not on the same level set constraints as the edge. > The adjacent facets are not on the same parametric boundary as the edge. > Swapping would create an edge with both endpoints the same (a loop). > Swapping would create two edges with the same endpoints (an "ear"). All but the first two reasons print messages. This is a compromise between informing the user why edges were not switched and preventing a cascade of messages. When edge swapping is invoked through the 'u' command, none of these messages are printed. Examples: edgeswap edge[22] edgeswap edge where color == red <-------------------- edgeswap_count ----------------------------> Internal read-only variable. Number of edges swapped by edgeswap command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- edgeweed ----------------------------> Main prompt command. Deletes edges shorter than given value. Syntax: EDGEWEED expr Same as 't' command, except does not need interactive response. Same as "delete edge where length < expr". <-------------------- eff_area_sq_mean_curvature ----------------------------> Named method. Description: Integral of squared mean curvature of a surface, with a slightly different definition from sq_mean_curvature or normal_sq_mean_curvature. The area around a vertex is taken to be the magnitude of the gradient of the volume. This is less than the true area, so makes a larger curvature. This also eliminates the spike instability, since a spike has more area gradient but the same volume gradient. Letting N be the volume gradient at vertex v, h = (1/2)(F/N)), and E = h^2 A/3 = (3/4)(F.F/N.N)A. The facets of the surface must be consistently oriented for this to work, since the evolver needs an `inside' and `outside' of the surface to calculate the volume gradient. There are still possible instabilities where some facets grow at the expense of others. If the parameter or vertex attribute h_zero is defined, then the value per vertex is E = (h-h_0)^2 A/3 = (3/4)(F.N/N.N-2h_0)^2A. This does not reduce to the non-h_zero formula when h_zero has the value zero, but is actually a pretty good formula in its own right (see star_perp_sq_mean_curvature . If the vertex is on one or several constraints, the F and N are projected to the constraints, essentially making the constraints act as mirror symmetry planes. WARNING: For some extreme shapes, Evolver may have problems detecting consistent local surface orientation. The assume_oriented toggle lets Evolver assume that the facets have been defined with consistent local orientation. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: quantity effsq energy method eff_area_sq_mean_curvature global <-------------------- effective area ----------------------------> A type of mobility. Simple area normalization as described in "area_normalization" paragraph isn't what's really wanted in certain circumstances, since it has equal resistance for motion in all directions, both parallel and normal to the surface. If a vertex is a triple junction and migrating along the direction of one of the edges, it shouldn't matter how long that edge is. Therefore, if the effective area mode is in effect, the area associated with a vertex is the area of its star projected normal to the force at the vertex. This is a little more complicated calculation, but it is still local. S and M are block diagonal matrices, with one block for each vertex (see mobility). At a free edge not on any constraint, the force is tangent to the surface, the resistance is zero, and the mobility is infinite. But this accurately describes a popping soapfilm. Effective area can be toggled with the effective_area toggle. Note that area normalization itself must still be toggled with a or area_normalizaton. <-------------------- effective polyhedral ----------------------------> Approximate polyhedral curvature with effective area. Polyhedral curvature does not make any distinction between motion parallel and perpendicular to the surface. A better approximation is to count only motion perpendicular to the surface. This can be done by projecting the interpolated vectorfields normal to the facets before integrating their scalar product. Now the rate of area decrease is equal to the rate geometric volume is swept out, as opposed to the slightly flaky way one had to calculate volume sweeping in the previous paragraph. Again S is a sparse matrix with entries corresponding to each pair of vertices joined by an edge, and M is its dense inverse. The effective area option may be toggled with effective_area. <-------------------- effective_area ----------------------------> Evolver toggle command. In area normalization, the resistance factor to motion is taken to be only the projection of the vertex star area perpendicular to the motion. If squared mean curvature is being calculated, this projected area is used in calculating the curvature. <-------------------- efixed ----------------------------> Obsolete datafile attribute to make an edge fixed without fixing its endpoints. Keyword retained just for compatibility with old dump files. <-------------------- eigen_neg ----------------------------> <-------------------- eigenneg ----------------------------> Internal read-only variable. Number of negative eigenvalues in last Hessian factoring. eigen_neg and eigenneg are synonyms. <-------------------- eigen_pos ----------------------------> <-------------------- eigenpos ----------------------------> Internal read-only variable. Number of positive eigenvalues in last Hessian factoring. eigen_pos and eigenpos are synonyms. <-------------------- eigen_zero ----------------------------> <-------------------- eigenzero ----------------------------> Internal read-only variable. Number of zero eigenvalues in last Hessian factoring. eigen_zero and eigenzero are synonyms. <-------------------- eigenprobe ----------------------------> Main prompt command. For finding the number of eigenvalues of the energy Hessian that are less than, equal to, and greater than a given value. Syntax: EIGENPROBE expr EIGENPROBE(expr,expr) The first form prints the number of eigenvalues of the energy Hessian that are less than, equal to, and greater than expr. It is OK to use an exact eigenvalue (like 0, often) for the value, but not really recommended. Useful for probing stability. Second form will further do inverse power iteration to find an eigenvector. The second argument is the limit on the number of iterations. The eigenvalue will be stored in the last_eigenvalue internal variable, and the eigenvector can be used by the move command. The direction of the eigenvector is chosen to be downhill in energy, if the energy gradient is nonzero. <-------------------- eigenvalues ----------------------------> Internal read-only array. Contains the list of eigenvalues produced by the ritz command. Example: ritz(-1,10) print eigenvalues[2] <-------------------- element lists ----------------------------> Element lists The datafile lists of geometric elements follow a general format. Each element is defined on one line. The first entry on a line is the element number. Numbering need not be consecutive, and may omit numbers, but be aware that internally elements will be renumbered in order. The original number in the datafile is accessible as the original attribute of an element. After the element number comes the basic defining data, followed by optional attributes in arbitrary order. Besides the particular attributes for each element type listed below, one may specify values for any extra attributes defined earlier. The syntax is attribute name followed by the appropriate number of values. Also an arbitrary number of named quantities or method instances may be listed. These add method values for this element to the named quantity. The named quantity or instance must have been declared in the top section of the datafile. <-------------------- element orientation ----------------------------> Some methods, those that logically depend on the orientation of the element, can be applied with a relative orientation. When applied to individual elements in the datafile, a negative orientation is indicated by a '-' after the instance name. When applied at runtime with the set command, the orientation will be negative if the element is generated with negative orientation, i.e. set body[1].facet method_instance qqq. The methods currently implementing this feature are: edge_vector_integral, string_gravity, facet_vector_integral, facet_2form_integral, facet_volume, facet_torus_volume, simplex_vector_integral, simplex_k_vector_integral, edge_k_vector_integral, gravity_method, and full_gravity_method. See also "edge orientation" and "facet orientation" <-------------------- element_modulus ----------------------------> A method instance may be declared to use a different modulus for each element by specifying an element extra attribute to use for that purpose. The extra attribute has to have already been declared. Example: define facet attribute mymod real quantity myquant energy method facet_area global element_modulus mymod Of course, it is up to the user to properly initialize the values of the extra attribute. <-------------------- ellipticE ----------------------------> ellipticE(x),ellipticK(x): Complete elliptic functions. <-------------------- ellipticK ----------------------------> ellipticE(x),ellipticK(x): Complete elliptic functions. <-------------------- else ----------------------------> IF ... THEN ... ELSE Commands may be conditionally executed by the syntax IF expr THEN command IF expr THEN command ELSE command expr is true if nonzero. Parentheses around expr are not needed, but do not hurt. Do not use a semicolon to end the first command. Example: if max(edges,length) > 0.02 then {r; g 100} else g 4 <-------------------- energies ----------------------------> <-------------------- energy ----------------------------> The Evolver usually works by minimizing the total energy of the surface, subject to constraints. This energy can have several components: > Surface tension > Gravitational potential energy > Constraint energy integrals > Named quantity energies > Convex constraint gap energy > Prescribed pressure energy > Compressibility energy > Crystalline energy The read-only variable total_energy has the total energy of the surface. <-------------------- energy quantity ----------------------------> Each named quantity is one of four types: > "energy" quantities which are added to the total energy of the surface; > "fixed" quantities that are constrained to a fixed target value (by Newton steps at each iteration); and > "conserved" quantities are like fixed, but the value is irrelevant. The quantity gradient is used to eliminate a degree of freedom in motion. Rarely used, but useful to eliminate rotational degree of freedom, for example. Will not work with optimizing parameters, since they do gradients by differences. > "info_only" quantities whose values are merely reported to the user. This type is initially set in a quantity's datafile declaration. A quantity can be toggled between fixed and info_only with the "fix quantityname" and "unfix quantityname" commands. <-------------------- eprint ----------------------------> Function that prints an expression and returns the value. Syntax: eprint expr. Meant for debugging; probably an archaic leftover from when the command language was not as developed. Example: print sum(facet, eprint area) will print out all the facet areas and then the sum. <-------------------- equi_count ----------------------------> Internal read-only variable. Number of edges flipped by equiangulation. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- equiangulate ----------------------------> Main prompt command. This command tests the given edges to see if flipping them would improve equiangularity. It is the u command applied to a specified set of edges. It differs from the edgeswap command in that only edges that pass the test are flipped. Syntax: EQUIANGULATE edge_generator Examples: equiangulate edge[3]; equilangulate edge where color == red; <-------------------- equiangulation ----------------------------> <-------------------- u ----------------------------> Main command. This command, called "equiangulation" or "u", tries to polish up the triangulation. In the soapfilm model, each edge that has two neighboring facets (and hence is the diagonal of a quadrilateral) is tested to see if switching the quadrilateral diagonal would make the triangles more equiangular. For a plane triangulation, a fully equiangulated triangulation is a Delaunay triangulation, but the test makes sense for skew quadrilaterals in 3-space also. It may be necessary to repeat the command several times to get complete equiangulation. The edgeswap command can force flipping of prescribed edges. In the simplex model, equiangulation works only for surface dimension 3. There, two types of move are available when a face of a tetrahedron violates the Delaunay void condition: replacing two tetrahedra with a common face by three, or the reverse operation of replacing three tetrahedra around a common edge by two, depending on how the condition is violated. This command is inoperative in the string model. <-------------------- ergb ----------------------------> Edge attribute used for RGB colors. See rgb_colors. <-------------------- error handling ----------------------------> When the Surface Evolver detects an error, it prints an error message and tries to take appropriate action. If the -x command line option was given when Evolver was started, then Evolver exits immediately with a nonzero error code. This is useful when running Evolver from shell scripts. There are several categories of errors: > WARNING - Something has happened that you should know about, but Evolver proceeds normally after printing the message. > SYNTAX ERROR - The parser has detected an error. Recent input up to the detection of the error is printed, but the actual problem may be earlier. > DATAFILE ERROR - There is an error in the datafile being read in. Evolver attempts to recover by skipping to the next recognizable part of the datafile, but will abandon the datafile after 5 such errors. The surface data that was read in is available for your inspection, but it probably forms an inconsistent surface and you should not try to evolve it. > ERROR - This is an error encountered during the execution of a command. The command is abandoned and Evolver returns to the main prompt. The actions of the command are not undone. > FATAL ERROR - An error from which recovery is impossible. Evolver exits immediately. <-------------------- errprintf ----------------------------> Main prompt command. Same as printf, except it sends its output to stderr instead of stdout. Useful in reporting error messages in scripts that have their output redirected to a file. <-------------------- estimate ----------------------------> Evolver toggle command. Activates estimation of energy decrease in each gradient descent step (g command). For each "g" iteration, it prints the estimated and actual change in energy. The estimate is computed by the inner product of energy gradient with actual motion. Useful only for a fixed scale factor much less than optimizing, so linear approximation is good. The internal variable estimated_change records the estimated value. <-------------------- estimated_change ----------------------------> Internal read-only variable. Estimated change of energy during last iteration with estimate option in effect. <-------------------- Euclidean metric ----------------------------> The default metric on the ambient space is the ordinary Euclidean metric. There are no built-in units of measurement like meters or grams, so the user should express all physical quantities in some consistent system of units, such as MKS or cgs. <-------------------- everything_quantities ----------------------------> Keyword in top section of the datafile. Causes all areas, volumes, etc. to be converted to named quantities and methods. Equivalent to the command line option -q, or the convert_to_quantities command. <-------------------- evolver_version ----------------------------> If a datafile contains features present only after a certain version of the Evolver, the datafile can contain a line of the form evolver_version "2.10" This will generate a version error message if the current version is earlier, or just a syntax error if run on an Evolver version earlier than 2.10. <-------------------- EVOLVERPATH ----------------------------> Evolver consults the system environment variable EVOLVERPATH for a list of directories to search when it tries to open a file. The format and setting of EVOLVERPATH depend on the system, but is the same as the standard PATH environment variable. Unix C shell: setenv EVOLVERPATH /usr/you/evolver/fe:/usr/you/evolver/doc Bourne shell: EVOLVERPATH=/usr/you/evolver/fe:/usr/you/evolver/doc export EVOLVERPATH Windows: Open Control Panel/System/Advanced/Environment Variables. Click "New" under System Variables, entering EVOLVERPATH for the Variable name, and c:\evolver\fe;c:\evolver\doc for the Variable value. You may add further paths of your own to this list if you wish. <-------------------- exec ----------------------------> Main prompt command. Executes a command in string form. Good for runtime generation of commands. Syntax: EXEC stringexpr Example: exec sprintf "define vertex attribute prop%d real",propnumber <-------------------- exit error option ----------------------------> <-------------------- option -x ----------------------------> Command line option -x : Causes Evolver to exit whenever a warning occurs. Meant to be used when Evolver is run in a shell script. <-------------------- exit warning option ----------------------------> <-------------------- option -w ----------------------------> Command line option -w : Causes Evolver to exit whenever a warning occurs. Meant to be used when Evolver is run in a shell script. <-------------------- exp ----------------------------> log(x) ,exp(x) : Natural log, exponentiation base e. <-------------------- expressions ----------------------------> Arithmetic expressions Arithmetic expressions evaluate to real numbers. Boolean expressions are a subclass, with zero as false and nonzero as true; true results evaluate as 1. Ordinary algebraic notation is used. Types of expressions: atomic values arithmetic operators boolean operators operator precedences math functions aggregate functions <-------------------- exprint ----------------------------> Main prompt command. Prints the original input string defining a user-defined command, including comments. Syntax: EXPRINT commandname Example: Enter command: aa := { print 5; /* this is a test */ } Enter command: exprint aa { print 5; /* this is a test */ } <-------------------- extra attributes ----------------------------> Geometric element read-write attributes. If extra attributes have been defined in the datafile or with a define command, they can be accessed with attribute syntax. Extra attribute values in the datafile can be initialized for an element by adding the attribute name and value to the line defining the element. Example: define vertex attribute oldx real vertices 1 2 0 0 oldx 3 The command language can use the name with the same syntax as built-in attributes, and can define extra attributes at run time: set vertex oldx x define edge attribute vibel real[2] set edge[2] vibel[1] 3; set edge[2] vibel[2] 4 print vertex[3].oldx The value of an extra attribute can also be calculated by user-supplied code. The attribute definition is followed by the keyword "function" and then the code in brackets. In the code, the keyword "self" is used to refer to the element the attribute is being calculated for. Example: To implement the lowest z value of a facet as an attribute: define facet attribute minz real function {self.minz := min(self.vertex,z);} These attributes can also be indexed. Due to current parser limitations on parsing executable code, this type of extra attribute definition cannot occur in the top section of the datafile, although the non-function version can to declare the attribute name, and the function part added in a re-definition in the READ section of the datafile. <-------------------- extra declaration ----------------------------> Extra attribute declarations. It is possible for the user to define extra attributes for elements, which may be single values or up to eight-dimensional arrays. If these attributes are to be included in the datafile, then the top section of the datafile must contain appropriate definitions. The definition syntax is the same as used by the define runtime command: DEFINE elementtype ATTRIBUTE name type [dim]... where elementtype is vertex, edge, facet, or body, name is an identifier of your choice, type is REAL or INTEGER (internally, there is also a ULONG unsigned long type also), and dim is an optional expression for the vector dimension. There is no practical distinction between real and integer types at the moment, since everything is stored internally as reals. But there may be more datatypes added in the future. Extra attributes are inherited by elements of the same type generated by subdivision. The type may be followed by FUNCTION followed by a procedure in brackets to be evaluated whenever the value of the attribute is read; in the formula, self may be used to refer to the element in question to use its attributes, in particular to at some point assign a value to the attribute. The print command may be used to print attribute arrays or array slices in bracketed form. Examples: define edge attribute charlie real define vertex attribute oldx real[3] define facet attribute knots real[5][5][5] define edge attribute bbb real function { self.bbb := self.x+self.y } WARNING: there is a syntax ambiguity if you mean to define a stand-alone function in the top of the datafile and put it after an attribute declaration. You should define stand-alone functions before attributes, or separate them with some other kind of declaration. <-------------------- extra_boundary ----------------------------> <-------------------- extra_boundary_param ----------------------------> Interpolation requires that both endpoints of an edge be on the same boundary, which cannot happen where edges on different boundaries meet. To handle that case, it is possible to add extra boundary information to a vertex by declaring two particular vertex extra attributes, extra_boundary and extra_boundary_param: interp_bdry_param define vertex attribute extra_boundary integer define vertex attribute extra_boundary_param real[1] Then declare attribute values on key vertices, for example vertices 1 0.00 boundary 1 fixed extra_boundary 2 extra_boundary_param 2*pi If the extra_boundary attribute is not set on a vertex when wanted, Evolver will silently fall back on interpolation. <-------------------- extrapolate ----------------------------> Main command. Same as 'e'. Extrapolates total energy to infinite refinement if at least two r commands have been done. Uses last energy values at three successive levels of refinement, and uses a power law fit for the error. For best results, use only the r command to refine, and iterate to complete convergence at each level of refinement. <-------------------- f ----------------------------> Single letter main command. Sets diffusion constant. Prints old and prompts for new. The diffusion constant can also be set by assigning a value to the variable diffusion_constant. <-------------------- F ----------------------------> Single letter main command. Toggle logging of commands in file. If starting logging, you will be prompted for the name of a log file. Any existing file of that name will be appended to. Logging stops automatically when the surface is exited. Only correctly parsed commands are logged. Output resulting from commands is not logged. Responses to interactive single-letter commands are logged, but not responses to other interactive commands. <-------------------- __f_constraint_list ----------------------------> This read-only attribute gives access to the list of constraints a facet is on. __f_constraint_list[1] is the number of constraints in the list, followed by the numbers of the constraints. Note that for named constraints, the internally assigned numbers are used. <-------------------- face ----------------------------> <-------------------- faces ----------------------------> <-------------------- faces section ----------------------------> The datafile face list follows the edge list, and is started by the keyword FACES at the start of a line. It is followed by lines with one facets specification per line in this format: k e1 e2 ... [FIXED] [TENSION constexpr] [BOUNDARY b] \ [CONSTRAINTS c1 [c2 ...]] [NODISPLAY] \ [COLOR n]} [FRONTCOLOR n] [BACKCOLOR n] \ [PHASE n] [quantityname ...] [methodname ...] Here k is the face number, with numbering following the same rules as for vertices. There follows a list of oriented edge numbers in counterclockwise order around the face. A negative edge number means the opposite orientation of the edge from that defined in the edge list. The head of the last edge must be the tail of the first edge (except if you're being tricky in the string model). There is no limit on the number of edges. The face will be automatically subdivided into triangles if it has more than three edges in the soapfilm model. The TENSION (synonym: DENSITY) value is the energy per unit area (the surface tension) of the facet; the default is 1. Density 0 facets exert no force, and can be useful to define volumes or in displays. Fractional density is useful for prescribed contact angles. NODISPLAY prevents the facet from being displayed. The COLOR attribute applies to both sides of a facet; FRONTCOLOR applies to the positive side (edges going counterclockwise) and BACKCOLOR to the negative side. The PHASE number is used in the string model to determine the surface tension of edges between facets of different phases, if phases are used. If the simplex model is in effect, the edge list should be replaced by an oriented list of vertex numbers. An arbitrary number of named quantities or method instances may be listed. These add method values for this element to the must have been declared in the top section of the datafile. If the quantity or instance has orientation-dependent methods, the name may be followed by a dash to reverse the applied orientation. The faces section is optional in the string model. The "list facets" command prints the datafile format listing of facets. <-------------------- facet ----------------------------> In general, one of the basic geometric elements. As a keyword, it is used in element generators. Synonym: facets. Examples: list facets where area < 0.02 set edge[2].facet color red <-------------------- facet area ----------------------------> Facet read-only attribute. The area of the facet. Example: list facet where area < .1 <-------------------- facet bodies ----------------------------> Facet read-only attribute. Generates bodies around a facet, first the body the facet is positive boundary of, then the body the facet is negative boundary of, if they exist. "body" and "bodies" are synonymous. Example: list facet[3].bodies See also "frontbody" and "backbody". <-------------------- facet boundary ----------------------------> Facet read-write attribute. If a facet is on a parametric boundary, then any facets, edges, and vertices generated from the facet will inherit the boundary. By default, new vertex parameter values are calculated by extrapolating from one vertex of the facet. This avoids wrap-around problems that would arise from interpolating parameter values. But if the interp_bdry_param toggle is on, then interpolation is used. The status of whether a facet is on a boundary can be queried with the Boolean attribute on_boundary. Facets can be unset from boundaries, and set on them (but care is needed to do this properly). Examples: list facets where on_boundary 1 unset facets boundary 2 <-------------------- facet color ----------------------------> Facet read-write attribute. Color of both sides of facet for graphics. Default is white. Datafile example: Faces 1 1 2 3 color red Command examples: list facets where color == red set facet[3] color green set facet color red where area > 2 <-------------------- facet constraints ----------------------------> Facet read-write attribute. Putting a facet on a constraint means that every vertex, edge, or facet generated by refining the facet will inherit that constraint. Setting a facet on a constraint does not set any of its existing edges or vertices on the constraint. Facets may be put on constraints in the datafile by listing the constraint numbers after the keyword constraint on the line defining the facet, or with the set command. They may be removed with the unset command. Examples: list facets where on_constraint 1 set facet[2] constraint 2 unset facet constraint 1 <-------------------- facet density ----------------------------> <-------------------- facet tension ----------------------------> Facet read-write attribute. Energy per unit area of facet; surface tension. Default 0 in string model, 1 in soapfilm model. May be set in the datafile by adding "tension value" to the line defining the facet. The density is inherited by any facets generated by refining. "Tension" and "density" are synonyms. Examples: set facet tension 3 where original == 1 list facet where density < .4 <-------------------- facet edges ----------------------------> Facet read-only attribute. Generates edges around a facet, oriented as the facet boundary. "edge" and "edges" are synonymous. In the string model, if the edges of the facet do not make a closed loop, then the edges will be listed in order starting from one end. If the given facet has negative orientation, the edges will be listed accordingly. Example: list facet[3].edges list facet[-3].edge[2] set facet ff color red where ff.edge[3].length < 1 <-------------------- facet noncontent ----------------------------> Facet read-write attribute. When set, indicates this facet should not be used in volume calculations. Useful, for example, if you want to have facets be part of a body boundary for display purposes, but want to use constraint integrands for greater accuracy in volume calculations. Example: set facet noncontent where on_constraint 1 <-------------------- facet normal ----------------------------> Facet read-only attribute. The components of the facet normal vector may be referred to as x,y,z or x1,x2,x3,... in the linear model. Length is equal to facet area. In quadratic model or lagrange model, only the three facet corner vertices are used to calculate the normal. When used in integrals, the normal is calculated at each integration points. Not defined in simplex model. <-------------------- facet phase ----------------------------> Facet read-write attribute. If there is a phasefile, this attribute determines the edge tension of an edge between two facets in the string model. Example: list facet where phase == 1 <-------------------- facet valence ----------------------------> Facet read-only attribute. The valence of a facet is the number of edges (or vertices) that it contains. Most useful in the string model. Example: list facets where valence != 3 <-------------------- facet vertices ----------------------------> Facet read-only attribute. Generates vertices around a facet, oriented as the facet boundary. "vertex" and "vertices" are synonymous. In the string model, if the facet is not a closed loop of edges, the vertices will be generated in order from one end. If the given facet has negative orientation, then the vertices will be generated accordingly. Examples: list facet[3].vertex printf facet[2].vertex[1].id <-------------------- facet_2form_integral ----------------------------> Named method. Description: Integral of a 2-form over a facet. Meant for ambient dimensions higher than 3. Uses Gaussian cubature of order integral_order_2D. Element: facet. Parameters: form_integrand (components in lexicographic order). Models: linear, Lagrange, simplex. Ambient dimension: any. Hessian: yes. Orientable: yes. Example datafile declaration in 4D: quantity formex energy method facet_2form_integral form_integrand: q1: x2 // 12 component q2: 0 // 13 component q3: x4 // 14 component q4: 0 // 23 component q5: 0 // 24 component q6: x3*x2 // 34 component <-------------------- facet_2form_sq_integral ----------------------------> facet_2form_sq_integral Named method. Description: Integral of the square of a 2-form over a facet. Meant for ambient dimensions higher than 3. Uses Gaussian cubature of order integral_order_2D. Element: facet. Parameters: form_integrand (components in lexicographic order). Models: linear. Ambient dimension: any. Hessian: no. Orientable: no. Example datafile declaration in 4D: space_dimension 4 // symplectic area // Correspondence: z1 = (x1,x2) z2 = (x3,x4) #define DENOM ((x1^2+x2^2+x3^2+x4^2)^2) quantity symplectic_sq energy method facet_2form_sq_integral global form_integrand: q1: -2*(x3^2 + x4^2)/DENOM // dx1 wedge dx2 term q2: 2*(x2*x3-x1*x4)/DENOM // dx1 wedge dx3 term q3: 2*(x1*x3+x2*x4)/DENOM // dx1 wedge dx4 term q4: -2*(x1*x3+x2*x4)/DENOM // dx2 wedge dx3 term q5: 2*(x2*x3-x1*x4)/DENOM // dx2 wedge dx4 term q6: -2*(x1^2 + x2^2)/DENOM // dx3 wedge dx4 term <-------------------- facet_area ----------------------------> <-------------------- facet_tension ----------------------------> Named method. Description: Area of facet. Does not multiply by facet density; density_facet_area does that. Quadratic model uses Gaussian cubature of order integral_order_2D. Beware that this is an approximation to the area, and if the facets in the quadratic or Lagrange model get too distorted, it can be a bad approximation. Furthermore, facets can distort themselves in seeking the lowest numerical area. By default, changing the model to quadratic or Lagrange will set an appropriate integral_order_2D. Element: facet. Parameters: none. Models: linear, quadratic, Lagrange, simplex. Ambient dimension: any. Hessian: yes. Example datafile declaration: quantity farea energy method facet_area global <-------------------- facet_area_u ----------------------------> Named method. Description: Area of facet. In quadratic model, it is an upper bound of area, by the Schwarz Inequality. For the paranoid. Same as facet_area in linear model. Sets integral_order_2D to 6, since it doesn't work well with less. Using the density_facet_area_u name automatically incorporates the facet tension, but facet_area_u doesn't. Element: facet. Parameters: none. Models: linear, quadratic. Ambient dimension: any. Hessian: yes. Example datafile declaration: quantity area_u energy method facet_area_u global <-------------------- facet_colors ----------------------------> volver toggle command. Enables coloring of facets in certain graphics interfaces (e.g. xgraph). If off, facet color is white. Default on. <-------------------- facet_count ----------------------------> Internal read-only variable. Number of facets. <-------------------- facet_delete_count ----------------------------> Internal read-only variable. Number of facets deleted by delete command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- facet_dissolve_count ----------------------------> Internal read-only variable. Number of facets dissolved by dissolve command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- facet_edge ----------------------------> <-------------------- facetedge ----------------------------> <-------------------- facet_edges ----------------------------> <-------------------- facetedges ----------------------------> facetedge In general, an internal data structure representing the incidence of an edge and a facet. As a keyword, it can used in element generators in LIST commands. For gurus only. But it is the source of the standard "fe" extension I use for datafilenames, since in early versions of Evolver, the datafile had to explicitly list all the facetedges. Synonyms: facetedges, facet_edge, facet_edges. <-------------------- facet_general_integral ----------------------------> Named method. Description: Integral of a scalar function of position and normal vector over a facet. Uses Gaussian cubature of order integral_order_2D. The components of the normal vector are represented by continuing the coordinate indices. That is, in 3D the position coordinates are x1,x2,x3 and the normal components are x4,x5,x6. For proper behavior, the integrand should be homogeneous of degree 1 in the normal components. Element: facet. Parameters: scalar_integrand. Models: linear, quadratic, Lagrange. Ambient dimension: any. Hessian: yes. Example: The facet area could be calculated with this quantity: quantity surfacearea energy method facet_general_integral scalar_integrand: sqrt(x4^2 + x5^2 + x6^2) <-------------------- facet_knot_energy ----------------------------> Named method. Description: Charge on vertex is proportional to area of neighboring facets. Meant for knotted surfaces in 4D. Power law of potential is adjustable via the global parameter `surface_knot_power'. See also facet_knot_energy_fix. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: parameter knot_power 2 // the default quantity knotten energy method facet_knot_energy global <-------------------- facet_knot_energy_fix ----------------------------> Named method. Description: Provides adjacent vertex correction to facet_knot_energy. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: parameter surface_knot_power 2 // the default quantity knotten energy method facet_knot_energy global method facet_knot_energy_fix global <-------------------- facet_merge ----------------------------> Main prompt command. Merges two soapfilm-model facets into one in a side-by-side fashion. Meant for joining together surfaces that bump into each other. The pairs of vertices to be merged are selected in a way to minimize the distance between merged pairs subject to the orientations given, so there are three choices the algorithm has to choose from. It is legal to merge facets that already have some vertices or edges merged. Syntax: facet_merge(integer,integer) Note the syntax is a function taking signed integer facet id arguments, not element generators. IMPORTANT: The frontbody of the first facet should be equal to the backbody of the second (this includes having no body); this is the body that will be squeezed out when the facets are merged. If this is not true, then facet_merge will try flipping the facets orientations until it finds a legal match. Example: facet_merge(3,-12) The merged facet will keep the number and attributes of the first facet. <-------------------- facet_refine_count ----------------------------> Internal read-only variable. Number of facets refined by refine facets command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- facet_scalar_integral ----------------------------> Named method. Description: Integral of a scalar function over facet area. Uses Gaussian cubature of order integral_order_2D. Element: facet. Parameters: scalar_integrand. Models: linear, quadratic, Lagrange. Ambient dimension: any. Hessian: yes. Example datafile declaration: quantity fint energy method facet_scalar_integral global scalar_integrand: x^2+y^2 <-------------------- facet_torus_volume ----------------------------> Named method. Description: For 3D soapfilm model, calculates body volume integral for a facet, with corrections for edge wraps. You may have to set the quantity volconst attribute in the torus model, since the volume calculation is ambiguous up to one torus volume. Element: facet. Parameters: none. Models: linear,quadratic,lagrange. Ambient dimension: 3. Hessian: yes. Orientable: yes. Example datafile declaration: quantity body_vol energy method facet_torus_volume <-------------------- facet_vector_integral ----------------------------> Named method. Description: Integral of a vectorfield inner product with the surface normal over a facet. The normal is the right-hand rule normal of the facet as defined in the datafile. Uses Gaussian cubature of order integral_order_2D. Element: facet. Parameters: vector_integrand. Models: linear, quadratic, Lagrange, simplex. Ambient dimension: any. Hessian: yes. Orientable: yes. Example datafile declaration, for volume equivalent: quantity fvint energy method facet_vector_integrand vector_integrand: q1: 0 q2: 0 q3: z <-------------------- facet_volume ----------------------------> Named method. Description: Integral of z dx dy over an oriented facet. Valid in the torus domain. Not valid for other symmetry groups. You may have to set the quantity volconst attribute in the torus model, since the volume calculation is ambiguous up to one torus volume. Element: facet. Parameters: none. Models: linear, quadratic, Lagrange. Ambient dimension: 3. Hessian: yes. Orientable: yes. Example datafile declaration: quantity vol fixed = 1.3 method facet_volume <-------------------- facetedge_count ----------------------------> Internal read-only variable. Number of facetedges. <-------------------- facetedges ----------------------------> A facetedge is a pairing of a facet and one of its edges, with orientation such that the edge orientation is consistent with the facet orientation. Facetedges are used internally by Evolver, and are seldom of interest to the user. They carry no energy. The C command will sometimes refer to facetedges if the surface is inconsistent. "Facetedge" can be used as an element generator. The attributes available are id, edge, facet, and extra attributes. <-------------------- facets ----------------------------> In the soapfilm model, a facet is an oriented triangle defined by a cycle of three edges. In the linear model, a facet is a flat triangle. In the quadratic model, the facet is a curved surface defined by quadratic interpolation among the three facet corner vertices and the three edge midpoints. In the Lagrange model, lagrange_order interpolation is done among (lagrange_order+1)(lagrange_order+2)/2 vertices. Although individual facets are oriented, there are no restrictions on the orientations of adjacent facets. By default, a facet carries a surface tension energy equal to its area. In the string model, a facet is a chain of an arbitrary number of edges. The chain need not be closed. Usually a facet is defined in the string model in order to define a body, so the space dimension is 2 and the facet is planar, one facet corresponding to a body. Facets carry no energy by themselves. In the simplex model, a facet is a simplex of dimension surface_dimension defined by surface_dimension+1 vertices. The surface_dimension may be any dimension less than or equal to the space_dimension. The simplex is oriented according to the order of the vertices. By default, a simplex carries a surface tension energy proportional to its volume. Facets may carry additional energy by having named quantity energies applied to them. The facets of the original surface are defined in the faces section of the datafile. Attributes: > id > oid > original > area > fixed > constraints > on_constraint > constraint list > boundary > on_boundary > density or tension > color > frontcolor > backcolor > no_refine > noncontent > edges > vertices > bodies > frontbody > backbody > valence > normal vector > phase > quantities > on_quantity > on_method_instance > nodisplay > extra attributes <-------------------- fbrgb ----------------------------> Facet extra attribute used for rgb colors used for facet backcolor. See rgb_colors. <-------------------- file option ----------------------------> <-------------------- option -f ----------------------------> Command line option -f filename : Specifies the name of a file to be used as command input after the datafile is read. At the end of this file, input reverts to standard input. The effect is the same as redirecting input from the file, except that -f will echo commands to the screen and revert to standard input at the end. Also note that errors will cause input to revert to standard input. <-------------------- fix ----------------------------> Main prompt command. For setting the FIXED attribute of elements. Syntax: FIX generator Example: fix vertices where on_constraint 2 Can also convert a parameter from optimizing to non-optimizing. Example: fix radius Can also convert a named quantity from info_only to fixed. See also unfix. <-------------------- fix_count ----------------------------> Internal read-only variable. Number of elements fixed by fix command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- fixed ----------------------------> Attribute that can be applied to vertices, edges, facets, or named quantities. See "fixed vertex", etc. <-------------------- fixed edge ----------------------------> Edge read-write attribute. For an edge to be "fixed" means that any vertex or edge created by refining the edge will inherit the "fixed" attribute. Declaring an edge fixed in the datafile will also fix all vertices on the edge. However, fixing an edge from the command prompt will not fix any vertices. An edge may be declared fixed in the datafile edges section by adding fixed to the line defining the edge. From the command prompt, one can fix or unfix edges with the fix and unfix commands. Examples: fix edge where on_constraint 1 list edges where fixed set edge color red where fixed unfix edge[3] <-------------------- fixed facet ----------------------------> Facet read-write attribute. For a facet to be "fixed" means that any vertex, edge, or facet created by refining a facet will inherit the fixed attribute. Fixing a facet in the datafile or at the command prompt does not fix any edges or vertices. A face may be declared fixed in the datafile by putting fixed on the line defining the face, after the coordinates. From the command prompt, one can fix or unfix facets with the fix and unfix commands. <-------------------- fixed quantity ----------------------------> Each named quantity is one of four types: > "energy" quantities which are added to the total energy of the surface; > "fixed" quantities that are constrained to a fixed target value (by Newton steps at each iteration); and > "conserved" quantities are like fixed, but the value is irrelevant. The quantity gradient is used to eliminate a degree of freedom in motion. Rarely used, but useful to eliminate rotational degree of freedom, for example. Will not work with optimizing parameters, since they do gradients by differences. > "info_only" quantities whose values are merely reported to the user. This type is initially set in a quantity's datafile declaration. A quantity can be toggled between fixed and info_only with the "fix quantityname" and "unfix quantityname" commands. <-------------------- fixed vertex ----------------------------> Vertex read-write attribute. A fixed vertex will not move during iteration (except to satisfy level set constraints) or other operations, except if coordinates are explicitly changed by a "set vertices ..." command. A vertex may be declared fixed in the datafile by putting fixed on the line defining the vertex, after the coordinates. From the command prompt, one can fix or unfix vertices with the fix and unfix commands. Examples: list vertex where fixed fix vertex where on_constraint 1 unfix vertices where on_boundary 1 <-------------------- flip-rotate symmetry group ----------------------------> The defined surface can be treated as the fundamental region of a larger surface with a symmetry. See "symmetry group". Flip_rotate is the cyclic symmetry group of rotations in the x-y plane with a flip z -> -z on every odd rotation, where the order of the group is given by the internal variable rotation_order, which had better be even. Note: Since this group has points that are fixed under an even number of rotations, some special precautions are necessary. Vertices on the rotation axis must be labelled with the attribute "double_axial_point" in the datafile. Edges out of an axial point must have the axial point at their tail, and must have zero wrap. Facets including an axial point must have the axial point at the tail of the first edge in the facet. Datafile declaration: symmetry_group "flip_rotate" parameter rotate_order = 6 Group element encoding: An element is encoded as the power of the group generator. <-------------------- floor ----------------------------> ceil(x),floor(x): Round up or down to integer. <-------------------- flush_counts ----------------------------> Main prompt command. Causes the printing of various internal counters that have become nonzero. The counters are: equi_count, edge_delete_count, facet_delete_count, edge_refine_count, facet_refine_count, notch_count, vertex_dissolve_count, edge_dissolve_count, facet_dissolve_count, body_dissolve_count, vertex_pop_count, edge_pop_count, facet_pop_count, pop_tri_to_edge_count, pop_edge_to_tri_count, pop_quad_to_quad_count, where_count, edgeswap_count, fix_count, unfix_count, t1_edgeswap_count, and notch_count. Normally, these counts are accumulated during the execution of a command and printed at the end of the command. Flush_counts can be used to display them at some point within a command. Flush_counts is usually followed by reset_counts, which resets all these counters to 0. <-------------------- for ----------------------------> This is the Evolver's version of the C language "for" construct. Syntax: FOR ( command1 ; expr ; commmand2 ) command3 The first command is the initialization command; note that it is a single command, rather than an expression as in C. If you want multiple commands in the initialization, use a compound command enclosed in curly braces. The middle expression is evaluated at the start of each loop iteration; if its value is true (i.e. nonzero) then the loop is executed; otherwise the flow of control passes to the command after command3. The command2 is executed at the end of each loop iteration; again, it is a single command. The body of the loop is the single command command3, often a compound] command. Note: Command3 should end with a semicolon, unless it is the if clause of an if-then statement. Examples: for ( inx := 1 ; inx < 3 ; inx += 1 ) print facet[inx].area; for ( {inx := 1; factorial := 1; } ; inx < 7 ; inx += 1 ) { factorial *= inx; printf "factorial %d is %d\n",inx,factorial; }; <-------------------- __force ----------------------------> __force Vertex read-only array attribute. This is an indexed attribute giving the components of the force (negative energy gradient as projected to constraints). Meant for debugging use. This is not directly used for the motion; see __velocity. <-------------------- force_pos_def ----------------------------> Toggle. If this is on during YSMP factoring of Hessian and the Hessian turns up indefinite, something will be added to the diagonal element to make it positive definite. Left over from some experiment probably. <-------------------- force_deletion ----------------------------> Toggle. In the soapfilm model, overrides the refusal of the delete command to delete edges or facets when that would create two edges with the same endpoints. Sometimes it is necessary to have such edges, for example in pinching off necks. But usually it is a bad idea. Also see star_finagling. Default is off. <-------------------- foreach ----------------------------> Repeat a command for each element produced by an element generator. Syntax: FOREACH generator DO command Examples: foreach vertex do print x^2 + y^2 + z^2 foreach edge ee where ee.dihedral > .4 do { printf "id %g\n",id; foreach ee.vertex do printf " %g %g %g\n",x,y,z; } <-------------------- form_integrand ----------------------------> Certain named methods require an n-form integrand. The syntax is FORM_INTEGRAND: Q1: expr Q2: expr Q3: expr ... where the expressions are functions of the coordinates. Element attributes of the appropriate type element may also be used. When used in the facet_2form_integral method. The form components are listed in lexicographic order, i.e. in 4D the six components 12,13,14,23,24,34 would be listed as Q1 through Q6. <-------------------------- form_factors -------------------------> form_factors A facet extra attribute vector used by several elastic energy named methods: linear_elastic, neo_hookean, relaxed_elastic, relaxed_elastic_A, SVK_elastic, and dirichlet_elastic. The entries are the dot products of the sides of the relaxed facet. Due to symmetry, there are only three distinct dot products, so form_factors just has length 3. The entries are {s11,s12,s22}, where s11 = dot(v2-v1,v2-v1), s12 = dot(v2-v1,v3-v1), and s22 = dot(v3-v1,v3-v1). If form_factor is not defined by the user, it will be created by Evolver, and the initial facet shape will be assumed to be unstrained. <-------------------- formula ----------------------------> Datafile keyword used in level set constraints to introduce the function formula. Synonym: function <-------------------- free_discards ----------------------------> free_discards Main prompt command. Frees deleted elements in internal storage. Ordinarily, deleting elements does not free their memory for re-use until the command completes, so that element iteration loops do not get disrupted. If for some reason this behavior leads to excess memory usage or some other problem, the user may use the free_discards command to free element storage of deleted elements. Just be sure not to do this inside any element iteration loop that might be affected. <-------------------- frgb ----------------------------> Facet array extra attribute for facet color used when rgb colors are used. See rgb_colors. <-------------------- frontbody ----------------------------> Facet read-write attribute. The id of the body of which the facet is on the positively oriented boundary. Useful after creating a new body with the new_body command. As a read attribute, the value is 0 if there is no such body. Examples: newb := new_body; set facet frontbody newb where color == red print facet[2].frontbody Frontbody also works for adding edges to a facet in the string model, but the added edge must be attach to one end of the edge arc, or close the arc. <-------------------- frontcolor ----------------------------> Facet read-write attribute. Color of positive side of facet for graphics. Default is white. Datafile example: Faces 1 1 2 3 frontcolor green backcolor red Command examples: list facets where frontcolor == red set facet[3] frontcolor green set facet frontcolor red where area > 2 <-------------------- full_bounding_box ----------------------------> Evolver toggle command. Causes bounding box in PostScript output to be the full window, rather than the actual extent of the surface within the window. Default off. <-------------------- full_gravity_method ----------------------------> <-------------------- gravity_method ----------------------------> Named method. Description: Gravitational energy, integral of p z^2/2 dxdy over a facet, where p is difference in adjacent body densities. Note: this method uses the gravitational constant as the modulus if invoked as full_gravity_method. Just gravity_method does not automatically use the gravitational constant. Element: facet. Parameters: none. Models: linear, quadratic, Lagrange. Ambient dimension: 3. Hessian: yes. Orientable: yes. Example datafile declaration: quantity grav energy modulus 980*8.5 method gravity_method <-------------------- function ----------------------------> Datafile keyword used in level set constraints to introduce the function formula. Synonym: function <-------------------- function definition ----------------------------> Users may define their own functions that have arguments and return values with the syntax function type identifier ( type arg1, type arg2, ... ) { commands } This syntax works both in the top of the datafile and at runtime. Right now the implemented types for return value and arguments are real and integer. The argument list can be empty. The return value is given in a return expr statement. Example: function real func1 ( real ht, real wd ) { local prod; prod := ht*wd; return prod; } Note that the function arguments act as local variables, i.e. their scope is the function body, and they have stack storage so functions may be recursive. Function prototypes may be used to declare functions before their bodies are defined with the same syntax, just replacing the body of the function with a semicolon. Prototype syntax: function type identifier ( type arg1, type arg2, ... ); Note that a procedure is used as a command, and a function is used in a numerical expression. <-------------------- functions ----------------------------> Usually stand-alone user-defined functions and procedures are defined in the read section of the datafile, but sometimes it is necessary to define them in the top section of the datafile so they may be used in other top section declarations. It is possible to define them in the top section with the same syntax as in the read section. Note this applies to the parameter-passing variety of functions and procedures, denoted by the leading keyword "function" or "procedure", and not command definitions like "gg := {...}". WARNING: there is a syntax ambiguity if you mean to define a stand-alone function in the top of the datafile and put it after an attribute declaration. You should define stand-alone functions before attributes, or separate them with some other kind of declaration. <------------------ function_quantity_sparse ---------------------> function_quantity_sparse Toggle command controlling whether sparse matrices are used in evaluation of Hessians of quantities using functions of methods. Default is off, but I recomment on if you are using a quantity function applying to anything but a small number of elements. <-------------------- g ----------------------------> <-------------------- go ----------------------------> Single letter main command. Do one iteration step. The output consists of the number of iterations left (for people who wonder how close their 1000 iterations are to ending), the area and energy, and the scale factor. g is commonly used with an iteration count, as in "g 100". The user can abort repeated iterations by sending an interrupt to the process (SIGINT, to be precise; CTRL-C or whatever on your keyboard). As a special dispensation to lazy users, the syntax "gn" is equivalent to "g n". Synonym: go See "iteration step" for a detailed explanation of what happens during a 'g' step. <-------------------- G ----------------------------> Single letter main command. Toggles gravity on or off. Gravity starts ON if any body has a nonzero density; otherwise OFF. If followed by a value, sets gravity to that value. Otherwise prints old value of gravitational constant and prompts for new. When used in numerical expressions, G evaluates to the current value of the gravitational constant. <-------------------- gap constant declaration ----------------------------> The initial value of the gap constant for gap energy may be set in the datafile with the syntax GAP_CONSTANT value The default value is 1. Synonym: spring_constant. <-------------------- gap energy ----------------------------> Convex constraint can have what I call a gap energy. Consider a soap film spanning a circular cylinder. The Evolver must approximate this surface with a collection of facets. The straight edges of these facets cannot conform to the curved wall, and hence the computed area of the surface leaves out the gaps between the outer edges and the wall. The Evolver will naturally try to minimize area by moving the outer vertices around so the gaps increase, ultimately resulting in a surface collapsed to a line. This is not good. Therefore there is provision for a "gap energy" to discourage this. A level-set constraint may be declared CONVEX in the datafile. For an edge on such a constraint, an energy is calculated as E = k\left\Vert \vec S \times \vec Q \right\Vert / 6 where \vec S is the edge vector and \vec Q is the projection of the edge on the tangent plane of the constraint at the tail vertex of the edge. The constant k is a global constant called the "gap constant". A gap constant of 1 gives the best approximation to the actual area of the gap. A larger value minimizes gaps and gets vertices nicely spread out along a constraint. You can set the value of k in the datafile or with the k command. The gap energy falls off quadratically as the surface is refined. That is, refining once reduces the gap energy by a factor of four. You can see if this energy has a significant effect on the surface by changing the gap constant. Note: gap energy is effective only in the linear model. Example datafile: tankex.fe <-------------------- gap_constant ----------------------------> The initial value of the gap constant for gap energy may be set in the datafile with the syntax GAP_CONSTANT value The default value is 1. Synonym: spring_constant. See "gap energy" for more. <-------------------- gap_energy ----------------------------> Named method. Description: Implementation of gap energy, which is designed to keep edges from short-cutting curved constraint surfaces. This method serves the same purpose as declaring a constraint convex. Automatically incorporates the gap_constant set in the datafile or by the k command. Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: quantity gappy energy method gap_energy global <-------------------- gauss_bdry_e ----------------------------> Edge extra attribute used by gauss_curvature_integral. <-------------------- gauss_bdry_v ----------------------------> Vertex extra attribute used by gauss_curvature_integral. <-------------------- gauss_curvature_integral ----------------------------> Named method. Description: This computes the total Gaussian curvature of a surface with boundary. The Gaussian curvature of a polyhedral surface may be defined at an interior vertex as the angle deficit of the adjacent angles. But as is well-known, total Gaussian curvature can be computed simply in terms of the boundary vertices, which is what is done here. The total Gaussian curvature is implemented as the total geodesic curvature around the boundary of the surface. The contribution of a boundary vertex is E = (\sum_i \theta_i) - pi. For reasons due to the Evolver's internal architecture, the sum is actually broken up as a sum over facets, adding the vertex angle for each facet vertex on the boundary and subtracting pi for each boundary edge. The total over all boundary vertices is exactly equal to the total angle deficit of all interior vertices plus 2*pi*chi, where chi is the Euler characteristic of the surface. Boundary vertices are deemed to be those that are fixed or on a parametric boundary. Alternately, one may define a vertex extra attribute gauss_bdry_v and an edge extra attribute gauss_bdry_e and set them nonzero on the relevant vertices and edges; this overrides the fixed/boundary criterion. Element: facet. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: quantity gint energy method gauss_curvature_integral global <-------------------- gauss_curvature ----------------------------> Datafile keyword that automatically creates an energy named quantity using the method gauss_curvature_integral as a global method. May be followed in the datafile with a modulus value. <-------------------- elastic_basis ----------------------------> Facet extra attribute used by the general_linear_elastic named method. See general_linear_elastic for details. <-------------------- elastic_coeff ----------------------------> Facet extra attribute used by the general_linear_elastic named method. See general_linear_elastic for details. <-------------------- general_linear_elastic ----------------------------> Named method. Description: To calculate the nonisotropic linear elastic strain energy energy for facets. Let A be the linear transformation from the unstrained shape to the strained shape. Then the Cauchy-Green strain tensor is C = (A^TA - I)/2. Let S_1 and S_2 be the sides of the unstrained facet. Let W_1 and W_2 be the transformed facet sides. Let F be the Gram matrix of strained facet. Define S = [ S_1 S_2 ], Q = S^-1 W = [ W_1 W_2 ] = AS F = W^TW = S^TA^TAS Then A^TA = Q^TFQ C = (Q^TFQ - I)/2 The energy density is (1/2)C_ij K_ijkl C_kl where K_ijkl is the full tensor of elastic constants. By using symmetries, this can be reduced to (1/2) [ C_11 C_22 C_12 ] [ E_1 E_3 E_4 ] [ C_11 ] [ E_3 E_2 E_5 ] [ C_22 ] [ E_4 E_5 E_6 ] [ C_12 ] Each facet has extra attribute elastic_coeff of size 6 containing { E_1, E_2, E_3, E_4, E_5, E_6 }, and extra attribute array elastic_basis of size 2x2 containing { {s11,s12},{s21,s22}}, which are the two sides of the unstrained facet. Note that the E_i are defined with respect to the original sides as defined by the form factors, so it is up to you to make sure everything works out right. Test carefully!!! The elastic_coeff attribute must be created and initialized by the user. Element: facet. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: yes. Example datafile declaration: define facet attribute elastic_basis real[2][2] define facet attribute elastic_coeff real[6] quantity genlastic energy method general_linear_elastic global <-------------------- generators ----------------------------> Various commands can act on specified sets of elements, which are defined by "element generator" syntax. Attributes of the iteration element may be used later in the command. The general form of a generator is elementgen name where expr elementgen may be 1. A multiple element generator, which can be A. An element type, vertex, edge, facet, or body, which generates all elements of that type in the surface. But new elements created during a loop will not be generated, so "refine edges" will refine all existing edges just once. B. A single element subelement. The implemented subelements are: of a vertex: edge, facet (in no particular order) i. of an edge: vertex (in tail, head order), facet (in geometric order) ii. of a facet: vertex, edge, body (all in order around the facet) iii. of a body: facet (in no particular order) 2. A single element, which can be A. an element name of an active generator B. an indexed element type, vertex, edge, facet, or body. Indexing starts at 1. The index may be negative, in which case the generated element has negative orientation. C. an indexed subelement of an element (error if no element of that index). Indexing starts at 1. The indexing is the same as the order produced by the foreach generator. Indexed subelements of an edge or facet follow the orientation of the edge or facet. "name" is an optional identifier which can be used in the body of a loop to refer to the generated element. expr is interpreted as a boolean expression, 0 for false, nonzero for true. Only elements for which expr is true are generated. The where expr clause is optional. The innermost generator generates a default element, which can have its attributes referred to just by attribute name. But be sure to remember that in a nested iteration, an unqualified element type generates all elements of that type, not just those associated with the parent element. Examples: list facet where color == red foreach edge ee where ee.length < .3 do list ee.vertex print facet[2].edge[1].vertex[2].id foreach facet ff do { printf "facet %g:\n"; list ff.edge } print max(edge where on_constraint 1, length) <-------------------- genus2 symmetry group ----------------------------> The defined surface can be treated as the fundamental region of a larger surface with a symmetry. See "symmetry group". genus2 is a symmetry group on the Klein model of hyperbolic space whose quotient group is a genus 2 hyperbolic manifold. The fundamental region is an octagon. Datafile declaration: Klein_metric symmetry_group "genus2" Group element encoding: There are 8 translation elements that translate the fundamental region to one of its neighbors. Translating around a vertex gives a circular string of the 8 elements. The group elements encoded are substrings of the 8, with null string being the identity. Encoding is 4 bits for each element. See khyp.fe for an example. <-------------------- geometric elements ----------------------------> he surface is defined in terms of its geometric elements of each dimension. Each element has its own set of attributes. Some may be set by the user; others are set internally but may be queried by the user. It is also possible to dynamically define extra attributes for any type of element, which may be single values or vectors of values. Attribute values can be specified in the datafile, and queried with commands. Elements: vertices, edges, facets, bodies, facet-edges. <-------------------- geompipe ----------------------------> Main prompt command. Redirects Evolver's geomview output to a command in place of sending it to geomview. Syntax: geompipe stringexpr The redirection can be closed with the command "P 9". geompipe is useful for debugging geomview data; but be sure to toggle gv_binary OFF to get ascii data to look at. <-------------------- geomview ----------------------------> Excellent screen graphics on Unix systems can be done through the free 3D viewing program geomview. Geomview can be started with the P command, option 8. One caution: geomview does not deal well with object sizes below 1e-5, so displaying micron-size objects using MKS units is ill-advised. Main prompt commands relevant to geomview: "geomview" or "P 8" to start geomview "P 9" to end geomview "geompipe" to pipe geomview input someplace else "geomview string" to send user commands to geomview "gv_binary" toggle to control interface mode "view_4D" to toggle sending 3D or 4D info to geomview "D" or "autodisplay" for toggling automatic redraw when the surface changes. Default is automatic redraw when geomview is started. <-------------------- geomview command ----------------------------> Main prompt command. The plain form "geomview" toggles the geomview display on and off. The form geomview stringexpr will send a command to an already started geomview. This string must be in the geomview command language, for which consult the geomview documentation. <-------------------- geomview install ----------------------------> If your system does not have OpenGL/GLUT, I suggest you get the geomview package from www.geomview.org There are pre-compiled binaries for many unix systems here. Follow geomview's installation directions, and make sure that geomview is accessible through your PATH. NOTE: The X windows versions of geomview seem to require some event to occur in the geomview display window before it will redraw after loading a new datafile. So if geomview seems hung, just run the mouse over the window to give it an event to wake it up. <-------------------- global ----------------------------> Named method instance or named quantity attribute indicating the instance or quantity is to be applied to all geometric elements of the proper type. This can only be done in the datafile top. Example: quantity george info_only edge_length global <-------------------- global_method ----------------------------> The methodlist version of the quantity definition may contain one or more method instances. To incorporate a previously explicitly defined instance, include METHOD instancename. GLOBAL_METHOD may be used instead of METHOD to indicate the method applies to all elements of the appropriate type; it is equivalent to using GLOBAL in the method definition. To instantiate a method in the quantity definition, you essentially incorporate the instance definition, but without an instance name. Example of a quantity with one predefined method instance and one implicitly defined instance: method_instance qwerty method facet_scalar_integral scalar_integrand: x^2 quantity foobar energy method qwerty method edge_scalar_integral scalar_integrand: y^3 Usually the second, implicit definition will be more convenient. Several method instances may be included in one methodlist (up to a current limit of 50), and their values are added together and multiplied by the quantity modulus to get the quantity value. The FUNCTION methodexpr variant defines the quantity as a function of previously defined method instances. Example: method_instance qwerty method facet_scalar_integral scalar_integrand: x^2 quantity foobar energy function qwerty^3 Non-global quantities may be applied to elements individually by adding the quantity name to the datafile line defining an element. They may also be applied or unapplied at runtime with the set and unset commands. Orientable methods can be applied with negative orientation in the datafile by following the name with a dash. The orientation in a set command follows the orientation the element is generated with. Methods applying to different types of elements may be combined in one quantity. If such a quantity is applied to an element, then all method instances of that quantity of the appropriate type are applied to the element. Original attachments of quantities are remembered, so if an edge method is applied to a facet, then edges created from refining that facet will inherit the edge method. <-------------------- graphics ----------------------------> Surface Evolver graphics Surface Evolver graphics consists of drawing edges and facets. There is a single graphics driver routine which produces colored edges and facets in 3D and sends them to a set of display routines. There are three main sets of display routines: > Native screen graphics, built-in Evolver graphics of various qualities on various platforms, > Interface to the Geomview 3D viewer, for Unix/Linux users without OpenGL/GLUT graphics compiled into Evolver, > PostScript files, for everybody wanting to make 2D images for publications, web sites, etc. Internal Evolver state information relevant to graphics: > edge color > facet color, frontcolor, and backcolor > edge show expression > facet show expression > facet shading toggle > facet brightness > facet_colors toggle > view matrix > view transform expression > transforms toggle > torus model display mode: raw_cells, clipped, or connected > surface thickness > show_inner and show_outer toggles > background color > window_aspect_ratio variable Datafile features that are relevant to graphics are: > edge color > facet color, frontcolor, and backcolor > view transforms > view transform generators > viewing matrix Main prompt commands that are relevant to all graphics are: > show, showq, and show_expr for controlling which elements are displayed. > set edge color > set facet color, frontcolor, backcolor > transforms toggle > transform_expr > thickness > torus model display mode toggles: raw_cells, clipped, or connected > show_inner and show_outer toggles <-------------------- graphics actions ----------------------------> There are many mouse and keyboard actions that can be performed in the graphics window itself. Holding down and dragging the left mouse button moves the surface continuously, and the clicking right mouse button picks vertices, edges, and facets. Picked element id numbers are printed in the console window. With the graphics window in the foreground, these keyboard commands are active: h Print a help screen on the console window. r Rotate mode for left mouse button. t Translate mode for left mouse button. z Zoom mode for left mouse button (and use F to focus on particular vertex). c Clockwise/counterclockwise spin mode for left mouse button. + Widen edges. - Narrow edges. b Decrement edge front bias by .001. B Increment edge front bias by .001 (to show edges more clearly). R Reset the view. m Center the image. M Have the right mouse button bring up a menu instead of picking. P Have the right mouse button do picking instead of menu (default). p Toggle orthogonal/perspective projection. s Toggle cross-eyed stereo. e Toggle showing edges, regardless of "show edge" condition. f Toggle showing facets obeying "show facet" condition or no facets. F Move the rotate/zoom origin to the last picked vertex. G Start another graphics window with independent camera. o Toggle drawing a bounding box. g Toggle Gourard (smooth) shading. x Close the graphics window. arrow keys Translate a bit. And some more advanced commands most users will never use, but are listed here for completeness: H Print advanced help. a Toggle using OpenGL element arrays. i Toggle interleaved elements in OpenGL arrays. I Toggle indexed OpenGL arrays. S Toggle OpenGL triangle strips. Y Toggle strip coloring (I was curious as to what OpenGL triangle strips would look like). D Toggle using a display list. Q Toggle printing drawing statistics. <-------------------- graphics commands ----------------------------> When the native graphics display is invoked by the 's' command or the various `show' commands, the Evolver enters graphics mode, with the prompt `Graphics command: '. A graphics command is a string of letters followed by RETURN. Each letter causes an action. Some commands may be preceded by an integer count of how many repetitions to do. Example command: 15u2z, which does 'u' 15 times and 'z' twice. Rotation commands may be preceded by a real number giving the degrees of rotation; an integer will give a repetition count with the default angle of 6 degrees. A real number is indicated by including a decimal point. The letter commands in graphics mode are: u Rotate up 6 degrees. d Rotate down 6 degrees r Rotate right 6 degrees. l Rotate left 6 degrees. c Rotate clockwise 6 degrees. C Rotate counterclockwise 6 degrees. z Zoom by factor of 1.2. s Shrink by factor of 1.2. arrow keys - Translate image. m Center image. R Reset viewing parameters. T Toggle additional viewing transforms. e Toggle facet edge drawing. B Toggle display of boundary facets. v Toggle ridge and valley coloring. w Toggle facets with three vertices on constraints. b Draw bounding box. +,- Increment, decrement fill color. H Toggle hidden surface removal. t Set clipping mode for torus. ?,h help (this display) x,q Exit to main menu. See help topics on individual letters, "r", "l", etc., for more details. <-------------------- gravity ----------------------------> Evolver toggle command. Includes the gravitational energy of bodies with density in total energy. The gravitational constant can be set in the top of the datafile with "gravity_constant = value", or with the runtime command "G value". <-------------------- gravity energy ----------------------------> If a body has a density, then that body contributes its gravitational energy to the total. The acceleration of gravity G is under user control with the G command. Letting \rho be the body density, the energy is defined as E = \int\int\int_{body} G \rho z dV but is calculated using the Divergence Theorem as E = \int\int_{body surface} G\rho {z^2\over 2} \vec k \cdot \vec{dS}. This integral is done over each facet that bounds a body. If a facet bounds two bodies of different density, then the appropriate difference in density is used. Facets lying in the z = 0 plane make no contribution, and may be omitted if they are otherwise unneeded. Facets lying in constraints may be omitted if their contributions to the gravitational energy are contained in constraint energy integrals. In the string model, all this happens in one lower dimension. Example datafile: mound.fe <-------------------- gravity_constant ----------------------------> Internal read-write variable. Value of the gravitational constant, which can also be set by the G command. The initial value of the gravitational constant may be set in the top of the datafile with the syntax GRAVITY_CONSTANT value The default value is 1. <-------------------- ps_gridflag ----------------------------> Evolver toggle command. If toggled on, the postscript command will show all edges of displayed facets, instead of just those satisfying the current edge show condition. <-------------------- gv_binary ----------------------------> Evolver toggle command. Toggles sending data to geomview in binary format, which is faster than ascii. Default is binary on SGI, ascii on other systems, since there have been problems with binary format on some systems. Ascii is also useful for debugging. <-------------------- H ----------------------------> <-------------------- h ----------------------------> Single letter main command. Prints a very primitive help message listing common commands. help is much better, as it accesses the full HTML documentation. Or best, use a separate HTML browser on this documentation. Graphics mode command. Toggle hiding hidden surfaces. When ON, takes longer to display images, but looks better. <-------------------- h_inverse_metric ----------------------------> Toggle. Replaces force by Laplacian of force. For doing motion by Laplacian of mean curvature. <----------------------- h_zero ----------------------------> Some physical surfaces have their lowest energy state when their mean curvature is some nonzero value. An example is a membrane made of parallel stacked molecules which have one end bigger than the other. Their curvature energy is thus proportional to (h-h0)^2, where h is the actual mean curvature and h0 is the equilibrium or intrinsic mean curvature. Several of the squared mean curvature named methods can use h0, either in the form of a variable h_zero or a real-valued vertex attribute h_zero. If the vertex attribute exists, it takes precedence. Methods using h_zero: sq_mean_curv_cyl sq_mean_curvature eff_area_sq_mean_curvature normal_sq_mean_curvature star_normal_sq_mean_curvature star_perp_sq_mean_curvature sqcurve2_string Methods not using h_zero: star_sq_mean_curvature star_eff_area_sq_mean_curvature sqcurve_string sqcurve_string_marked sqcurve3_string circle_willmore <-------------------- help ----------------------------> Main prompt command. Prints what Evolver knows about an identifier or keyword. User-defined variables, named quantities, named methods, named constraints, and element attributes are identified as such. Information for syntax keywords comes from a file evhelp.txtt in the doc subdirectory of your Evolver installation, so that subdirectory should be on your EVOLVERPATH environment variable. Syntax: help keyword The keyword need not be in quotes, unless there are embedded blanks. After printing the help section exactly matching the keyword, a list of related terms is printed. These are just the keywords containing your keyword as a substring. Graphics mode command: 'h' or '?' will print a brief help screen for graphics mode commands. <-------------------- hessian ----------------------------> Main prompt command. Does one step using Newton's method with the Hessian matrix of the energy. If the Hessian is not positive definite, a warning will be printed, but the move will be made anyway. If the check_increase toggle is on, then no move will be made if it would increase energy. Hessian_seek will use a variable step size to seek minimum energy in the direction of motion. The motion vector is stored, and may be accessed with the move command. Not all energies and constraints have Hessian calculations yet. See the Hessian tutorial for more. If the surface is too far from equilibrium, a hessian move can blow up. For safer hessian moves, see hessian_seek, which does a line search along the direction of the hessian move for minimum energy. <-------------------- hessian caveats ----------------------------> When not near enough to an equilibrium, incautious use of "hessian" can wreck a surface. If you don't know what's going to happen, save the surface first. Or use hessian_seek. Or use hessian_menu, where you can restore the surface with option 7. Or set " check_increase", which will abort a hessian iteration that would increase energy. But remember sometimes hessian should increase energy, for example to satisfy a volume constraint or reach an unstable equilibrium. Not all energies have built-in Hessians. If Evolver complains about lacking a Hessian for a particular energy, you will either have to forego hessian or find a way to phrase your problem in terms of an energy with a Hessian. There are three methods of sparse matrix factoring built into Evolver: YSMP (Yale Sparse Matrix Package), my own minimal degree algorithm, and METIS recursive bisection. The ysmp and metis_factor toggles can be used to control which is active. METIS is the best overall, particularly on large surfaces, but requires a separate download and compilation of the METIS library (but it's easy; see the installation instructions). <-------------------- hessian intro ----------------------------> Consider a particular surface in a particular configuration X, and consider a small perturbation Y added to X. Then the energy may be expanded in a Taylor series: E(X+Y) = E_0 + G^T Y + 1/2 Y^T H Y + ... Here the constant E_0 is the original energy, E_0 = E(X). G is the vector of first derivatives, the gradient, which is a vector the same dimension as Y or X (G^T is a 1-form, if you want to get technical). Here ^T denotes the transpose. H is the square matrix of second derivatives, the Hessian. The Hessian is automatically a symmetric matrix. The gradient G determines the best linear approximation to the energy, and the Hessian H determines the best quadratic approximation. Positive definiteness. If the quadratic term 1/2 Y^T H Y is always positive for any nonzero Y, then H is said to be positive definite. At an equilibrium point, this means the point is a strict local minimum; this is the multivariable version of the second derivative test for a minimum. If the quadratic term is zero or positive, then H is positive semi-definite. No conclusion can be drawn about being a local minimum, since third-order terms may prevent that. If the quadratic term has positive and negative values, then H is indefinite. Constraints. If there are constraints on the surface, then the the perturbation vector Y is required to be tangent to the feasible set in configuration space. The curvature of the feasible set can have an effect on H, but this is taken care of automatically by the Evolver. This effect arises from the slight change in energy due to the projection back to the feasible set after motion. This does not affect the gradient, but it does affect the Hessian. In particular, if Q is the Hessian of a global constraint and q is the Lagrange multiplier for the constraint, then the adjusted energy Hessian is H - qQ. Therefore it is necessary to do a 'g' step to calculate Lagrange multipliers before doing any Hessians when there are global constraints. <-------------------- hessian iteration ----------------------------> Or how to converge really really fast. Suppose we assume the quadratic approximation to the surface energy is a good one. Then we can find an equilibrium point by solving for motion Y that makes the energy gradient zero. Recalling that G and H depend only on X, the energy gradient as a function of Y is grad E = G^T + Y^T H. So we want G^T + Y^T H = 0, or transposing, G + H Y = 0. Solving for Y gives Y = - H^-1 G. This is actually the Newton-Raphson Method applied to the gradient. The Evolver's "hessian" command does this calculation and motion. It works best when the surface is near an equilibrium point. It doesn't matter if the equilibrium is a minimum, a saddle, or a maximum. However, nearness does matter. Remember we are dealing with thousands of variables, and you don't have to be very far away from an equilibrium for the equilibrium to not be within the scope of the quadratic approximation. When it does work, "hessian" will converge very quickly, 4 or 5 iterations at most. Example: Start with the cube datafile cube.fe. Evolve with "r; g 5; r; g 5;". This gets very close to an equilibrium. Doing "hessian" a couple of times gets to the equilibrium: Enter command: hessian 1. area: 4.85807791572284 energy: 4.85807791572284 Enter command: hessian 1. area: 4.85807791158432 energy: 4.85807791158432 Enter command: hessian 1. area: 4.85807791158431 energy: 4.85807791158431 So Hessian iteration converged in two steps. Furthermore, this is a local minimum rather than a saddle point, since Evolver did not complain about a non-positive definite Hessian. NOTE: The hessian command will work with indefinite Hessians, as long as there are no eigenvalues too close to zero. The warning about non-positive definite is for your information; it is does not mean hessian has failed. <-------------------- hessian metric ----------------------------> Hessian with metric. One would expect that refining the surface would lead the eigenvalues to converge to the eigenvalues for the smooth surface. But as the previous section showed, refining caused the eigenvectors to all shrink by about a factor of 4. This is no way to converge. The explanation is that so far we have been looking at eigenvectors in slightly the wrong way. An eigenvector is supposed to represent a mode of perturbation that is proportional to the force. However, the response of a surface to a force need not be numerically equal to the force. After all, forces and displacements are different kinds of things. They have different units: displacement has units of distance, and force has units of energy per distance. In other words, displacement is a vector and force is a covector. Note that matrix multiplication by the Hessian H turns a vector into a covector. In general, to turn a vector into an equivalent covector, one needs an inner product, or metric. So far we have been using the Euclidean inner product on configuration space, but that is not the proper one to use if you want to approximate smooth surfaces. The proper inner product of perturbations f and g of a smooth surface is the integral over the surface of the pointwise inner product: / = | dA. / In discrete terms, the inner product takes the form of a square matrix M such that = Y^T M Z for vectors Y,Z. We want this inner product to approximate integration with respect to area. Having such an M, the eigenvalue equation becomes H Q = q M Q. Officially, Q is now called a "generalized eigenvector" and q is a "generalized eigenvalue". But we will drop the "generalized". An intuitive interpretation of this equation is as Newton's Law of Motion, Force = Mass * Acceleration where HQ is the force, M is the mass, and qQ is the acceleration. In other words, in an eigenmode, the acceleration is proportional to the perturbation. The Evolver command "linear_metric" includes M in the eigenvector calculations. Two metrics are available. In the simplest, the "star metric", M is a diagonal matrix, and the "mass" associated with each vertex is 1/3 of the area of the adjacent facets (1/3 since each facet gets shared among 3 vertices). The other, the "linear metric", extends functions from vertices to facets by linear interpolation and integrates with respect to area. By default, "linear_metric" uses a 50/50 mix of the two, which seems to work best. See the more detailed discussion below in the eigenvalue accuracy section. The fraction of linear metric can be set by assigning the fraction to the internal variable linear_metric_mix. By default, linear_metric_mix is 0.5. In quadratic mode, however, only the quadratic interpolation metric is used, since the star metric restricts convergence to order h^2 while the quadratic interpolation metric permits h^4 convergence. Example: Run square.fe, and refine twice. Do "linear_metric" and "ritz(0,10)". Enter command: linear_metric Using linear interpolation metric with Hessian. Enter command: ritz(0,10) Eigencounts: 0 1. 2.036549240354335 2. 4.959720306550875 3. 4.959720306550875 4. 7.764634143334637 5. 10.098798069316683 6. 10.499717069102575 7. 12.274525789880887 8. 12.274525789880890 9. 15.7679634642721 10. 16.7214142904405 Iterations: 127. Total eigenvalue changes in last iteration: 1.9602375e-014 After refining again: Enter command: ritz(0,10) Eigencounts: 0 1. 2.009974216370147 2. 4.999499042685446 3. 4.999499042685451 4. 7.985384943789077 5. 10.090156419079085 6. 10.186524915471155 7. 13.008227978344527 8. 13.008227978344527 9. 17.242998229925817 10. 17.2429982299600 Iterations: 163. Total eigenvalue changes in last iteration: 1.9186042e-014 This looks much more convergent. Using linear_metric does NOT change the inertia of the Hessian, by Sylvester's Law of Inertia. So the moral of the story is that if you care only about stability, you don't need to use linear_metric. If you do care about the actual values of eigenvectors, and want them relatively independent of your triangulation, then use linear_metric. <-------------------- hessian normal mode ----------------------------> The alert reader will have notice that in the examples so far there has been only one degree of freedom per vertex, instead of the three one might expect, since a vertex has three degrees of freedom to move in space. The answer is that in Hessian activities, it is usually best to only allow motion perpendicular to the surface, and suppress the two degrees of freedom of motion of a vertex tangential to the surface. The reason is that tangential motion changes the energy of the surface very little if at all, leading to many small eigenvalues and a severely singular Hessian. For example, run square.fe, refine twice, and do "hessian_normal off" to enable all degrees of freedom. Now "eigenprobe 0" reveals 50 zero eigenvalues: Enter command: hessian_normal off hessian_normal OFF. (was on) Enter command: eigenprobe 0 Eigencounts: 0 For a curved surface, the extra eigenvalues generally won't be zero, but they will all be close to zero, both positive and negative, which can really foul things up. The default value of hessian_normal is therefore the ON state. The moral of the story is to always leave hessian normal on, unless you really really know what you are doing. On some surfaces, for example soap films with triple junctions and tetrahedral points, there are vertices with no natural normal vector. Evolver hessian_normal mode assigns those points normal subspaces instead, so that vertices on a triple line can move in a two-dimensional normal space perpendicular to the triple line, and tetrahedral points can move in all three dimensions. The reason for possible negative extra eigenvalues when hessian_normal is off is that one rarely has the best possible vertex locations for a given triangulation of a surface, even when its overall shape is very close to optimal. Vertices always seem to want to slither sideways to save very very small amounts of energy. The amount of energy saved this way is usually much less than the error due to discrete approximation, so it is usually advisable not to try to get the absolute best vertex positions. There is one effect of hessian_normal that may be a little puzzling at first. Many times a surface is known to have modes with zero eigenvalue; translational or rotational modes, for example. Yet no zero eigenvalues are reported. For example, with the cube eigenvalues found above, Eigencounts: 0 1. 0.001687468263013 2. 0.001687468263013 3. 0.001687468263013 4. 0.2517282974725 5. 0.2517282974731 one might expect 6 zero eigenvalues from three translational modes and three rotational modes. But the rotational modes are eliminated by the hessian_normal restriction. The three translational modes have eigenvalue near 0, but not exactly 0, since normal motion can approximate the translation of a cube, but not exactly. The effective translation results from vertices moving in on one hemisphere and out on the other. This distorts the triangulation, raising the energy, hence the positive eigenvalue. This effect decreases with refinement. There are times when the normal direction is not the direction one wants. If one knows the perturbation direction, one can use the hessian_special_normal feature to use that instead of the default normal. Beware that hessian_special_normal also applies to the normal calculated by the vertexnormal attribute and the normal used by regular vertex averaging. <-------------------- hessian tutorial ----------------------------> For a tutorial on using the hessian and related commands, see eigentut.htm in the doc subdirectory of the Evolver distribution, or browse http://www.susqu.edu/brakke/evolver/html/eigentut.htm. <-------------------- hessian_diff ----------------------------> Evolver toggle command. Toggles computing the hessian matrix by finite differences. Used only for debugging, since it is very slow, and there are some features it cannot handle. <-------------------- hessian_double_normal ----------------------------> Evolver toggle command. When hessian_normal is also on, and the space dimension is even, this toggle makes the normal vector components in the last half of the dimensions copies of the components in the first half. Sounds weird, huh? But it is useful in calculating the stability of cylindrically symmetric surfaces using a string model in 4D. <-------------------- hessian_epsilon ----------------------------> Internal read-write variable. This is the magnitude regarded as zero by the hessian command when factoring the Hessian matrix. If a zero appears on the diagonal at the pivot during factoring, a check is made to see if the rest of the row is zero. If it is, then a 1 is placed on the diagonal; else an error is reported. <-------------------- hessian_menu ----------------------------> Main prompt command. Brings up a menu of experimental stuff involving the energy Hessian matrix. Not all of it works well, and may disappear in future versions. A one-line prompt with options appears. Use option '?' to get a fuller description of the choices. For those options that calculate an eigenvalue, the eigenvalue (or first, if several) is saved in the internal variable last_eigenvalue. A quick summary of the current options, with the hessian_menu help line and additional comments: 1. Fill in hessian matrix. Allocation and calculation of Hessian matrix. 2. Fill in right side. (Do 1 first) Calculates gradient and constraint values. 3. Solve. (Do 2 first) Solves system for a motion direction. 4. Move. (Do 3, A, B, C, E, K, or L first) Having a motion direction, this will move some stepsize in that direction. Will prompt for stepsize. The direction of motion is saved and is available in the move command. 7. Restore original coordinates. Will undo any moves. So you can move without fear. 9. Toggle debugging. (Don't do this!) Prints Hessian matrix and right side as they are calculated in other options. Produces copious output, and is meant for development only. Do NOT try this option. B. Chebyshev (For Hessian solution ). Chebyshev iteration to solve system. This option takes care of its own initialization, so you don't have to do steps 1 and 2 first. Not too useful. C. Chebyshev (For most negative eigenvalue eigenvector). Chebyshev iteration to find most negative eigenvalue and eigenvector. Will ask for number of iterations, and will prompt for further iterations. End by just saying 0 iterations. Prints Rayleigh quotient every 50 iterations. After finding an eigenpair, gives you the chance to find next lowest. Last eigenvector found becomes motion for step 4. Self initializing. Not too useful. E. Lowest eigenvalue. (By factoring. Do 1 first) Uses factoring to probe the inertia of the shifted Hessian H-cI until it as the lowest eigenvalue located within .01. Then uses inverse iteration to find eigenpair. F. Lowest eigenvalue. (By conjugate gradient. Do 1 first) Uses conjugate gradient to minimize the Rayleigh quotient. L. Lanczos. (Finds eigenvalues near probe value. ) Uses Lanczos method to solve for 15 eigenvalues near the probe value left over from menu choices 'P' or 'V'. These are approximate eigenvalues, but the first one is usually very accurate. Do not trust apparent multiplicities. From the main command prompt, you can use the lanczos command. R. Lanczos with selective reorthogonalization. Same as 'L', but a little more elaborate to cut down on spurious multiplicities by saving some vectors to reorthogonalize the Lanczos vectors. Not quite the same as the official "selective reorthogonalization" found in textbooks. Z. Ritz subspace iteration for eigenvalues. (Do 1 first) : Calculate a number of eigenpairs near a probe value. Will prompt for probe value and number of eigenpairs. Same as ritz main command. Can be interrupted gracefully by keyboard interrupt. Afterwards, one can use the X option to pick a particular eigenvector to look at. X. Pick Ritz vector for motion. (Do Z first) Selects an eigenvector calculated by the Z option for use in motion (option 4). First eigenvalue listed is number 1, etc. Particularly useful for discriminating among high multiplicity eigenvalues, which the V option does not let you do. You can enter the eigenvector by its number in the list from the Z option. As a special bonus useful when there are multiple eigenvectors for an eigenvalue, you can enter the vector as a linear combination of eigenvectors, e.g. ``0.4 v1 + 1.3 v2 - 2.13 v3''. P. Eigenvalue probe. (By factoring. Do 1 first) Reports the inertia of the shifted Hessian H-cI for user-supplied values of the shift c. The Hessian H includes the effects of constraints. Will prompt repeatedly for c. Null response exits. From the main command prompt, you can use the eigenprobe command. V. Eigenvalue probe with eigenvector. (By factoring. Do 1 first) Reports the inertia of the shifted Hessian H-cI for user-supplied values of the shift c, and calculates the eigenvector for the eigenvalue nearest c by inverse power iteration. You will be prompted for c and for the maximum number of iterations to do. From the main command prompt, you can use the eigenprobe command. S. Seek along direction. (Do 3, A, B, E, C, K, or L first) Can do this instead of option 4 if you want Evolver to seek to lowest energy in an already found direction of motion. Uses the same line search algorithm as the optimizing `g' command. Y. Toggle YSMP/alternate minimal degree factoring. Default Hessian factoring is by Yale Sparse Matrix Package. The alternate is a minimal degree factoring routine of my own devising that is a little more aware of the surface structure, and maybe more efficient. If YSMP gives problems, like running out of storage, try the alternate. This option is available at the main prompt as the ysmp toggle. U. Toggle Bunch-Kaufman version of min deg. YSMP is designed for positive definite matrices, since it doesn't do any pivoting or anything. The alternate minimal degree factoring method, though, has the option of handling negative diagonal elements in a special way. This option is available at the main prompt as the bunch_kaufman toggle. M. Toggle projecting to global constraints in move. Toggles projecting to global constraints, such as volume constraints. Default is ON. Don't mess with this. Actually, I don't remember why I put it in. G. Toggle minimizing square gradient in seek. For converging to unstable critical points. When this is on, option 'S' will minimize the square of the energy gradient rather than minimizing the energy. Also the regular saddle and hessian_seek commands will minimize square gradient instead of energy. =. Subshell. Starts a command prompt while still in hessian_menu. You can do pretty much any command, but you should not do anything that changes the surface, thus invalidating the Hessian data. This is meant, for example, for creating a graphics file of an eigenvalue perturbation and then returning to the hessian_menu prompt. You exit the subshell with the "q" command. 0. Exit hessian. Exits the menu. `q' also works. For example, to inspect what eigenvectors look like, one would do steps 1 and z, then repeatedly use x to pick an eigenvector, 4 to move, and 7 to restore. <-------------------- hessian_normal ----------------------------> Evolver toggle command. Constrains Hessian to move each vertex perpendicular to the surface. This eliminates all the fiddly sideways movement of vertices that makes convergence difficult. Perpendicular is usually defined as the volume gradient, but at triple (and higher) junction lines it is a subspace perpendicular to the line, and singular points where several triple lines meet have full degrees of freedom. See hessian_normal_one to alter this behavior. Default is ON. <-------------------- hessian_normal_one ----------------------------> Evolver toggle command. If this and hessian_normal are on, then the normal at any point will be one dimensional. This is meant for soap films with Plateau borders, where there are triple junctions with tangent films. Ordinary hessian_normal permits lateral movement of such triple junctions, but hessian_normal_one does not. Valid only for string model in 2D and soapfilm model in 3D. The normal vector is computed as the eigenvector of the largest eigenvalue of the sum of the normal projection matrices of all edges or facets adjoining a vertex. <-------------------- hessian_normal_perp ----------------------------> Evolver toggle command. If this is on, then the Hessian linear_metric uses only the component of the normal perpendicular to the facet or edge. This raises eigenvalues slightly. <-------------------- hessian_quiet ----------------------------> Evolver toggle command. Toggles suppression of printing of information during hessian calculations. Default is ON. <-------------------- hessian_seek ----------------------------> Main prompt command. Seeks to minimize energy along the direction found by Newton's method using the Hessian. Otherwise same as the hessian command. Syntax: HESSIAN_SEEK maxscale where maxscale is an optional upper bound for the distance to seek. The default maxscale is 1, which corresponds to a plain hessian step. The seek will look both ways along the direction, and will test down to 1e-6 of the maxscale before giving up and returning a scale of 0. This command is meant to be used when the surface is far enough away from equilibrium that the plain hessian command is unreliable, as hessian_seek guarantees an energy decrease, if it moves at all. <-------------------- hessian_slant_cutoff ----------------------------> Internal read-write variable. Makes Hessian commands treat vertices whose normal vector is nearly perpendicular to constraints as fixed. hessian_slant_cutoff is the cosine of angle. Works on vertices with one degree of freedom in hessian_normal mode. Default value 0. <-------------------- hessian_special_normal ----------------------------> Evolver toggle command. When hessian_normal is on, this toggles using a special vectorfield for the direction of the perturbation, rather than the usual surface normal. The vectorfield is specified in the hessian_special_normal_vector section of the datafile header. Beware that hessian_special_normal also applies to the normal calculated by the vertexnormal attribute and the normal used by regular vertex averaging. <------------------ hessian_special_normal_vector ----------------------------> Evolver toggle command. When hessian_special_normal is on, hessian commands use a special vectorfield for the direction of the perturbation, rather than the usual surface normal. The vectorfield is specified in the top of the datafile in the format HESSIAN_SPECIAL_NORMAL_VECTOR c1: expr c2: expr c3: expr One can use vertex attributes in the expressions. Beware that hessian_special_normal also applies to the normal calculated by the vertexnormal attribute and the normal used by regular vertex averaging. <-------------------- hexadecimal numbers ----------------------------> The format for hexadecimal integers is to start with 0x, as in 0x12Af. There is no bound on the size, but values too large for the internal representation of integers will have their higher digits truncated. <-------------------- high_boundary ----------------------------> Internal read-only variable giving the number of the highest parametric boundary defined. Remember that the name of a named boundary is internally interpreted as a number, and that boundary may be referred to by that number. Useful in iterating through all boundaries, for example for ( bnum := 1 ; bnum <= high_boundary ; bnum += 1 ) if valid_boundary(bnum) then printf "Boundary %d has %d vertices on it\n",bnum, sum(vertex,on_boundary bnum); <-------------------- high_constraint ----------------------------> Internal read-only variable giving the number of the highest level-set constraint. Remember that the name of a named constraint is internally interpreted as a number, and that constraint may be referred to by that number. Useful in iterating through all constraints, for example for ( cnum := 1 ; cnum <= high_constraint ; cnum += 1 ) if valid_constraint(cnum) then printf "Constraint %d has %d vertices on it\n",cnum, sum(vertex,on_constraint cnum); <-------------------- histogram ----------------------------> <-------------------- loghistogram ----------------------------> Main prompt command. For printing histograms in text form to standard output. Syntax: HISTOGRAM(generator, expr) LOGHISTOGRAM(generator, expr) Prints a histogram of the values of expr for the generated elements. It uses 20 bins evenly divided between minimum and maximum values. It finds its own maximum and minimum values, so the user does not have to specify binsize. The log version will lump all zero and negative values into one bin. Examples: histogram(edge,dihedral*180/pi) loghistogram(facet where color == red, area) histogram(vertex where on_constraint 1, sqrt(x^2+y^2+z^2)) <-------------------- history ----------------------------> Main prompt command. Print the saved history list of commands. <-------------------- history list ----------------------------> Successfully parsed commands are saved in a history list, up to 100 commands. They may be accessed with !! for the last command or !string for the latest command with matching initial string. !n will repeat a command by history list number. The command will be echoed. The saved history list may be printed with the "history" command. <-------------------- hit_constraint ----------------------------> Vertex read-only attribute. Boolean attribute for whether a vertex exactly satisfies a given constraint. Particularly meant for vertices on one-sided constraints. The full syntax of the attribute is "hit_constraint n" where n is the number or name of the constraint. Examples: list vertex where hit_constraint 3 print vertex[3].hit_constraint 1 <-------------------- homothety ----------------------------> Evolver toggle command. Adjust total volume of all bodies to fixed value after each 'g' iteration by uniformly scaling entire surface. The total volume is in the read-write variable homothety_target, which may also be set in the top of the datafile with the syntax HOMOTHETY_TARGET constexpr <-------------------- hooke2_energy ----------------------------> <-------------------- hooke2_power ----------------------------> hooke2_energy Named method. Description: Same as hooke_energy, but each edge has an equilibrium length extra attribute `hooke_size' (which the user need not declare). If the user does not set hooke_size by the time the method is first called, the value will default to the current length. Hooke_size is not automatically adjusted by refining. It is the responsibility of the user to reset hooke_size after refining; you could re-define the 'r' command r :::= { 'r'; set vertex hooke_size hooke_size/2 } to take care of it automatically. The power of displacement used is given by the internal read-write variable hooke2_power, which has default value 2. Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration: parameter hooke2_power 2 // the default define edge attribute hooke_size real quantity slinky energy method hooke2_energy global ... read r;r;set edge hooke_size length <-------------------- hooke3_energy ----------------------------> <-------------------- hooke3_power ----------------------------> <-------------------- frickenhaus_flag ----------------------------> hooke3_energy Named method. Description: Same as hooke2_energy, but uses an elastic model instead of a spring. The energy is energy = 0.5*(length-hooke_size)^2/hooke_size. The exponent can be altered from 2 by setting the parameter hooke3_power. If the internal variable frickenhaus_flag is nonzero, then the energy is taken to be 0 if the length is less than the equilibrium length. Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration: parameter hooke3_power 2 // the default quantity slinky energy method hooke3_energy global ... read r;r;set edge hooke_size length <-------------------- hooke_energy ----------------------------> <-------------------- hooke_power ----------------------------> hooke_energy Named method. Description: One would often like to require edges to have fixed length. The total length of some set of edges may be constrained by defining a fixed quantity. This is used to fix the total length of an evolving knot, for example. But to have one constraint for each edge would be impractical, since projecting to n constraints requires inverting an n x n matrix. Instead there is a Hooke's Law energy available to encourage edges to have equal length. Its form per edge is energy = | L - L_0| ^p where L is the edge length, L_0 is the equilibrium length, embodied as an global variable `hooke_length', and the power p is an adjustable parameter `hooke_power'. The default power is p = 2, and the default equilibrium length is the average edge length in the initial datafile. You will want to adjust this, especially if you have a total length constaint. A high modulus will decrease the hooke component of the total energy, since the restoring force is linear in displacement and the energy is quadratic (when p=2). As an extra added bonus, a `hooke_power' of 0 will give energy = -\log|L-L_0|. See hooke2_energy for individual edge equilibrium lengths. Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration: parameter hooke_length 0.3 // will apply to all edges parameter hooke_power 2 // the default quantity slinky energy method hooke_energy global <--------------------- hooke_length ----------------------------> Internal read-write variable that specifies the equilibrium length for the hooke_energy named method. It is the responsibility of the user to divide hooke_length by 2 when refining. <--------------------- hooke_size -------------------------> Edge extra attribute that specifies the equilibrium length for that edge for the hooke2_energy and hooke3_energy methods. If not initialized by the user, its default value is the initial length of the edge. It is the responsibility of the user to reset hooke_size after refining; you could re-define the 'r' command r :::= { 'r'; set vertex hooke_size hooke_size/2 } to take care of it automatically. <-------------------- i ----------------------------> Single letter main command. Prints miscellaneous information: Name of datafile Total energy Total area of facets Count of elements and memory usage Area normalization, if on LINEAR or QUADRATIC model Whether conjugate gradient on Order of numerical integration Scale factor value and option (fixed or optimizing) Diffusion option and diffusion constant value Gravity option and gravitational constant value Jiggling status and temperature Gap constant (for gap energy, if active) Ambient pressure (if ideal gas model in effect) <-------------------- id ----------------------------> Geometric element read-only attribute. The id of an element is a positive integer uniquely associated with that element. The Evolver will assign id's to elements read from the datafile in the order they are read, unless the -i command line option or keep_originals is in the top of the datafile, in which case the datafile element number is the id. In either case, you can access the datafile id with the original attribute. Examples: list vertex where id < 10 set edge color red where id == 4 or id == 6 or id == 9 foreach facet ff do { printf "%g %g %g %g\n",ff.id,ff.edge[1].id, ff.edge[2].id,ff.edge[3].id } See also the "oid" attribute, for a signed version of the id indicating element orientation. <-------------------- ideal gas declaration ----------------------------> A line in the top section of the datafile of the form PRESSURE constexpr specifies that bodies are compressible and the ambient pressure is the value of constexpr. The default is that bodies with given volume are not compressible. <-------------------- identifiers ----------------------------> Identifiers follow standard C rules (composed of alphanumeric characters and '_' with the leading character not a digit) and must not be keywords. Identifiers are used for macro names (in the datafile) and user-defined variables and commands. Identifiers must have at least two characters, since single characters can be confused with commands. To find out if a name is already in use as a keyword or user-defined name interactively, use help command. In scripts, one can use the is_defined function, which has the syntax is_defined(stringexpr) The stringexpr must be a quoted string or other string expression. The return value is 0 if the name is undefined, 1 if defined. This function is evaluated at run-time, but variables in the whole command are parsed before the command is executed, so a command like if is_defined("newvar") then newvar := 1 else newvar := 2 will give newvar the value 1 even if this is its first appearance. A better way in scripts to test is to use the define command to define the variable without initialization, and then test to see if it has the default value, i.e. 0 for a numeric variable and a sizeof 0 for a string variable. <-------------------- idiv ----------------------------> Integer divide. Rounds toward zero, then does integer division. Examples: "7 idiv 2" is 3; "-3.5 idiv 2.1" is -1; "-3 idiv 2" is -1. <-------------------- if ----------------------------> <-------------------- then ----------------------------> Commands may be conditionally executed by the syntax IF expr THEN command IF expr THEN command ELSE command where expr is true if nonzero. Parentheses around expr are not needed, but do not hurt. Do not use a semicolon to end the first command. Examples: if ( denom == 0 ) then return; if max(edges,length) > 0.02 then {r; g 100} else g 4; <-------------------- ignore_constraints ----------------------------> <-------------------- ignore_fixed ----------------------------> For squared curvature methods calculating at a boundary vertex, the default calculation gives the proper curvature under the assumption the surface is continued by reflection across the constraint. This permits symmetric surfaces to be represented by one fundamental region. If the edge of the surface is a fixed edge or on a 1-dimensional boundary, then there is no way to calculate the curvature on a boundary vertex from knowledge of neighboring facets. For example, the rings of facets around the bases of a catenoid and a spherical cap may be identical. Therefore curvature is calculated only at interior vertices, and when the surface integral is done, area along the boundary is assigned to the nearest interior vertex. However, including IGNORE_FIXED or IGNORE_CONSTRAINTS in the method declaration will force the calculation of energy even at fixed points or ignoring constraints respectively. <-------------------- immediate_autopop ----------------------------> Toggle. Modifies the autopop mode. Causes deletion of a short edge or small facet immediately upon detection, before proceeding with further detection of small edges or facets. Original behavior was to do all detection before any elimination, which could cause bad results if a lot of edges got short simultaneously. Default off for backward compatibility, but you should probably turn it on. <-------------------- imod ----------------------------> Integer arithmetic modulus, defined by x imod y = floor(x) - floor(floor(x)/floor(y))*floor(y) <-------------------- implemented symmetries ----------------------------> The currently implemented symmetry groups are: torus - The underlying group for the torus model. rotate - Cyclic group of rotations in the x-y plane. flip_rotate - Cyclic group of rotations in the x-y plane with z -> -z with every odd rotation. cubocta - Full point group of a cube. xyz - The orientation-preserving subgroup of cubocta. genus2 - For a 2 dimensional genus 2 hyperbolic quotient space. dodecahedron - For a 3D hyperbolic quotient space with dodecahedral fundamental region. central_symmetry - Inversion through the origin, X -> -X. screw_symmetry - Screw motion along z axis. <-------------------- include files ----------------------------> The standard C language method of including other files is available in the datafile or files input with the "read" command. The file name must be in double quotes. If the file is not in the current directory, EVOLVERPATH will be searched. Includes may be nested to 10 deep. Example: #include "common.stuff" <-------------------- incompleteEllipticE ----------------------------> incompleteEllipticE(phi,m), incompleteEllipticF(phi,m): Incomplete elliptic functions of parameters (phi,m); agrees with Mathematica definition. <-------------------- incompleteEllipticF ----------------------------> incompleteEllipticE(phi,m), incompleteEllipticF(phi,m): Incomplete elliptic functions of parameters (phi,m); agrees with Mathematica definition. <-------------------- index ----------------------------> <-------------------- inertia ----------------------------> Index and inertia. The number of negative eigenvalues of H is called the index of H. The triple of numbers of negative, zero, and positive eigenvalues is called the inertia of H. One can also speak of the inertia relative to a value c as the inertia of H-cI, i.e. the number of eigenvalues less than c, equal to c, and greater than c. Sylvester's Law of Inertia says that if P is a nonsingular matrix of the same dimension as H, then the matrix K = P H P^T has the same inertia as H, although not the same eigenvalues. The Evolver can factor a Hessian into a form H = L D L^T where L is a lower triangular matrix and D is diagonal. Hence the inertia of H can be found by counting signs on the diagonal of D. Inertia relative to c can be found by factoring H-cI. The eigenvectors associated to a particular eigenvalue form a subspace, whose dimension is called the "multiplicity" of the eigenvalue. <-------------------- indexed elements ----------------------------> In aggregate commands or element generators, an element type name referring to the whole set of an element type may be given an index to generate just the one element with that id, for example: list vertex[3] When the element type refers to a subelement of another element, then the index refers to subelements in Evolver's internal order, for example list edge ee where ee.vertex[1].z > 1.2 <-------------------- info_only ----------------------------> One of the types of named quantities, along with energy, fixed, and conserved. INFO_ONLY quantities whose values are merely reported to the user. Info_only status may be set in the datafile declaration of the quantity, for example, quantity george info_only method facet_area or may be set at runtime with the "set" command: set george info_only <-------------------- initialization ----------------------------> Whenever the Surface Evolver loads a new surface, either on startup or in response to the q or load commands, the following actions occur: 1. Any previous surface has all memory deallocated. All user-defined variables and commands are deleted, including any currently executing commands. 2. Internal variables are initialized to default values. 3. The datafile is read in. 4. In the soapfilm model, any nontriangular face is divided into triangles by creating a vertex in the center and making edges from the center to the vertices. 5. In the soapfilm model, the order of facets around each edge is determined geometrically. 6. Vertices are projected to any level set constraints. 7. Checks as described for the C command are done. 8. Initial areas, energies, and volumes are calculated. 9. The viewing matrix is reset to center the object in the graphics window. 10. If any graphics are active, the new surface is drawn. 11. The main command prompt is started. <-------------------- input ----------------------------> Command input The Surface Evolver command interpreter reads from an input stream, which may be from the end of the datafile, from a file given on the system command line, from stdin (the terminal), or from a file given in a READ command. The interactive command prompt is "Enter command: ". Commands are read one at a time, parsed, and executed. By default, a line is expected to contain a complete command, so no special end-of-command token is needed. Multi-line commands may be entered by enclosing them in braces {...}. If a line ends while nested in braces or parenthesis, Evolver will ask for more input with the prompt "more> ". It will also ask for more if the line ends with certain tokens (such as `+') that cannot legally end a command. Unclosed quotes will also ask for more, and embedded newlines will be omitted from the final string. Explicit continuation to the next line may be indicated by ending a line with a backslash (linesplicing). You may want to use the read command to read long commands from a file, since there is no command line editing. Successfully parsed commands are saved in a history list, up to 100 commands. They may be accessed with !! for the last command or !string for the latest command with matching initial string. !n will repeat a command by history list number. The command will be echoed. The saved history list may be printed with the history command. Some single-letter commands require interactive input. For those, there are equivalent commands that take input information as part of the command. This is so commands may be read from a script without having to put input data on additional lines after the command, although that can still be done for the single-letter versions. General note: Some commands will prompt you for a value. A null response (just RETURN) will leave the old value unchanged and return you to the command prompt. On options where a zero value is significant, the zero must be explicitly entered. Commands that need a real value will accept an arbitrary expression. Many commands that change the surface or change the model will cause energies and volumes to be recalculated. If you suspect a command has not done this, the recalc command will recalculate everything. It will also update any automatic display. In the command syntax descriptions, keywords are shown in upper case, although case is irrelevant in actual commands, except for single-letter commands. <-------------------- instance modulus ----------------------------> Every method instance has a "modulus", which is multiplied times the basic method value to give the instance value. A modulus of 0 causes the entire instance calculation to be omitted whenever quantities are calculated. The modulus may be set in the datafile or with the A command or by assignment. Example commands: print moment.modulus moment.modulus := 1.3 A method instance may be declared to use a different modulus for each element by specifying an element extra attribute to use for that purpose. The extra attribute has to have already been declared. Example: define facet attribute mymod real quantity myquant energy method facet_area global element_modulus mymod Of course, it is up to the user to properly initialize the values of the extra attribute. <-------------------- instance_name modulus ----------------------------> Internal read-write variable. Modulus of a named method instance. Examples, supposing a named method instance "george" exists: print george.modulus george.modulus := 4 <-------------------- instance_name value ----------------------------> Internal read-only variable. Value of a named method instance. Example, supposing a named method instance "george" exists: print george.value <-------------------- insulating_knot_energy ----------------------------> Datafile keyword that automatically creates an energy named quantity using the method uniform_knot_energy as a global method. May be followed in the datafile with a modulus value. <-------------------- integer ----------------------------> One of the datatypes possible for extra attributes. It is possible for the user to define extra attributes for elements, which may be single values or up to eight-dimensional arrays. If these attributes are to be included in the datafile, then the top section of the datafile must contain appropriate definitions. The definition syntax is the same as used by the define runtime command: DEFINE elementtype ATTRIBUTE name type [dim]... where elementtype is vertex, edge, facet, or body, name is an identifier of your choice, type is REAL or INTEGER (internally, there is also a ULONG unsigned long type also), and dim is an optional expression for the vector dimension. There is no practical distinction between real and integer types at the moment, since everything is stored internally as reals. But there may be more datatypes added in the future. Extra attributes are inherited by elements of the same type generated by subdivision. The type may be followed by FUNCTION followed by a procedure in brackets to be evaluated whenever the value of the attribute is read; in the formula, self may be used to refer to the element in question to use its attributes, in particular to at some point assign a value to the attribute. The print command may be used to print attribute arrays or array slices in bracketed form. Examples: define edge attribute charlie real define vertex attribute oldx real[3] define facet attribute knots real[5][5][5] define edge attribute bbb real function { self.bbb := self.x+self.y } WARNING: there is a syntax ambiguity if you mean to define a stand-alone function in the top of the datafile and put it after an attribute declaration. You should define stand-alone functions before attributes, or separate them with some other kind of declaration. <-------------------- integral_order ----------------------------> Internal read-write variable. Order of polynomial done by 1D and 2D Gaussian integration. Much better to set 1D and 2D separately. Synonym: integration_order <-------------------- integral_order_1d ----------------------------> Internal read-write variable. Order of polynomial done by 1D Gaussian integration. Synonym: integration_order_1D <-------------------- integral_order_2d ----------------------------> Internal read-write variable. Order of polynomial done by 2D Gaussian integration. Synonym: integration_order_2D <-------------------- integration_order ----------------------------> Internal read-write variable. Order of polynomial done by 1D and 2D Gaussian integration. Much better to set 1D and 2D separately. Synonym: integration_order <-------------------- integration_order_1d ----------------------------> Internal read-write variable. Order of polynomial done by 1D Gaussian integration. Synonym: integration_order_1D <-------------------- integration_order_2d ----------------------------> Internal read-write variable. Order of polynomial done by 2D Gaussian integration. Synonym: integration_order_2D <-------------------- internal variables ----------------------------> These are pre-defined names for some of Evolver's internal variables. They may be used in the same way as user-defined variables, except the values of read-only variables may not be changed by the user. > ambient_pressure_value > background > body_count > breakflag > body_dissolve_count > brightness > check_count > clock > constraint_tolerance > cpu_counter > datafilename > date_and_time > delete_count > dissolve_count > edge_count > edge_delete_count > edge_dissolve_count > edge_refine_count > edge_pop_count > edgeswap_count > eigenneg > eigenpos > eigenvalues > eigenzero > equi_count > estimated_change > facet_count > facetedge_count > facet_delete_count > facet_dissolve_count > fix_count > gravity_constant > hessian_epsilon > hessian_slant_cutoff > instance_name.modulus > instance_name.value > inverse_periods > integral_order > integral_order_1d > integral_order_2d > iteration_counter > jiggle_temperature > lagrange_order > last_eigenvalue > last_error > last_hessian_scale > linear_metric_mix > memory_arena > memory_used > notch_count > pickvnum > pickenum > pickfnum > pop_count > pop_edge_to_tri_count > pop_quad_to_quad_count > pop_tri_to_edge_count > quadratic_metric_mix > quantity_name.modulus > quantity_name.pressure > quantity_name.target > quantity_name.value > random > random_seed > refine_count > rotation_order > scale > scale_scale > scrollbuffersize > simplex_representation > space_dimension > surface_dimension > symmetry_group > t1_edgeswap_count > target_tolerance > temperature > thickness > torus > torus_periods > total_area > total_energy > total_length > total_time > transform_count > unfix_count > vertex_count > vertex_dissolve_count > vertex_pop_count > where_count > window_aspect_ratio <-------------------- interp_bdry declaration ----------------------------> Interpolation of parameters: To use interpolation instead of extrapolation in calculating the parameters of edge midpoints during refining, use the keyword INTERP_BDRY_PARAM in the top of the datafile. This should be done only if there are no periodic parameters. There is also the runtime toggle command interp_bdry_param. <-------------------- interp_bdry_param ----------------------------> Evolver toggle command. For edges on parametric boundaries, calculate the parameter values of new vertices (introduced by refining) by interpolating parameter values, rather than extrapolating from one endpoint. Useful only when parameters are not periodic. <-------------------- interp_normals ----------------------------> Evolver toggle command. Display using interpolated vertex normals for shading for those graphics interfaces that support it. <-------------------- interrupts ----------------------------> Evolver operation may be interrupted with the standard keyboard interrupt, CTRL-C usually (SIGINT for you unix gurus). During repeated operations, this will set a flag which is checked at the end of each loop. Repetition will cease after the current step and control will return to the main prompt. If you give a second interrupt before the loop has ended, Evolver will abort the command and return to the main prompt. Beware that this may leave the surface in an inconsistent state if surface topology changing operations were going on. An immediate abort will also happen if an interrupt is received outside a loop. If Evolver receives SIGTERM (say from the unix kill command), it will dump to the default dump file and exit. This is useful for stopping a background Evolver running a script. The same thing will happen with SIGHUP, so losing a modem connection will save the current surface. Note: In Microsoft Windows, the second interrupt doesn't do anything much since Windows creates a separate thread to handle the interrupt, and I can't find any way to force the offending thread to stop and longjmp back to where it should. So if the Evolver is really, really stuck, you may just have to kill the whole program. <-------------------- inverse_periods ----------------------------> inverse_periods[expr][expr] : Internal read-only variable. Inverse matrix of the torus_periods matrix. Uses 1-based indexes. Useful for normalizing vertex coordinates to period basis. <-------------------- is_defined ----------------------------> Funcion to find out if a name is already in use as a keyword or user-defined namer. Syntax: is_defined(stringexpr) The stringexpr must be a quoted string or other string expression. The return value is 0 if the name is undefined, 1 if defined. This function is evaluated at run-time, but variables in the whole command are parsed before the command is executed, so a command like if is_defined("newvar") then newvar := 1 else newvar := 2 will give newvar the value 1 even if this is its first appearance. A better way in scripts to test is to use the define command to define the variable without initialization, and then test to see if it has the default value, i.e. 0 for a numeric variable and a sizeof 0 for a string variable. <-------------------- itdebug ----------------------------> Evolver toggle command. Prints some debugging information during a 'g' step. For gurus only. <-------------------- iteration step ----------------------------> Each 'g' command iteration does the following: 1. Calculates the force vector at each vertex as the gradient of the total energy. 2. Calculates the gradients at each vertex of constrained body volumes and named quantities. Also calculates the multiple of gradient motion needed to restore target values of the constraints. 3. Projects the forces to be tangent to level set constraints, volume constraints (pressure is calculated here), and fixed quantity constraints. 4. If any mobility options are in effect, the proper force to velocity conversion is done. 5. If the normal_motion toggle is on, then velocities are projected to the surface normal. 6. Does the conjugate gradient projection, if conjugate gradient is in effect. 7. The current vertex positions are saved. 8. If the optimizing mode is on, then trial motions are made to find the optimal scale factor. 9. The scale factor is multiplied by the value of the scale_scale internal variable. (Useful only if you want to muck around with modifying the optimal scale factor for some strange reason.) 10. Each vertex is moved by the scale factor times the velocity, plus the volume and fixed quantity restoring motions. If runge_kutta is in effect, then a fourth-order Runge-Kutta step is done instead of a simple Euler step. 11. All level set constraints are enforced. Vertices violating an inequality or equality constraint are projected to the constraint (Newton's method). Several projection steps may be needed, until the violation is less that a certain tolerance or a certain number of steps are done (which generates a warning message). The default constraint tolerance is 1e-12, but it can be set with the CONSTRAINT_TOLERANCE option in the datafile, or setting the constraint_tolerance variable. 12. If jiggling is on, the surface is randomly perturbed. 13. If autopop or autochop are on, then the appropriate edge deletion or division is done. 14. New energies, volumes, and quantities are calculated. 15. If check_increase is on and the surface energy has increased, then the vertices are restored to their old values. 16. If estimating is on, then a linear estimate of the energy change is printed. This is calculated from the velocities, gradients, and scale factor. 17. The number of iterations left, the new area, energy, and scale factor are printed. 18. If a graphics display is active and set to autodisplay, then graphics are redrawn. <-------------------- iteration_counter ----------------------------> iteration_counter : Internal read-only variable. Loop count of last loop in a command. <-------------------- j ----------------------------> Single letter main command. Jiggles all vertices once. Meant to be used for simulated annealing. Useful for shaking up surfaces that get in a rut, especially crystalline integrands. Syntax: j value You will be prompted for a value, which is used as a scaling factor, if you don't give a value with the command. Default value is the value of the "jiggle_temperature" internal variable, which starts as 0.05. The actual jiggle is a random displacement of each vertex independently with a Gaussian distribution with deviation being the temperature times the mean edge length. See the "longj" command for a user-definable perturbation. <-------------------- J ----------------------------> Single letter main command. Toggles jiggling on every iteration of the g command. If jiggling gets turned on, prompts for temperature value. Default temperature is the value of the jiggle_temperature internal variable. <-------------------- jiggle ----------------------------> Evolver toggle command. Toggles jiggling on every iteration. <-------------------- jiggle_temperature ----------------------------> jiggle_temperature : Internal read-only variable. Current temperature for jiggling. <-------------------- johndust ----------------------------> Named method. Description: For all point pairs (meant to be on a sphere), energy = (pi - asin(d/2))/d, where d is chord distance. For point packing problems on the sphere. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: constraint 1 formula: x^2+y^2+z^2 = 1 quantity jms energy method johndust global <-------------------- k ----------------------------> Single letter main command. Sets "gap constant" for gap energy for convex constraints. Adds energy roughly proportional to area between edge and boundary. You will be prompted for a value if you don't give a value on the command line. Normal values are on the order of magnitude of unity. Value k = 1 is closest to true area. Use 0 to eliminate the energy. <-------------------- K ----------------------------> Single letter main command. Finds skinny triangles whose smallest angle is less than a specified cutoff. You will be prompted for a value if you don't give a value on the command line. Such triangles will have their longest edge subdivided. Should be followed with tiny edge removal 't' and equiangulation 'u'. <-------------------- k_form_order ----------------------------> Some named methods, such as form_integrand, can handle a differential form of arbitrary order, and the k_form_order attribute in the method declaration specifies the order of the form. <-------------------- k_vector_order ----------------------------> simplex_k_vector_integral Named method. Description: Integral of a simple (n-k)-vector over an oriented k-dimensional simplicial facet in n-space. The vector integrand lists the components of each of the k vectors sequentially. Evaluation is done by forming a determinant whose first k rows are k vectors spanning the facet, and last (n-k) rows are vectors of the integrand. Element: facet. Parameters: k_vector_order, vector_integrand. Models: simplex. Ambient dimension: any. Hessian: yes. Orientable: yes. Example datafile declaration, for 3D surface in 5D: quantity kvec energy method simplex_k_vector_integral k_vector_order 3 vector_integrand: q1: 0 // first vector q2: 0 q3: 0 q4: 0 q5: x4 q6: 0 // second vector q7: 0 q8: 0 q9: x3 q10: 0 <-------------------- keep_macros ----------------------------> The keyword keep_macros in the top of the datafile will keep macro definitions active during runtime, until the next datafile is loaded. <-------------------- keep_originals ----------------------------> The presence of the keyword keep_originals in the top of the datafile has the same effect as the -i command line option, which is to keep the id numbers of geometric elements internally the same as in the datafile, instead of renumbering them in the order they are read in. <-------------------- Kelvin foam example ----------------------------> Example: Torus partitioned into two cells (Kelvin's foam) This example has a flat 3-torus (i.e. periodic boundary conditions) divided into two bodies. The unit cell is a unit cube, and the surface has the topology of Kelvin's partitioning of space into tetrakaidecahedra [TW], which was the least area partitioning of space into equal volumes known until recently [WP]. The datafile handles the wrapping of edges around the torus by specifying for each direction whether an edge wraps positively (+), negatively (-), or not at all (*). Note the use of the keyword TORUS_FILLED in the datafile. This informs Evolver that one of the volume constraints is redundant, preventing a singular matrix when the time comes to enforce volume constraints. One could use just TORUS and only put on one volume constraint. The display of a surface in a torus can be done several ways. The connected command (my favorite) makes each body show as a single unit. The clipped command shows the surface clipped to the fundamental parallelpiped. The raw_cells command shows the unedited surface. The Weaire-Phelan structure [WP]. is in the datafile phelanc.fe. It has area 0.3% less than Kelvin's. The initial two-cell Kelvin shape. Note that due to periodidity, a single vertex or edge may appear multiple times in the image. // twointor.fe // Two Kelvin tetrakaidecahedra in a torus. TORUS_FILLED // signals that domain is a torus and bodies fill it. periods 1.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 1.000000 vertices // values from another program 1 0.499733 0.015302 0.792314 2 0.270081 0.015548 0.500199 3 0.026251 0.264043 0.500458 4 0.755123 0.015258 0.499302 5 0.026509 0.499036 0.794636 6 0.500631 0.015486 0.293622 7 0.025918 0.750639 0.499952 8 0.499627 0.251759 0.087858 9 0.256701 0.499113 0.087842 10 0.026281 0.500286 0.292918 11 0.500693 0.765009 0.086526 12 0.770240 0.499837 0.087382 edges // with wraps in axis directions 1 1 2 * * * 2 2 3 * * * 3 1 4 * * * 4 3 5 * * * 5 2 6 * * * 6 2 7 * - * 7 1 8 * * + 8 4 6 * * * 9 5 9 * * + 10 3 10 * * * 11 3 4 - * * 12 6 8 * * * 13 6 11 * - * 14 7 4 - + * 15 8 12 * * * 16 9 8 * * * 17 9 11 * * * 18 10 7 * * * 19 11 1 * + - 20 12 5 + * - 21 5 7 * * * 22 11 12 * * * 23 10 12 - * * 24 9 10 * * * faces 1 1 2 4 9 16 -7 2 -2 5 12 -16 24 -10 3 -4 10 18 -21 4 7 15 20 -4 11 -3 5 -1 3 8 -5 6 6 14 -11 -2 7 5 13 -17 24 18 -6 8 -12 13 19 7 9 -16 17 22 -15 10 -10 11 8 12 15 -23 11 -21 9 17 19 1 6 12 -14 -18 23 -22 -13 -8 13 -24 -9 -20 -23 14 -19 22 20 21 14 -3 bodies 1 -1 -2 -3 -4 -5 9 7 11 -9 10 12 5 14 3 volume 0.500 2 2 -6 -7 8 -10 -12 -11 -13 1 13 -14 6 4 -8 volume 0.500 Doing some refining and iterating will show that the optimal shape is curved a bit. <-------------------- keylogfile ----------------------------> Main prompt command. Syntax: KEYLOGFILE stringexpr KEYLOGFILE OFF Starts recording all input keystrokes to the file specified by stringexpr, which must be a quoted string or a string variable or expression. Appends to an existing file. To end logging, use keylogfile off. To record both input and output, use logfile. <-------------------- Klein hyperbolic metric ----------------------------> One special metric is available built-in. It is the Klein model of hyperbolic space in n dimensions. The domain is the unit disk or sphere in Euclidean coordinates. Including the keyword KLEIN_METRIC in the top section of the datafile will invoke this metric. Lengths and areas are calculated exactly, but as with other metrics you are on your own for volumes and quantities. <-------------------- klein_area ----------------------------> Named method. Description: Facet area in Klein hyperbolic 3D space model. Does not depend on klein_metric being declared in the datafile. Vertices should be inside the unit sphere. Element: facet. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: quantity kleinarea energy method klein_area global <-------------------- klein_length ----------------------------> Named method. Description: Edge length in Klein hyperbolic plane model. Does not depend on klein_metric being declared. Vertices should be inside unit sphere. Element: edge. Parameters: none. Models: linear. Ambient dimension: 2. Hessian: no. Example datafile declaration: quantity kleinlen energy method klein_length global <-------------------- klein_metric ----------------------------> A Riemannian metric on the ambient space may be declared in the top section of the datafile with the syntax METRIC expr expr expr expr expr expr expr expr expr or CONFORMAL_METRIC expr or KLEIN_METRIC The keyword METRIC is followed by the N^2 components of the metric tensor, where N is the dimension of space. The components do not have to obey any particular line layout; they may be all on one line, or each on its own line, or any combination. It is up to the user to maintain symmetry. A conformal metric is a scalar multiple of the identity matrix, and only the multiple need be given. A conformal metric will run about twice as fast. The Klein metric is a built-in metric for hyperbolic n-space modelled on the unit disk or ball. <-------------------- kmetis ----------------------------> Main prompt command. Partitions the set of facets (edges in string model) into n parts using the METIS library of Karypis and Kumar, if this library has been compiled into the Evolver. Meant for experiments in partitioning the surface for multiprocessors. The partition number of facet is left in the facet extra attribute fpart (edge epart for string model), which will be created if it does not already exist. METIS uses the PMETIS algorithm, KMETIS uses the KMETIS algorithm. Syntax: METIS n KMETIS n Example: metis 20; set facet color (fpart imod 15) + 1; For partitioning bodies, see body_metis. <-------------------- knot_energy ----------------------------> Named method. Description: An electrostatic-type energy in which vertices are endowed with equal charges. Inverse power law of potential is adjustable via the global parameter `knot_power', default value 2 (which is not electrostatic, but the knot theorists like it). If the extra attribute `node_charge' is defined for vertices, then that value is used for the vertex charge. Use of this energy is not restricted to knots; it has been used to embed complicated network graphs in space. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration: parameter knot_power 2 // the default quantity knotten energy method knot_energy global <-------------------- knot_local_thickness ----------------------------> knot_local_thickness Named method. Description: Calculates the radius of curvature at a vertex of the circle containing the vertex and its two neighbor vertices. Meant to investigate the radius at individual vertices. Element: vertex. Parameters: none. Models: linear. Ambient dimension: 3. Gradient: no. Hessian: no. Example datafile declaration: quantity klocalthick info_only method knot_local_thickness global <--------------------------- knot_power --------------------------> knot_power Internal read-write variable used as exponent on some term in various knot energies. Named methods using knot_power are: buck_knot_energy charge_gradient knot_energy knot_thickness_0 knot_thickness_p knot_thickness_p2 proj_knot_energy uniform_knot_energy uniform_knot_energy_normalizer uniform_knot_normalizer1 uniform_knot_normalizer2 <-------------------- knot_thickness ----------------------------> knot_thickness Named method. Description: Calculates global radius of curvature at one vertex v, as the minimum radius of circle containing the vertex and the endpoints of any non-adjacent edge. Because of "min", this has no gradient, so should be used in info_only quantities. Element: vertex. Parameters: none. Models: linear. Ambient dimension: 3. Gradient: no. Hessian: no. Example datafile declaration: quantity kthick info_only method knot_thickness global <-------------------- knot_thickness2 ----------------------------> knot_thickness2 Named method. Description: calculates global radius of curvature at one vertex v, as the minimum radius of circle containing the vertex and the neighbor vertices of any non-adjacent vertex. Because of "min", this has no gradient, so should be used in info_only quantities. Element: vertex. Parameters: none. Models: linear. Ambient dimension: 3. Gradient: no. Hessian: no. Example datafile declaration: quantity kthick info_only method knot_thickness2 global <-------------------- knot_thickness_0 ----------------------------> knot_thickness_0 Named method. Description: Calculates global radius of curvature at one vertex, as Lp integral of radius of curvature of circle containing the vertex and the endpoints of edges not adjacent to the vertex. Integrand raised to -p power. The power p is taken from the global variable knot_power. No factor of length in integral. This method has a gradient. Element: vertex. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: quantity kthick info_only method knot_thickness_0 global <-------------------- knot_thickness_p ----------------------------> knot_thickness_p Named method. Description: purpose: calculates global radius of curvature at one vertex v, as Lp integral of radius of curvature of v and endpoints of nonadjacent edges. Includes factors of length at v and w. This method has a gradient. The power p is taken from the global variable knot_power. Element: vertex. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: quantity kthick info_only method knot_thickness_p global <-------------------- knot_thickness_p2 ----------------------------> knot_thickness_p2 Named method. Description: Calculates the global radius of curvature at one vertex v, as Lp integral of r(v,w1,w2) over all vertices w. Here w1 and w2 are the two neighbors of vertex w. Includes factors of length at v and w. This has not been extended to allow open arcs (valence 1 vertices). This method does have a gradient. The power p is taken from the global variable knot_power. Element: vertex. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: quantity kthick info_only method knot_thickness_p2 global <-------------------- kraynikpopedge ----------------------------> Main command toggle. Toggles edge-popping mode (in "O" or "pop" commands) in which poppable edges look for adjacent facets of different edge_pop_attribute values to split off from the original edge; failing that it reverts to the regular mode of popping the edge. This is meant to give the user greater control on how edge popping is done. It is up to the user to declare the edge_pop_attribute integer facet attribute and assign values. <-------------------- kraynikpopvertex ----------------------------> Toggles 3D vertex popping mode in which a poppable vertex is examined to see if it is a special configuration of six edges and 9 facets. If it is, a special pop is done that is much nicer than the default pop. <-------------------- kusner ----------------------------> Evolver toggle command. Calculation of squared curvature by edge formula rather than vertex formula. <-------------------- l ----------------------------> l (lower case L) Single letter main command. Subdivides long edges, creating new facets as necessary. You will be prompted for a cutoff edge length, if you don't give a value with the command. Existing edges longer than the cutoff will be divided once only. Newly created edges will not be divided. Hence there may be some long edges left afterward. If you enter h, you will get a histogram of edge lengths. If you hit RETURN with no value, nothing will be done. It is much better to use the refine command r than to subdivide all edges. A synonym for "l value" is "edge_divide value". This command does not respect the no_refine attribute. Graphics mode command. Rotate left. Rotates about vertical axis, default 6 degrees. Integer prefix specifies how many 6 degree rotations to do; decimal prefix specifies degrees of rotation. Example: `15l' does 90 degree rotation, `15.0l' does 15 degree rotation. <-------------------- labelflag ----------------------------> Evolver toggle command. When on, the postscript command will print element labels in the postscript file. Synonym for ps_labelflag. <-------------------- labels ----------------------------> Do labels? (i for ids, o for originals) : This PostScript "P 3" command subprompt gives you a chance to put numeric labels on vertices, edges, and facets, which is useful for debugging or modifying a datafile. Edge labels are slightly displaced toward the head of the edge, and facet labels are signed according to which side of the facet is visible. Choose 'i' or 'y' for the current element id, or 'o' for the original element number. If you don't want any labels, just hit RETURN. The postscript command uses the ps_labelflag toggle to control this. The relative size of the labels can be controlled with the ps_labelsize variable, whose default value is 3.0. <-------------------- lagrange ----------------------------> Main prompt command. Changes to Lagrange model from quadratic or linear models. Syntax: LAGRANGE n where n is the lagrange_order, which is between 1 and some built-in maximum (currently 8). This command can also convert between Lagrange models of different orders. Note that lagrange 1 gives the Lagrange model of order 1, which has a different internal representation than the linear model. Likewise, lagrange 2 does not give the quadratic model. <-------------------- Lagrange model ----------------------------> The Evolver has a very limited implementation of higher-order elements. In the Lagrange model of order n, each edge is defined by interpolation on n+1 vertices evenly spaced in the parameter domain, and each facet is defined by interpolation on (n+1)(n+2)/2 vertices evenly spaced in a triangular pattern in the parameter domain. That is, the elements are Lagrange elements in the terminology of finite element analysis. The Lagrange model is defined only for named quantities and methods, so Evolver will automatically do convert_to_quantities when you invoke the Lagrange model. The methods that currently accept the Lagrange model are vertex_scalar_integral edge_length edge_area edge_scalar_integral edge_vector_integral edge_general_integral facet_area facet_volume facet_vector_integral facet_scalar_integral facet_general_integral A surface may be converted to an order n Lagrange model with the command "lagrange n". This will convert linear or quadratic models to Lagrange, and will convert between different order Lagrange models. The commands linear and quadratic will convert Lagrange model back to the linear or quadratic models. No triangulation manipulations are available in the Lagrange model. No refining, equiangulation, or anything. There is some vertex averaging, but just internal to edges and facets. Use the linear or quadratic model to establish your final triangulation, and just use the Lagrange model to get extra precision. The current order can be accessed through the read-only internal variable lagrange_order. The Lagrange model can be dumped and reloaded. As the Lagrange order is raised, calculations slow down rapidly. This is not only due to the large number of points involved, but is also due to the fact that the order of Gaussian integration is also raised. Lagrange elements are normally plotted subdivided on their vertices, but if the smooth_graph flag is on, they are plotted with 8-fold subdivision. The toggle command bezier_basis toggle replaces the Lagrange interpolation polynomials (which pass through the control points) with Bezier basis polynomials (which do not pass through interior control points, but have positive values, which guarantees the edge or facet is within the convex hull of the control points). This is experimental at the moment, and not all features such as graphing or refinement have been suitably adjusted. <-------------------- lagrange_multiplier ----------------------------> The syntax for defining a named quantity in the data file is: QUANTITY name ENERGY|FIXED=value|CONSERVED|INFO_ONLY [LAGRANGE_MULTIPLIER constexpr] [TOLERANCE constexpr] [MODULUS constexpr] methodlist | FUNCTION methodexpr For fixed quantities, the optional Lagrange multiplier value supplies the initial value of the Lagrange multiplier (the "pressure" attribute of the quantity). It is meant for dump files, so on reloading no iteration need be done to have a valid Lagrange multiplier. <-------------------- lagrange_order ----------------------------> Internal read-only variable. Order of Lagrange model. Set with the "lagrange n" command. <-------------------- lanczos ----------------------------> Main prompt command. For finding eigenvalues of the energy Hessian near a given value. Syntax: LANCZOS expr LANCZOS (expr,expr) Does a little Lanczos algorithm and reports the nearest approximate eigenvalues to the given probe value. In the first form, expr is the probe value, and 15 eigenvalues are found. In the second form, the first argument is the probe value, the second is the number of eigenvalues desired. The output begins with the number of eigenvalues less than, equal to, and greater than the probe value. Then come the eigenvalues in distance order from the probe. Not real polished yet. Beware that multiplicities reported can be inaccurate. The eigenvalue nearest the probe value is usually very accurate, but others can be misleading due to incomplete convergence. Since the algorithm starts with a random vector, running it twice can give an idea of its accuracy. <-------------------- laplacian_mean_curvature ----------------------------> Named method. Description: Calculates the velocity of a vertex as the Laplacian of the mean curvature of the surface, meant to model the surface diffusion of atoms in sintering. The mean curvature at each vertex is calculated as a scalar, in the same way as for area_normalized area gradient, i.e. area gradient dotted with volume gradient, divided by the area of the surrounding facets. Then finite differences are used to calculate the Laplacian of the mean curvature. This calculates velocity only; the energy is always 0. This method should only be used with fixed scale in the 'g' command. The relative speed of vertices can be controlled by the vertex attribute lmc_mobility, which the user should declare if wanted. If the user wants to access the values of mean curvature the method finds, the user should define the vertex scalar attribute lmc_mean_curvature. This method conserves volume ideally, but you might want to put on volume constraints anyway due to numerical inaccuracies. Warning: This method should only be used with a fixed 'g' scale factor. And for stability, the factor should be proportional to the fourth power of the shortest edge, since Laplacian of mean curvature is a fourth-derivative operator, something like 0.001*length^4. This can make for very slow evolution for highly refined surfaces. Element: vertex. Parameters: none. Models: linear string and linear soapfilm. Ambient dimension: any. Hessian: no. Example datafile declaration: area_method_name "null_area" define facet attribute lmc_mobility real define facet attribute lmc_mean_curvature real quantity lmc energy method laplacian_mean_curvature global <-------------------- lmc_mobility ----------------------------> A vertex attribute used by the laplacian_mean_curvature named method to control the relative velocity of a vertex. See laplacian_mean_curvature for more. <-------------------- lmc_mean_curvature ----------------------------> A vertex attribute used by the laplacian_mean_curvature named method to record the mean curvature found at a vertex. See laplacian_mean_curvature for more. <-------------------- last_eigenvalue ----------------------------> Internal read-only variable. Eigenvalue from last saddle or ritz command. For the full list of ritz eigenvalues, use the eigenvalues[] array. <-------------------- last_error ----------------------------> Internal read-write variable. Has error number of last error message. <-------------------- last_hessian_scale ----------------------------> Internal read-only variable. Stepsize from last hessian_seek command. <---------------------- LEBweight ------------------------> Facet extra attribute used to weight individual facets in the named methods linear_elastic_B, relaxed_elastic, and relaxed_elastic_A. <-------------------- length ----------------------------> Edge read-only attribute. Length of the edge. Examples: histogram(edge where on_constraint 1, length) print edge[3].length <-------------------- length_method_name ----------------------------> This item in the top of the datafile, length_method_name, specifies the name of the pre-defined method to use as the method to compute edge length in place of the default edge_length method. It is optional. Developed so circular arcs can be used in two-dimensional foams. Current reasonable methods are circular_arc_length and spherical_arc_length. Usage implies converting to everything_quantities mode. Syntax: length_method_name quoted_method_name For example, string space_dimension 2 length_method_name "circular_arc_length" area_method_name "circular_arc_area" <-------------------- level set constraints ----------------------------> A level-set constraint is a restriction of vertices to lie on the zero level-set of a function. The formula may include any expressions whose values are known to the Evolver, given the particular vertex. Most commonly one just uses the coordinates (x,y,z) of the vertex, but one can use variables, quantity values, or vertex extra attributes. Using a vertex extra attribute is a good way to customize one formula to individual vertices. For example, if there were a vertex extra attribute called zfix, one could force vertices to individual z values with one constraint with the formula z = zfix, after of course assigning proper values to zfix for each vertex. A level set constraint may have several roles: > Vertices may be required to lie on a constraint (equality constraint) or on one side (inequality constraint). A constraint may be declared GLOBAL, in which case it applies to all vertices. See mound.fe for an example. > A constraint may have an energy vectorfield associated with it that is integrated over edges lying in the constraint to give an energy. This is useful for specifying wall contact angles and for calculating gravitational energy. Integrals are not evaluated over edges that are FIXED. See mound.fe for an example. In the string model, the energy integrand is a single component evaluated on vertices on the constraint. > A constraint may have a content vectorfield associated with it that is integrated over edges lying in the constraint to give a volume contribution to a body whose boundary facets contain the edges. This is useful for getting correct volumes for bodies without completely surrounding them with otherwise useless facets. It is important to understand how the content is added to the body in order to get the signs right. The integral is evaluated along the positive direction of the edge. If the edge is positively oriented on a facet, and the facet is positively oriented on a body, then the integral is added to the body. This may wind up giving the opposite sign to the integrand from what you think may be natural. Integrals are not evaluated over edges that are FIXED. See tankex.fe for an example. In the string model, the content integrand is a single component evaluated on vertices on the constraint. > A constraint may be declared CONVEX, in which case edges in the constraint have an energy associated with them that is proportional to the area between the straight edge and the curved wall. This energy (referred to as "gap energy") is meant to compensate for the tendency for flat facets meeting a curved wall to minimize their area by lengthening some edges on the wall and shortening others, with the net effect of increasing the net gap between the edges and the wall. See tankex.fe for an example. Level set constraints are declared in the top section of the datafile. They may be applied to vertices, edges, or facets. Constraints are usually applied to vertices and edges, as in mound.fe. Remember that you need to apply a constraint to an edge to get that constraint to apply to vertices created on that edge by refining. Sometimes one applies constraints to facets, usually to get the facet to conform to a predetermined shape. Be sure that the constraints applied to a vertex are linearly independent at the vertex. Constraints are usually applied in the datafile vertices, edges, and faces sections, but they may also be set or removed with the set or unset commands. Examples: set vertex[4] constraint 4 unset edge constraint 1 where id < 10 It does not hurt to unset an element that isn't on the constraint. When a vertex is set to a constraint, the vertex coordinates are immediately projected to the constraint. Setting an edge on a constraint does not set its vertices. Likewise for facets. <-------------------- lexical format ----------------------------> For those who know about such things, the datafile and commands are read with a lexical analyzer generated by the lex program. The specification is in datafile.lex. Commands are further parsed by a yacc-generated parser. In parsing an expression, the longest legal expression is used. This permits coordinates to be specified by several consecutive expressions with no special separators. <-------------------- line splicing ----------------------------> The datafile and any file Evolver inputs with the "read" command is made up of lines. Line breaks are significant. The next physical line can be spliced onto the current line by having \ be the last character of the current line. Line splicing is not effective in // comments. Blank lines and comment lines may be placed freely anywhere. The various combinations of CR and NL that various computer systems use are all recognized. <-------------------- linear ----------------------------> Main prompt command. Changes to linear model from quadratic or Lagrange models. <-------------------- linear model ----------------------------> In the linear model, all edges and triangular facets are flat line segments and triangles, respectively. For all calculations, an edge is defined by its two endpoints, and a facet (in the soapfilm model) is defined by its three vertices. This is the default. Quadratic or Lagrange models may be changed to linear with the M 1 or linear commands. An exception is if the spherical_arc_length method is used for length_method_name in the string model, in which case edges are computed and drawn on a sphere centered at the origin. <-------------------- linear_elastic ----------------------------> Named method. Description: To calculate the isotropic linear elastic strain energy energy for facets based on the Cauchy-Green strain matrix. Let S be Gram matrix of unstrained facet (dots of sides). Let Q be the inverse of S. Let F be Gram matrix of strained facet. Let C = (FQ-I)/2, the Cauchy-Green strain tensor. Let v be Poisson ratio. Then energy density is (1/2/(1+v))(Tr(C^2) + v*(Tr C)^2/(1-(dim-1)*v)) Each facet has extra attribute poisson_ratio and extra attribute array form_factors[3] = {s11,s12,s22}, which are the entries in S. That is, s11 = dot(v2-v1,v2-v1), s12 = dot(v2-v1,v3-v1), and s22 = dot(v3-v1,v3-v1). If form_factor is not defined by the user, it will be created by Evolver, and the initial facet shape will be assumed to be unstrained. For a version of this method that gives compression zero energy, see relaxed_elastic_A. Element: facet. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: yes. Example datafile declaration: quantity lastic energy method linear_elastic global <-------------------- linear_elastic_B ----------------------------> Named method. Description: A variation of the linear_elastic method. To calculate the linear elastic strain energy energy for facets based on the Cauchy-Green strain matrix. Let S be Gram matrix of unstrained facet (dots of sides). Let Q be the inverse of S. Let F be Gram matrix of strained facet. Let C = (FQ-I)/2, the Cauchy-Green strain tensor. Let v be Poisson ratio. Then energy density is (1/2/(1+v))(Tr(C^2) + v*(Tr C)^2/(1-(dim-1)*v)) Each facet has extra attribute poisson_ratio and each vertex has two extra coordinates, the coordinates of the unstrained surface in a plane. Hence the surface must be set up as five dimensional. There can also be a real-valued facet extra attribute LEBweight, which can be used to give a per-facet weighting of the energy. For a version of this method that gives compression zero energy, see relaxed_elastic. Element: facet. Parameters: none. Models: linear. Ambient dimension: 5. Hessian: yes. Example datafile declaration: space_dimension 5 quantity lastic energy method linear_elastic_B global <-------------------- linear_metric ----------------------------> Evolver toggle command. Eigenvalues and eigenvectors of the Hessian are defined with respect to a metric. This command toggles a metric that imitates the smooth surface natural metric of L_2 integration on the surface. Use with hessian_normal to get eigenvalues and eigenvectors similar to those on smooth surfaces. <-------------------- linear_metric_mix ----------------------------> Internal read-write variable. Fraction of linear interpolation in Hessian metric. <-------------------- list ----------------------------> Main prompt command. List elements on the screen in the same format as in the datafile, or lists individual constraint, boundary, quantity, or method instance definitions. Syntax: LIST generator LIST constraintname LIST CONSTRAINT constraintnumber LIST boundaryname LIST BOUNDARY boundarynumber LIST quantityname LIST instancename On unix systems, piping to "more" or other commands can be used for long displays. Examples: list edges where id <= 12 list vertices | "more" list vertices where x < 1 and y > 2 and z >= 3 | "tee vfile" list facet[3] list facet[3].edges where on_constraint 1 list facet[3].edge[2].vertex[1] list constraint 1 See also "list attributes", "list bottomingo", "list procedures", and "list topingo". <-------------------- list attributes ----------------------------> Prints a list of the "extra attributes" of each type of element. Besides user-defined extra attributes, this list also contains the predefined attributes that make use of the extra attribute mechanism (being of variable size), such as coordinates, parameters, forces, and velocities. It does not list permanent, fixed-size attributes such as color or fixedness, or possible attributes that are not used at all. <-------------------- list bottominfo ----------------------------> Main prompt command. Prints what would be dumped in the "read" section at the end of a dumpfile: command definitions and various toggle states. <-------------------- list procedures ----------------------------> <-------------------- procedures ----------------------------> list procedures Main prompt command. Prints names all current user-defined commands, procedures, and functions. <-------------------- list topinfo ----------------------------> Main prompt command. Prints the first section of the datafile on the screen. This is everything that would be before the vertices section in a dump file. <-------------------- little_endian ----------------------------> Evolver toggle command. Controls the order of bytes in binary_printf numerical output. Little-endian is least significant byte first. To change to big-endian, use big_endian, not "little_endian off". <-------------------- load ----------------------------> Main prompt command. For loading a new surface. Syntax: LOAD filename Terminates the current surface and loads a new datafile. The filename is the datafile name, and can be either a quoted string or a string variable. This completely re-initializes everything, including the command interpreter. In particular, the currently executing command ends. Useful only as the last command in a script. For loading a new surface and continuing with the current command, see permload. Wildcard matching is in effect on some systems (Windows, linux, maybe others), but be very careful when using wildcards since there can be unexpected matches. <-------------------- load_library ----------------------------> To load a dynamic load library of compiled functions, the syntax is LOAD_LIBRARY "filename" where the double-quoted filename is the library. The current directory and the EVOLVERPATH will be searched for the library. See "dynamic load library" for more. <-------------------- local ----------------------------> The scope of a variable name may be restricted to a compound command block by declaring the name to be local. Example: do_stuff := { local inx; for ( inx := 1 ; inx < 5 ; inx += 1 ) { local jnx; jnx := inx*2; print jnx; }; } Using local variables is good for avoiding pollution of global namespace and for writing recursive functions (storage space for locals is allocated on the runtime stack). Note that the local declaration is a scope declaration, not a type declaration. Also, it cannot be combined with initialization of the variable (yet), and there is one name per declaration. Function arguments also act as local variables. <-------------------- local_hooke_energy ----------------------------> Named method. Description: Energy of edges as springs with equilibrium length being average of lengths of neighbor edges. Actually, the energy is calculated per vertex, E = ({L_1 - L_2 \over L_1 + L_2})^2 where L_1 and L_2 are the lengths of the edges adjacent to the vertex. Meant for loops of string. (by John Sullivan) Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: quantity slinky energy method local_hooke_energy global <-------------------- log ----------------------------> log(x) ,exp(x) : Natural log, exponentiation base e. <-------------------- logfile ----------------------------> Main prompt command. Syntax: LOGFILE stringexpr LOGFILE OFF Starts recording all input and output to the file specified by stringexpr, which must be a quoted string or a string variable or expression. Appends to an existing file. To end logging, use logfile off. To record just input keystrokes, use keylogfile. <-------------------- longj ----------------------------> Main prompt command. For perturbing the surface. This does a "long jiggle", which provides long wavelength perturbations that can test a surface for stability. The parameters are a wavevector, a phase, and a vector amplitude. The user will be prompted for values. Numbers for vectors should be entered separated by blanks, not commas. An empty reply will accept the defaults. A reply of r will generate random values. Any other will exit the command without doing a jiggle. In the random cases, a random amplitude $\vec A$ and a random wavelength $\vec L$ are chosen from a sphere whose radius is the size of the object. The wavelength is inverted to a wavevector $\vec w$. A random phase $\psi$ is picked. Then each vertex $\vec v$ is moved by $\vec A\sin(\vec v \cdot \vec w + \psi)$. This command is archaic. More control over perturbations may be had with the "set vertex x ..." type of command. <-------------------- M ----------------------------> Single letter main command. Syntax: M n where n is the order of the model. Sets model type to linear , quadratic, or Lagrange depending on n. <-------------------- m ----------------------------> Single letter main command. Toggles quadratic search for optimal global motion scale factor. If search is toggled OFF, you will be prompted for a fixed scale factor. If you give a value with the command, then you are setting a fixed scale factor. Graphics mode command. Center image in viewing window. <-------------------- macros ----------------------------> Macros are text substitutions done by replacing an identifier by a string of characters before parsing. Macros are only defined in the datafile, and do not work from the command prompt. Simple macros (no parameters) may be defined as in C: #DEFINE identifier string "identifier" must be an identifier without other special meaning to the parser. "string" is the rest of the logical line, not including comments. It will be substituted for identifier whenever identifier occurs as a token subsequently. Substitutions are re-scanned. No checks for recursiveness are made. There is a maximum length (currently 500 characters) on a macro definition. Note: macro identifiers are separate tokens, so if "-M" translates into "-2", this will be read as two tokens, not a signed number. The keyword keep_macros in the datafile will keep macro definitions active during runtime, until the next datafile is loaded. <-------------------- matrix_determinant ----------------------------> matrix_determinant(A) Built-in matrix determinant of a square array. Has function syntax, so it returns the value of the determinant. Example: Enter command: define aaa real[2][2] Enter command: aaa[1][1] := 2; aaa[1][2] := 3; aaa[2][1] := 4; aaa[2][2] := 5 Enter command: print matrix_determinant(aaa) -2 Does not yet work on array attributes of elements. <-------------------- matrix_inverse ----------------------------> Main prompt command. For computing the inverse of a square matrix. Currently applies only to global matrices, not element attribute matrices. Syntax: MATRIX_INVERSE(matrix1, matrix2) Here matrix1 is the name of the original matrix, and matrix2 is the name of the inverse matrix. They may be the same matrix to get an in-place inverse. Examples: define mata real[5][5] define matb real[5][5] ... // fill in values of mata matrix_inverse(mata,matb) matrix_inverse(mata,mata) <-------------------- matrix_multiply ----------------------------> Main prompt command. For computing the product of matrices. Currently applies only to global matrices, not element attribute matrices. Syntax: MATRIX_MULTIPLY(matrix1, matrix2, matrix3) Here matrix1 and matrix2 are the names of the multiplicands, and matrix3 is the name of the product matrix. The product matrix may be the same as one (or both) of the multiplicands. The matrices can be one-dimensional or two-dimensional, so you can do vector-matrix or matrix-vector multiplication (but you can't do vector times vector). Examples: define mata real[5][5] define matb real[5][5] define matc real[5][5] ... // fill in values of mata and matb matrix_multiply(mata,matb,matc) matrix_multiply(mata,mata,mata) <-------------------- MAXCOORD ----------------------------> By default, surfaces live in 3 dimensional space. However, the phrase "SPACE_DIMENSION n" in the datafile header sets the dimension to n. This means that all coordinates and vectors have n components. The only restriction is that Evolver has to be compiled with the MAXCOORD macro defined to be at least n in Makefile or in model.h. The default MAXCOORD is 4. Change MAXCOORD and recompile if you want more than four dimensions. The actual space dimension can be accessed in commands through the read-only variable space_dimension. Graphics will display only the first three dimensions of spaces with more than three dimensions, except for geomview, which has a four-dimensional viewer built in (although its use is awkward now). <-------------------- maximum ----------------------------> minimum(a,b),maximum(a,b): Extreme of two arguments. <--------------------- mean_curvature --------------------------> Vertex read-only attribute, available in the string and soapfilm model. The mean curvature is calculated as the magnitude of the gradient of area (or length in the string model) divided by the area (or length) associated with the vertex, which is one-third the area of the facets adjacent to the vertex (or one-half of the length of adjacent edges). It is divided by 2 in the soapfilm model to account for the "mean" part of the definition. The sign of the mean curvature is relative to the orientation of the first adjacent facet (or edge) Evolver finds. This calculation can be done even if the vertex is on a triple junction or other non-planar topology, even if it doesn't interpret well as mean curvature there. <-------------------- mean_curvature_integral ----------------------------> mean_curvature_integral Named method. Description: Integral of signed scalar mean curvature of a 2D surface. The computation is exact, in the sense that for a polyhedral surface the mean curvature is concentrated on edges and singular there, but the total mean curvature for an edge is the edge length times its dihedral angle. Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration: quantity mci energy method mean_curvature_integral The method mean_curvature_integral_a does the same thing, but uses a numerical formulation which may be better behaved. There is an obsolete use of mean_curvature_integral in the top of the datafile to indicate the integral of the mean curvature should be included as an energy, with syntax mean_curvature_integral: modulus where modulus is the multiplier for the energy. The modulus winds up as the internal read-write variable mean_curvature_modulus. <-------------------- mean_curvature_modulus ----------------------------> There is an obsolete use of mean_curvature_integral in the top of the datafile to indicate the integral of the mean curvature should be included as an energy, with syntax mean_curvature_integral: modulus where modulus is the multiplier for the energy. The modulus winds up as the internal read-write variable mean_curvature_modulus. <-------------------- mean_curvature_integral ----------------------------> <-------------------- mean_curvature_integral_a ----------------------------> Named method. Description: Integral of signed scalar mean curvature of a 2D surface. The computation is exact, in the sense that for a polyhedral surface the mean curvature is concentrated on edges and singular there, but the total mean curvature for an edge is the edge length times its dihedral angle. Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: quantity mci energy method mean_curvature_integral The method mean_curvature_integral_a does the same thing, but uses a numerical formulation which may be better behaved. <-------------------- memdebug ----------------------------> Evolver toggle command. When ON, the 'c' command prints full memory usage statistics on some systems. Also, each allocation or freeing of memory is printed. Causes heap checking to be done on some systems at each memory operation. <-------------------- memory debug option ----------------------------> <-------------------- option -m ----------------------------> Command line option -m : Turn memory debugging on at start of program. Same effect as runtime memdebug command. <-------------------- memory_arena ----------------------------> Internal read-only variable. Total memory allocated to the program's heap. Available only on SGI and Win32 versions. <-------------------- memory_used ----------------------------> Internal read-only variable. Total memory used in the program's heap. Available only on SGI and Win32 versions. <-------------------- merit_factor ----------------------------> If the keyword MERIT_FACTOR is present in the top of the datafile, then the i command will print the ratio total_area^3/total_volume^2, which measures the efficiency of area enclosing volume. A holdover from the early days of trying to beat Kelvin's partition of space. <-------------------- method ----------------------------> A "method" is a way of calculating a scalar value from some particular type of element (vertex, edge, facet, body), and used as a component of a named quantity. Each method is implemented internally as a set of functions for calculating the value and its gradient as a function of vertex positions. The most common methods also have Hessian functions. Methods are referred to by their names. See "implemented methods" for a list of available methods. Adding a new method involves writing C routines to calculate the value and the gradient (and maybe the Hessian) as functions of vertex coordinates, adding the function declarations to quantity.h, and adding a structure to the method declaration array in quantity.c. All the other syntax for invoking it from the datafile is already in place. <-------------------- method instance declaration -----------------------> <-------------------- method_instance ----------------------------> Method instances are usually defined as part of the definition of a named quantity, but there are circumstances where a quantity is composed of several method instances and the method instances need to be referred to individually; perhaps the user wants to know the values of the individual instances. The general syntax for defining an instance of a named method in a datafile is: METHOD_INSTANCE name METHOD methodname [MODULUS constexpr] [ELEMENT_MODULUS attrname] [GLOBAL] [parameters] Here, name is a user-assigned name for referring to this particular instance. methodname is one of the pre-defined methods in Evolver. The modulus value multiplies the method value to give the instance value. The default modulus is 1. Individual elements may be given multipliers by specifying an extra attribute attrname for the type of element; the attribute must have been defined earlier. GLOBAL makes the method apply to all elements of the appropriate type. Non-global instances may be applied to elements individually by adding the instance name to the datafile line defining an element. They may also be applied or unapplied at runtime with the set and unset commands. Orientable methods can be applied with negative orientation in the datafile by following the name with a dash. The orientation in a set command follows the orientation the element is generated with. Each method may have various parameters to specialize it to an instance. Currently the only parameters specified are: SCALAR_INTEGRAND: expr : where expr is a scalar function of coordinates (and of tangent or normal vector components in edge_general_integral or facet_general_integral). Element attributes of the appropriate type element may also be used. VECTOR_INTEGRAND: Q1: expr Q2: expr Q3: expr : where the expressions are functions of the coordinates. Element attributes of the appropriate type element may also be used. FORM_INTEGRAND: Q1: expr Q2: expr Q3: expr ... : where the expressions are functions of the coordinates. Element attributes of the appropriate type element may also be used. When used in the facet_2form_integral method. The form components are listed in lexicographic order, i.e. in 4D the six components 12,13,14,23,24,34 would be listed as Q1 through Q6. PARAMETER_1 constexpr : For specifying miscellaneous numeric parameters to certain methods. K_FORM_ORDER constexpr : For methods that use differential k-forms, this specifies the value of k. Should occur before FORM_INTEGRAND when needed. <-------------------- method instances ----------------------------> A "method instance" is the sum of a particular method applied to a particular set of geometric elements. Some methods (like facet_area) are completely self-contained. Others (like facet_vector_integral) require the user to specify some further information. For these, each instance has a specification of this further information. Method instances are defined in the datafile, and may either be unnamed parts of named quantity definitions or separate named method instances for inclusion in named quantities. The separate named version is useful if you want to inspect instance values for the whole surface or individual elements. An instance total value can be printed with the A commands, or may be referred to as "instancename.value" in commands. The instance name itself may be used as an element attribute. For example, supposing there is an instance named moment, which applies to facets. Then typical commands would be print moment.value print facet[3].moment list facet where moment > 0.1 Every method instance has a "modulus", which is multiplied times the basic method value to give the instance value. A modulus of 0 causes the entire instance calculation to be omitted whenever quantities are calculated. The modulus may be set in the datafile or with the A command or by assignment. Example commands: print moment.modulus moment.modulus := 1.3 A method instance may be declared to use a different modulus for each element by specifying an element extra attribute to use for that purpose. The extra attribute has to have already been declared. Example: define facet attribute mymod real quantity myquant energy method facet_area global element_modulus mymod Of course, it is up to the user to properly initialize the values of the extra attribute. <-------------------- metis ----------------------------> Main prompt command. Partitions the set of facets (edges in string model) into n parts using the METIS library of Karypis and Kumar, if this library has been compiled into the Evolver. Meant for experiments in partitioning the surface for multiprocessors. The partition number of facet is left in the facet extra attribute fpart (edge epart for string model), which will be created if it does not already exist. METIS uses the PMETIS algorithm, KMETIS uses the KMETIS algorithm. Syntax: METIS n KMETIS n Example: metis 20; set facet color (fpart imod 15) + 1; For partitioning bodies, see body_metis. <-------------------- metis_factor ----------------------------> Evolver toggle command. Computes and uses an ordering for Hessian factoring using the METIS library of Karypis and Kumar, if this library has been compiled into the Evolver. <-------------------- metis_readjust --------(MPI Evolver)----> metis_readjust Main prompt command for MPI Evolver. Does a repartition of the surface among the tasks, but using a METIS partitioning algorithm in a way that is supposed to be based on the current partition rather than repartioning from scratch. Syntax: metis_readjust n where n is the desired number of partitions; n must be between 1 and mpi_maxtask. <-------------------- metric ----------------------------> For length and area to make sense, the ambient space must be endowed with a metric. The Evolver offers several choices, but keep in mind that they are only used to calculate default length and area. Other quantities that depend on the metric, such as volume, are up to the user to put in by hand with named quantities. All displaying is done as if the metric is Euclidean. Available metrics: Euclidean metric (default) Riemannian metric Conformal metric Klein hyperbolic metric <-------------------- metric declaration -------------------------> A Riemannian metric on the ambient space may be declared in the top section of the datafile with the syntax METRIC expr expr expr expr expr expr expr expr expr or CONFORMAL_METRIC expr or KLEIN_METRIC The keyword METRIC is followed by the N^2 components of the metric tensor, where N is the dimension of space. The components do not have to obey any particular line layout; they may be all on one line, or each on its own line, or any combination. It is up to the user to maintain symmetry. A conformal metric is a scalar multiple of the identity matrix, and only the multiple need be given. A conformal metric will run about twice as fast. The Klein metric is a built-in metric for hyperbolic n-space modelled on the unit disk or ball. <-------------------- metric_conversion ----------------------------> <-------------------- metric_convert ----------------------------> Evolver toggle command. If a Riemannian metric is defined, whether to use the metric to do gradient form to vector conversions. Synonym: metric_conversion. <-------------------- metric_edge_length ----------------------------> Named method. Description: In the string model with a Riemannian metric, this is the length of an edge. Element: edge. Parameters: none. Models: linear,quadratic,simplex. Ambient dimension: any. Hessian: yes. Example datafile declaration: string space_dimension 2 metric 1+x^2 y y 1+y^2 quantity mel energy method metric_edge_length global <-------------------- metric_facet_area ----------------------------> Named method. Description: For a Riemannian metric, this is the area of a facet. Element: edge. Parameters: none. Models: linear,quadratic,simplex. Ambient dimension: any. Hessian: yes. Example datafile declaration: metric 1+x^2 0 z 0 1+y^2 0 z 0 1+z^2 quantity mfa energy method metric_facet_area global <-------------------- mid_edge ----------------------------> Vertex read-only attribute. True (1) if the vertex is on an edge but not an endpoint. Relevant in the quadratic model or Lagrange model. Example: list edge[23].vertex vv where vv.mid_edge <-------------------- mid_facet ----------------------------> Vertex read-only attribute. True (1) if the vertex is an interior control point of a facet in the Lagrange model. Example: list facet[23].vertex vv where vv.mid_facet <-------------------- midv ----------------------------> Edge read-only attribute. In the quadratic model, gives the id of the midpoint vertex of an edge. Example: print edge[23].midv <-------------------- mindeg_debug_level ----------------------------> mindeg_debug_level Internal read-write variable controlling verbosity of messages during Hessian sparse matrix factoring when my own minimal degree algorithm is in effect (ysmp off). 0 for no messages, 7 for most. Special value of -1 toggles graphical display of stages of factorization; sets all edges between non-eliminated vertices to red, others to black. Pauses for user response. For my own use, not users. <-------------------- mindeg_margin ----------------------------> mindeg_margin Internal read-write variable controlling how high to go above minimum degree in seeking good elimination when my own minimal degree Hessian factoring algorithm is in effect (ysmp off). Default 5. For my use, not users. <-------------------- mindeg_min_region_size ----------------------> mindeg_min_region_size Internal read-write variable controlling smallest size region desired when my own minimal degree Hessian factoring algorithm is in effect (ysmp off); smaller regions will be merged with parent node. Default 0. For my use, not users. <-------------------- minimum ----------------------------> minimum(a,b),maximum(a,b): Extreme of two arguments. <-------------------- mobility ----------------------------> There is a choice to be made in the conversion of the forces on vertices into velocities of vertices. Technically, force is the gradient of energy, hence a covector on the manifold of all possible configurations. In the Evolver representations of surfaces, that global covector can be represented as a covector at each vertex. The velocity is a global vector, which is represented as a vector at each vertex. Conversion from the global covector to the global vector requires multiplication by a metric tensor, i.e. singling out a particular inner product on global vectors and covectors. The tensor converting from force to velocity is the mobility tensor, represented as the mobility matrix M in some coordinate system. Its inverse, converting from velocity to force, is the resistance tensor S = M^{-1}. The same inner product has to be used in projecting the velocity tangent to the constraints, whether they be level set constraints on vertices or constraints on body volumes or quantity integrals. There are several choices implemented in the Evolver, corresponding to several different physical pictures of how the medium resists the motion of the surface through it: unit mobility area normalization area normalization with effective area approximate polyhedral curvature user-defined mobility <-------------------- mobility declaration ---------------------------> <-------------------- mobility_tensor ----------------------------> A mobility matrix may be defined in the top section of the datafile by the syntax MOBILITY_TENSOR expr expr expr expr expr expr expr expr expr or MOBILITY expr The first form gives the full mobility matrix, and the second form gives the matrix as a scalar multiple of the identity matrix. The formulas are evaluated at each vertex at each iteration, and so formulas may depend on vertex position and any vertex attributes. The velocity of a vertex is calculated as velocity = mobility x force. <-------------------- mod ----------------------------> %, mod: Real arithmetic modulus, x % y = x - floor(x/y)*y. <-------------------- models ----------------------------> The Surface Evolver can handle several different models of surfaces. See "linear model", "quadratic model", "lagrange model", "string model", "soapfilm model", "simplex model". <-------------------- modulus ----------------------------> A keyword that is an attribute of a named quantity or a method instance, which is a multiplier for the calculated value. <-------------------- mound example ----------------------------> Example: Mound with gravity. This example is a mound of liquid sitting on a tabletop with gravity acting on it. The contact angle between the drop surface and the tabletop is adjustable, to simulate the different degrees to which the liquid wets the table. This example illustrates macros, variables, constraints with energy, and omitting faces from body surfaces. The drop starts as a cube with one face (face 6 of the cube example) on the tabletop (the z = 0 plane). The most straightforward way to specify a contact angle is to declare face 6 to be constrained to stay on the tabletop and give it a surface tension different than the default of 1. But this leads to problems described below. The way the contact angle is handled instead is to omit face 6 and give the edges around face 6 an energy integrand that results in the same energy we would get if we did include face 6. If we let the interface energy density for face 6 be T, then we want a vectorfield w such that / / | T k . dS = | w . dl / face 6 / bdry of face 6 So by Green's Theorem, all we need is curl w = Tk, and I will use w = -Tyi. Here i j k are the standard unit basis vectors. In practice, I don't think about Green's Theorem as such; I just write down a line integral that sums up strips of surface. I have chosen to parameterize the contact angle as the angle in degrees between the table and the surface on the interior of the drop. This angle can be adjusted by assigning a new value to the variable "angle" at runtime. I could have made WALLT the parameter directly, but then I wouldn't have had an excuse to show a macro. Here is the datafile mound.fe: // mound.fe // Evolver data for drop of prescribed volume sitting on plane with gravity. // Contact angle with plane can be varied. PARAMETER angle = 90 // interior angle between plane and surface, degrees gravity_constant 0 // start with gravity off #define WALLT (-cos(angle*pi/180)) // virtual tension of facet on plane constraint 1 /* the table top */ formula: x3 = 0 energy: // for contact angle e1: -(WALLT*y) e2: 0 e3: 0 vertices 1 0.0 0.0 0.0 constraint 1 /* 4 vertices on plane */ 2 1.0 0.0 0.0 constraint 1 3 1.0 1.0 0.0 constraint 1 4 0.0 1.0 0.0 constraint 1 5 0.0 0.0 1.0 6 1.0 0.0 1.0 7 1.0 1.0 1.0 8 0.0 1.0 1.0 9 2.0 2.0 0.0 fixed /* for table top */ 10 2.0 -1.0 0.0 fixed 11 -1.0 -1.0 0.0 fixed 12 -1.0 2.0 0.0 fixed edges /* given by endpoints and attribute */ 1 1 2 constraint 1 /* 4 edges on plane */ 2 2 3 constraint 1 3 3 4 constraint 1 4 4 1 constraint 1 5 5 6 6 6 7 7 7 8 8 8 5 9 1 5 10 2 6 11 3 7 12 4 8 13 9 10 fixed /* for table top */ 14 10 11 fixed 15 11 12 fixed 16 12 9 fixed faces /* given by oriented edge loop */ 1 1 10 -5 -9 2 2 11 -6 -10 3 3 12 -7 -11 4 4 9 -8 -12 5 5 6 7 8 7 13 14 15 16 density 0 fixed /* table top for display */ bodies /* one body, defined by its oriented faces */ 1 1 2 3 4 5 volume 1 density 1 read re := refine edges where on_constraint 1 The mound itself was basically copied from cube.fe, but with face 6 deleted. The reason for this is that face 6 is not needed, and would actually get in the way. It is not needed for the volume calculation since it would always be at z = 0 and thus not contribute to the surface integral for volume. The bottom edges of the side faces are constrained to lie in the plane z = 0, so face 6 is not needed to keep them from catastrophically shrivelling up. We could have handled the contact angle by including face 6 with a surface tension equal to the interface energy density between the liquid and surface, but that can cause problems if the edges around face 6 try to migrate inward. After refinement a couple of times, interior vertices of the original face 6 have no forces acting on them, so they don't move. Hence it would be tough for face 6 to shrink when its outer vertices ran up against its inner vertices. The tabletop face, face 7, is entirely extraneous to the calculations. Its only purpose is to make a nice display. You could remove it and all its vertices and edges without affecting the shape of the mound. It's constraint 1 that is the tabletop as far as the mound is concerned. To see what happens with the bottom face present, load moundB.fe and do "run". Now run Evolver on mound.fe. The command "re" defined at the end of the datafile is good to use first in order to refine some edges that need it. Refine and iterate a while. You should get a nice mound. It's not a hemisphere, since gravity is on by default with G = 1. If you use the G command to set "G 0" and iterate a while, you get a hemisphere. Try changing the contact angle, to 45 degrees (with the command "angle := 45"} or 135 degrees for example. You can also play with gravity. Set "G 10" to get a flattened drop, or "G -5" to get a drop hanging from the ceiling. "G -10" will cause the drop to try to break loose, but it can't, since its vertices are still constrained. <-------------------- move ----------------------------> Main prompt command. For moving along the current direction of motion. Syntax: MOVE expr Moves the surface along the previous direction of motion by the stepsize given by expr. The previous direction can be either from a gradient step (g command) or a hessian step (hessian, saddle, hessian_seek, hessian_menu option 4, etc.). The stepsize does not affect the current scale factor. A negative step is not a perfect undo, since it cannot undo projections to constraints. "Move" sometimes does not work well with optimizing parameters and hessian together. <-------------------- MPI ----------------------------> MPI (Message Passing Interface) is a portable message-passing interface for distributed parallel processing. The MPI Evolver is not too sophisticated yet, passing all data to all processes. Hence it should be regarded as experimental rather than practical. Improvements are planned some day. The only practical use of it so far has been massive knot energy calculations, where every process does need to have all data. Interested users should contact brakke@susqu.edu. <-------------------- MPI commands ----------------------------> Execution of commands All user commands are entered into the master task and executed by the master task. Some commands (listed below) have been modified to execute in parallel across all tasks in a coordinated way. The rest will just execute on the master task. Variables exist independently on each task; they are not automatically synchonized. Special MPI version commands: task_exec n,string : Have task n execute the string as a command. Any aggregate commands execute across all local and imported elements. <-------------------- MPI compilation ----------------------------> Compilation All regular Evolver files, but not metis.c, along with the mpi*.c files (except mpi_sparse.c, for now) should be compiled with these manifest constants defined in the compiler command in your makefile: MPI_EVOLVER TASK_ID_BITS=22 LONG_ID The resulting object files should be linked with the appropriate MPI library. The same executable is used for the master and slave tasks. Note: the variables nproc and procs_requested you might find in variable.c have nothing whatsoever to do with MPI; do not change them. If you want to easily repartition the surface among the tasks, it is advisable to link in the PARMETIS and METIS libraries, which can be found here. In this case, you should also define these manifest constants in compiling: METIS PARMETIS <-------------------- MPI datafiles ----------------------------> Datafiles There are several ways to set up the datafiles: Use the same regular Evolver datafile for each task. Then the entire surface is allocated to task 1 (the first slave task), but all tasks read the same header information. The surface can be reallocated among the slave tasks with the "repartition" command described below. Have all the surface in one datafile, but with the various elements labelled with the task they are allocated to. The labelling is done by appending "@n" to each element number, where n is the task number. One advantage of this type of datafile is that it can be read by the regular Surface Evolver, which just ignores the "@n" labels. For example: Vertices 1@4 0.0000000000 0.0000000000 1.0000000000 fixed 2@1 2.0000000000 0.0000000000 0.0000000000 fixed 3@2 2.0000000000 2.0000000000 1.0000000000 fixed 4@3 0.0000000000 2.0000000000 0.0000000000 fixed ... Edges 1@4 1@4 546@4 fixed 2@1 2@1 547@1 fixed 3@2 3@2 548@2 fixed 4@3 4@3 549@3 fixed ... Faces 1@4 -3139@4 -3137@4 -3138@4 2@1 -3142@1 -3140@1 -3141@1 3@2 -3145@2 -3143@2 -3144@2 4@3 -3148@3 -3146@3 -3147@3 ... Bodies 1@1 362@3 384@3 383@3 382@2 381@3 ... Have distinct datafiles for each task. The datafile structure is the same as in method 2, but elements not allocated to a particular task are omitted. It is legal to have the same element numbers used on different tasks; that is, vertex 23@2 is an entirely distinct element from vertex 23@4. Each datafile should have identical header info. In any case, only the master task reads material in the "read" section at the bottom of the datafile. Slave tasks do NOT read the "read" section. This is so that the master task has complete control, and the same file can be read by all tasks. If slave tasks need to do initialization, the master task should instruct it with one of the methods described below. <-------------------- MPI Evolver ---------------(MPI Evolver)------> MPI Surface Evolver - Experimental version of Surface Evolver that distributes a surface among multiple processors and machines. See: MPI Overview MPI Compilation MPI Datafiles MPI Invocation MPI Execution of commands MPI Graphics <-------------------- MPI graphics ---------------(MPI Evolver)-------> There are two ways to get screen graphics with MPI Evolver: 1. Use the regular screen graphics on a task. This will display on the same machine as the task is executing on. Most useful when executing a few tasks all on one machine for testing purposes. Use the "showq" command to avoid going into the graphics prompt. For example, parallel_exec "showq" to see all the pieces. 2. Use the screen graphics on the master task, and have it import data from other tasks. I've only tested this for the OpenGL graphics. First, start graphics in the master task with 's'. This will show the task 1 part of the surface by default. The task to display may be chosen in the graphics display by hitting 'M' for menu mode, then using the right mouse key to display the main menu, then going to the MPI task submenu near the bottom, and picking the task you want. The 'y' key toggles showing of the thick corona, if it is present. <-------------------- MPI invocation --------------(MPI Evolver)------> However you invoke MPI tasks, each task (master and slave) should have the name of a datafile on its command line. The datafiles can be different for each task, or the same one, as described above. If the datafile name contains "%d", then that will be automatically replaced by the task number to form the actual datafile name (actually, any version of the C printf %d format can be used, e.g. %03d to guarantee 3 digits for the task number). This permits a single MPI command line to load different datafiles on different tasks. Each task's datafile must be accessible from the machine it runs on. The master task, task 0, should be run on the machine being used as the console. <-------------------- MPI overview -------------(MPI Evolver)-----> MPI (Message Passing Interface) is a protocol for passing messages between multiple processes, usually on different machines. The MPI version of the Surface Evolver can execute Evolver on multiple processors on multiple machines, all working on the same surface, with one machine controlling the others. It is assumed the user is familiar with MPI, and has MPI installed. MPI Evolver is still early in development, and still does not do things necessary for production use. In particular, it does not do topology changes safely. Anybody using MPI Evolver at this point is doing so just because they like playing with new toys. MPI Evolver is organized to run task 0 as the master task that interacts with the user through the command line interface, and a set of slave tasks. Each slave task is a full version of the Evolver, except it receives its commands from the master task and there is synchronization of data between the tasks at key points. Each slave task has a piece of the whole surface, but the master task does not. Vertices, edges, and facets are allocated among the slave tasks, but all tasks (including the master task) know about all bodies (for now, at least). The surface on each task is divided into "native" elements, that belong to the task, and "corona" elements copied from other tasks. There are three levels of corona state currently implemented: Level 0: No corona elements. This is the state immediately after the datafiles are read in, but ordinarily the user never sees it. Level 1: Corona vertices and edges of native facets are present. This suffices for calculation of ordinary surface tension evolution, and is the default state the user sees after loading. Level 2: All corona edges and facets adjacent to native vertices are present, along with their vertices. This state is needed for certain energies like squared mean curvature that need more extensive information around a vertex. In the OpenGL graphics display, the key 'y' toggles showing the level 2 corona. <-------------------- mpi_debug ------------------(MPI Evolver)---> mpi_debug Main prompt command that toggles the printing of trace messages by MPI activity in MPI Evolver. Don't do this. <-------------------- mpi_maxtask ----------------(MPI Evolver)----> mpi_maxtask MPI Evolver read-only variable. Highest task number, so task numbers run from 0 to mpi_maxtask. Since task 0 does not have a piece of the surface, mpi_maxtask is also the number of pieces the surface can be divided into. <-------------------- mpi_task -----------------(MPI Evolver)---> mpi_task MPI Evolver read-only element attribute, whose value is which task this element belongs to. For example, if you wanted to see which vertices task 5 imports from task 2, you could say task_exec 5,"list vertex where mpi_task == 2" <-------------------- multiprocessor option ------------------> <-------------------- option -p ----------------------------> Command line option -pn : Forces use of n processes for an Evolver compiled in multi-processor mode. n may be larger or smaller than the physically available number of processors. The default is 1. This option should be regarded as experimental; there is still too much overhead for it to be useful usually. <-------------------- N ----------------------------> Single letter main command. Set all body target volumes to current actual volumes. <-------------------- n ----------------------------> Single letter main command. Notching ridges and valleys. Finds edges that have two adjacent facets, and those facets' normals make an angle greater than some cutoff angle. You will be prompted for the cutoff angle (radians) if you don't give a value with the command. Qualifying edges will have the adjacent facets subdivided by putting a new vertex in the center. Should follow with equiangulation. In the string model, it will refine edges next to vertices with angle between edges (parallel orientation) exceeding the given value. Optionally takes cutoff angle on command line. <-------------------- named methods ----------------------------> A "method" is a way of calculating a scalar value from some particular type of element (vertex, edge, facet, body). Each method is implemented internally as a set of functions for calculating the value and its gradient as a function of vertex positions. The most common methods also have Hessian functions. Methods are referred to by their names. See Implemented methods for a list of available methods. Adding a new method involves writing C routines to calculate the value and the gradient (and maybe the Hessian) as functions of vertex coordinates, adding the function declarations to quantity.h, and adding a structure to the method declaration array in quantity.c. All the other syntax for invoking it from the datafile is already in place. <-------------------- named quantities ----------------------------> A "named quantity" is the sum total of various method instances, although usually just one instance is involved. The instances need not apply to the same type of element; for example, both facet and edge integrals may be needed to define a volume quantity. Each named quantity is one of four types: 1. "energy" quantities which are added to the total energy of the surface; 2. "fixed" quantities that are constrained to a fixed target value (by Newton steps at each iteration); and 3. "conserved" quantities are like fixed, but the value is irrelevant. The quantity gradient is used to eliminate a degree of freedom in motion. Rarely used, but useful to eliminate rotational degree of freedom, for example. Will not work with optimizing parameters, since they do gradients by differences. 4. "info_only" quantities whose values are merely reported to the user. This type is initially set in a quantity's datafile declaration. A quantity can be toggled between fixed and info_only with the "fix quantityname" and "unfix quantityname" commands. The value of a quantity may be displayed with the A or v commands, or as an expression "quantityname.value". Furthermore, using the quantity name as an element attribute evaluates to the sum of all the applicable component instance values on that element. For example, supposing there is a quantity named vol, one could do print vol.value print facet[2].vol histogram(facet,vol) Each quantity has a "modulus", which is just a scalar multiplier for the sum of all instance values. A modulus of 0 will turn off calculation of all the instances. The modulus can be set in the datafile declaration, with the A command, or by assignment: quantityname.modulus := 1.2 Each fixed quantity has a target value, to which the Evolver attempts to constraint the quantity value. Each time an iteration is done ( g command or the various Hessian commands), Newton's Method is used to project the surface to the constrained values. The target value can be displayed with the A or v commands, or as "quantityname.target". It can be changed with the A command or by assignment. Example: print qname.target qname.target := 3.12 A quantity can have a constant value added to it, similar to the body attribute volconst. This quantity attribute is also called volconst. It is useful for adding in known values of say integrals that are omitted from the actual calculation. It can be set in the quantity's datafile definition, or by an assignment command. Each fixed quantity has a Lagrange multiplier associated to it. The Lagrange multiplier of a constraint is the rate of energy change with respect to the constraint target value. For a volume constraint, the Lagrange multiplier is just the pressure. Lagrange multipliers are calculated whenever an iteration step is done. They may be displayed with the v command in the "pressure" column, or as an expression "quantityname.pressure". A fixed quantity can have a tolerance attribute, which is used to judge convergence. A surface is deemed converged when the sum of all ratios of quantity discrepancies to tolerances is less than 1. This sum also includes bodies of fixed volume. If the tolerance is not set or is negative, the value of the variable target_tolerance is used, which has a default value of 0.0001. The sample datafile column.fe contains some examples of named quantities and instances. It is planned that eventually all energies and global constraints will be converted to named quantity system. However, existing syntax will remain valid wherever possible. Starting Evolver with the -q option will do this conversion now. Some methods, those that logically depend on the orientation of the element, can be applied with a relative orientation. When applied to individual elements in the datafile, a negative orientation is indicated by a '-' after the instance name. When applied at runtime with the set command, the orientation will be negative if the element is generated with negative orientation, i.e. set body[1].facet method_instance qqq. The methods currently implementing this feature are: edge_vector_integral, string_gravity, facet_vector_integral, facet_2form_integral, facet_volume, facet_torus_volume, simplex_vector_integral, simplex_k_vector_integral, edge_k_vector_integral, gravity_method, and full_gravity_method. <-------------------- named quantities option ----------------------------> <-------------------- option -q ----------------------------> Command line option -q : Convert everything to named quantities internally. There are a few things for which no quantities exist yet; they will produce error messages. <-------------------- named quantity declaration ----------------------------> The syntax for defining a named quantity in the datafile is: QUANTITY name ENERGY|FIXED=value|CONSERVED|INFO_ONLY [LAGRANGE_MULTIPLIER constexpr] [TOLERANCE constexpr] [MODULUS constexpr] methodlist | FUNCTION methodexpr Here name is an identifier assigned by the user in order to refer to the quantity. Any quantities must be declared to be one of three types: > ENERGY quantities are added to the overall energy of the surface; > FIXED quantities that are constrained to a fixed target value; > CONSERVED quantities are like FIXED in that the motion is projected to conserve the quantity, but the actual value is not projected to a given value. > INFO_ONLY quantities whose values are merely reported to the user. For fixed quantities, the optional Lagrange multiplier value supplies the initial value of the Lagrange multiplier (the "pressure" attribute of the quantity). It is meant for dump files, so on reloading no iteration need be done to have a valid Lagrange multiplier. For fixed quantities, the tolerance attribute is used to judge convergence. A surface is deemed converged when the sum of all ratios of quantity discrepancies to tolerances is less than 1. This sum also includes bodies of fixed volume. If the tolerance is not set or is negative, the value of the variable target_tolerance is used, which has a default value of 0.0001. Each quantity has a modulus, which is just a scalar multiplier of the whole quantity. A modulus of 0 will turn off an energy quantity. The default modulus is 1. The methodlist version of the quantity definition may contain one or more method instances. To incorporate a previously explicitly defined instance, include METHOD instancename. GLOBAL_METHOD may be used instead of METHOD to indicate the method applies to all elements of the appropriate type; it is equivalent to using GLOBAL in the method definition. To instantiate a method in the quantity definition, you essentially incorporate the instance definition, but without an instance name. Example of a quantity with one predefined method instance and one implicitly defined instance: method_instance qwerty method facet_scalar_integral scalar_integrand: x^2 quantity foobar energy method qwerty method edge_scalar_integral scalar_integrand: y^3 Usually the second, implicit definition will be more convenient. Several method instances may be included in one methodlist (up to a current limit of 50), and their values are added together and multiplied by the quantity modulus to get the quantity value. The FUNCTION methodexpr variant defines the quantity as a function of previously defined method instances. Example: method_instance qwerty method facet_scalar_integral scalar_integrand: x^2 quantity foobar energy function qwerty^3 Non-global quantities may be applied to elements individually by adding the quantity name to the datafile line defining an element. They may also be applied or unapplied at runtime with the set and unset commands. Orientable methods can be applied with negative orientation in the datafile by following the name with a dash. The orientation in a set command follows the orientation the element is generated with. Methods applying to different types of elements may be combined in one quantity. If such a quantity is applied to an element, then all method instances of that quantity of the appropriate type are applied to the element. Original attachments of quantities are remembered, soIf an edge method is applied to a facet, then edges created from refining that facet will inherit the edge method. <-------------------- neo_hookean ----------------------------> <-------------------- neo_lambda ----------------------------> <-------------------- neo_mu ----------------------------> neo_hookean Named method. Contributed by Prof. Rabah Bouzidi. I don't seem to have the compact formula for this one. Needs neo_lambda, neo_mu, and form_factors. Element: facet. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration: quantity bender energy method neo_hookean global <-------------------- new_body ----------------------------> Main prompt command. For creating a new body. The syntax is that of a function instead of a verb, since it returns the id number of the new body. There are no arguments. Syntax: newid := NEW_BODY The body is created with no facets. Use the set facet frontbody and set facet backbody commands to install the body's facets. The new body has the same default properties as if it had been created in the datafile with no attributes, so you will need to explicitly add any attributes you want, such as density or target volume. Example: newb := new_body set facet frontbody newb where color == red <-------------------- new_edge ----------------------------> Main prompt command. For creating a new edge. The syntax is that of a function instead of a verb, since it returns the id number of the new edge. The arguments are the id's of the tail and head vertices. Syntax: newid := NEW_EDGE(expr, expr) The new edge has the same default properties as if it had been created in the datafile with no attributes, so you will need to explicitly add any attributes you want. Example to create a set of coordinate axes in 3D: newv1 := new_vertex(0,0,0); fix vertex[newv1]; newv2 := new_vertex(1,0,0); fix vertex[newv2]; newv3 := new_vertex(0,1,0); fix vertex[newv3]; newv4 := new_vertex(0,0,1); fix vertex[newv4]; newe1 := new_edge(newv1,newv2); fix edge[newe1]; newe2 := new_edge(newv1,newv3); fix edge[newe2]; newe3 := new_edge(newv1,newv4); fix edge[newe3]; set edge[newe1] no_refine; set edge[newe1] bare; set edge[newe2] no_refine; set edge[newe2] bare; set edge[newe3] no_refine; set edge[newe3] bare; <-------------------- new_facet ----------------------------> Main prompt command. For creating a new facet. The syntax is that of a function instead of a verb, since it returns the id number of the new facet. The arguments are the oriented id's of the edges around the boundary of the facet, in the same manner that a face is defined in the datafile. The number of edges is arbitrary, and they need not form a closed loop in the string model. In the soapfilm model, if more than three edges are given, the new face will be triangulated by insertion of a central vertex. In that case, the returned value will be the original attribute of the new facets. In the simplex model, the arguments are the id's of the facet vertices. Syntax: newid := NEW_FACET(expr, expr,...) The new facet has the same default properties as if it had been created in the datafile with no attributes, so you will need to explicitly add any attributes you want. Example: newf := new_facet(1,2,-3,-4); fix facet where original == newf; <-------------------- new_vertex ----------------------------> Main prompt command. For creating a new vertex. The syntax is that of a function instead of a verb, since it returns the id number of the new vertex. The arguments are the coordinates of the vertex. The new vertex is not connected to anything else; use the new_edge command to connect it. Syntax: newid := NEW_VERTEX(expr, expr,...) Examples: newid1 := new_vertex(0,0,1) newid2 := new_vertex(pi/2,0,max(vertex,x)) <-------------------- no autoconvert option ----------------------------> <-------------------- option -a ----------------------------> Command line option -a- : Do not enable automatic conversion to named methods and quantities mode when a situation requiring it arises; prompt instead. <-------------------- no renumbering option ----------------------------> <-------------------- option -i ----------------------------> command line option -i : Keeps elements numbers as listed in the datafile, instead of renumbering them consecutively. The same effect can be achieved by putting the keyword keep_originals in the top of the datafile. <-------------------- no_display ----------------------------> <-------------------- nodisplay ----------------------------> Facet read-write attribute. When set, suppresses the display of the facet in graphics. Can be set in the datafile by adding "no_display" to the line defining the facet. Can also be manipulated by the set and unset commands. Nodisplay is a synonym. Example: set facet nodisplay where color != red <-------------------- no_refine ----------------------------> Edge and facet read-write Boolean attribute. An edge with the "no_refine" attribute will not be refined by the r command. This is useful for avoiding needless refining of lines or planes that are used only for display. Giving a facet the no_refine attribute has no effect except that edges created within the facet by refining will inherit the no_refine attribute. So to avoid refinement of a plane, all edges and facets in the plane must be given the no_refine attribute. The no_refine attribute may be specified on the datafile line for an edge or facet, or the set command may be used. Examples: set edge no_refine where fixed unset edge[2] no_refine list edge where no_refine print edge[3].no_refine <-------------------- node_charge ----------------------------> An extra attribute used by the knot_energy named method. <-------------------- noncontent ----------------------------> Noncontent Edge read-write attribute. When set, indicates this facet should not be used in volume calculations in the soapfilm model or facet area calculations in the string model. Useful, for example, if you want to have edges be part of a body boundary for display purposes, but want to use constraint integrands for greater accuracy in volume calculations. Example: set edge noncontent where on_constraint 1 <-------------------- nonnegative ----------------------------> Level set constraint attribute that vertices on the constraint are confined to nonnegative values of the constraint. See "constraint declaration" for syntax, and "one-sided constraints" for semantics. <-------------------- nonpositive ----------------------------> Level set constraint attribute that vertices on the constraint are confined to nonpositive values of the constraint. See "constraint declaration" for syntax, and "one-sided constraints" for semantics. <-------------------- nonwall ----------------------------> Level set constraint attribute. NONWALL indicates this constraint is to be ignored in vertex and edge popping. See "constraint declaration" for syntax. <-------------------- normal_curvature ----------------------------> Evolver toggle command. Calculation of squared curvature by taking area of vertex to be the component of the volume gradient parallel to the mean curvature vector. <-------------------- normal_motion ----------------------------> Evolver toggle command. Projects motion to surface normal, defined as the volume gradient. May be useful with squared curvature if vertices tend to slither sideways into ugly patterns. <-------------------- normal_sq_mean_curvature ----------------------------> Named method. Description: Integral of squared mean curvature of a surface, with a slightly different definition from sq_mean_curvature or eff_area_sq_mean_curvature. To alleviate the instability of eff_area_sq_mean_curvature, normal_sq_mean_curvature considers the area around the vertex to be the component of the volume gradient parallel to the mean curvature vector, rather than the magnitude of the volume gradient. Thus h = (1/2)(F·F/N·F) E = h^2A/3 = (3/4)(F·F/N·F)^2 A. This is still not perfect, but is a lot better. WARNING: For some extreme shapes, Evolver may have problems detecting consistent local surface orientation. The assume_oriented toggle lets Evolver assume that the facets have been defined with consistent local orientation. If the parameter or vertex attribute h_zero is defined, then the value per vertex is E = (h-h_0)^2 A/3 = (3/4)(F·F/N·F - 2h_0)^2A If the vertex is on one or several constraints, the F and N are projected to the constraints, essentially making the constraints act as mirror symmetry planes. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: quantity nsq energy method normal_sq_mean_curvature global <-------------------- notch ----------------------------> Main prompt command. For refining a surface in regions of high curvature. Syntax: NOTCH expr Notches all edges with dihedral angle greater than given value. Same as 'n' command, or the command foreach edge ee where ee.dihedral > expr do refine ee.facet Notching is done by adding a vertex in the middle of adjacent facets. Should be followed by equiangulation. <-------------------- notch_count ----------------------------> Internal read-only variable. Number of edges notched in last notch command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- null_area ----------------------------> Named method. Description: Simply returns 0 for any facet. Useful with area_method_name when you don't want area as energy, but you still want to assign edges tension. Element: edge. Parameters: none. Models: any. Ambient dimension: any. Hessian: yes. Example datafile declaration: area_method_name "null_area" <-------------------- null_length ----------------------------> Named method. Description: Simply returns 0 for any edge. Useful in the string model with length_method_name when you don't want edge energy, but you still want to assign edges tension. Element: edge. Parameters: none. Models: any. Ambient dimension: any. Hessian: yes. Example datafile declaration: length_method_name "null_length" <-------------------- numbers ----------------------------> Constant values may be in any of the usual forms. This includes integers, fixed point, and scientific notation such as 2 -3 .5 23. 5e-10 +0.7D2 Hexadecimal integers starting with 0x, as in 0x12Af, are also accepted, as are binary numbers such as 11001b, indicated by a trailing 'b'. Color names are interpreted as integers. <-------------------- O ----------------------------> Single letter main command. Pop non-minimal edges. Scans for edges with more than three facets attached. Splits such edges into triple-facet edges. Splits propagate along a multiple edge until they run into some obstacle. This command is meant for surfaces that have equal tension on all facets. Also tries to pop edges on walls properly. For finer control on which edges to try, use the pop command. Try octa.fe for an example. <-------------------- o ----------------------------> Single letter main command. Pop non-minimal vertices. This command scans the surface for vertices that don't have the topologies of one of the three minimal tangent cones that are legal in soap films (plane, triple edge, tetrahedral point). These are "popped" to proper local topologies. The algorithm is to replace the vertex with a sphere. The facets into the original vertex are truncated at the sphere surface. The sphere is divided into cells by those facets, and the largest cell is deleted, which preserves the topology of the complement of the surface. A special case is two cones meeting at a vertex; if the cones are broad enough, they will be merged, otherwise they will be split. In case of merging cones, if both cone interiors are defined to be part of the same body, then no facet is placed across the neck created by the merger; if they are different bodies or no bodies, a facet will be placed across the neck. Only vertices in the interior of a surface, not fixed or on constraints or boundaries, are tested. Try popstr.fe and octa.fe for examples. <-------------------- oid ----------------------------> Geometric element read-only attribute. The oid of an element is the "oriented id" of an element as used in an expression. It is the id number signed according to whether the use of the element is with the same or opposite orientation as the way it is stored. Example: to get an edge list for a facet as in the datafile, use oid instead of id: foreach facet ff do { printf "%g %g %g %g\n",ff.id,ff.edge[1].oid, ff.edge[2].oid,ff.edge[3].oid } <-------------------- old_area ----------------------------> Evolver toggle command. In the string model with area normalization, at a triple vertex Evolver normally tries to calculate the motion so that Von Neumann's Law will be obeyed, that is, the rate of area change is proportional to the number of sides of a cell. If old_area is ON, then motion is calculated simply by dividing force by star area. <------------------ old_force_ribiere --------------------------> Vertex extra attribute used internally by conjugate gradient mode. <-------------------- ometis ----------------------------> Main prompt command. Computes an ordering for Hessian factoring using the METIS library of Karypis and Kumar, if this library has been compiled into the Evolver (not part of the public distribution yet). Prints ordering tree. To actually use METIS ordering during factoring, use the toggle metis_factor. Note: ometis no longer works for Metis version 3 or later, since Metis does not return the tree any more. But metis_factor still works. Syntax: OMETIS n // n is smallest partition size OMETIS // defaults to n = 100 <-------------------- on_boundary ----------------------------> Vertex, edge, or facet read-only attribute. The status of whether an element is on a boundary can be queried with the Boolean attribute on_boundary. Elements can be unset from boundaries, but not set on them (since parameter values would be unknown). Examples: list vertex where on_boundary 1 unset vertex boundary 2 <-------------------- on_constraint ----------------------------> Vertex, edge, or facet read-only attribute. Boolean attribute for whether an element is on a given constraint. The full syntax of the attribute is "on_constraint n" where n is the number of the constraint. Examples: list edge where on_constraint 3 print vertex[3].on_constraint 1 <-------------------- on_method_instance ----------------------------> Vertex, edge, or facet read-only attribute. Boolean attribute for whether an element contributes to a given named method instance. The full syntax of the attribute is "on_method_instance instancename". Examples: list facet where on_method_instance center_of_mass_x_edges print vertex[3].on_method_instance blue_area_1 <-------------------- on_quantity ----------------------------> Vertex, edge, or facet read-only attribute. Boolean attribute for whether an element contributes to a given named quantity. Actually, it tests whether the element is on any of the method instances comprising a quantity. The full syntax of the attribute is "on_quantity quantityname". Examples: list facet where on_quantity center_of_mass_x print vertex[3].on_quantity blue_area <-------------------- one-sided constraints ----------------------------> <-------------------- one_sided_lagrange ----------------------------> one-sided constraints If a level set constraint is declared NONNEGATIVE or NONPOSITIVE in the datafile, the vertices subject to the constraint must stay in that part of the domain of the level set function. It is usually unwise to give edge integrals to edges on one-sided constraints, or to declare them CONVEX. Whether a vertex exactly satisfies the constraint may be queried with the vertex hit_constraint attribute. The 'g' iteration step will check for a vertex wanting to leave a one-sided constraint it has hit, but hessian commands do not; therefore it is wise to intersperse 'g' with hessian or hessian_seek when there are one-sided constraints involved. Example: Suppose one wanted to keep a bubble inside a spherical tank of radius 5. Then one would define the constraint in the datafile constraint 1 nonpositive formula: x^2 + y^2 + z^2 = 25 For purposes of evaluating nonnegativity or nonpositivity, all terms are shifted to the left side of the formula. One would then apply this constraint to all vertices, edges, and facets of the bubble surface. If you define the real-valued vertex extra attribute one_sided_lagrange, the Lagrange multipliers for vertices hitting one-sided constraints will be recorded. one_sided_lagrange may be defined as an array. If a vertex hits more constraints than the size of one_sided_lagrange, then the first ones that fit will be recorded. <-------------------- ooglfile ----------------------------> Main prompt command. Writes a file containing OOGL-formatted graphics data for the surface as a POLY or CPOLY quad file. This is a non-interactive version of the P 2 command. Syntax: ooglfile stringexpr The string gets ".quad" appended to form the filename. This command does not ask any of the other questions the P 2 command asks; it uses the default values, or whatever the last responses were to the previous use of the interactive P 2 command. Good for use in scripts. Example: ooglfilename := sprintf "frame%d",framecounter; ooglfile ooglfilename; framecounter += 1; <-------------------- opacity ----------------------------> Facet attribute for transparency on some geomview systems. Syntax: set facet opacity value where value is between 0 and 1. Actually, this just sets a global variable for all facets simultaneously. The value is passed to geomview in the alpha slot of the color. <-------------------- OpenGL ----------------------------> Ideally, you have a version of the Evolver that uses OpenGL/GLUT for its screen graphics. OpenGL is standard on Mac OSX, most unix systems, and Microsoft Windows. Tbe graphics display is invoked with the 's' command, which leaves you at the graphics prompt, which you should quit 'q' right away since graphics commands are better given in the graphics window. There are many mouse and keyboard actions that can be performed in the graphics window itself. Holding down and dragging the left mouse button moves the surface continuously, and the clicking right mouse button picks vertices, edges, and facets. Picked element id numbers are printed in the console window. With the graphics window in the foreground, these keyboard commands are active: h Print a help screen on the console window. r Rotate mode for left mouse button. t Translate mode for left mouse button. z Zoom mode for left mouse button (and use F to focus on particular vertex). c Clockwise/counterclockwise spin mode for left mouse button. + Widen edges. - Narrow edges. b Decrement edge front bias by .001. B Increment edge front bias by .001 (to show edges more clearly). R Reset the view. m Center the image. M Have the right mouse button bring up a menu instead of picking. P Have the right mouse button do picking instead of menu (default). p Toggle orthogonal/perspective projection. s Toggle cross-eyed stereo. e Toggle showing edges, regardless of "show edge" condition. f Toggle showing facets obeying "show facet" condition or no facets. F Move the rotate/zoom origin to the last picked vertex. G Start another graphics window with independent camera. o Toggle drawing a bounding box. g Toggle Gourard (smooth) shading. x Close the graphics window. arrow keys Translate a bit. And some more advanced commands most users will never use, but are listed here for completeness: H Print advanced help. a Toggle using OpenGL element arrays. i Toggle interleaved elements in OpenGL arrays. I Toggle indexed OpenGL arrays. S Toggle OpenGL triangle strips. Y Toggle strip coloring (I was curious as to what OpenGL triangle strips would look like). D Toggle using a display list. Q Toggle printing drawing statistics. <-------------------- operators ----------------------------> These are the arithmetic operators that may appear in expressions: +,-,*,/: Usual real arithmetic. NOTE: A '+' or '-' preceded by whitespace and followed by a number is taken to be a signed number. Thus "3 - 5" and "3-5" are single expressions, but "3 -5" is not. This is for convenience in separating multiple expressions listed on the same line for vertex coordinates, metric components, etc. in the datafile. idiv : Integer divide. Rounds toward zero, then does integer division. Ex: 7 idiv 2 is 3; -3.5 idiv 2.1 is -1; -3 idiv 2 is -1. %, mod: Real arithmetic modulus, x % y = x - floor(x/y)*y. imod: Integer arithmetic modulus, x imod y = floor(x) - floor(floor(x)/floor(y))*floor(y). (,): Parentheses for grouping and functional notation. ^,**: Raise to real power. ? : : Conditional expression, as in the C language. x ? y : z evaluates to y if x is nonzero and to z if x is zero. <-------------------- optimise ----------------------------> Main prompt command. Set gradient descent iteration to optimizing mode, with an upper bound on the scale factor. "Optimise" is a synonym. Syntax: OPTIMIZE expr <-------------------- optimising_parameter ----------------------------> <-------------------- optimizing_parameter ----------------------------> A variable may be made subject to optimization during iteration or hessian commands with the datafile declaration OPTIMIZING_PARAMETER identifier=constexpr PDELTA=constexpr PSCALE=constexpr Such a variable joins the vertex coordinates as an independent variable during optimization. However, it differs from a coordinate in that gradients with respect to it are calculated numerically, rather than analytically. Thus it may be used anywhere a variable is permitted. Hessians with optimizing parameters are implemented. The optional pdelta value is the parameter difference to use in finite differences; the default value is 0.0001. The optional pscale value is a multiplier for the parameter's motion, to do "impedance matching" of the parameter to the surface energy. These attributes may be set on any parameter, for potential use as an optimizing parameter. At runtime, a parameter may be toggled to be optimizing or not with the FIX and UNFIX commands. That is, fix radius would make the radius variable non-optimizing (fixed value). Also, the pdelta and pscale attributes may be accessed at runtime, as in height.pscale := 2*height.pscale "Optimising_parameter" is a synonym. <-------------------- optimize ----------------------------> Main prompt command. Set gradient descent iteration to optimizing mode, with an upper bound on the scale factor. "Optimise" is a synonym. Syntax: OPTIMIZE expr <-------------------- optimizing scale ----------------------------> In using gradient descent to seek a minimum energy, one finds a direction of motion and does a line search along that direction to find the minimum energy. Evolver will do that in optimizing scale mode. The line search consists of halving or doubling the current scale factor until an energy minimum is bracketed; then quadratic interpolation is used to estimate the optimum scale. Optimizing scale is the default; it also may be turned on with the m command or the optimizing command. For safety, there is an upper bound to the scale; it defaults to 1 but may be changed with the optimizing command. There is also a lower bound; if Evolver gets a scale below 1e-12 of the scale bound when attempting to find a minimum, it gives up and just uses scale 0. Scale 0 is not a null operation since it still projects to constraints, if they are not exactly satisfied. In general, a good scale factor depends on the type of energy being minimized and the level of refinement. However, for minimizing area, when the triangulation is well-behaved and area normalization is off, the best scale factor is usually around 0.2, independent of refinement. In optimizing mode, a scale factor getting small, say below 0.01, indicates triangulation problems. Too large a fixed scale factor will show up as total energy increasing. If you have motion by area normalization ON use a small scale factor, like 0.001, until you get a feel for what works. If check_increase is toggled on, then the motion is not done if it would increase energy. But be aware that energy sometimes may have to increase in order to satisfy constraints. <-------------------- options ----------------------------> Command line options: The syntax for starting the Evolver from the system command prompt is: evolver [-ffilename] [options] [datafile] Options: [-a-] [-d] [-e] [-i] [-m] [-pn] [-q] [-Q] [-w] [-x] [-y] The current directory and EVOLVERPATH will be searched for datafile. If the datafile is not found, then a new search with extension .fe is done. Wildcard matching is in effect on some systems (Windows, linux, maybe others), but be very careful when using wildcards since there can be unexpected matches. If the datafile still not found, or the datafile is not given on the command line, then the user will be prompted. For information on individual options, see "option -a" etc. <-------------------- orientation ----------------------------> Facet read-write attribute. Controls the sign of oriented integrals on a facet. Value +1 or -1. Useful when triangulation manipulations create a facet with an undesired orientation. Example: set facet[123] orientation -1 <-------------------- original ----------------------------> Geometric element read-only attribute. For elements read from the datafile, this is the number given to the element in the datafile, which may be overridden by an explicit original attribute value in the datafile line defining the element. The value is inherited by all elements of the same type that result from subdivision. For elements otherwise generated at run time, the original attribute value is -1. Example: to show which facets descended from face 1 in the datafile: set facet color red where original == 1 <-------------------- p ----------------------------> Single letter main command. Sets ambient pressure in ideal gas model. If you don't give a value with the command, you will be prompted. A large value gives more incompressible bodies. <-------------------- P ----------------------------> Single letter main command. Produce graphics output files. "P" is for "picture". This brings up a menu, unless you give the menu option on the command line. For the 2D graphics options, the view is the same as seen with the s command. For options that output to a file, you will be prompted for a filename. Some other possible options you may be asked: Display raw cells, connected bodies or clipped cells? (0,1,2) : If you are doing torus model, you will be asked for a display option, unless you have already set one. Do normal interpolation? : Some formats are capable of doing interpolation between vertex normals for smoother shading, and you will be asked if you want to do that. Do inner, outer, or all surfaces? (i,o,a) : When bodies are present, there is an option to plot the inner surfaces(adjacent to two bodies), outer surfaces (adjacent to 0 or 1 bodies), or all surfaces of the bodies. Do body colors? : This gives you a chance to color the bodies differently. If you do, the current colormap file will be used to color the bodies according to id number. This scheme is a relict of early days of the Evolver, and it is suggested that you use the color, frontcolor and backcolor facet attributes instead. Enter name of colormap file: : If there is no current colormap file, you will be prompted. The colormap file has the format of RGB values, one set per line, values between 0 and 1. (This map may not be effective on all devices.) Thicken? (n | y [thickness(0.001)]) : You may also be asked if you want thickening. If you do, each facet will be recorded twice, with opposite orientations, with vertices moved from their original positions by the thickening distance (which the option lets you enter) in the normal direction. The normal used at each vertex is the same as used for normal interpolation, so all the facets around a planar vertex will have that vertex moved the same amount. Triple junctions will be separated. Thickening is good for rendering programs that insist on consistently oriented surfaces, or that have problems with show-through of the backside of a surface. Choosing 'y' or 'n' will reset the thicken toggle. If you answer 'y', you can optionally specify the thickness, which defaults to the value of the thickness internal variable. The menu choices for types of output are: 1. Pixar file : For Pixar format. Actually same format as option 2. 2. OOGL file : This is a file in a file format used by geomview, which is Object Oriented Graphics Language. Suitable for direct input into geomview. 3. PostScript file : Generates a PostScript file. 4. Triangle file : A private format file, just listing data. Not much use any more. 5. Softimage file : Output file in Softimage format. 8. Start simultaneous geomview : If you have the geomview package installed, this command will start geomview and display the current surface. Changes to the surface are automatically displayed unless autodisplay is toggled off. 9. End simultaneous geomview : Terminates any geomview program or pipe. A. Start OOGL pipe. : Geomview uses a pipe interface at the moment. This starts a named pipe with geomview output, but without invoking geomview. You will be told the name of the pipe, and it is up to you to start a pipe reader. Evolver blocks until a pipe reader is started. This is useful for having a second instance of Evolver feed a second surface to geomview by having geomview load the pipe. Also good for checking exactly what Evolver is sending to geomview. The geompipe command does the same thing. Terminate the pipe with "P 9". Note that only one geomview output at a time is possible, so you can't have a geomview display and separate pipe active at the same time. B. End OOGL pipe. : Same as option 9. <-------------------- parallel_exec -----------(MPI Evolver)-----> parallel_exec Main prompt command in MPI Evolver. Causes each slave task to execute the string. Syntax: parallel_exec string where string may be a double-quoted string or a string variable. In individual tasks, any aggregate commands such as "sum" execute across all local and imported elements. <-------------------- parameter ----------------------------> <-------------------- parameters ----------------------------> Refers to either a "parametric boundary" parameter, or a user-defined variable. In the latter case, a variable can be declared in the top of the datafile with the syntax parameter pname = value where "pname" is the name of the variable and "value" is a numeric expression involving known variables. The initial value is required. See also "optimizing_parameter". <-------------------- parameter scale ----------------------------> <-------------------- pscale ----------------------------> Optimizing paramater attribute, used as a multiplier for the parameter's motion, to do "impedance matching" of the parameter to the surface energy. Default value is 1. The pscale attribute may be set in the datafile parameter declaration, for example optimizing_parameter height pscale = 10.0 and accessed at runtime with the usual attribute syntax, as in height.pscale := 2*height.pscale <-------------------- parameter values ----------------------------> Vertex read-write attribute. Vertices on parametric boundaries are located according to the parameter values. Parameters are referred to as p1,p2,... Usually only p1 is used, since one-parameter curves used as boundary wires are most common. Such vertices in the original surface have their parameter values given in the vertices section of the datafile instead of their coordinates. Vertex parameters may be read or modified with the command language. Example: foreach vertex do printf "%g %f\n",id,p1 set vertex[1] p1 1.2 <-------------------- parameter_file ----------------------------> In the top section of the datafile, a variable can be initialized with a set of values in a file with the syntax PARAMETER name PARAMETER_FILE string I forget exactly how it is all supposed to work. <----------------------- parameter_1 ---------------------------> A generic parameter used by a couple of named methods. <-------------------- parametric boundaries ----------------------------> <-------------------- parametric boundary ----------------------------> Parametric "boundary" curves and surfaces Vertex locations may be given in terms of parameters on a parameterized curve or surface. Such curves or surfaces are called "boundaries" in Evolver terminology, since they are usually used as boundary curves of surfaces, for example a soap film on a wire loop could have the wire implemented as a boundary. Vertices, edges, and facets may be deemed to lie in a boundary. For a vertex, this means that the fundamental parameters of the vertex are the parameters of the boundary, and its coordinates are calculated from these. Vertices on boundaries may move during iteration, unless declared fixed. See cat.fe for an example. Boundaries are defined in the top section of the datafile. Vertices on boundaries are listed in the datafile with their parameter values instead of their coordinates, with "boundary n" appended to each such vertex definition. Edges and faces on boundaries are defined as usual, but with "boundary n" appended to each definition. So the datafile has lines like these: boundary 1 parameters 1 x1: cos(p1) x2: sin(p1) x3: 0.75 ... Vertices 1 0.0 boundary 1 2 pi/3 boundary 1 ... Edges 1 1 2 boundary 1 ... Putting an edge on a boundary means that vertices created on that edge will be on the boundary. An edge on a boundary must have at least one endpoint on the boundary, for use in extrapolating the boundary parameters of any created vertices. Extrapolating instead of interpolating midpoint parameters solves the problem of wrap-arounds on a boundary such as a circle or cylinder. However if you do want interpolation, you can use the keyword INTERP_BDRY_PARAM in the top of the datafile, or use the toggle command interp_bdry_param. Interpolation requires that both endpoints of an edge be on the same boundary, which cannot happen where edges on different boundaries meet. To handle that case, it is possible to add extra boundary information to a vertex by declaring two particular vertex extra attributes, extra_boundary and extra_boundary_param: interp_bdry_param define vertex attribute extra_boundary integer define vertex attribute extra_boundary_param real[1] Then declare attribute values on key vertices, for example vertices 1 0.00 boundary 1 fixed extra_boundary 2 extra_boundary_param 2*pi If the extra_boundary attribute is not set on a vertex when wanted, Evolver will silently fall back on interpolation. Putting a face on a boundary means that all edges and vertices created from refining the face will be on the boundary. In this case, the boundary should have two parameters (or whatever the dimension of the surface is). This is good for getting a surface to conform to a known parametric shape. Edges on boundaries have energy and content integrals like level-set constraints edges, but they are internally implemented as. named quantities. Whether an element is on a particular boundary can be queried with the on_boundary Boolean attribute. Elements can be removed from boundaries with the unset command, but they cannot be set on boundaries. A typical use of unset is to define an initial surface using a 2-parameter boundary, refine a couple of times, then unset. Examples: list vertex where on_boundary 2 unset vertex boundary 1 where on_boundary 1 unset edge boundary 1 unset facet boundary 1 It does not hurt to unset an element not on the boundary. Vertex parameters can be accessed in expressions as the attribute p1 (and p2,... for further parameters). Vertex parameters can be changed with the set command. Example: print vertex[5].p1 set vertex p1 p1+.1 where id < 4 vertex[2].p1 := 3 It is not an error to access the parameters of a vertex not on a boundary as long as some vertex is on a boundary (so that space is allocated in the vertex structure for parameters). A general guideline is to use constraints for two-dimensional walls and boundaries for one-dimensional wires. If you are using a boundary wire, you can probably declare the vertices and edges on the boundary to be FIXED. Then the boundary becomes just a guide for refining the boundary edges. NOTE: A vertex on a boundary cannot also have constraints. <-------------------- pause ----------------------------> Main prompt command. Pauses execution until the user hits RETURN. Useful in scripts to give the user a chance to look at some output before proceeding. <-------------------- pdelta ----------------------------> Optimizing parameter attribute, used for the magnitude of change in numerical differencing to find the gradient with respect to the paramter. Default value 0.0001. May be set in the parameter's datafile declaration, for example optimizing_parameter height pdelta = 0.000001 and accessed at runtime with the usual attribute syntax: height.pdelta := 1e-5 <-------------------- periods ----------------------------> If periodic boundary conditions are used (the torus model) , the period vectors of the fundamental unit cell parallelpiped may be defined in the top section of the datafile. Default is the unit cube. The syntax is the keyword PERIODS followed by expressions for the components of each period vector: PERIODS expr expr expr expr expr expr expr expr expr he size of this matrix depends on the space dimension. Variables may be used in the expressions, so the fundamental domain may be changed interactively by assigning new values to the variables. Be sure to give a recalc command whenever you change such a variable, in order to get the period matrix re-evaluated. <-------------------- permanent assignment ----------------------------> The permanent assignment operator ::= can be used to make assignments to variables and commands that are not forgotten when a new datafile is loaded. Such a command may only make reference to permanent variables, permanent commands, and internal variables. See permload command for an example of use. <-------------------- permload ----------------------------> Main prompt command. Loads a new datafile and continues with the current command after the read section of the datafile finishes. The filename is the datafile name, and can be either a quoted string or a string variable. Since the automatic re-initialization makes Evolver forget all non-permanent variables, care should be taken that the current command only uses permanently assigned variables (assigned with ::= ). Useful for writing scripts that run a sequence of evolutions based on varying parameter values. Using permload is a little tricky, since you don't want to be redefining your permanent commands and variables every time you reload the datafile, and your permanent command cannot refer directly to variables parameterizing the surface. One way to do it is to read in commands from separate files. For example, the catenoid of cat.fe has height controlled by the variable zmax. You could have a file permcat.cmd containing the overall series script command run_series ::= { for ( height ::= 0.5 ; height < 0.9 ; height ::= height + .05 ) { permload "cat"; read "permcat.gogo"; } } and a file permcat.gogo containing the evolution commands u; zmax := height; recalc; r; g 10; r; g 10; hessian; printf "height: %f area: %18.15f\n",height,total_area >> "permcat.out"; Then at the Evolver command prompt, Enter command: read "permcat.cmd" Enter command: run_series For loading a new surface and not continuing with the current command, see load. Wildcard matching is in effect on some systems (Windows, linux, maybe others), but be very careful when using wildcards since there can be unexpected matches. <-------------------- phase ----------------------------> <-------------------- phase declaration ----------------------------> <-------------------- phasefile ----------------------------> To declare that the surface tension of an edge or facet depends on the phases of its adjacent facets or bodies, the top section of the datafile should contain a line of the form PHASEFILE "filename" The information is read from an ASCII file, whose name is given in a double-quoted string. The first line of the file has the number of different phases. Each line after consists of two phase numbers and the surface tension between them. Lines not starting with a pair of numbers are taken to be comments. If a pair of phases is not mentioned, the surface tension between them is taken to be 1.0. Facets in the string model or bodies in the soapfilm model can be labelled with phases with the PHASE n phrase in the datafile. <-------------------- pi ----------------------------> Mathematical constant, ratio of circle circumference to radius. <-------------------- pickenum ----------------------------> Internal read-write variable. Number of last edge picked in geomview. <-------------------- pickfnum ----------------------------> Internal read-write variable. Number of last facet picked in geomview. <-------------------- picking ----------------------------> One of the big advantages of using geomview or the OpenGL version is that you can pick vertices, edges, and facets in the geomview window by right-mouse-clicking, and the id numbers of the picked objects will be printed in the main window. Be careful when picking; it does not always work as you might hope. It may be necessary to zoom in on the surface to get a clear shot at the element you want. Be wary when the element returned is 1; that seems to be a common response when Evolver is confused as to what element was picked. Also, Evolver polls geomview for pick results only when at a prompt awaiting user input. Picked vertex, edge, and facet numbers are stored in the internal variables pickvnum, pickenum, and pickfnum, respectively. The 'F' key command on the graphics window sets the rotation and scaling center to the pickvnum vertex. Pickvnum is settable with ordinary assignment commands, so the user can zoom in on any vertex desired. Note: Since vertices are not drawn individually, Evolver reports a vertex as picked only when two edges with a common vertex are simultaneously picked. Therefore a vertex at the end of a single edge cannot be picked. <-------------------- pickvnum ----------------------------> Internal read-write variable. Number of last vertex picked in geomview. <-------------------- pinning ----------------------------> Evolver toggle command. Check for vertices that can't move because adjacent vertices are not on same constraint when they could be. Obscure. <-------------------- piping ----------------------------> The output of a command can be piped to a system command using the unix-style pipe symbol `|'. Syntax: command | stringexpr The stringexpr is interpreted as a system command. Examples: list facets | "more" list vertices | "tee vlist" ; g 10 | "tee g.out" { {g 10; u } 20 } >> "logfile" {foreach facet do print area} | "cat >areafile" <----------------------- poisson_ratio ---------------------------> poisson_ratio Facet extra attribute used by various elastic named methods: linear_elastic, linear_elastic_B, neo_hookean, relaxed_elastic, relaxed_elastic_A, relaxed_elastic_B, SVK_elastic, and dirichlet_elastic. This is the two-dimensional isotropic poisson ratio. <-------------------- polyhedral curvature ----------------------------> Approximate polyhedral curvature type of mobility. Following a suggestion of Gerhard Dzuik and Alfred Schmidt, the inner product of global vectors is taken to be the integral of the scalar product of their linear interpolations over the facets (or edges in the string model). This has the advantage that the rate of area decrease of the surface is equal to the rate volume is swept out by the surface, which is a characteristic of motion by mean curvature. A big disadvantage is that the matrices M and S are no longer local (see mobility). S is a sparse matrix with entries corresponding to each pair of vertices joined by an edge, and M is its dense inverse. Approximate polyhedral curvature can be toggled with the approx_curv toggle command. <-------------------- pop ----------------------------> Main prompt command. Pops an individual edge or vertex or set of edges or vertices, giving finer control than the universal popping of the O and o commands. The specified vertices or edges are tested for not being minimal in the soap film sense. For vertices, this means having more than four triple edges adjacent; higher valence edges are automatically popped. For edges, this means having more than three adjacent facets when not on constraints or otherwise restricted. It tries to act properly on constrained edges also, but beware that my idea of proper behavior may be different from yours. Normally, popping puts in new edges and facets to keep originally separated regions separate, but that behavior can be changed with the pop_disjoin toggle. The style of popping a cone over a triangular prism can be controlled with the pop_to_edge and pop_to_face commands. The pop_enjoin toggle forces joining cones to be popped by widening the vertex into a neck. Examples: pop edge[2] pop edge where valence==5 <-------------------- pop_count ----------------------------> Internal read-only variable. Sum of vertex_pop_count and edge_pop_count. Kept for backwards compatibility. <-------------------- pop_disjoin ----------------------------> Evolver toggle command. Changes the behavior of popping edges and vertices to act like merging Plateau borders, i.e. produce disjoined films instead of films joined with cross-facets. In the edge case, if four facets meet along an edge and two opposite bodies are the same body, then popping the edge will join the bodies if pop_disjoin is in effect. In the vertex case, if the vertex has one body as an annulus around it, then the vertex will be separated into two vertices so the annulus becomes a continuous disk. This is all done regardless of the angles at which facets meet. Applies to pop, o, and O commands. <-------------------- pop_enjoin ----------------------------> pop_enjoin Evolver toggle command. Changes the behavior of popping vertices in the soapfilm model so that when two distinct cones are detected meeting at a common vertex, the popping result is a widening of the cone vertex into a neck rather than a disjoining of the cones. meet. Applies to pop and o commands. <-------------------- pop_edge_to_tri ----------------------------> Main prompt command. This command does a particular topological transformation common in three-dimensional foam evolution. An edge with tetrahedral point endpoints is transformed to a single facet. A preliminary geometry check is made to be sure the edge satisfies the necessary conditions, one of which is that the triple edges radiating from the endpoints have no common farther endpoints. If run in verbose mode, messages are printed when a specified edge fails to be transformed. This command is the inverse of the pop_tri_to_edge command. Works in linear and quadratic mode. Examples: pop_edge_to_tri edge[2] pop_edge_to_tri edge where valence==3 and length < 0.001 <-------------------- pop_edge_to_tri_count ----------------------------> Internal read-only variable. Number of edges flipped to triangles by the pop_edge_to_tri command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- pop_quad_to_quad ----------------------------> Main prompt command. This command does a particular topological transformation common in three-dimensional foam evolution. A quadrilateral bounded by four triple edges is transformed to a quadrilateral oriented in the opposite direction. The shortest pair of opposite quadrilateral edges are shrunk to zero length, converting the quadrilateral to an edge, then the edge is expanded in the opposite direction to form the new quadrilateral. The new quadrilateral inherits attributes such as color from the first quadrilateral, although all the facet numbers are different. A preliminary geometry check is made to be sure the edge satisfies the necessary conditions, one of which is that the triple edges radiating from the quadrilateral corners have no common farther endpoints. If run in verbose mode, messages are printed when a specified quadriteral fails to be transformed. The specified facet can be any one of the facets of the quadrilateral with a triple line on its border. It doesn't hurt to apply the command to all the facets of the quadrilateral, or to facets of multilple quadrilaterals. Quadrilaterals may be arbitrarily subdivided into facets; in particular, they may have some purely interior facets. Works in linear and quadratic mode. Examples: pop_quad_to_quad facet[2] pop_quad_to_quad facet where color==red <-------------------- pop_quad_to_quad_count ----------------------------> Internal read-only variable. Number of quadrilaterals flipped by the pop_quad_to_quad command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- pop_to_edge ----------------------------> Evolver toggle command. The non-minimal cone over a triangular prism frame can pop in two ways. If this toggle is on, then popping to an edge rather that a facet will be done. Default off. <-------------------- pop_to_face ----------------------------> Evolver toggle command. The non-minimal cone over a triangular prism frame can pop in two ways. If this toggle is on, then popping to a facet rather that an edge will be done. Default off. <-------------------- pop_tri_to_edge ----------------------------> Main prompt command. This command does a particular topological transformation common in three-dimensional foam evolution. A facet with three tetrahedral point vertices is transformed to a single facet. A preliminary geometry check is made to be sure the edge satisfies the necessary conditions, one of which is that the triple edges radiating from the vertices have no common farther endpoints. If run in verbose mode, messages are printed when a specified edge fails to be transformed. This command is the inverse of the pop_edge_to_tri command. Works in linear and quadratic mode. Examples: pop_tri_to_edge facet[2] pop_tri_to_edge facet where color == red <-------------------- pop_tri_to_edge_count ----------------------------> Internal read-only variable. Number of triangles flipped to edges by the pop_tri_to_edge command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- pos_area_hess ----------------------------> <-------------------------- fgagfa_coeff ----------------------------> <-------------------------- gfa_2_coeff ----------------------------> <-------------------------- gfaafg_coeff ----------------------------> <-------------------------- gfagfa_coeff ----------------------------> <-------------------------- gga_coeff ----------------------------> pos_area_hess Named method. Description: Same as the facet_area method, but the Hessian can be adjusted various ways by setting the variables fgagfa_coeff, gfa_2_coeff, gfagfa_coeff, and gfaafg_coeff. This will make sense if you look at the Dirichlet section of the Technical Reference chapter of the printed manual. The default values of the coefficients are -1, 1, -1, and 0 respectively. Element: facet. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration: quantity parea energy method pos_area_hess global <-------------------- post_project ----------------------------> Evolver toggle command. Introduces extra projections to volume and fixed quantity constraints each g iteration. If convergence fails after 10 iterations, you will get a warning message, repeated iterations will stop, and the internal variable iteration_counter will be negative. <-------------------- postscript ----------------------------> The Surface Evolver can generate PostScript files by either the postscript command or the P command option 3, or just "P 3". The image is the same one shown with the native screen graphics, so one should use the s command and graphics mode commands to get the image looking as desired. The variable brightness can be used to set the median gray level. The PostScript image is put into an 8 inch square at the lower left of the page. With the P command, you will be prompted for options. Show grid lines? : This is asked if you are graphing a 2D surface. If you reply 'y', all triangle edges will be plotted. If 'n', only special edges will be plotted (triple junctions, borders, etc.; this can be controlled with the show edges command). Default 'n'. The postscript command uses the ps_gridflag toggle to control this. Do colors? : If you reply 'y', edges and facets will be plotted with their color attributes and shading (if activated). If 'n', then all edges are plotted as black, and all facets as white with shading. Default 'n'. The postscript command uses the ps_colorflag toggle to control this. Do crossings? : This is asked if the surface is 1-dimensional (the string model) and the dimension of space is at least 3. If you reply 'y', a 3D effect will be created by plotting edges back to front, with each edge plotted first as a thick white line and then as a thin black line. This creates a broken back line and continuous foreground line at each crossing. Default 'n'. The postscript command uses the ps_crossingflag toggle to control this. Do labels? (i for ids, o for originals) : This PostScript P 3 command subprompt gives you a chance to put numeric labels on vertices, edges, and facets, which is useful for debugging or modifying a datafile. Edge labels are slightly displaced toward the head of the edge, and facet labels are signed according to which side of the facet is visible. Choose 'i' or 'y' for the current element id, or 'o' for the original element number. If you don't want any labels, just hit RETURN. The postscript command uses the ps_labelflag toggle to control this. The relative size of the labels can be controlled with the ps_labelsize variable, whose default value is 3.0. Enter file name (.ps will be added): : Give the name of the PostScript output file. A ".ps" extension will be added if ".ps" or ".eps" is missing. Not a good idea to just hit RETURN, since that will produce the file ".ps". The linewidth of PostScript edges may be controlled by the user. Widths are relative to the image size, which is 3 units square. If the real-valued edge extra attribute ps_linewidth is defined, that value is used as the edge width. Otherwise some internal read-write variables are consulted for various types of edges, in order: ps_stringwidth - edges in the string model, default 0.004 ps_bareedgewidth - "bare" edges, no adjacent facets, default 0.005 ps_fixededgewidth - "fixed" edges, default 0.004 ps_conedgewidth - edges on constraints or boundaries, default 0.004 ps_tripleedgewidth - edges with three or more adjacent facets, default 0.003 ps_gridedgewidth - other edges, default 0.002 The bounding box listed in the PostScript file is normally the actual extent of the surface in the window (i.e. the bounding box is never bigger than the window, but may be smaller). The full_bounding_box toggle will force the bounding box to be the full window. This is useful in controlling the image size while making a series of images of different views or evolution stages of a surface. <-------------------- postscript command ----------------------------> Main prompt command. Creates a PostScript file of the current surface in a file. Syntax: POSTSCRIPT stringexpr The string gives the name of the file; a .ps extension will be appended if it is missing. It is the same as the P option 3 command, except that there are no interactive responses needed. Output options are controlled by the ps_colorflag, ps_gridflag, ps_crossingflag, and labelflag toggles. full_bounding_box toggles. <-------------------- pow ----------------------------> pow(x,y) : Raise x to real power y; x may be negative if y is an integer. <-------------------- precedence ----------------------------> <-------------------- associativity ----------------------------> Here is the order of operator precedence, listed from high to low with equal precedence on same line, with associativity. Operator Associativity ^,** left associative unary - right associative *,/,%,IMOD,IDIV left associative +,- left associative on_boundary on_constraint hit_constraint on_quantity on_method_instance nonassociative ==,>,<,>=,<=,!= right associative NOT, ! right associative AND, && left associative OR, || left associative ? : left associative = left associative <-------------------- pressure ----------------------------> Usually means an attribute of fixed-volume bodies or fixed named quantities that is actually the Lagrange multiplier for the constraint. As body attribute in the datafile, establishes fixed pressure for the body. Also used rarely in the top section of the datafile to establish the ideal gas model. <-------------------- pressure energy ----------------------------> Each body with a prescribed pressure P contributes energy E = PV. where V is the actual volume of the body. This can be used to generate surfaces of prescribed mean curvature, since mean curvature is proportional to pressure. Pressure can be prescribed in the bodies section of the datafile, and can be changed with the b command, or by assigning a value to the pressure attribute of a body. <-------------------- print ----------------------------> Main prompt command. For printing expression values, strings, commands, arrays, or accumulated warning messages. Syntax: PRINT expr PRINT stringexpr PRINT commandname PRINT arrayslice PRINT WARNING_MESSAGES The arrayslice option takes an array name or a partially indexed array name. If more than one element results, the slice is printed in nested curly braces. The arrayslice can also be that of an array attribute of an element. The warning_messages option is handy for reviewing warning messages that occur early in the loading of a datafile but scroll off the screen too rapidly to see. PRINT expr can also be used inside an expression, where it prints the expression and evaluates to the value of its expression. Examples: print datafilename; print max(edge,length); print max(vertex, print (x^2+y^2+z^2) ); gg := {list vertex where id < 10; g 5}; print gg; define parts real[3][2][3]; print parts; print parts[3][2]; <-------------------- profiling ----------------------------> <-------------------- print profiling ----------------------------> <-------------------- reset_profiling ----------------------------> Expression Evaluation Profiling On systems where Evolver has cpu_counter available and Evolver has been compiled with the manifest constant PROF_EVALS defined, the expression evaluator inside Evolver keeps track of the clock cycles elapsed during each expression evaluation. These expressions include procedures, functions, constraint and boundary formulas, content integrands, energy integrands, quantity integrands, etc; everything that prints out as code in a dump file. The "print profiling" command will print the accumulated CPU cycles so far for each type of expression. The times are inclusive of any child functions or procedures. An example, from mound.fe after running "gogo": Enter command: print profiling Inclusive profiling counts: Name CPU Cycles re 1,952,792 gogo 125,201,889 gogo2 0 gogo3 0 gogo4 0 gogo5 0 Constraint expressions Constraint Formula Cycles Energy Cycles Content Cycles 1 877,047 2,337,727 0 Note that hard-coded evaluations of area, volume, etc. do not show up here, except for their effect on overall elapsed time. The command reset_profiling will set all the cycle values back to 0. <-------------------- printf ----------------------------> Main prompt command. For printing formatted output. Syntax: PRINTF string,expr,expr,... Prints to standard output using the standard C sprintf function. All string, integer, and floating point formats are valid. Integer formats force floating point arguments to be converted to integer. The format string can be a string variable or a quoted string. There is a limit of 1000 characters on the format string, otherwise there is no limit on the number of arguments. Example: printf "This is %s with total energy %f\n",datafilename,total_energy <-------------------- procedure definition ----------------------------> Users may define their own procedures with arguments with the syntax procedure identifier ( type arg1, type arg2, ... ) { commands } Right now the implemented types for arguments are real and integer. The argument list can be empty. Example: procedure proc1 ( real ht, real wd ) { prod := ht*wd; // this would make prod a global variable return; } Note that the procedure arguments act as local variables, i.e. their scope is the procedure body, and they have stack storage so procedures may be recursive. Procedure prototypes may be used to declare procedures before their bodies are defined with the same syntax, just replacing the body of the procedure with a semicolon. Prototype syntax: procedure identifier ( type arg1, type arg2, ... ); Note that a procedure is used as a command, and a function is used in a numerical expression. <-------------------- proj_knot_energy ----------------------------> Named method. Description: This energy is due to Gregory Buck. It tries to eliminate the need for a normalization term by projecting the energy to the normal to the curve. Its form is E_{e_1e_2} = {L_1L_2 \cos^p\theta\over |x_1 - x_2|^p} where x_1,x_2 are the midpoints of the edges and \theta is the angle between the normal plane of edge e_1 and the vector x_1 - x_2. The default power is 2. Power law of potential is adjustable via the global parameter `knot_power'. Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: parameter knot_power 2 // the default quantity knotten energy method proj_knot_energy global <-------------------- ps_bareedgewidth ----------------------------> Internal read-write variable for width of bare edges in PostScript output. The linewidth of PostScript edges may be controlled by the user. Widths are relative to the image size, which is 3 units square. If the real-valued edge extra attribute ps_linewidth is defined, that value is used as the edge width. Otherwise some internal read-write variables are consulted for various types of edges, in order: ps_stringwidth - edges in the string model, default 0.004 ps_bareedgewidth - "bare" edges, no adjacent facets, default 0.005 ps_fixededgewidth - "fixed" edges, default 0.004 ps_conedgewidth - edges on constraints or boundaries, default 0.004 ps_tripleedgewidth - edges with three or more adjacent facets, default 0.003 ps_gridedgewidth - other edges, default 0.002 <-------------------- ps_colorflag ----------------------------> <-------------------- pscolorflag ----------------------------> Evolver toggle command. When on, the postscript command will do color. <-------------------- ps_conedgewidth ----------------------------> Internal read-write variable for width of constraint edges in PostScript output. The linewidth of PostScript edges may be controlled by the user. Widths are relative to the image size, which is 3 units square. If the real-valued edge extra attribute ps_linewidth is defined, that value is used as the edge width. Otherwise some internal read-write variables are consulted for various types of edges, in order: ps_stringwidth - edges in the string model, default 0.004 ps_bareedgewidth - "bare" edges, no adjacent facets, default 0.005 ps_fixededgewidth - "fixed" edges, default 0.004 ps_conedgewidth - edges on constraints or boundaries, default 0.004 ps_tripleedgewidth - edges with three or more adjacent facets, default 0.003 ps_gridedgewidth - other edges, default 0.002 <-------------------- ps_fixededgewidth ----------------------------> Internal read-write variable for width of fixed edges in PostScript output. The linewidth of PostScript edges may be controlled by the user. Widths are relative to the image size, which is 3 units square. If the real-valued edge extra attribute ps_linewidth is defined, that value is used as the edge width. Otherwise some internal read-write variables are consulted for various types of edges, in order: ps_stringwidth - edges in the string model, default 0.004 ps_bareedgewidth - "bare" edges, no adjacent facets, default 0.005 ps_fixededgewidth - "fixed" edges, default 0.004 ps_conedgewidth - edges on constraints or boundaries, default 0.004 ps_tripleedgewidth - edges with three or more adjacent facets, default 0.003 ps_gridedgewidth - other edges, default 0.002 <-------------------- ps_gridedgewidth ----------------------------> Internal read-write variable for width of ordinary edges in PostScript output. The linewidth of PostScript edges may be controlled by the user. Widths are relative to the image size, which is 3 units square. If the real-valued edge extra attribute ps_linewidth is defined, that value is used as the edge width. Otherwise some internal read-write variables are consulted for various types of edges, in order: ps_stringwidth - edges in the string model, default 0.004 ps_bareedgewidth - "bare" edges, no adjacent facets, default 0.005 ps_fixededgewidth - "fixed" edges, default 0.004 ps_conedgewidth - edges on constraints or boundaries, default 0.004 ps_tripleedgewidth - edges with three or more adjacent facets, default 0.003 ps_gridedgewidth - other edges, default 0.002 <-------------------- ps_labelflag ----------------------------> Evolver toggle command. When on, the postscript command will print element labels in the postscript file. Synonym for labelflag. <-------------------- ps_labelsize ----------------------------> Internal read-write variable for the relative size of element labels in PostScript output. Default value is 3.0, for historical reasons. <-------------------- ps_linewidth ----------------------------> Edge extra attribute that if defined is used as the edge thickness in PostScript graphics. Value is relative to image size; values around 0.003 are reasonable. <-------------------- ps_stringwidth ----------------------------> Internal read-write variable for width of string model edges in PostScript output. Default value 0.004. <-------------------- ps_tripleedgewidth ----------------------------> Internal read-write variable for width of triple edges in PostScript output. The linewidth of PostScript edges may be controlled by the user. Widths are relative to the image size, which is 3 units square. If the real-valued edge extra attribute ps_linewidth is defined, that value is used as the edge width. Otherwise some internal read-write variables are consulted for various types of edges, in order: ps_stringwidth - edges in the string model, default 0.004 ps_bareedgewidth - "bare" edges, no adjacent facets, default 0.005 ps_fixededgewidth - "fixed" edges, default 0.004 ps_conedgewidth - edges on constraints or boundaries, default 0.004 ps_tripleedgewidth - edges with three or more adjacent facets, default 0.003 ps_gridedgewidth - other edges, default 0.002 <-------------------- Q ----------------------------> Single letter main command. Report current values of user-defined method instances and named quantities. If the show_all_quantities toggle is on, then internal quantities and method instances are also shown. This is particularly informative if convert_to_quantities has been done (same as -q command line option), since then internal values such as constraint integrals are in the form of method instances. <-------------------- q ----------------------------> Single letter main command. Exit program. You will be given a chance to have second thoughts. You may also load a new datafile. Automatically closes graphics if you really quit. Does not save anything. q ,x: Graphics mode command. Exit from graphics mode, and return to main command mode. <-------------------- quadratic ----------------------------> Main prompt command. Changes to quadratic model from linear or Lagrange models. <-------------------- quadratic declaration ----------------------------> To declare that the datafile lists a surface in the quadratic model, the top section of the datafile should contain the line QUADRATIC The only effect on datafile syntax is that the edge section may list edge midpoint vertices. <-------------------- quadratic model ----------------------------> By default, edges and facets are linear. But it is possible to represent edges as quadratic curves and facets as quadratic patches by using the quadratic model. Each edge is endowed with a midpoint vertex. Most features are implemented for the quadratic representation, but some named quantity methods are not available, such as those involving curvature. A special case is circular arc mode, which is effective in quadratic mode in the string model with the circular_arc_length method used for length_method_name. Then edges are calculated and drawn as exact circular arcs through their three vertices. The quadratic model can be invoked by putting the QUADRATIC keyword in the top section of the datafile or by using the quadratic or M 2 commands. <-------------------- quadratic_metric_mix ----------------------------> Internal read-write variable. Fraction of linear interpolation in Hessian metric in the quadratic model. Not very useful. <-------------------- quantities_only ----------------------------> Evolver toggle command. Inactivates all energies except named quantities. Meant for programmer's debugging use. <-------------------- quantity ----------------------------> There is a systematic scheme of calculating global quantities such as area, volume, and surface integrals that supplements and can replace the original ad hoc scheme in the Evolver. Briefly, a "method" is a built-in function for calculating a value for a particular type of geometric element, and a "named quantity" is a combination of instances of methods. See the ringblob datafile for an example. The original ad hoc calculations are still the default where they exist, but all new quantities are being added in the named quantity scheme. Some new features will work only with named quantities. To convert everything to named quantities, start Evolver with the -q option or use the convert_to_quantities command. This has not been made the default since named quantities can be slower than the originals. The sample datafiles qcube.fe, qmound.fe, and ringblob.fe contains some examples of named quantities and instances. The first two are quantity versions of cube.fe and mound.fe. These illustrate the most general and useful methods, namely facet_vector_integral, facet_scalar_integral, and edge_vector_integral, rather than the faster but more specialized methods such as facet_area. My advice is that the user stick to the old implicit methods for area, volume, and gravitational energy, and use named quantities only for specialized circumstances. <-------------------- quantity attribute ----------------------------> Geometric element read-only attribute. Named quantities and method instances can be applied to geometric elements either in the datafile (by adding the quantity or method name to the line defining an element) or with the "set" command. Nonglobal quantities or methods can be unset for individual elements. The values for individual elements can be accessed using attribute syntax. Examples: Suppose there is a named quantity "xmoment" that can be evaluated for facets. Then one could give commands foreach facet do printf "%g %f\n",id,xmoment list facet where xmoment > 4 set facet quantity xmoment where original == 1 unset facet quantity xmoment <-------------------- quantity Lagrange multiplier ----------------------------> <-------------------- quantity pressure ----------------------------> Each "fixed" quantity has a Lagrange multiplier associated to it. The Lagrange multiplier of a constraint is the rate of energy change with respect to the constraint target value. For a volume constraint, the Lagrange multiplier is just the pressure. Lagrange multipliers are calculated whenever an iteration step is done. They may be displayed with the v command in the "pressure" column, or as an expression "quantityname.pressure". <-------------------- quantity modulus ----------------------------> Each quantity has a "modulus", which is just a scalar multiplier for the sum of all instance values. A modulus of 0 will turn off calculation of all the instances. The modulus can be set in the datafile declaration, with the A command, or by assignment: quantityname.modulus := 1.2 <-------------------- quantity target ----------------------------> Each "fixed" quantity has a target value, to which the Evolver attempts to constraint the quantity value. Each time an iteration is done ( g command or the various Hessian commands), Newton's Method is used to project the surface to the constrained values. The target value can be displayed with the A or v commands, or as "quantityname.target". It can be changed with the A command or by assignment. Example: print qname.target qname.target := 3.12 <-------------------- quantity tolerance ----------------------------> A fixed quantity can have a tolerance attribute, which is used to judge convergence. A surface is deemed converged when the sum of all ratios of quantity discrepancies to tolerances is less than 1. This sum also includes bodies of fixed volume. If the tolerance is not set or is negative, the value of the variable target_tolerance is used, which has a default value of 0.0001. The sample datafile column.fe contains some examples of named quantities and instances. <-------------------- quantity value ----------------------------> The value of a namee quantity may be displayed with the A or v commands, or as an expression "quantityname.value". Furthermore, using the quantity name as an element attribute evaluates to the sum of all the applicable component instance values on that element. For example, supposing there is a quantity named vol, one could do print vol.value print facet[2].vol histogram(facet,vol) <-------------------- quantity volconst ----------------------------> A quantity can have a constant value added to it, similar to the body attribute volconst. This quantity attribute is also called volconst. It is useful for adding in known values of say integrals that are omitted from the actual calculation. It can be set in the quantity's datafile definition, or by an assignment command. <---------------------- quarter_turn ----------------------> <---------------------- quarter_turn_period --------------------> quarter_turn Symmetry group. 3D torus with quarter turn in identification of top and bottom. x and y periods taken to be 1. z period is the user-defined variable quarter_turn_period. Generators x,y,z. x and y as in regular torus mode. z is vertical translation with quarter turn: (x,y,z)->(-y,x,z). Relations: x z = z y^-1, y z = z x Numerical representation: as in torus, for powers of x,y,z with generators applied in that order. <-------------------- quiet ----------------------------> Evolver toggle command. Suppresses all normal output messages automatically generated by commands. Good while running scripts, or for loading datafiles with long read sections. Explicit output from print, printf, and list commands will still appear, as will prompts for user input. Applies to redirected output as well as console output. An error or user interrupting a command (i.e. with CTRL-C) will turn QUIET off, for sanity. <-------------------- quietgo ----------------------------> Evolver toggle command. Suppresses only g iteration step output. <-------------------- quietload ----------------------------> Evolver toggle command. Suppresses echoing of files being read in. This applies to the read section at the end of the datafile and any files read in with the read command. This toggle does not get reset at the start of a new datafile. This toggle can be set with the -Q command line option, to suppress echoing in the first datafile loaded. Default is OFF. <-------------------- quietload option ----------------------------> <-------------------- option -Q ----------------------------> Command line option -Q : Suppresses echoing of read section of datafile, and of files input with the read command; same as quietload toggle. <-------------------- quotient spaces ----------------------------> Symmetry groups and quotient spaces. As a generalization of the torus model, you may declare the domain to be the quotient space of R^n with respect to some symmetry group. Several built-in groups are available, and ambitious users can compile C code into Evolver to define group operations. Group elements are represented by integers attached to edges (like the wrap specifications in the torus model at runtime). You define the integer representation of the group elements. See the file quotient.c for an example. See khyp.c and khyp.fe for a more intricate example modelling an octagon in Klein hyperbolic space identified into a genus 2 surface. The datafile requires the keyword SYMMETRY_GROUP followed by the name for the group in quotes. Edges that wrap have their group element specified in the datafile by the phrase "wrap n", where n is the number of the group element. The wrap values are accessible at run time with the wrap attribute of edges. The group operations are accessible by the functions wrap_inverse(w) and wrap_compose( w1,w2). Using any Hessian commands with any symmetry group (other than the built-in torus model) will cause automatic converting to named quantities (with the " convert_to_quantities" command, since only named quantity Hessian evaluation routines have the proper symmetry transformation of the Hessian programmed in. Volumes of bodies might not be calculated correctly with a symmetry group. The volume calculation only knows about the built-in torus model. For other symmetry groups, if you declare a body, it will use the Euclidean volume calculation. It is up to you to design an alternate volume calculation using named quantities and methods. The currently implemented symmetry groups are: torus - The underlying group for the torus model. rotate - Cyclic group of rotations in the x-y plane. flip_rotate - Cyclic group of rotations in the x-y plane with z -> -z with every odd rotation. cubocta - Full point group of a cube. xyz - The orientation-preserving subgroup of cubocta. genus2 - For a 2 dimensional genus 2 hyperbolic quotient space. dodecahedron - For a 3D hyperbolic quotient space with dodecahedral fundamental region. central_symmetry - Inversion through the origin, X -> -X. screw_symmetry - Screw motion along z axis. <-------------------- r ----------------------------> Single letter main command. Refines the triangulation. Edges are divided in two, and facets are divided into four facets with inherited attributes. Reports the number of element structures and amount of memory used by those structures. Edges and facets with the no_refine attribute set are not refined. Graphics mode command. Rotate right. Rotates about vertical axis, default 6 degrees. Integer prefix indicates how many 6 degree rotations to do, and decimal prefix indicates angle of rotation. Examples: `15r' does 90 degree rotation, `15.0r' does 15 degree rotation. <-------------------- R ----------------------------> Graphics mode command. Reset viewing angles to original defaults and rescale the image to fit the viewing window. <-------------------- random ----------------------------> Internal read-only variable. Random number between 0 and 1 that is different every time the variable is evaluated. Assign a value to the random_seed variable to initialize the random number generator to a known state. <-------------------- random_seed ----------------------------> Internal read-write variable. Seed for random number generator, used when the "random" variable is evaluated, in jiggling, and in finding random initial vectors in various Hessian eigenvector algorithms. Defaults to 1 at start of datafile. Assigning a value to random_seed initializes the random number generator to a known state. <-------------------- raw_cells ----------------------------> Evolver toggle command. Sets torus model display for plain, unwrapped facets. Not an on-off toggle; 3-way toggle with "clipped" and "connected". <-------------------- raw_vertex_average ----------------------------> Main prompt command. Does vertex averaging on selected vertices without conserving volume on each side of surface, as vertex_average does. Will only average vertices with those of like type of constraints. Doesn't move vertices on boundaries. See "rawest_vertex_average" for averaging without any restrictions. Having the "verbose" toggle on will print messages. Syntax: RAW_VERTEX_AVERAGE generator Example: raw_vertex_average vertex where valence == 6 <-------------------- rawest_vertex_average ----------------------------> Main prompt command. Does vertex averaging on selected vertices without conserving volume on each side of surface, or attention to being on like constraints. Doesn't move vertices on boundaries. See "vertex_average" and "raw_vertex_average" for more restricted averaging. Having the "verbose" toggle on will print messages. Syntax: RAWEST_VERTEX_AVERAGE generator Example: rawest_vertex_average vertex[3] <-------------------- rawestv ----------------------------> Main prompt command. Does vertex averaging for all vertices without regard for conserving volume or whether averaged vertices have like constraints. But doesn't move vertices on boundaries. To do a selected group of vertices, use rawest_vertex_average. See "V" and "rawv" for more restricted averaging. <-------------------- rawv ----------------------------> Main prompt command. Does vertex averaging for all vertices without conserving volume on each side of surface. Will only average vertices with those of like type of constraints. Doesn't move vertices on boundaries. To do a selected group of vertices, use raw_vertex_average. See also "V" and "rawestv". <-------------------- raw_velocity ----------------------------> Internal vertex read-only attribute used when one-sided level-set constraints are present, so the Lagrange multipliers for said constraints can be calculated. This is the velocity before any projection to volume or level-set constraints. Not of interest to the ordinary user. <-------------------- read ----------------------------> Main prompt command. For reading commands from a file. Syntax: READ filename The filename can be either a quoted string or a string variable. The effect is as if the file were typed in at the keyboard. Hence main commands, responses to commands, and graphics mode commands can be included. Read commands may be nested. On the occurence of an error, input reverts to the original standard input. Echoing of input may be suppressed with the "quietload" toggle. Example: read "zebra.cmd" <-------------------- read section ----------------------------> The final section of the datafile may contain commands. These commands are read and executed immediately, just as if they had been entered at the command prompt. Encountering the keyword READ in the datafile causes the Evolver to switch from datafile mode to command mode and read the rest of the datafile as command input. This feature is useful for automatic initialization of the surface with refining, iteration, defining your own commands, etc. The READ section is optional. Example: bodies 1 1 2 3 4 5 6 volume 1 read // automatically do this when datafile is loaded refine edge where on_constraint 1 // typical evolution gogo := { g 5; r; g 10; r; g 20 } The "list bottominfo" command prints the READ section that would be printed in a dump file. <-------------------- real ----------------------------> Possible type for user-defined variables, arrays, functions, and extra attributes. Example declarations, which work both in the top of the datafile and at runtime: define george real define my_array real[3][4] define edge attribute charlie real define vertex attribute oldx real[3] define facet attribute knots real[5][5][5] define edge attribute bbb real function { self.bbb := self.x+self.y } <-------------------- reasonable scale factor ----------------------------> Trouble in evolving is usually signaled by a small scale, which means there is some obstacle to evolution. Of course, that means you have to know what a reasonable scale is, and that depends on the type of energy you are using and how refined your surface si. In normal evolution, the size of the scale is set by the development of small-scale roughness in the surface. Combined with a little dimensional analysis, that leads to the conclusion that the scale should vary as L^2-q, where L is the typical edge length and the units of energy are length^q. The dimensional analysis goes like this: Let D be the perturbation of one vertex away from an equilibrium surface. In general, energy is quadratic around an equibrium, so E = D^2L^q-2 So the gradient of energy at the vertex is grad E = 2 D L^q-2 The motion is the scale times the gradient, which we want proportional to D, so scale * grad E = scale * 2 D L^q-2 = D So scale is on the order of L ^2-q. Some examples of dimensional dependence of scale factor: Energy Energy dimension Scale Example file Area of soapfilm L^2 L^0 quad.fe Length of string L^1 L^1 flower.fe String squared curvature L^-1 L^3 elastic8.fe Surface squared mean curvature L^0 L^2 sqcube.fe In particular, the scale for area evolution is independent of refinement, but for most other energies the scale decreases with refinement. Another common influence on the scale for area evolution is the surface tension. Doing a liquid solder simulation in a system of units where the surface tension of facets is assigned a value 470, say, means that all calculated gradients are multiplied by 470, so the scale decreases by a factor of 470 to get the same geometric motion. Thus you should set scale_limit to be the inverse of the surface tension. <-------------------- rebody ----------------------------> Main prompt command. Recalculates connected bodies. Useful after a body has been disconnected by a neck pinching off. Facets of an old body are divided into edge-connected sets, and each set defines a new body (one of which gets the old body id). The new bodies inherit the attributes of the old body. If the original body volume was fixed, then the new bodies' target volumes become the new actual volumes. If the original body had a volconst, the new bodies will inherit the same value. This will likely lead to incorrect values, so you will have to adjust the volconsts by hand. In commands, you may specify the new bodies descended from an original body by using the 'original' atttribute. <-------------------- recalc ----------------------------> Main prompt command. Recalculates and redisplays everything. Useful after changing some variable or something and recalculation is not automatically done. Evolver tries to automatically recalculate when some significant change is made, but doesn't always know. Also see autorecalc. <-------------------- redefinition ----------------------------> Single letter redefinition: It is possible to reassign a single letter to your own command by the syntax letter :::= command but this should only be used in special circumstances, such as redefining 'r' to do additional actions along with refinement. The standard meaning can be restored with a null assignment: letter :::= Use single quotes around the letter to get the standard meaning, i.e. 'r' will do a standard refine when r has been redefined. Redefinitions are cleared when a new surface is loaded. Be careful when using redefined commands in defining other commands. Redefinition is effective on execution of the redefinition command, not on parsing. Redefinition is not retroactive to uses in previously defined commands, but restoring the standard meaning is retroactive. Examples: V :::= { fix vertex[5]; 'V'; unfix vertex[5] } r :::= { divisions := divisions * 2; hooke_length := hooke_length/2; 'r' } <-------------------- redirection ----------------------------> The output of a command can be redirected to a file with the unix-style append symbol '>>'. This appends output to the file; it does not erase any existing file. Syntax: command >> stringexpr The output of a command can be redirected to a file with the symbol '>>>'. This overwrites an existing file. Syntax: command >>> stringexpr Redirection with just `>' is not available due to the use of `>' as an comparison operator. The output of a command can be piped to a system command using the unix-style pipe symbol `|'. Syntax: command | stringexpr The stringexpr is interpreted as a system command. Examples: list facets | "more" list vertices | "tee vlist" ; g 10 | "tee g.out" { {g 10; u } 20 } >> "logfile" {foreach facet do print area} | "cat >areafile" <-------------------- refine ----------------------------> Main prompt command. For subdividing sets of edges or facets. Syntax: REFINE generator Subdivides the generated edges or facets. Subdivides edges by putting a vertex in the middle of each edge, and splitting neighboring facets in two in the soapfilm model. It is the same action as the long edge subdivide command (command l). Facets will be subdivided by putting a vertex in the center and creating edges out to the old vertices. It is strongly suggested that you follow this with equiangulation to nicen up the triangulation. Edge refinement is better than facet refinement as facet refinement can leave long edges even after equiangulation. This command does not respect the no_refine attribute. Example: refine edges where not fixed and length > .1 <-------------------- refine_count ----------------------------> Internal read-only variable. Number of elements refined in last refine command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- relaxed_elastic ----------------------------> <-------------------- relaxed_elastic1 ----------------------------> <-------------------- relaxed_elastic2 ----------------------------> relaxed_elastic Named method. Description: A variation of the linear_elastic method. Calculates the linear elastic strain energy energy for facets based on the Cauchy-Green strain matrix, with compression counting for zero energy, simulating, say, plastic film. The effect is to permit wrinkling. Let S be Gram matrix of unstrained facet (dots of sides). Let Q be the inverse of S. Let F be Gram matrix of strained facet. Let C = (FQ-I)/2, the Cauchy-Green strain tensor. Let v be Poisson ratio. Then energy density is (1/2/(1+v))(Tr(C^2) + v*(Tr C)^2/(1-(dim-1)*v)) Each facet has extra attribute poisson_ratio and each vertex has two extra coordinates, the coordinates of the unstrained surface in a plane. Hence the surface must be set up as five dimensional. The compression is detected by doing an eigenvalue analysis of the strain tensor, and discarding any negative eigenvalues. The eigenvalues may be separately accessed by the relaxed_elastic1 (lower eigenvalue) and relaxed_elastic2 (higher eigenvalue) methods, which are meant to be used in info_only mode. For a sample datafile, see mylarcube.fe. There can also be a real-valued facet extra attribute LEBweight, which can be used to give a per-facet weighting of the energy. For a version of relaxed elastic energy using only three ambient dimensions but an additional facet form_factor array facet attribute, see relaxed_elastic_A. Element: facet. Parameters: none. Models: linear. Ambient dimension: 5. Hessian: yes. Example datafile declaration: space_dimension 5 quantity lastic energy method relaxed_elastic global quantity lastic_lo info_only method relaxed_elastic1 global quantity lastic_hi info_only method relaxed_elastic2 global <-------------------- relaxed_elastic_A ----------------------------> <-------------------- relaxed_elastic1_A ----------------------------> <-------------------- relaxed_elastic2_A ----------------------------> Named method. Description: Calculates the linear elastic strain energy energy for facets based on the Cauchy-Green strain matrix, with compression counting for zero energy, simulating, say, plastic film. The effect is to permit wrinkling. Let S be the Gram matrix of unstrained facet (dots of sides). Let Q be the inverse of S. Let F be Gram matrix of strained facet. Let C = (FQ-I)/2, the Cauchy-Green strain tensor. Let v be Poisson ratio. Then the energy is (1/2/(1+v))(Tr(C^2) + v*(Tr C)^2/(1-(dim-1)*v)) Each facet has extra attribute poisson_ratio and extra attribute array form_factors[3] = {s11,s12,s22}, which are the entries in S. That is, s11 = dot(v2-v1,v2-v1), s12 = dot(v2-v1,v3-v1), and s22 = dot(v3-v1,v3-v1). If form_factor is not defined by the user, it will be created by Evolver, and the initial facet shape will be assumed to be unstrained. The compression is detected by doing an eigenvalue analysis of the strain tensor, and discarding any negative eigenvalues. Facets which are stressed in one or two dimensions can be separately counted by the relaxed_elastic1_A (one stress direction, and one wrinkle direction) and relaxed_elastic2_A (two stressed directions) methods, which are meant to be used in info_only mode. There can also be a real-valued facet extra attribute LEBweight, which can be used to give a per-facet weighting of the energy. For a sample datafile, see mylarcube.fe. For a version of relaxed elastic energy that uses two extra ambient dimensions instead of form_factors, see relaxed_elastic. For a version of this method that gives compression positive energy, see linear_elastic. Element: facet. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: yes. Example datafile declaration: quantity lastic energy method relaxed_elastic_A global <-------------------- renumber_all ----------------------------> Reassigns element id numbers of all types of elements in accordance with order in storage, i.e. as printed with the LIST commands. Besides renumbering after massive topology changes, this can be used with the reorder_storage command to number elements as you desire. Do NOT use this command inside an element generator loop! <-------------------- reorder_storage ----------------------------> Reorders the storage of element data structures, sorted by the extra attributes vertex_order_key, edge_order_key, facet_order_key, body_order_key, and facetedge_order_key. Originally written for testing dependence of execution speed on storage ordering, but could be useful for other purposes, particularly when renumber_all is used afterwards. Example: define vertex attribute vertex_order_key real define edge attribute edge_order_key real define facet attribute facet_order_key real define body attribute body_order_key real define facetedge attribute facetedge_order_key real reorder := { set vertex vertex_order_key x+y+z; set edge ee edge_order_key min(ee.vertex,vertex_order_key); set facetedge fe facetedge_order_key fe.edge[1].edge_order_key; set facet ff facet_order_key min(ff.vertex,vertex_order_key); set body bb body_order_key min(bb.facet,facet_order_key); reorder_storage; } <-------------------- repartition -------------------(MPI Evolver)---> repartition Main prompt command. In the MPI version of Evolver, this command re-distributes the elements of the surface according to the task numbers set in respective element attributes that are declared thus: define vertex attribute v_newpart integer[2] define edge attribute e_newpart integer[2] define facet attribute f_newpart integer[2] define facetedge attribute fe_newpart integer[2] define body attribute b_newpart integer[2] Probably a good idea just to include the above lines in any MPI Evolver datafile. The user sets the first component of each; the second is for internal use. For example, assuming you have Metis linked into your Evolver: mpi_equalize := { // set new partition numbers metis mpi_maxtask; parallel_exec "set vertex v_newpart[1] vpart+1"; parallel_exec "set edge e_newpart[1] epart+1"; parallel_exec "set facet f_newpart[1] fpart+1"; parallel_exec "set body b_newpart[1] 0"; parallel_exec "set facetedge fe fe_newpart[1] fe.facet[1].fpart"; repartition; } <-------------------- reset_counts ----------------------------> Main prompt command. Resets to 0 various internal counters. The counters are: > equi_count, > edge_delete_count, > facet_delete_count, > edge_refine_count, > facet_refine_count, > notch_count, > vertex_dissolve_count, > edge_dissolve_count, > facet_dissolve_count, > body_dissolve_count, > vertex_pop_count, > edge_pop_count, > facet_pop_count, > pop_tri_to_edge_count, > pop_edge_to_tri_count, > pop_quad_to_quad_count, > where_count, > edgeswap_count, > fix_count, > unfix_count, > t1_edgeswap_count, and > notch_count. Normally, a count is set to 0 at the start of a command that potentially affects it, accumulated during the execution of the command, and printed at the end of the command. To be precise, each counter has a "reported" bit associated with it, and if the "reported" bit is set when the appropriate command (such as 'u') is encountered, the counter will be reset to 0 and the "reported" bit cleared. The "reported" bit is set by either flush_counts or the end of a command. The idea is to have the counts from previous commands available to subsequent commands as long as possible, but still have the counter reflect recent activity. <-------------------- return ----------------------------> Command syntax for exiting the current command. This is essentially a return from a subroutine. If the current command is a user-defined command called by another command, the parent command continues. Example: if ( acc < 1.e-10 ) then return; <-------------------- reverse_orientation ----------------------------> Main prompt command. For reversing the orientation of sets of edges or facets. Syntax: REVERSE_ORIENTATION generator Reverses the internal orientation of selected edges or facets, as if they had been entered in the datafile with the opposite orientation. Useful, for example, when edges come in contact with a constraint and you want to get them all oriented in the same direction. Relative orientations of constraint and quantity integrals change to compensate, so energy, volumes, etc. should be the same after the command, but it would be wise to check in your application. Examples: reverse_orientation edge[7] reverse_orientation facets where backbody != 0 <-------------------- rgb_colors ----------------------------> Evolver toggle command. Toggles graphics to use user-specified red-green-blue components of color for elements rather than the "color" attribute indexing the pre-defined 16-color palette. The individual element rgb values are in element extra attributes: ergb for edges, frgb for facets, and fbrgb for facet backcolor. It is up to the user to define these attributes; if they are not defined, then they are not used and do not take up space. If frgb is defined but not fbrgb, then frgb is used for both front and back color. The attributes are type real of dimension 3 or 4; if 4 dimensional, the fourth component is passed to the graphics system as the alpha value, but probably won't have any effect. The value range is 0 to 1. Be sure to initialize the rgb attributes, or else you will get an all-black surface. The attribute definitions to use are: define edge attribute ergb real[3] define facet attribute frgb real[3] define facet attribute fbrgb real[3] <-------------------- ribiere ----------------------------> Evolver toggle command. Makes the conjugate gradient method use the Polak-Ribiere version instead of Fletcher-Reeves. (The toggle doesn't turn on conjugate gradient.) Polak-Ribiere seems to recover much better from stalling. Ribiere is the default mode. <-------------------- Riemannian metric ----------------------------> The ambient space can be endowed with a general Riemannian metric by putting the keyword METRIC in the datafile followed by the elements of the metric tensor. Only one coordinate patch is allowed, but the quotient space feature makes this quite flexible. Edges and facets are linear in coordinates, they are not geodesic. The metric is used solely to calculate lengths and areas. It is not used for volume. To get a volume constraint on a body, you will have to define your own named quantity constraint. See quadm.fe for an example of a metric. <-------------------- ringblob ----------------------------> Example: Ring around rotating rod This example consists of a ring of liquid forming a torus around a rod rotating along its long axis (z axis) in weightlessness. The liquid has controllable contact angle with the rod. The interesting question is the stability of the ring as the spin increases. The effect of the rotation is incorporated in the energy through an integral using the Divergence Theorem: /// E = - ||| (1/2) p r^2 w^2 dV ///B // = - || (1/2) p w^2 (x^2+y^2) z k . dA //bdry B where B is the region of the liquid, r is radius from the axis, p is the fluid density and w is the angular velocity. Note the energy is negative, because spin makes the liquid want to move outward. This has to be countered by surface tension forces holding the liquid on the rod. If p is negative, then one has a toroidal bubble in a rotating liquid, and high spin stabilizes the torus. The spin energy is put in the datafile using the named quantity syntax (see below). "centrip" is a user-chosen name for the quantity, "energy" declares that this quantity is part of the total energy, "global_method" says that the following method is to be applied to the whole surface, "facet_vector_integral" is the pre-defined name of the method that integrates vector fields over facets, and "vector_integrand" introduces the components of the vectorfield. The rod surface is defined to be constraint 1 with equation x^2 + y^2 = R^2, where R is the radius of the rod. The contact energy of the liquid with the rod is taken care of with an edge integral over the edges where the liquid surface meets the rod: // / / E = || -T cos(a) dA = -T cos(a) | z ds = T cos(a) | (z/R)(yi - xj).ds //S /bdry S / bdry S Here S is the rod surface not included as facets in bdry B, T is the surface tension of the free surface, and a is the internal contact angle. A more intuitive way to arrive at this integral is to think in cylindrical coordinates and imagine the replaced surface of the rod between the contact line and z = 0 to be divided into thin vertical strips of height z and width R dtheta. The energy of a strip is -T cos(a) z R dtheta. Converting back to Cartesian coordinates, dtheta = (x dy - y dx)/(x^2 + y^2), so // // E = -T cos(a) || z R (x dy - y dx)/R^2 = -T cos(a) || (z/R)(x dy - y dx) // // Constraint 2 is a horizontal symmetry plane. By assuming symmetry, we only have to do half the work. Constraint 3 is a one-sided constraint that keeps the liquid outside the rod. Merely having boundary edges on the rod with constraint 1 is not enough in case the contact angle is near 180 degrees and the liquid volume is large. Constraint 3 may be put on any vertices, edges, or faces likely to try to invade the rod. However, it should be noted that if you put constraint 3 on only some vertices and edges, equiangulation will be prevented between facets having different constraints. Constraint 4 is a device to keep the vertices on the rod surface evenly spaced. Edges on curved constraints often tend to become very uneven, since long edges short-cutting the curve can save energy. Hence the need for a way to keep the vertices evenly spread circumferentially, but free to move vertically. One way to do that is with another constraint with level sets being vertical planes through the z axis at evenly spaced angles. Constraint 4 uses the real modulus function with arctangent to create a periodic constraint. Each refinement, the parameters need to be halved to cut the period in half. This is done by redefining the "r" refinement command at the end of the datafile. Note that autorecalc is temporarily turned off to prevent projecting vertices to the constraint when it is in an invalid state. Also note the pi/6 offset to avoid the discontinuity in the modulus function. pi/6 was cleverly chosen so that all refinements would also avoid the discontinuity. One way of detecting stability is to perturb the torus and seeing if it evolves back to equilibrium. The datafile defines a command perturb := set vertex y y+.01 where not on_constraint 1 This sets the y coordinate of each vertex to y+.01. This command is defined in the "read" section at the end of the datafile, where you can put whatever commands you want to execute immediately after the datafile is loaded. To detect small perturbations, and get numerical values for the size of perturbations, the y moment of the liquid is calculated in the named quantity "ymoment". It is not part of the energy, as indicated by the info_only keyword. You can see the value with the `v' command. A better way to check stability is to examine the eigenvalues of the Hessian matrix. First, evolve normally to reasonably near an equilibrium. Then use the hessian command several times to converge exactly to the equilibrium. Then use the command "ritz(0,5)" to display the 5 eigenvalues of the Hessian nearest 0. By gradually raising the spin and tracking the lowest eigenvalue, one can detect the onset of instability, where the lowest eigenvalue becomes 0. Note that the datafile toggles on hessian_normal so that the Hessian only considers perturbations normal to the surface. The initial ringblob skeleton, with vertices and edges numbered. Taking advantage of symmetry, just the top half is represented. // ringblob.fe // Toroidal liquid ring on a rotating rod in weightlessness. // Half of full torus // Using second periodic constraint surface intersecting rod to // confine vertices on rod to vertical motion. // Important note to user: Use only the 'rr' command defined at // the end of this file to do refinement. This is due to the // nature of constraint 4 below. // This permits drawing both halves of the ring view_transforms 1 1 0 0 0 0 1 0 0 0 0 -1 0 0 0 0 1 // Basic parameters. These may be adjusted at runtime with the // 'A' command. Only spin is being adjusted in these experiments. parameter rodr = 1 // rod radius parameter spin = 0.0 // angular velocity parameter angle = 30 // internal contact angle with rod parameter tens = 1 // surface tension of free surface #define rode (-tens*cos(angle*pi/180)) // liquid-rod contact energy parameter dens = 1 // density of liquid, negative for bubble // spin centripetal energy quantity centrip energy global_method facet_vector_integral vector_integrand: q1: 0 q2: 0 q3: -0.5*dens*spin*spin*(x^2+y^2)*z // y moment, for detecting instability quantity ymoment info_only global_method facet_vector_integral vector_integrand: q1: 0 q2: 0 q3: y*z // Constraint for vertices and edges confined to rod surface, // with integral for blob area on rod constraint 1 formula: x^2 + y^2 = rodr^2 energy: e1: -rode*z*y/rodr e2: rode*z*x/rodr e3: 0 // Horizontal symmetry plane constraint 2 formula: z = 0 // Rod surface as one-sided constraint, to keep stuff from caving in // Can be added to vertices, edges, facets that try to cave in constraint 3 nonnegative formula: x^2 + y^2 = rodr^2 // Constraint to force vertices on rod to move only vertically. // Expressed in periodic form, so one constraint fits arbitrarily // many vertices. Note offset to pi/6 to avoid difficulties with // modulus discontinuity at 0. parameter pp = pi/2 /* to be halved each refinement */ parameter qq = pi/6 /* to be halved each refinement */ constraint 4 formula: (atan2(y,x)+pi/6) % pp = qq //initial dimensions #define ht 2 #define wd 3 vertices 1 0 -wd 0 constraints 2 // equatorial vertices 2 wd 0 0 constraints 2 3 0 wd 0 constraints 2 4 -wd 0 0 constraint 2 5 0 -wd ht // upper outer corners 6 wd 0 ht 7 0 wd ht 8 -wd 0 ht 9 0 -rodr ht constraints 1,4 // vertices on rod 10 rodr 0 ht constraints 1,4 11 0 rodr ht constraints 1,4 12 -rodr 0 ht constraints 1,4 edges 1 1 2 constraint 2 // equatorial edges 2 2 3 constraint 2 3 3 4 constraint 2 4 4 1 constraint 2 5 5 6 // upper outer edges 6 6 7 7 7 8 8 8 5 9 9 10 constraint 1,4 // edges on rod 10 10 11 constraint 1,4 11 11 12 constraint 1,4 12 12 9 constraint 1,4 13 1 5 // vertical outer edges 14 2 6 15 3 7 16 4 8 17 5 9 // cutting up top face 18 6 10 19 7 11 20 8 12 faces /* given by oriented edge loop */ 1 1 14 -5 -13 tension tens // side faces 2 2 15 -6 -14 tension tens // Remember you can't change facet tension 3 3 16 -7 -15 tension tens // dynamically just by changing tens; you have 4 4 13 -8 -16 tension tens // to do "tens := 2; set facet tension tens" 5 5 18 -9 -17 tension tens // top faces 6 6 19 -10 -18 tension tens 7 7 20 -11 -19 tension tens 8 8 17 -12 -20 tension tens bodies /* one body, defined by its oriented faces */ 1 1 2 3 4 5 6 7 8 volume 25.28 read // some initializations transforms off // just show fundamental region to start with // special refinement command redefinition r :::= { autorecalc off; pp := pp/2; qq := qq % pp; 'r'; autorecalc on; } // a slight perturbation, to check stability perturb := set vertex y y+.01 where not on_constraint 1 hessian_normal // to make Hessian well-behaved linear_metric // to normalize eigenvalues <-------------------- ritz ----------------------------> Main prompt command. For finding eigenvalues of the energy Hessian near a given value. Syntax: RITZ(expr,expr) Applies powers of inverse shifted Hessian to a random subspace to calculate eigenvalues near the shift value. First argument is the shift. Second argument is the dimension of the subspace. Prints out eigenvalues as they converge to machine accuracy. This may happen slowly, so you can interrupt it by hitting whatever your interrupt key is, such as CTRL-C, and the current values of the remaining eigenvalues will be printed out. Good for examining multiplicities of eigenvalues. It is legal to shift to an exact eigenvalue, but not wise, as they will not be printed. See the Hessian tutorial for more. The first eigenvalue is subsequently available in the last_eigenvalue internal variable. The full list of eigenvalues produced is subsequently available in the eigenvalues[] array. Example: To get the lowest 5 eigenvalues of a Hessian you know is positive definite: ritz(0,5) <-------------------- rotate ----------------------------> See "graphics commands". <-------------------- rotate symmetry group ----------------------------> This is the cyclic symmetry group of rotations in the x-y plane, where the order of the group is given by the internal variable rotation_order (default value 4). There is also an internal variable generator_power (default 1) such that the angle of rotation is 2*pi*generator_power/rotation_order. Note: Since this group has fixed points, some special precautions are necessary. Vertices on the rotation axis must be labelled with the attribute axial_point in the datafile. Edges out of an axial point must have the axial point at their tail, and must have zero wrap. Facets including an axial point must have the axial point at the tail of the first edge in the facet. Example datafile declaration: symmetry_group "rotate" parameter rotate_order = 6 Group element encoding: An element is encoded as the power of the group generator. <-------------------- rotation_order ----------------------------> Internal read-write variable. Order of rotation group for symmetry groups rotate and flip_rotate. <-------------------- runge_kutta ----------------------------> Evolver toggle command. Use Runge-Kutta method in a g iteration step (fixed scale factor only). <-------------------- s ----------------------------> Single letter main command. Shows the surface with screen graphics. Goes into the graphics command mode. Torus model surfaces have display options you will be asked for the first time. The graphics window may be closed with the close_show command. Graphics mode command. Shrink. Contracts image by factor, default 1.2. arrow keys : Graphics mode command. Move image in appropriate direction. May be prefixed by a real number, which is multiple of thirds of screen width to move. Default move is 1/12 screen width. May not work on all terminals. <-------------------- saddle ----------------------------> Main prompt command. Seek to minimum energy along the eigenvector of the lowest negative eigenvalue of the Hessian. If there is no negative eigenvalue, then the surface is unchanged. The alternate form SADDLE expr will limit the step size to expr. The motion vector is available afterwards through the move command. <-------------------- scalar_integrand ----------------------------> One of the possible types of supplementary information needed for named methods such as vertex_scalar_integral, edge_scalar_integral, facet_scalar_integral, edge_general_integral, and facet_general_integral. The syntax consists of appending a scalar_integrand definition to a quantity or method declaration. Example: quantity facet_weight energy method facet_scalar_integral scalar_integrand: G*rho*z <-------------------- scale ----------------------------> Internal read-write variable. Current scale factor for multiplying the vertex velocity to get the motion in the 'g' command. <-------------------- scale factor ----------------------------> In the 'g' command, once a direction of motion is found, the direction must be multiplied by a scale factor to compute the actual motion. If one interprets the direction of motion as a velocity (as in motion by mean curvature), then the scale factor becomes a time step. The scale factor may be fixed with the "m" command, or it may be in "optimizing" mode. The default is to start in optimizing mode. Set scale_limit to set an upper bound on the scale factor. <-------------------- scale_limit ----------------------------> To set an upper bound of valueon the gradient descent scale factor, include the line SCALE_LIMIT value in the top section of the datafile. The upper bound can be changed at runtime with the m command, or by setting the scale_limit variable. If surface tension is the main energy, the scale_limit should be set to the inverse of the surface tension. <-------------------- scale_scale ----------------------------> Internal read-write variable. Multiplier for the scale factor used in the 'g' command. Useful in optimizing scale mode if you only want to move a fraction of the optimum scale for better linearity, or force greater than optimum motion in hopes of accelerated convergence. Default value 1. <-------------------- screen graphics ----------------------------> The Surface Evolver has the ability to produce its own screen graphics directly. The Windows version has nice OpenGL/GLUT graphics, which should also be available on any Unix/Linux/Mac OSX system. The Mac OS 9 version has some simple graphics, and there is a primitive X-windows graphics module for Unix/Linux systems that for some reason can't do OpenGL. Those compiling unix versions must link in the appropriate graphics module. Screen graphics appear in their own window but can be controlled by typing graphics commands at the "graphics command: " prompt in the main window. OpenGL graphics can also be controlled by mouse and keyboard actions in the graphics window. Main prompt commands relevant to screen graphics: "s" or "show" for starting screen graphics and entering graphics command mode "close_show" for ending screen graphics "D" or "autodisplay" for toggling automatic redraw when the surface changes. Default is automatic redraw. "transform_expr" for showing multiple transforms of a surface. "show edges" and "show facets" for showing subsets of elements. The native screen graphics view is controlled by a view transformation matrix, which may be specified in the datafile, and which is dumped by the d or list topinfo commands. The view matrix may be changed with graphics mode commands. The view matrix elements may be read or set at runtime by view_matrix[i][j], where the indices start at 1. In particular, one can write command scripts to save and reload particular view matrices; see saveview.cmd in the distribution package. The view matrix does not affect geomview. The display consists entirely of facets and edges. Special edges (fixed edges, boundary edges, constraint edges, triple edges, bare edges) are always shown, unless you make their color CLEAR. The individual facet edges can be toggled with the graphics mode command `e'. <-------------------- screw_symmetry ----------------------------> <-------------------- screw_height ----------------------------> <-------------------- screw_angle ----------------------------> screw_symmetry This is the symmetry group of screw motions along the z axis. The global parameter screw_height is the translation distance (default 1), and the global parameter screw_angle is the rotation angle in degrees (default 0). Datafile declaration: parameter screw_height = 4.0 parameter screw_angle = 180.0 symmetry_group "screw_symmetry" Group element encoding: the integer value is the power of the group generator. <-------------------- scrollbuffersize ----------------------------> Internal read-write variable. The command window scroll buffer size on Windows machines. Meant for non-NT Windows that don't have menu properties option for this. <-------------------- self ----------------------------> Name of parent element in extra attribute functions. Example declaration: define edge attribute bbb real function { self.bbb := self.x+self.y } <-------------------- self_similar ----------------------------> Evolver toggle command. If squared mean curvature energy is being used, this scales the velocity toward a self-similar motion. Applies only when the old top-of-datafile "squared_curvature" declaration is used, or the sqcurve named method. The global read-write variable self_sim_coeff is used as a multiplier. <-------------------- self_sim_coeff ----------------------------> Global read-write variable used as magnitude coefficent when self_similar is toggled on. <-------------------- set ----------------------------> Main prompt command. For setting element attributes to values and other miscellaneous things. Syntax: SET elementtype [name] attrib expr1 where expr2 SET elementtype attrib expr1 where expr2 SET name.attrib expr SET quantityname.attrib expr SET instancename.attrib expr The first two forms set the value of the attribute attrib to the value expr1 for all elements of the given type that satisfy expr2. elementtype can be vertex, edge, facet, or body, or any element generator without a where clause. The optional name refers to the element under consideration, and can be used in expr1 and expr2 to refer to attributes of that element. Even without name, attributes of the element can be referred to if the references are not nested in element generators in expr1 or expr2. The next form can be used inside an element generator which defines "name". When name is not used, a '.' can be used, for those who like that sort of thing. SET can change the following attributes: constraint, coordinates, density, orientation, non-global named quantity or named method, user-defined extra attributes, body target volume, body volconst, fixed, frontbody, backbody, pressure, color, frontcolor, backcolor, boundary, and opacity (for the appropriate type elements). Fixed, named quantity, and named method attributes are just toggled on; they do not need the first expr. Setting the pressure on a body automatically unfixes its volume. For constraint, the expr is the constraint number. If using set to put a vertex on a parametric boundary, set the vertex's boundary parameters p1, p2, etc. first. Examples: set facets density 0.3 where original == 2 set vertices x 3*x where id < 5 // multiplies x coordinate by 3 set body target 5 where id == 1 // sets body 1 target volume to 5 set vertices constraint 1 where id == 4 set facet color clear where original < 5 foreach facet ff do set ff color red define vertex attribute weight real; set vertex weight 3 set vertex quantity my_quantity set vertex[1].facet color red Note the first form of syntax has the attribute and new value in the middle of an element generator. Syntactically inconsistent with other commands that use element generators, but more natural English. Actually, the syntactically consistent set facet where id does work. The last two forms set the value of a named quantity or named method instance attribute. For a named quantity, the settable attributes are target, modulus, volconst, and tolerance. For a named method instance, only modulus. There is no implicit reference to the quantity in the expression, so say set myquant target myquant.value rather than set myquant target value. Also see "unset". <-------------------- shading ----------------------------> Evolver toggle command. Toggles facet shading in certain graphics interfaces (xgraph, psgraph). Darkness of facet depends on angle of normal from vertical, simulating a light source above surface. Default is ON. <-------------------- shell ----------------------------> Main prompt command. Invokes a system subshell for the user on systems where this is possible. No arguments. See the "system" command for execution of an explicit shell command. <-------------------- show ----------------------------> Main prompt command. Used alone, it starts the native graphics display and changes to graphics command mode (use "showq" to start graphics without changing to graphics command mode). Also, which edges and facets are actually shown in graphics displays can be controlled by defining boolean expressions that edges or facets must satisfy in order to be passed to the graphics display. There are two expressions internally: one for edges and one for facets. They may be set with the syntax show edges where expr show facets where expr The default is to show all facets, and to show all special edges: fixed edges, constraint edges, boundary edges, and edges without exactly two adjacent facets. The defaults can be restored with "show facets" and "show edges". Some graphics modules (like geomview) can show edges of facets on their own initiative. This is separate from the edge show criterion here; to show the colors of edges, the edges must satisfy the criterion. Show causes graphics to be redrawn. If a graphics display is not active, show will start screen graphics. Show_expr is the same as show in setting the show expressions, except it does not start graphics. Show alone will just start screen graphics. Examples: show facets where color == red show edges where 1 show edges where color != black As an edge or facet attribute, "show" is a Boolean read-only attribute giving the current status of the edge or facet, for example, to report the number of edges being shown, do print sum(edge,show) <-------------------- show_all_quantities ----------------------------> Evolver toggle command. By default, only explicitly user-defined named quantities are shown by the Q or v commands. If show_all_quantities is on, then all internal quantities created by the command line option -q or by doing convert_to_quantities are also shown. <-------------------- show_expr ----------------------------> Main prompt command. This does the same as "show", except it does not start or redraw graphics; it just sets a show expression for edges or facets. Good for use in the "read" section of the datafile for controlling which elements will be displayed without automatically starting a display. Examples: show_expr facets where color == red show_expr edges where 1 show_expr edges where color != black <-------------------- show_inner ----------------------------> Evolver toggle command. Display interior facets, those on 2 bodies. <-------------------- show_off ----------------------------> Main prompt command. Closes the native graphics window started by the `s' or SHOW commands. Does not affect geomview version. <-------------------- show_outer ----------------------------> Evolver toggle command. Display outer facets, those on 0 or 1 body. <-------------------- show_trans ----------------------------> Main prompt command. Applies string of graphics commands to the image transformation matrix without doing any graphic display. The string must be in double quotes or be a string variable, and is the same format as is accepted by the regular graphics command prompt. Example: show_trans "rrdd5z" <-------------------- show_vol ----------------------------> Main prompt command. Synonym for 'v'. Shows target volume, actual volume, and pressure of each body. Also shows named quantities. Pressures are really the Lagrange multipliers. Pressures are computed before an iteration, so the reported values are essentially are one iteration behind. <-------------------- showq ----------------------------> Main prompt command. Displays screen graphics, but returns immediately to the main prompt and does not go into graphics command mode. <-------------------- shrink ----------------------------> Graphics mode command 's'. Shrink. Contracts image by factor, default 1.2. arrow keys : Graphics mode command. Move image in appropriate direction. May be prefixed by a real number, which is multiple of thirds of screen width to move. Default move is 1/12 screen width. May not work on all terminals. <-------------------- simon_knot_energy_normalizer ----------------------------> Named method. Description: Another normalization of edge_knot_energy, which I don't feel like deciphering right now. Element: edge. Parameters: none. Models: string linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: quantity kenergy energy method edge_knotenergy global method simon_knot_energy_normalizer global <-------------------- simplex declaration ----------------------------> To declare that the datafile lists a surface in the simplex model, the top section ot the datafile should contain the line SIMPLEX_REPRESENTATION The main effect on the datafile is that faces are defined by oriented vertex lists rather than edge lists. <-------------------- simplex model ----------------------------> The simplex model enables the representation of arbitrary dimension surfaces, but many Evolver features are not available with it. Here each facet is represented as an oriented list of k+1 vertices, where k is the dimension of the surface. Edges may be specified as k-1 dimensional simplices, but they are used only to compute constraint and named quantity integrals; a complete list of edges is not needed. Bodies are specified as lists of oriented facets. The datafile must have the keyword SIMPLEX_REPRESENTATION in the top section, and the phrase 'SURFACE_DIMENSION k' if k isn't 2. k = 1 and k = 2 are allowed, but not very useful in comparison to the string or soapfilm models. If the domain is not 3-dimensional, then 'SPACE_DIMENSION n' must also be included. The datafile edges section is optional. Each facet should list k+1 vertex numbers. Non-simplicial facets are not allowed. See the sample datafile simplex3.fe. Most features are not implemented. The quadratic model is not allowed, but the Lagrange model is. Vertices may be FIXED. Constraints are allowed, with energy integrands. Several basic named quantity methods work. No torus model or symmetry groups. No changing of surface topology or combinatorics is allowed except global refining with the r command. Refining subdivides each simplex 1-edge, with the edge midpoint inheriting the common attributes of the edge endpoints. Refining will increase the number of facets by a factor of 2^k. <-------------------- simplex_k_vector_integral ----------------------------> Named method. Description: Integral of a simple (n-k)-vector over an oriented k-dimensional simplicial facet in n-space. The vector integrand lists the components of each of the k vectors sequentially. Evaluation is done by forming a determinant whose first k rows are k vectors spanning the facet, and last (n-k) rows are vectors of the integrand. Element: facet. Parameters: k_vector_order, vector_integrand. Models: simplex. Ambient dimension: any. Hessian: yes. Orientable: yes. Example datafile declaration, for 3D surface in 5D: quantity kvec energy method simplex_k_vector_integral k_vector_order 3 vector_integrand: q1: 0 // first vector q2: 0 q3: 0 q4: 0 q5: x4 q6: 0 // second vector q7: 0 q8: 0 q9: x3 q10: 0 <-------------------- simplex_representation ----------------------------> Internal read-only variable. Whether the simplex model is in effect. <-------------------- simplex_to_fe ----------------------------> Simplex_to_fe Main prompt command. Converts a simplex model surface to a string or soapfilm model surface. Only works for dimension 1 or 2 surfaces, but works in any ambient dimension. <-------------------- simplex_vector_integral ----------------------------> Named method. Description: Integral of a vectorfield over a (n-1)-dimensional simplicial facet in n-space. Vectorfield is dotted with normal of facet; actually the side vectors of the simplex and the integrand vector are formed into a determinant. Element: facet. Parameters: vector_integrand. Models: simplex. Ambient dimension: any. Hessian: no. Orientable: yes. Example datafile declaration, for 4-volume under a 3D surface in 4D: quantity xvint energy method simplex_vector_integral vector_integrand: q1: 0 q2: 0 q3: 0 q4: x4 <-------------------- sin ----------------------------> sin(x),cos(x),tan(x): Trig functions, argument in radians. <-------------------- sin_knot_energy ----------------------------> Named method. Description: Another weird way to calculate a nonsingular energy between midpoints of pairs of edges. (by John Sullivan) Element: edge. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: quantity knotten energy method sin_knot_energy global <-------------------- single letter ----------------------------> Single letter main commands The oldest and most commonly used Surface Evolver commands are just single letters. Case is significant for these. Single letters are always interpreted as commands, so you may not use single letters for variable names. Single letter commands may be redefined. Single letter commands may be summarized in five groups: Reporting: C Run consistency checks. c Report count of elements. e Extrapolate. i Information on status. v Report volumes. v List extra attributes. z Do curvature test. Model characteristics: A Display and set variables and various parameters. a Toggle area normalization b Set body pressures. f Set diffusion constant. G Set gravity. J Toggle jiggling on every move. k Set boundary gap constant. M Toggle linear/quadratic model. m Toggle fixed motion scale. p Set ambient pressure. Q Report or set quantities. U Toggle conjugate gradient method. W Homothety toggle. Surface modification g Go one iteration step. Often followed by a repetition count. j Jiggle once. K Skinny triangle long edge divide. l Subdivide long edges. N Set target volumes to actual. n Notch ridges and valleys. O Pop non-minimal edges. o Pop non-minimal vertices. r Refine triangulation. t Remove tiny edges. u Equiangulate. V Vertex averaging. w Weed out small triangles. y Torus duplication. Z Zoom in on vertex. Output: D Toggle display every iteration. d Dump surface to datafile. P Graphics output (geomview, Postscript, etc.). s Screen display (native graphics). Miscellaneous: F Toggle command logging. H,h,? Help screen. q,x Exit. <-------------------- sinh ----------------------------> sinh(x),cosh(x),tanh(x): Hyperbolic functions. <-------------------- sizeof ----------------------------> Returns the number of entries in an array or array extra attribute. Can also be applied to a string or string variable to get the number of characters in the string. Syntax: SIZEOF(name) SIZEOF(string) where in the first form, name is the name of the array or extra attribute, not in quotes. Example: Enter command: define aa real[3][4] Enter command: print sizeof(aa) 12 Enter command: print sizeof(datafilename) 6 <-------------------- slice_view ----------------------------> <-------------------- slice_coeff ----------------------------> slice_view Main prompt command that toggles displaying a cross-section of the surface. The slice is defined by a plane of the form ax + by + cz = d. The coefficients a,b,c,d are stored in the array slice_coeff[] (which the user does not have to create). To use slice_view, first set the coefficients and then use the slice_view toggle. For example, to get a vertical slice parallel to the x and y axes and a little in front of them: slice_coeff[1] := 1; slice_coeff[2] := 0; slice_coeff[3] := 0; slice_coeff[4] ;= .2; slice_view; Evolver initializes slice_coeff[] with a vertical plane through the middle of the surface, so you can do just "slice_view". The cross-section will be in the form of line segments of the same color as the facets they are sections of. With OpenGL graphics, the slice plane can be varied interactively by hitting the 'l' key (lower case 'L') in the graphics window and dragging the mouse horizontally. The 'k' key will make mouse dragging change the orientation of the slice plane. Hit 'r' or 'c' or 't' to get back to another mouse mode. The 'L' key will end slice_view. Slice view works separately, and after, torus model viewing modes such as clipped and connected, so it is no problem to have them together. In case slice_view and clip_view are both in effect, slice_view operates instead of clip_view. <-------------------- smooth_graph ----------------------------> Evolver toggle command. In string quadratic and Lagrange model, causes edges to be plotted with many subdivisions for smooth look. In soapfilm Lagrange model, causes edges and facets to be plotted with 8-fold subdivision rather than Lagrange order subdivision. Is not implemented in quadratic soapfilm model. Default off. <-------------------- soapfilm ----------------------------> The default dimension of the surface is 2. If not, it must be declared in the top section of the datafile. For a 1-dimensional surface (the string model), simply include the line STRING The default dimension 2 soapfilm model is equivalent to using SOAPFILM In general, the line SURFACE_DIMENSION n defines the surface to have dimension n. Dimension over 2 is valid only in the simplex model. The surface dimension may be accessed at runtime through the read-only variable surface_dimension. <-------------------- soapfilm model ----------------------------> The term "soapfilm model" means that the dimension of the surface is 2. This is the default model. The surface is subdivided into triangular facets, and the default energy is surface area. Edges are defined by their vertices. Facets are defined by an oriented list of three edges, which must form a closed loop. However, faces in the datafile may have more than three edges, since they are automatically refined into facets when loaded. In official Evolver-speak, a "face" is what appears in the datafile, and a "facet" is the triangle in the internal Evolver representation of the surface. Bodies are defined by a set of oriented facets, which need not form the complete boundary of the body, for example if part of the boundary is on a constraint. Internally, the surface is held together by a set of structures called "facet-edges". There is one such structure for each incidence of an edge on a facet. There is a doubly linked list of facet-edges around each facet, so edges can be traversed in order, and there is a doubly-linked list around each edge, so the facets around an edge can be traversed in geometric order. Evolver figures out the geometric order from the geometric data in the datafile. If geometric order does not make sense, as when the space dimension is 4 or more, then the order is random. <-------------------- sobolev ----------------------------> <-------------------- sobolev_seek ----------------------------> Main prompt command. Uses a positive definite approximation to the area Hessian to do one Newton iteration, following a scheme due to Renka and Neuberger [RN]. Works only on area with fixed boundary; no volume constraints or anything else. Seems to converge very slowly near minimum, so not a substitute for other iteration methods. But if you have just a simple soap film far, far from the minimum, then this method can make a big first step. SOBOLEV_SEEK will do an energy-minimizing search in the direction. <-------------------- sobolev_area ----------------------------> Named method. Description: Same as the facet_tension method, but the Hessian is modified to be guaranteed positive definite, after the scheme of Renka and Neuberger. [RN]. Hence the hessian command always works, but final convergence may be slow (no faster than regular iteration) since it is only an approximate Hessian. Also see the sobolev command. Element: facet. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration: quantity sobarea energy method sobolev_area global <-------------------- sobolev_mode ----------------------------> Evolver toggle command. When the facet_area method is being used to calculate areas in hessian commands, this toggles using an approximate facet_area hessian that is positive definite. This permits hessian iteration to make big steps in a far-from-minimal surface without fear of blowing up. However, since it is only an approximate hessian, final convergence to the minimum can be slow. Linear model only. Does convert_to_quantities implicitly. Another variant of this is triggered by dirichlet_mode. <-------------------- space dimension ----------------------------> By default, surfaces live in 3 dimensional space. However, the phrase "SPACE_DIMENSION n" in the datafile header sets the dimension to n. This means that all coordinates and vectors have n components. The only restriction is that Evolver has to be compiled with the MAXCOORD macro defined to be at least n in Makefile or in model.h. The default MAXCOORD is 4. Change MAXCOORD and recompile if you want more than four dimensions. The actual space dimension can be accessed in commands through the read-only variable space_dimension. Graphics will display only the first three dimensions of spaces with more than three dimensions, except for geomview, which has a four-dimensional viewer built in (although its use is awkward now). <-------------------- space dimension declaration ----------------------------> The default dimension of space is 3. Otherwise it must be declared in the top section of the datafile, with syntax SPACE_DIMENSION n The dimension must be at most the value of MAXCOORD in model.h, which is 4 in the distributed version. The space dimension may be accessed at runtime through the read-only variable space_dimension. <-------------------- space_dimension ----------------------------> space_dimension : Internal read-only variable. Dimension of ambient space. <-------------------- sparse_constraints ----------------------------> Evolver toggle command. Toggles using sparse matrix techniques to accumulate and handle body and quantity gradients in iteration and hessian commands. Now the default. <-------------------- sphere_knot_energy ----------------------------> <-------------------- surface_cos_power ----------------------------> sphere_knot_energy Named method. Description: This is the 2D surface version of the circle energy. Its most general form is E_{f_1f_2} = { A_1A_2(1 - \cos\alpha)^p \over |x_1 - x_2|^q}, where A_1,A_2 are the facet areas, x_1,x_2 are the barycenters of the facets, and \alpha is the angle between f_1 and the sphere through x_1 tangent to f2 at x_2. The energy is conformally invariant for p = 1 and q = 4. For p=0 and q=1, one gets electrostatic energy for a uniform charge density. Note that facet self-energies are not included. For electrostatic energy, this is approximately 2.8A^{3/2} per facet. The powers p and q are Evolver variables surface_knot_power and surface_cos_power respectively. The defaults are p=1 and q=4. Element: facet. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: parameter surface_knot_power 1 // the default parameter surface_cos_power 4 // the default quantity knotten energy method sphere_knot_energy global <-------------------- spherical_arc_area_n ----------------------------> <-------------------- spherical_arc_area_s ----------------------------> Named method. Description: Area on a sphere between an edge (considered as a great circle arc) and the north (or south) pole. This is an exact calculation in the linear model. Meant for calculating the areas of facets in the string model with the string network confined to a sphere of arbitrary radius centered at the origin. There are two versions of this method, since calculation of facet areas by means of edges necessarily has a singularity somewhere on the sphere. Spherical_arc_area_n has its singularity at the south pole, and spherical_arc_area_s has its singularity at the north pole. Thus spherical_arc_area_s will work accurately for facets not including the north pole in there interiors; a facet including the north pole will have its area calculated as the negative complement of its true area, so a body defined using it could get the correct area by using a volconst of a whole sphere area. If the singular pole falls on an edge or vertex, then results are unpredictable. With these caveats, these methods are suitable for use with the area_method_name feature for substituting the default edge area method. If you do a facet as an explicit quantity, you are responsible for applying or unapplying the quantity after topology changes!! Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Orientable: yes. Hessian: yes. Example datafile declaration: parameter rad = 2 constraint 1 formula: x^2 + y^2 + z^2 = rad^2 area_method_name "spherical_arc_area_s" <-------------------- spherical_arc_area_s ----------------------------> Named method. Description: Area on a sphere between an edge (considered as a great circle arc) and the north (or south) pole. This is an exact calculation in the linear model. Meant for calculating the areas of facets in the string model with the string network confined to a sphere of arbitrary radius centered at the origin. There are two versions of this method, since calculation of facet areas by means of edges necessarily has a singularity somewhere on the sphere. Spherical_arc_area_n has its singularity at the south pole, and spherical_arc_area_s has its singularity at the north pole. Thus spherical_arc_area_s will work accurately for facets not including the north pole in there interiors; a facet including the north pole will have its area calculated as the negative complement of its true area, so a body defined using it could get the correct area by using a volconst of a whole sphere area. If the singular pole falls on an edge or vertex, then results are unpredictable. With these caveats, these methods are suitable for use with the area_method_name feature for substituting the default edge area method. If you do a facet as an explicit quantity, you are responsible for applying or unapplying the quantity after topology changes!! Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Orientable: yes. Hessian: yes. Example datafile declaration: parameter rad = 2 constraint 1 formula: x^2 + y^2 + z^2 = rad^2 area_method_name "spherical_arc_area_s" <-------------------- spherical_arc_length ----------------------------> Named method. Description: Edge length, modelling the edge as a spherical great circle arc between its two endpoints, which are assumed to lie on an arbitrary radius sphere centered at the origin. This method is meant for modelling string networks on spheres, and is suitable for use with the length_method_name feature for substituting the default edge length calculation method. Note that this method is an exact spherical calculation in the linear model, so there is no need to refine edges or use higher order models for accuracy. Edges are graphed as spherical arcs (actually, lots of segments). Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: yes. Example datafile declaration: parameter rad = 2 constraint 1 formula: x^2 + y^2 + z^2 = rad^2 length_method_name "spherical_arc_length" <-------------------- spherical_area ----------------------------> Named method. Description: Area of the facet projected to unit sphere. The vertices of the facet are assumed to be on the unit sphere. Element: facet. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: constraint 1 formula: x^2 + y^2 + z^2 = 1 quantity spharea energy method spherical_area global <-------------------- spinning ring example ----------------------------> Example: Ring around rotating rod This example consists of a ring of liquid forming a torus around a rod rotating along its long axis (z axis) in weightlessness. The liquid has controllable contact angle with the rod. The interesting question is the stability of the ring as the spin increases. The effect of the rotation is incorporated in the energy through an integral using the Divergence Theorem: /// E = - ||| (1/2) p r^2 w^2 dV ///B // = - || (1/2) p w^2 (x^2+y^2) z k . dA //bdry B where B is the region of the liquid, r is radius from the axis, p is the fluid density and w is the angular velocity. Note the energy is negative, because spin makes the liquid want to move outward. This has to be countered by surface tension forces holding the liquid on the rod. If p is negative, then one has a toroidal bubble in a rotating liquid, and high spin stabilizes the torus. The spin energy is put in the datafile using the named quantity syntax (see below). "centrip" is a user-chosen name for the quantity, "energy" declares that this quantity is part of the total energy, "global_method" says that the following method is to be applied to the whole surface, "facet_vector_integral" is the pre-defined name of the method that integrates vector fields over facets, and "vector_integrand" introduces the components of the vectorfield. The rod surface is defined to be constraint 1 with equation x^2 + y^2 = R^2, where R is the radius of the rod. The contact energy of the liquid with the rod is taken care of with an edge integral over the edges where the liquid surface meets the rod: // / / E = || -T cos(a) dA = -T cos(a) | z ds = T cos(a) | (z/R)(yi - xj).ds //S /bdry S / bdry S Here S is the rod surface not included as facets in bdry B, T is the surface tension of the free surface, and a is the internal contact angle. A more intuitive way to arrive at this integral is to think in cylindrical coordinates and imagine the replaced surface of the rod between the contact line and z = 0 to be divided into thin vertical strips of height z and width R dtheta. The energy of a strip is -T cos(a) z R dtheta. Converting back to Cartesian coordinates, dtheta = (x dy - y dx)/(x^2 + y^2), so // // E = -T cos(a) || z R (x dy - y dx)/R^2 = -T cos(a) || (z/R)(x dy - y dx) // // Constraint 2 is a horizontal symmetry plane. By assuming symmetry, we only have to do half the work. Constraint 3 is a one-sided constraint that keeps the liquid outside the rod. Merely having boundary edges on the rod with constraint 1 is not enough in case the contact angle is near 180 degrees and the liquid volume is large. Constraint 3 may be put on any vertices, edges, or faces likely to try to invade the rod. However, it should be noted that if you put constraint 3 on only some vertices and edges, equiangulation will be prevented between facets having different constraints. Constraint 4 is a device to keep the vertices on the rod surface evenly spaced. Edges on curved constraints often tend to become very uneven, since long edges short-cutting the curve can save energy. Hence the need for a way to keep the vertices evenly spread circumferentially, but free to move vertically. One way to do that is with another constraint with level sets being vertical planes through the z axis at evenly spaced angles. Constraint 4 uses the real modulus function with arctangent to create a periodic constraint. Each refinement, the parameters need to be halved to cut the period in half. This is done by redefining the "r" refinement command at the end of the datafile. Note that autorecalc is temporarily turned off to prevent projecting vertices to the constraint when it is in an invalid state. Also note the pi/6 offset to avoid the discontinuity in the modulus function. pi/6 was cleverly chosen so that all refinements would also avoid the discontinuity. One way of detecting stability is to perturb the torus and seeing if it evolves back to equilibrium. The datafile defines a command perturb := set vertex y y+.01 where not on_constraint 1 This sets the y coordinate of each vertex to y+.01. This command is defined in the "read" section at the end of the datafile, where you can put whatever commands you want to execute immediately after the datafile is loaded. To detect small perturbations, and get numerical values for the size of perturbations, the y moment of the liquid is calculated in the named quantity "ymoment". It is not part of the energy, as indicated by the info_only keyword. You can see the value with the `v' command. A better way to check stability is to examine the eigenvalues of the Hessian matrix. First, evolve normally to reasonably near an equilibrium. Then use the hessian command several times to converge exactly to the equilibrium. Then use the command "ritz(0,5)" to display the 5 eigenvalues of the Hessian nearest 0. By gradually raising the spin and tracking the lowest eigenvalue, one can detect the onset of instability, where the lowest eigenvalue becomes 0. Note that the datafile toggles on hessian_normal so that the Hessian only considers perturbations normal to the surface. The initial ringblob skeleton, with vertices and edges numbered. Taking advantage of symmetry, just the top half is represented. // ringblob.fe // Toroidal liquid ring on a rotating rod in weightlessness. // Half of full torus // Using second periodic constraint surface intersecting rod to // confine vertices on rod to vertical motion. // Important note to user: Use only the 'rr' command defined at // the end of this file to do refinement. This is due to the // nature of constraint 4 below. // This permits drawing both halves of the ring view_transforms 1 1 0 0 0 0 1 0 0 0 0 -1 0 0 0 0 1 // Basic parameters. These may be adjusted at runtime with the // 'A' command. Only spin is being adjusted in these experiments. parameter rodr = 1 // rod radius parameter spin = 0.0 // angular velocity parameter angle = 30 // internal contact angle with rod parameter tens = 1 // surface tension of free surface #define rode (-tens*cos(angle*pi/180)) // liquid-rod contact energy parameter dens = 1 // density of liquid, negative for bubble // spin centripetal energy quantity centrip energy global_method facet_vector_integral vector_integrand: q1: 0 q2: 0 q3: -0.5*dens*spin*spin*(x^2+y^2)*z // y moment, for detecting instability quantity ymoment info_only global_method facet_vector_integral vector_integrand: q1: 0 q2: 0 q3: y*z // Constraint for vertices and edges confined to rod surface, // with integral for blob area on rod constraint 1 formula: x^2 + y^2 = rodr^2 energy: e1: -rode*z*y/rodr e2: rode*z*x/rodr e3: 0 // Horizontal symmetry plane constraint 2 formula: z = 0 // Rod surface as one-sided constraint, to keep stuff from caving in // Can be added to vertices, edges, facets that try to cave in constraint 3 nonnegative formula: x^2 + y^2 = rodr^2 // Constraint to force vertices on rod to move only vertically. // Expressed in periodic form, so one constraint fits arbitrarily // many vertices. Note offset to pi/6 to avoid difficulties with // modulus discontinuity at 0. parameter pp = pi/2 /* to be halved each refinement */ parameter qq = pi/6 /* to be halved each refinement */ constraint 4 formula: (atan2(y,x)+pi/6) % pp = qq //initial dimensions #define ht 2 #define wd 3 vertices 1 0 -wd 0 constraints 2 // equatorial vertices 2 wd 0 0 constraints 2 3 0 wd 0 constraints 2 4 -wd 0 0 constraint 2 5 0 -wd ht // upper outer corners 6 wd 0 ht 7 0 wd ht 8 -wd 0 ht 9 0 -rodr ht constraints 1,4 // vertices on rod 10 rodr 0 ht constraints 1,4 11 0 rodr ht constraints 1,4 12 -rodr 0 ht constraints 1,4 edges 1 1 2 constraint 2 // equatorial edges 2 2 3 constraint 2 3 3 4 constraint 2 4 4 1 constraint 2 5 5 6 // upper outer edges 6 6 7 7 7 8 8 8 5 9 9 10 constraint 1,4 // edges on rod 10 10 11 constraint 1,4 11 11 12 constraint 1,4 12 12 9 constraint 1,4 13 1 5 // vertical outer edges 14 2 6 15 3 7 16 4 8 17 5 9 // cutting up top face 18 6 10 19 7 11 20 8 12 faces /* given by oriented edge loop */ 1 1 14 -5 -13 tension tens // side faces 2 2 15 -6 -14 tension tens // Remember you can't change facet tension 3 3 16 -7 -15 tension tens // dynamically just by changing tens; you have 4 4 13 -8 -16 tension tens // to do "tens := 2; set facet tension tens" 5 5 18 -9 -17 tension tens // top faces 6 6 19 -10 -18 tension tens 7 7 20 -11 -19 tension tens 8 8 17 -12 -20 tension tens bodies /* one body, defined by its oriented faces */ 1 1 2 3 4 5 6 7 8 volume 25.28 read // some initializations transforms off // just show fundamental region to start with // special refinement command redefinition r :::= { autorecalc off; pp := pp/2; qq := qq % pp; 'r'; autorecalc on; } // a slight perturbation, to check stability perturb := set vertex y y+.01 where not on_constraint 1 hessian_normal // to make Hessian well-behaved linear_metric // to normalize eigenvalues <-------------------- spring_constant ----------------------------> See "gap_constant". <-------------------- sprintf ----------------------------> Main prompt command. Prints to a string using the standard C sprintf function. May be used whereever a stringexpr is called for in syntax. Otherwise same as printf. Syntax: SPRINTF stringexpr,expr,expr,... Example: dumpname := SPRINTF "file%04g.dmp",counter <-------------------- sq_gauss_curvature ----------------------------> Named method. Description: Computes the integral of the squared Gaussian curvature. At each vertex, the Gaussian curvature is calculated as the angle defect divided by one third of the total area of the adjacent facets. This is then squared and weighted with one third of the area of the adjacent facets. This method works only on closed surfaces with no singularities due to the way it calculates the angle defect. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: quantity sqg energy method sq_gauss_curvature global <-------------------- sq_gaussian_curv_cyl ----------------------------> sq_gaussian_curv_cyl Named method. Description: Integral of the squared gaussian curvature of a surface of revolution. The generating curve is set up in the string model, and this method applied to its edges. The axis of rotation is the x-axis. Element: edge. Models: linear string. Ambient dimension: 2. Hessian: yes. Example datafile declaration: quantity sqgausscyl energy method sq_gaussian_curv_cyl global <-------------------- sq_mean_curv_cyl ----------------------------> sq_mean_curv_cyl Named method. Description: Integral of the squared mean curvature of a surface of revolution. The generating curve is set up in the string model, and this method applied to its edges. The axis of rotation is the x-axis. This method will do intrinsic curvature by means either of a global variable h_zero or a real-valued vertex attribute h_zero. Element: edge. Models: linear string. Ambient dimension: 2. Hessian: yes. Example datafile declaration: define vertex attribute h_zero real quantity sqcyl energy method sq_mean_curv_cyl global <-------------------- sq_mean_curv ----------------------------> <-------------------- sqcurve ----------------------------> Squared mean curvature Geometric element read-only attribute. SQCURVE is the squared mean curvature at a vertex. Valid only if squared mean curvature is part of the energy or in a quantity (but not the star versions of the squared mean curvature methods). <-------------------- sq_mean_curvature ----------------------------> Named method. Description: Integral of squared mean curvature of a surface. There are several methods implemented for calculating the integral of the squared mean curvature of a surface. The older methods sq_mean_curvature, eff_area_sq_mean_curvature, and normal_sq_mean_curvature, are now deprecated, since they don't have Hessians and the newer methods star_sq_mean_curvature, star_eff_area_sq_mean_curvature, star_normal_sq_mean_curvature, and my current favorite star_perp_sq_mean_curvature, do have Hessians and can now handle incomplete facet stars around vertices. But read the following for general remarks on squared curvature also. The integral of squared mean curvature in the soapfilm model is calculated for this method as follows: Each vertex v has a star of facets around it of area A. The force F due to surface tension on the vertex is the gradient of area, Since each facet has 3 vertices, the area associated with v is A/3. Hence the average mean curvature at v is h = (1/2)(F/(A/3)), where the 1/2 factor comes from the "mean" part of "mean curvature". This vertex's contribution to the total integral is then E = h^2A/3 = (3/4)F^2/A. Philosophical note: The squared mean curvature on a triangulated surface is technically infinite, so some kind of approximation scheme is needed. The alternative to locating curvature at vertices is to locate it on the edges, where it really is, and average it over the neighboring facets. But this has the problem that a least area triangulated surface would have nonzero squared curvature, whereas in the vertex formulation it would have zero squared curvature. Practical note: The above definition of squared mean curvature seems in practice to be subject to instablities. One is that sharp corners grow sharper rather than smoothing out. Another is that some facets want to get very large at the expense of their neighbors. Hence a couple of alternate definitions have been added. Curvature at boundary: If the edge of the surface is a free boundary on a constraint, then the above calculation gives the proper curvature under the assumption the surface is continued by reflection across the constraint. This permits symmetric surfaces to be represented by one fundamental region. If the edge of the surface is a fixed edge or on a 1-dimensional boundary, then there is no way to calculate the curvature on a boundary vertex from knowledge of neighboring facets. For example, the rings of facets around the bases of a catenoid and a spherical cap may be identical. Therefore curvature is calculated only at interior vertices, and when the surface integral is done, area along the boundary is assigned to the nearest interior vertex. However, including IGNORE_FIXED or IGNORE_CONSTRAINTS in the method declaration will force the calculation of energy even at fixed points or ignoring constraints respectively. If the parameter or vertex attribute h_zero is defined, then the value per vertex is the same as for the following method, eff_area_sq_mean_curvature. Element: vertex. Parameters: IGNORE_CONSTRAINTS, IGNORE_FIXED. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: quantity sqc energy method sq_mean_curvature global <-------------------- sqcurve_string ----------------------------> Named method. Description: Integral of squared curvature in string model. Assumes two edges per vertex, so don't use with triple points. Value zero at endpoint of curve. Calue is calculated as if the exterior angle at the vertex is evenly spread over the adjacent half-edges. More precisely, if s1 and s2 are the adjacent edge lengths and t is the exterior angle, value = 4*(1 - cos(t))/(s1+s2). Other powers of the curvature can be specified by using the parameter parameter_1 in the instance definition. If parameter_1 is not present, then the internal read-write variable curvature_power is used, which defaults to 2. Element: vertex. Parameters: parameter_1. Models: linear. Ambient dimension: any. Hessian: yes (but only for power 2). Example datafile declaration: quantity sq energy method sqcurve_string global parameter_1 3 <-------------------- sqcurve_string_marked ----------------------------> <-------------------- sqcurve_string_mark ----------------------------> sqcurve_string_marked Named method. Description: Integral of squared curvature in string model. Same as sqcurve_string, but only "marked" edges are used, so the topology of edges can be more complicated than a loop or curve. The marking is done by declaring an integer-valued edge attribute named sqcurve_string_mark and setting it to some nonzero value for those edges you want to be involved, usually two at each vertex to which this method is applied. Value zero at vertex with only one marked edge. Value is calculated as if the exterior angle at the vertex is evenly spread over the adjacent half-edges. More precisely, if s1 and s2 are the adjacent edge lengths and t is the exterior angle, value = 4*(1 - cos(t))/(s1+s2). Other powers of the curvature can be specified by using the parameter parameter_1 in the instance definition. Element: vertex. Parameters: parameter_1. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration: define edge attribute sqcurve_string_mark integer quantity sqmark energy method sqcurve_string_marked <-------------------- sqcurve2_string ----------------------------> sqcurve2_string Named method. Description: Integral of squared curvature in string model, but with an intrinsic curvature. The intrinsic curvature is specified by a global variable h_zero, or real-valued vertex attribute named h_zero. The value zero at endpoint of curve. The value is calculated as if the exterior angle at the vertex is evenly spread over the adjacent half-edges. More precisely, if s1 and s2 are the adjacent edge lengths, h0 is the intrinsic curvature, and t is the exterior angle, then value = (sin(t)/((s1+s2)/2)-h0)^2. T Element: vertex. Models: linear. Ambient dimension: 2 Hessian: no. Example datafile declaration: define vertex attribute h_zero real quantity sq2 energy method sqcurve2_string global <-------------------- sqcurve3_string ----------------------------> sqcurve3_string Named method. Description: Same as sqcurve_string, but uses a slightly different formula to encourage equal length edges The value zero at endpoint of curve. The value is calculated as if the exterior angle at the vertex is evenly spread over the adjacent half-edges. More precisely, if s1 and s2 are the adjacent edge lengths, h0 is the intrinsic curvature, and t is the exterior angle, value = 2*(1 - cos(t))*(1/s1+1/s2). Element: vertex. Models: linear. Ambient dimension: any Hessian: yes. Example datafile declaration: quantity sq3 energy method sqcurve3_string global <-------------------- sqgauss ----------------------------> <-------------------- square_gauss_modulue ----------------------------> <-------------------- squared_gaussian_curvature ----------------------------> squared_gaussian_curvature To add an energy of squared Gaussian curvature, include a line in the top of the datafile SQUARED_GAUSSIAN_CURVATURE modulus The modulus is a multiplier for the energy, and is available at runtime in the read-write variable square_gauss_modulus. Synonyms: square_gaussian_curvature, sqgauss <-------------------- sqr ----------------------------> sqr(x) : Square. <-------------------- sqrt ----------------------------> sqrt(x) : Square root. Argument must be nonnegative. <-------------------- square_curvature ----------------------------> To add an energy of squared mean curvature, include a line in the top of the datafile SQUARED_CURVATURE modulus The modulus is a multiplier for the energy. This is the original squared mean curvature energy; later versions are in the squared curvature named methods. <-------------------- square_gaussian_curvature ----------------------------> To add an energy of squared Gaussian curvature, include a line in the top of the datafile SQUARED_GAUSSIAN_CURVATURE modulus The modulus is a multiplier for the energy. Synonyms: square_gaussian_curvature, sqgauss <-------------------- squared_curvature ----------------------------> <-------------------- sq_curvature_modulus ----------------------------> To add an energy of squared mean curvature, include a line in the top of the datafile SQUARED_CURVATURE modulus The modulus is a multiplier for the energy, and is available as a read-write variable at runtime. This is the original squared mean curvature energy; later versions are in the squared curvature named methods. In the string model, the internal read-write variable curvature_power can be used to control the power of the curvature. <-------------------- squared_gradient ----------------------------> Evolver toggle command. In hessian_seek, use minimizing the square of the gradient of the energy as the objective rather than minimizing the energy. <-------------------- stability ----------------------------> The timestep of a 'g' iteration should not be so large as to amplify perturbations of the surface. Short wavelength perturbations are most prone to amplification. This section contains a sketch of the stability characteristics of the various mobility modes, enough to let the user relate the maximum timestep to the minimum facet or edge size. Two examples are discussed: a zigzag string and a nearly flat surface with equilateral triangulation. Effective area is not included, as it is an insignificant correction for nearly flat surfaces. The general moral of this section is that the maximum time step in iteration is limited by the length of the shortest edge or the area of the smallest facet, except in one case. <-------------------- stability_test ----------------------------> Command to find largest eigenvalue of mobility matrix. Don't really recall what this was for. <---------------- star_eff_area_sq_mean_curvature ----------------------------> Named method. Description: Integral of squared mean curvature over a surface. This is a different implementation of eff_area_sq_mean_curvature, and it has a Hessian. This method no longer requires a complete circle of vertices around a vertex; boundary edges are treated as if they are on mirror symmetry planes, which is usually true. The positive orientation of the surface is determined by the positive orientation of the first facet of the vertex's internal facet list. This method does not use the h_zero parameter. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration: quantity seffsq energy method star_eff_area_sq_mean_curvature global <------------------------ star_finagling ----------------------------> Toggle. In the soapfilm model, the delete command for edges or facets normally will not do the deletion if it would result in the creation of two edges with the same endpoints. Some simple configurations that cause this are detected and handled automatically, namely a "star" configuration in which there are three facets forming a triangle adjacent to the edge being deleted. Such a star is automatically removed by deleting one of its internal edges before deleting the original edge. But sometimes there are more complicated configurations that such unstarring won't handle, and then Evolver will not delete the edge unless the force_deletion toggle is on. An alternative is to first refine the edges that would have the common endpoints, and this is what the star_finagling toggle enables. Default off. <-------------------- star_gauss_curvature ----------------------------> Named method. Computes the angle deficit around vertices to which this method is applied. The angle deficit is 2*pi minus the sum of all the adjacent angles of facets. No compensation is made for vertices on the boundary of a surface; you just get big deficits there. Deficits are counted as positive, following the convention for gaussian curvature. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: quantity total_deficit energy method star_gauss_curvature global <------------------ star_normal_sq_mean_curvature ----------------------------> Named method. Description: Integral of squared mean curvature over a surface. This is a different implementation of normal_sq_mean_curvature which is more suitable for parallel calculation and has a Hessian. This method no longer requires a complete circle of vertices around a vertex; boundary edges are treated as if they are on mirror symmetry planes, which is usually true. The positive orientation of the surface is determined by the positive orientation of the first facet of the vertex's internal facet list. This method can use the h_zero parameter or vertex attribute for prescribed mean curvature. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration: quantity stnsq energy method star_normal_sq_mean_curvature global <-------------------- star_perp_sq_mean_curvature ----------------------------> Named method. Description: Integral of squared mean curvature over a surface. This is my current favorite implementation of squared mean curvature. It is an implementation specifically designed to agree with the mean curvature computed as the gradient of area when normal motion is on (either the normal_motion toggle for 'g' iteration, or Hessian with hessian_normal). Thus if you get zero squared mean curvature with this method, then switch to area energy, the hessian will report exact convergence. Likewise if you do prescribed curvature and then convert to area minimization with a volume constraint. This method has a Hessian. This method does not require a complete circle of vertices around a vertex; boundary edges are treated as if they are on mirror symmetry planes, which is usually true. This method can use the h_zero parameter or vertex attribute for prescribed mean curvature. The actual formula for the energy at a vertex is h = (1/2)(F·N/N·N) E = (h-h_0)^2 A/3 = (3/4)(F·N/N·N - 2h_0)^2A where F is the area gradient at the vertex, N is the volume gradient, and A is the area of the adjacent facets. If the vertex is on one or several constraints, the F and N are projected to the constraints, essentially making the constraints act as mirror symmetry planes. The positive orientation of the surface is determined by the positive orientation of the first facet of the vertex's internal facet list. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration: quantity stnsq energy method star_perp_sq_mean_curvature global <-------------------- star_sq_mean_curvature ----------------------------> Named method. Description: Integral of squared mean curvature over a surface. This is a different implementation of sq_mean_curvature, and it has a Hessian. This method no longer requires a complete circle of vertices around a vertex; boundary edges are treated as if they are on mirror symmetry planes, which is usually true. The positive orientation of the surface is determined by the positive orientation of the first facet of the vertex's internal facet list. This method does not do prescribed mean curvature with the h_zero parameter. Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: yes. Example datafile declaration: quantity starsq energy method star_sq_mean_curvature global <-------------------- stokes2d ----------------------------> <-------------------- stokes_type ----------------------------> <-------------------- stokes_velocity ----------------------------> stokes2d Named method. Description: Square of the Laplacian of z viewed as a function of (x,y). Meant for the calculation of two-dimensional Stokes flow of a fluid (i.e. slow steady-state flow where inertia is not significant) by having the Evolver surface be the graph of the velocity potential and minimizing the viscous dissipation, which is the square of the Laplacian of z. Boundary conditions are handled by declaring a vertex attribute "stokes_type" of type integer, and assigning each boundary vertex one of these values: 0 - vertex is not on a wall; treat as if on a mirror symmetry plane. 1 - vertex is on a slip wall. 2 = vertex is on a nonslip wall; normal derivative of potential is zero. Boundary values of z should be set to constants between 0 and 1 on various sections of boundary that represent walls. Element: vertex. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: yes. Example datafile declaration: quantity dissip energy method stokes2d global Note: Evolver creates a vertex attribute stokes_velocity for internal use. <-------------------- stokes2d_laplacian ----------------------------> Named method. Description: The Laplacian of z viewed as a function of (x,y). This is auxiliary to the stokes2d method. It is the same Laplacian, unsquared, with the same boundary rules. Meant for calculating pressures and such after stokes2d energy has been minimized. Element: vertex. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: yes. Example datafile declaration: quantity laplac info_only method stokes2d_laplacian global <-------------------- stress_integral ----------------------------> Named method. Description: Hmm. Looks like this one calculates integrals of components of a stress tensor. The scalar_integrand value is set as an integer standing for which component to do (a kludge). See the function stress_integral in method3.c for details. Does not have a gradient, so should be used for just info_only quantities. Element: facet. Parameters: scalar_integrand. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: quantity stressy info_only method stress_integral global scalar_integrand: 3 <-------------------- string ----------------------------> See "stringexpr" or "string model" for different uses of "string". <-------------------- string model ----------------------------> The term "string model" means that the surface is one-dimensional. The datafile must have the keyword "STRING" or "SURFACE_DIMENSION 1" in its top section. Edges are defined in terms of their vertices, and facets by a list of boundary edges. Facets are not divided into triangles, and may have any number of edges. The edges of a facet need not form a closed loop, for example if the facet is partly bounded by a constraint. A body is defined by associating one facet to it, and the volume of the body is the area of the facet. The default energy is edge length. <-------------------- string_curve_tolerance ----------------------------> string_curve_tolerance Internal read-write variable. In the quadratic model, the smoothness of graphing of curved quadratic edges can be controlled with the internal variable string_curve_tolerance, which is the desired angle in degrees between successive graphed segments making up the edge. <-------------------- string_gravity ----------------------------> Named method. Description: To calculate the gravitational potential energy of a body in the string model. Uses differences in body densities. Does not use gravitational constant G as modulus (unless invoked as internal quantity by convert_to_quantities). Element: edge. Parameters: none. Models: string linear, quadratic, lagrange. Ambient dimension: 2. Hessian: yes. Orientable: yes. Example datafile declaration: quantity cell_grav energy modulus 980*8.5 method string_gravity <-------------------- stringexpr ----------------------------> String expressions: A string expression evaluates to a string of characters. At present, the only ways to produce strings are: 1. double-quoted string literals, e.g. "this is a string". The following standard C escape sequences are recognized: \n newline \r carriage return \t tab \b backspace \q double-quote mark \c the character c elsewise In DOS, MS-Windows, or Windows NT paths, use / as the directory separator, since \ is an escape character. DOS and Windows have always accepted / as a directory separator. 2. successive double-quoted strings, which are concatenated into one string when read. 3. string variables, either internal like datafilename, or user-defined. 4. output from sprintf. Examples: print "Hello World!\n"; printf "This is a" " concatenated format string.\n"; print datafilename; dump sprintf"file%04d.dmp",dumpnumber; <-------------------- subcommand ----------------------------> subcommand Main prompt command. Invokes a subsidiary command interpreter. Useful if you want to pause in the middle of a script to give the user the chance to enter commands. A subcommand interpreter gives the prompt "Subcommand: " instead of "Enter command: ". Subcommands may be nested several deep, in which case the prompt will display the subcommand level. To exit a subcommand prompt, use q, quit, or exit. Note: The abort command will return to the prompt on the same subcommand level. <-------------------- suppress_warning ----------------------------> <-------------------- unsuppress_warning ----------------------------> Suppress_warning Datafile keyword instructing Evolver not to print a certain warning. Syntax: SUPPRESS_WARNING number where number is the number of the warning. Meant to suppress irritating warning messages that you know are irrelevant. Warnings can be restored with the syntax UNSUPPRESS_WARNING number <-------------------- surface_knot_power -----------------------> Internal read-write variable used in the facet_knot_energy and sphere_knot_energy named methods as the exponent of the denominator. <------------------- surface dimension declaration ----------------------------> The default dimension of the surface is 2. If not, it must be declared in the top section of the datafile. For a 1-dimensional surface (the string model), simply include the line STRING The default dimension 2 soapfilm model is equivalent to using SOAPFILM In general, the line SURFACE_DIMENSION n defines the surface to have dimension n. Dimension over 2 is valid only in the simplex model. The surface dimension may be accessed at runtime through the read-only variable surface_dimension. <-------------------- surface tension ----------------------------> Soap films and interfaces between different fluids have an energy content proportional to their area. Hence they shrink to minimize energy. The energy per unit area can also be regarded as a surface tension, or force per unit length. Each facet has a surface tension, which is 1 unless the datafile specifies otherwise (see TENSION attribute for faces). Different facets may have different surface tensions. Facet tensions may be changed interactively with the set facet tension ... command. The contribution to the total energy is the sum of all the facet areas times their respective surface tensions. The surface tension of a facet may also be specified as depending on the phases of the bodies it separates. In the string model, the tension resides on edges instead of facets. Example datafile: cube.fe <-------------------- surface_dimension ----------------------------> Internal read-only variable. Dimension of surface. <-------------------- surface_energy ----------------------------> An obsolete way of including vector surface energy integrals in the datafile. The present method is to use a named quantity with the facet_vector_integral method. <-------------------- SVK_elastic ----------------------------> <-------------------- SVK_alpha ----------------------------> <-------------------- SVK_lambda ----------------------------> <-------------------- SVK_mu ----------------------------> <-------------------- SVK_theta ----------------------------> SVK_elastic Named method. Description: SVK (Saint-Venant - Kirchhoff) potential. The facet energy is lambda/2*(tr(E))^2+mu*(E:E) - (3 lambda + 2 mu) * alpha*theta*tr(E) where E=(C-I)/2 is the Green-Lagrange Strain tensor, theta = T-T0 is the temperature deviation, and alpha is the thermal dilation coefficient. Needs real-valued facet attributes SVK_alpha, SVK_mu, SVK_lambda, and SVK_theta. Also needs the facet attribute form_factors, decribed in linear_elastic. Written by Dr. Rabah Bouzidi. Element: facet. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: yes. Example datafile declaration: define facet attribute SVK_alpha real define facet attribute SVK_lambda real define facet attribute SVK_mu real define facet attribute SVK_theta real define facet attribute form_factors real[3] quantity svk energy method SVK_elastic global <-------------------- swap_colors ----------------------------> A view transform attribute used to switch facet front and back colors, depending on the transformation. See "view_transforms" and "view_transform_generators". <-------------------- symmetric_content ----------------------------> The datafile keyword SYMMETRIC_CONTENT triggers the use of an alternate surface integral for calculating body volumes, namely the vectorfield (x,y,z)/3. It is useful if unmodelled sides of a body are radial from the origin, or if constraint content integrals (which is evaluated by an approximation) lead to asymmetric results on what should be a symmetric surface. <-------------------- symmetries ----------------------------> There are many interesting problems dealing with symmetric surfaces. A natural way to deal with a symmetric surface is to compute with only one fundamental domain of the symmetry, and use special boundary conditions. Some symmetries, such as mirror symmetries, can be handled with normal Evolver features. For example, a mirror can be implemented as a planar level set constraint. But symmetries such as translational or rotational symmetry require some built-in features. In any case, multiple copies of the fundamental domain may be displayed with the view_transforms command. No symmetry (default) Torus model (translational symmetry) Symmetry groups (general symmetry) <-------------------- symmetry declaration ----------------------------> To declare that the domain is the quotient space of a symmetry group, the top section of the datafile must contain a line of the form SYMMETRY_GROUP "name" "name" is a double-quoted name that is matched against the list of defined symmetry groups. See "symmetry groups". <-------------------- symmetry groups ----------------------------> As a generalization of the torus model, you may declare the domain to be the quotient space of R^n with respect to some symmetry group. Several built-in groups are available, and ambitious users can compile C code into Evolver to define group operations. Group elements are represented by integers attached to edges (like the wrap specifications in the torus model at runtime). You define the integer representation of the group elements. See the file quotient.c for an example. See khyp.c and khyp.fe for a more intricate example modelling an octagon in Klein hyperbolic space identified into a genus 2 surface. The datafile requires the keyword SYMMETRY_GROUP followed by the name for the group in quotes. Edges that wrap have their group element specified in the datafile by the phrase "wrap n", where n is the number of the group element. The wrap values are accessible at run time with the wrap attribute of edges. The group operations are accessible by the functions wrap_inverse(w) and wrap_compose( w1,w2). Using any Hessian commands with any symmetry group (other than the built-in torus model) will cause automatic converting to named quantities (with the " convert_to_quantities" command, since only named quantity Hessian evaluation routines have the proper symmetry transformation of the Hessian programmed in. Volumes of bodies might not be calculated correctly with a symmetry group. The volume calculation only knows about the built-in torus model. For other symmetry groups, if you declare a body, it will use the Euclidean volume calculation. It is up to you to design an alternate volume calculation using named quantities and methods. The currently implemented symmetry groups are: torus - The underlying group for the torus model. rotate - Cyclic group of rotations in the x-y plane. flip_rotate - Cyclic group of rotations in the x-y plane with z -> -z with every odd rotation. cubocta - Full point group of a cube. xyz - The orientation-preserving subgroup of cubocta. genus2 - For a 2 dimensional genus 2 hyperbolic quotient space. dodecahedron - For a 3D hyperbolic quotient space with dodecahedral fundamental region. central_symmetry - Inversion through the origin, X -> -X. screw_symmetry - Screw motion along z axis. <-------------------- symmetry_group ----------------------------> Internal read-only variable. Whether any symmetry group is active (Boolean). <-------------------- system ----------------------------> Main prompt command. For executing an external program. Syntax: SYSTEM stringexpr Invokes a subshell to execute the given command, on systems where this is possible. Command must be a quoted string or a string variable. Will wait for command to finish before resuming. <-------------------- t ----------------------------> Single letter main command. Eliminates tiny edges and their adjacent facets. You will be prompted for a cutoff edge length if you don't give a value with the command. If you enter h, you will get an edge length histogram. If you hit RETURN without a value, nothing will happen. Some edges may not be eliminable due to being FIXED or endpoints having different attrtibutes from the edge. Graphics mode command. Reset mode of displaying torus model. Choice of raw unit cell, clipped unit cell, or connected bodies. <-------------------- t1_edgeswap ----------------------------> Main prompt command. Does a T1 topological transition in the string model. When applied to an edge joining two triple points, it reconnects edges so that opposite faces originally adjacent are no longer adjacent, but two originally non-adjacent faces become adjacent. \_/ => \/ / \ | /\ It will silently skip edges it is applied to that don't fulfill the two triple endpoint criteria, or whose flipping is barred due to fixedness or constraint incompatibilities. The number of edges flipped can be accessed through the t1_edgeswap_count internal variable. Running with the verbose toggle on will print details of what it is doing. Syntax: T1_EDGESWAP edge_generator Examples: t1_edgeswap edge[23] t1_edgeswap edge where length < 0.1 <-------------------- t1_edgeswap_count ----------------------------> Internal read-only variable. Number of edges swapped by the t1_edgeswap command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- tag ----------------------------> An obsolete facet attribute, once used to associate an arbitrary value with a facet. Now superseded by extra attributes. <-------------------- tan ----------------------------> sin(x),cos(x),tan(x): Trig functions, argument in radians. <-------------------- tanh ----------------------------> sinh(x),cosh(x),tanh(x): Hyperbolic functions. <-------------------- target ----------------------------> Body read-write attribute. The target volume of a volume constraint. May be set in the datafile, by the b command, or the set command. A volume constraint may be removed by the unset, or with the b command. Command examples: set body[1] target 23 unset body target where id == 2 print body[2].target <-------------------- target_tolerance ----------------------------> Internal read-write variable. When volume constraints or named quantity constraints are enforced, Newton's method is repeated until the total deviation from target values is less than target_tolerance. Default value 1e-4. <-------------------- task_exec ---------------(MPI Evolver)----> task_exec Main prompt command for MPI Evolver. Causes a particular task to execute the string as a command. Syntax: task_exec n,string where n is the task number (between 1 and mpi_maxtask) and string is any string expression, such as a double-quoted string or a string variable. Any aggregate commands such as "sum" execute across all local and imported elements. <-------------------- temperature ----------------------------> Datafile top section keyword. Specifies amplitude for "j" or "jiggle" command. Sets the internal read-write variable "jiggle_temperature". <-------------------- tension ----------------------------> Edge read-write attribute. Energy per unit length of edge; line tension. Default 1 in string model, 0 in soapfilm model. May be set in the datafile by adding "tension expr" to the line defining an edge. The density is inherited by any facets generated by refining. "Tension" and "density" are synonyms. Examples: set edge tension 3 where original == 1 list edge where density < .4 Facet read-write attribute. Energy per unit area of facet; surface tension. Default 0 in string model, 1 in soapfilm model. May be set in the datafile by adding "tension value" to the line defining the facet. The density is inherited by any facets generated by refining. "Tension" and "density" are synonyms. Examples: set facet tension 3 where original == 1 list facet where density < .4 <-------------------- tetra_point ----------------------------> Vertex read-write attribute. For telling Evolver six films meet at this vertex. Used when effective_area is on to adjust motion of vertex by making the effective area around the vertex 1/sqrt(6) of actual. <-------------------- thicken ----------------------------> Evolver toggle command. Whether to display differently colored sides of a facet separated by thickness. Default on. This helps prevent weird striping due to limited resolution of depth buffers. <-------------------- thickness ----------------------------> Internal read-only variable. Thickness for thickened surfaces in graphics output in P command. Used when facet frontcolor and backcolor are different. Default value 0.001 times the maximum linear dimension of the surface. If you get backside color showing through, increase the thickness. The "thicken" toggle enables this, and is on by default. <-------------------- this_task ----------------(MPI Evolver)----> this_task MPI Evolver read-only variable. Task number of the task, from 0 (the master task) to mpi_maxtask. <-------------------- toggle commands ----------------------------> <-------------------- on ----------------------------> <-------------------- off ----------------------------> There are a large number of Surface Evolver features that can be turned on or off with simple "toggle" commands. The general syntax is "togglename ON" or "togglename OFF", with just "togglename" being a synonym for "togglename ON". The toggle names below have brief descriptions of their actions in the ON state. Toggles will usually print their previous state. A togglename used in an arithmetic expression is the current value of the toggle, 0 for OFF and 1 for ON. The current value of a toggle may be found by "print togglename". All toggles (except quietload) are reset when a surface is loaded. Default values are OFF unless otherwise noted. Full-word toggle commands are not to be confused with various single-letter toggle commands, which always change the state. All single-letter toggles have full word toggle equivalents. <-------------------- tolerance ----------------------------> As a keyword, "tolerance" refers to a fixed quantity attribute that is used as the criterion for convergence. Uses target_tolerance as the default value. <-------------------- topinfo ----------------------------> Top section of the datafile. "list topinfo" prints the first section of the datafile on the screen. This is everything before the vertices section. <-------------------- torus ----------------------------> Internal read-only variable. Whether the torus model is in effect. (Boolean). <-------------------- torus declaration ----------------------------> To declare periodic boundary conditions (i.e. make the domain a flat torus), include in the top section of the datafile the line TORUS All space dimensions will be periodic, with the period vectors given in the periods declaration. If the domain is completely filled by bodies with prescribed volumes, then the line TORUS_FILLED should be used instead to prevent degenerate volume constraints. Also see "periods". <-------------------- torus display ----------------------------> There are several commands for ways of displaying a torus surface: raw_cells - Graph the facets as they are, without clipping. The first vertex of a facet is used as the basepoint for any unwrapping of other vertices needed. connected - Each body's facets are unwrapped in the torus, so the body appears in one connected piece. Nicest option, but won't show facets not on bodies. clipped - Shows the unit cell specified in the datafile. Facets are clipped on the parallelpiped faces. <-------------------- torus model ----------------------------> The Evolver can take as its domain a flat torus with an arbitrary parallelpiped as its unit cell, i.e. the domain is a parallelpiped with its opposite faces identified. This is indicated by the TORUS keyword in the datafile. The defining basis vectors of the parallelpiped are given in the TORUS_PERIODS entry of the datafile. See twointor.fe for an example. Vertex coordinates are given as Euclidean coordinates within the unit cell, not as linear combinations of the period vectors. The coordinates need not lie within the parallelpiped, as the exact shape of the unit cell is somewhat arbitrary. The way the surface wraps around in the torus is given by saying how the edges cross the faces of the unit cell. In the datafile edges section, each edge has one symbol per dimension indicating how the edge vector crosses each identified pair of faces, and how the vector between the endpoints needs to be adjusted to get the true edge vector: * does not cross face + crosses in same direction as basis vector, so basis vector added to edge vector - crosses in opposite direction, so basis vector subtracted from edge vector. Wraps are automatically maintained by the various triangulation manipulation operations. There are several commands for ways of displaying a torus surface: raw_cells - Graph the facets as they are, without clipping. The first vertex of a facet is used as the basepoint for any unwrapping of other vertices needed. connected - Each body's facets are unwrapped in the torus, so the body appears in one connected piece. Nicest option, but won't show facets not on bodies. clipped - Shows the unit cell specified in the datafile. Facets are clipped on the parallelpiped faces. A few features are not available with the torus model, such as gravity and the simplex model. (Actually, you could put them in, but they will not take into account the torus model.) Volumes and volume constraints are available. However, if the torus is completely partitioned into bodies of prescribed volume, then the volumes must add up to the volume of the unit cell and the TORUS_FILLED keyword must be given in the datafile. Or just don't prescribe the volume of one body. Volumes are somewhat ambiguous. The volume calculation method is accurate only to one torus volume, so it is possible that a body whose volume is positive gets its volume calculated as negative. Evolver adjusts volumes after changes to be as continuous as possible with the previous volumes as possible, or with target volumes when available. You can also set a body's volconst attribute if you don't like the Evolver's actions. Level set constraints can be used in the torus model, but be cautious when using them as mirror symmetry planes with volumes. The torus volume algorithm does not cope well with such partial surfaces. If you must, then use y=const symmetry planes rather than x=const, and use the -q option or do convert_to_quantities. Double-check that your volumes are turning out correctly; use volconst if necessary. Display_periods:The displayed parallelogram unit cell can be different from the actual unit cell if you put an array called display_periods in the top of the datafile, in addition to the regular periods. For a string model example, parameter shear = 1 torus_filled periods 4 0 shear 4 display_periods 4 0 0 4 This will always display a square, no matter how much the actual unit cell is sheared. This feature works well for shears; it may not work nicely for other kinds of deformation. Display_periods works better for the string model than the soapfilm model. For the soapfilm model, it seems to do horizontal shears best, but it can't cope with large shears, so if your shear gets too large, I advise resetting your fundamental region to less shear, say with the unshear command in unshear.cmd. <-------------------- torus periods ----------------------------> If periodic boundary conditions are used (the torus model) , the period vectors of the fundamental unit cell parallelpiped may be defined in the top section of the datafile. Default is the unit cube. The syntax is the keyword PERIODS followed by expressions for the components of each period vector: PERIODS expr expr expr expr expr expr expr expr expr The size of this matrix depends on the space dimension. Variables may be used in the expressions, so the fundamental domain may be changed interactively by assigning new values to the variables. Be sure to give a recalc command whenever you change such a variable, in order to get the period matrix re-evaluated. <-------------------- torus symmetry group ----------------------------> This is the underlying symmetry for the torus model. Although the torus model has a number of special features built in to the Evolver, it can also be accessed through the general symmetry group interface. The torus group is the group on n-dimensional Euclidean space generated by n independent vectors, called the period vectors. The torus group uses the torus periods listed in the datafile. Datafile declaration: symmetry_group "torus" Group element encoding: The 32-bit code word is divided into 6-bit fields, one field for the wrap in each dimension, with low bits for the first dimension. Hence the maximum space dimension is 5. Within each bitfield, 1 codes for positive wrap and 011111 codes for negative wrap. The coding is actually a 2's complement 5-bit integer, so higher wraps could be represented. <-------------------- torus_filled ----------------------------> Whether Evolver should treat one "fixed" body volume as not fixed, for the purpose of avoiding redundant constraints in the case of a torus space being completely filled with bodies of fixed volume, for example a periodic foam. The top section of the datafile may contain a line TORUS_FILLED and there is also a "torus_filled" runtime toggle command. <-------------------- torus_periods ----------------------------> torus_periods[expr][expr] : Internal read-only variable. Current values of the period vectors in the torus model. Torus_periods[i][j] is component j of vector i. Uses 1 based indexes. For changing the torus_periods, define the periods in the datafile with variables, and alter the variables. Note that just "PERIODS" is used to declare the torus period matrix in the top of the datafile: PERIODS expr expr expr expr expr expr expr expr expr <-------------------- total_area ----------------------------> Internal read-only variable. Total area of the surface in soapfilm or simplex models. <-------------------- total_energy ----------------------------> Internal read-only variable. Total energy of the surface. <-------------------- total_length ----------------------------> Internal read-only variable. Total length of the edges in the string model. <-------------------- total_time ----------------------------> Internal read-only variable. Elapsed evolution time in the form of the sum of all the scale factors of 'g' steps. <-------------------- transform_count ----------------------------> Internal read-only variable. Number of image transforms active, as generated by the transform_expr command. <-------------------- transform_depth ----------------------------> Main prompt command. Quick way of generating all possible view transforms from view transform generators, to a given depth n. Syntax: TRANSFORM_DEPTH n where n is the maximum number of generators to multiply together. This will toggle immediate showing of transforms, if they are not already being shown. <-------------------- transform_expr ----------------------------> Main prompt command. If view transform generators were included in the datafile, then a set of view transforms may be generated by an expression with syntax much like a regular expression. An expression generates a set of transform matrices, and are compounded by the following rules. Here a lower-case letter stands for one of the generators, and an upper-case letter for an expression. a Generates set {I,a}. !a Generates set {a} AB Generates all ordered products of pairs from A and B. nA Generates all n-fold ordered products. A|B Generates union of sets A and B. (A) Grouping; generates same set as A. The precedence order is that nA is higher than AB which is higher than A|B. Note that the expression string must be enclosed in double quotes or be a string variable. The "!" character suppresses the identity matrix in the set of matrices generated so far. Examples: transform_expr "3(a|b|c)" //all products of 3 or fewer generators transform_expr "abcd" // generates 16 transforms transform_expr "!a!a!a!" // one copy shown, transformed three times All duplicate transforms are removed, so the growth of the sets does not get out of hand. Note the identity transform is always included. The letter denoting a single generator may be upper or lower case. The order of generators is the same as in the datafile. In the torus model, transforms along the three period vectors are always added to the end of the list of generators given in the datafile. If 26 generators are not enough for somebody, let me know. The current value of the expression may be accessed as a string variable, and the number of transformations generated can be accessed as transform_count. For example, print transform_expr print transform_count <-------------------- transforms ----------------------------> Evolver toggle command. Toggles graphing multiple images of the surface, according to the view transforms defined in the datafile, or according to the current transform expression applied to the view transform generators defined in the datafile. <-------------------- triple_point ----------------------------> Vertex read-write attribute. For telling Evolver three films meet at this vertex. Used when effective_area is on to adjust motion of vertex by making the effective area around the vertex 1/sqrt(3) of actual. <-------------------- true_average_crossings ----------------------------> Named method. Description: Calculates the average crossing number of an edge with respect to all other edges, averaged over all projections. Knot stuff. No gradient, so use just in info_only quantities. Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: quantity true_cross info_only method true_average_crossings global <-------------------- true_writhe ----------------------------> Named method. Description: For calculating the writhe of a link or knot. No gradient, so use just in info_only quantities. Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: quantity twrithe info_only method true_average_crossings global <-------------------- twist ----------------------------> Named method. Description: Another average crossing number calculation. No gradient, so use just in info_only quantities. Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: quantity twister info_only method twist global <-------------------- twointor ----------------------------> Example: Torus partitioned into two cells (Kelvin's foam) This example has a flat 3-torus (i.e. periodic boundary conditions) divided into two bodies. The unit cell is a unit cube, and the surface has the topology of Kelvin's partitioning of space into tetrakaidecahedra [TW], which was the least area partitioning of space into equal volumes known until recently [WP]. The datafile handles the wrapping of edges around the torus by specifying for each direction whether an edge wraps positively (+), negatively (-), or not at all (*). Note the use of the keyword TORUS_FILLED in the datafile. This informs Evolver that one of the volume constraints is redundant, preventing a singular matrix when the time comes to enforce volume constraints. One could use just TORUS and only put on one volume constraint. The display of a surface in a torus can be done several ways. The connected command (my favorite) makes each body show as a single unit. The clipped command shows the surface clipped to the fundamental parallelpiped. The raw_cells command shows the unedited surface. The Weaire-Phelan structure [WP]. is in the datafile phelanc.fe. It has area 0.3% less than Kelvin's. The initial two-cell Kelvin shape. Note that due to periodidity, a single vertex or edge may appear multiple times in the image. // twointor.fe // Two Kelvin tetrakaidecahedra in a torus. TORUS_FILLED // signals that domain is a torus and bodies fill it. periods 1.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 1.000000 vertices // values from another program 1 0.499733 0.015302 0.792314 2 0.270081 0.015548 0.500199 3 0.026251 0.264043 0.500458 4 0.755123 0.015258 0.499302 5 0.026509 0.499036 0.794636 6 0.500631 0.015486 0.293622 7 0.025918 0.750639 0.499952 8 0.499627 0.251759 0.087858 9 0.256701 0.499113 0.087842 10 0.026281 0.500286 0.292918 11 0.500693 0.765009 0.086526 12 0.770240 0.499837 0.087382 edges // with wraps in axis directions 1 1 2 * * * 2 2 3 * * * 3 1 4 * * * 4 3 5 * * * 5 2 6 * * * 6 2 7 * - * 7 1 8 * * + 8 4 6 * * * 9 5 9 * * + 10 3 10 * * * 11 3 4 - * * 12 6 8 * * * 13 6 11 * - * 14 7 4 - + * 15 8 12 * * * 16 9 8 * * * 17 9 11 * * * 18 10 7 * * * 19 11 1 * + - 20 12 5 + * - 21 5 7 * * * 22 11 12 * * * 23 10 12 - * * 24 9 10 * * * faces 1 1 2 4 9 16 -7 2 -2 5 12 -16 24 -10 3 -4 10 18 -21 4 7 15 20 -4 11 -3 5 -1 3 8 -5 6 6 14 -11 -2 7 5 13 -17 24 18 -6 8 -12 13 19 7 9 -16 17 22 -15 10 -10 11 8 12 15 -23 11 -21 9 17 19 1 6 12 -14 -18 23 -22 -13 -8 13 -24 -9 -20 -23 14 -19 22 20 21 14 -3 bodies 1 -1 -2 -3 -4 -5 9 7 11 -9 10 12 5 14 3 volume 0.500 2 2 -6 -7 8 -10 -12 -11 -13 1 13 -14 6 4 -8 volume 0.500 Doing some refining and iterating will show that the optimal shape is curved a bit. <-------------------- U ----------------------------> Single letter main command. This toggles conjugate gradient mode, which will usually give faster convergence to the minimum energy than the default gradient descent mode. The only difference is that motion is along the conjugate gradient direction. The scale factor should be in optimizing mode. The conjugate gradient history vector is reset after every surface modification, such as refinement or equiangulation. After large changes (say, to volume), run without conjugate gradient a few steps to restore sanity. <-------------------- u ----------------------------> Single letter main command. This command, called "equiangulation", tries to polish up the triangulation. In the soapfilm model, each edge that has two neighboring facets (and hence is the diagonal of a quadrilateral) is tested to see if switching the quadrilateral diagonal would make the triangles more equiangular. For a plane triangulation, a fully equiangulated triangulation is a Delaunay triangulation, but the test makes sense for skew quadrilaterals in 3-space also. It may be necessary to repeat the command several times to get complete equiangulation. The edgeswap command can force flipping of prescribed edges. In the simplex model, equiangulation works only for surface dimension 3. There, two types of move are available when a face of a tetrahedron violates the Delaunay void condition: replacing two tetrahedra with a common face by three, or the reverse operation of replacing three tetrahedra around a common edge by two, depending on how the condition is violated. This command is inoperative in the string model. Graphics mode command. Tip up. Rotates image about horizontal axis, default 6 degrees. Integer prefix indicates how many 6 degree rotations to do; decimal prefix indicates angle of tip in degrees. Example: `15u' does 90 degree rotation, `15.0u' does 15 degree rotation. <-------------------- unfix ----------------------------> Main prompt command. Removes the FIXED attribute from a set of elements, or change a parameter to an optimizing paramater, or change a named quantity to info_only mode. Syntax: UNFIX generator Example: unfix vertices where on_constraint 2 Can also convert a parameter from non-optimizing to optimizing. Example: unfix radius Can also convert a named quantity from fixed to info_only. Example: unfix quantityname <-------------------- unfix_count ----------------------------> Internal read-only variable. Number of elements unfixed by unfix command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- uniform_knot_energy ----------------------------> Named method. Description: A knot energy where vertex charge is proportional to neighboring edge length. This simulates an electrostatic charge uniformly distributed along a wire. Inverse power law of potential is adjustable via the global parameter `knot_power' (default 2). Element: vertex. Parameters: none. Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: parameter knot_power 2 // the default quantity knotten energy method uniform_knot_energy global <----------------- uniform_knot_energy_normalizer ----------------------------> Named method. Description: Supposed to approximate the part of uniform_knot_energy that is singular in the continuous limit. Element: vertex. Parameters: Models: linear. Ambient dimension: any. Hessian: no. Example datafile declaration: parameter knot_power 2 // the default quantity knottenorm energy method uniform_knot_energy global method uniform_knot_energy_normalizer global <-------------------- uniform_knot_normalizer1 ----------------------------> Named method. Description: Calculates internal knot energy to normalize singular divergence of integral of uniform_knot_energy. Actually a synonym for uniform_knot_energy_normalizer. No gradient. Element: vertex. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: parameter knot_power 2 // the default quantity knottenorm energy method uniform_knot_energy global method uniform_knot_energy_normalizer1 global <-------------------- uniform_knot_normalizer2 ----------------------------> Named method. Description: Calculates internal knot energy to normalize singular divergence of integral of uniform_knot_energy a different way from uniform_knot_energy_normalizer. Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: parameter knot_power 2 // the default quantity knottenorm energy method uniform_knot_energy global method uniform_knot_energy_normalizer2 global <-------------------- unit mobility ----------------------------> The default mobility, in which the vertex velocity is equal to the force. The physical interpretation of this is that there is a resistance to motion of each vertex through the medium proportional to its velocity, but not for the edges. This does not approximate motion by mean curvature, but it is very easy to calculate. <-------------------- unset ----------------------------> Main prompt command. Removes an attribute from a set of elements. Syntax: UNSET elements [name] attrib where clause Unsettable attributes are fixed (vertices, edges, or facets) , body target volume, body pressure, body gravitational density, non-global named quantities, non-global named methods, level-set constraints, parametric boundary. frontbody, or backbody. A use for the last is to use a boundary or constraint to define an initial curve or surface, refine to get a decent triangulation, then use "unset vertices boundary 1" and "unset edges boundary 1" to free the curve or surface to evolve. The form "unset facet bodies ..." is also available to disassociate given facets from their bodies. Examples: unset body[1] target unset vertices constraint 1; unset edges constraint 1 <-------------------- unstable surfaces ----------------------------> Detecting the onset of instability and evolving unstable surfaces. The Hessian features can be used to detect the onset of instability as some parameter changes, and even evolve unstable equilibrium surfaces. Instability detection is done by watching eigenvalues with the ritz command. As an example, consider a ring of liquid outside a cylinder, with the volume increasing until the symmetric ring becomes unstable. This is set up in the datafile catbody.fe, which is just cat.fe with a body defined from the facets. Run catbody.fe with this initial evolution: u g 5 r g 5 body[1].target := 2 g 5 r body[1].target := 3 g 5 hessian hessian linear_metric ritz(0,5) This gives eigenvalues Eigencounts: 0 1. 0.398411128930840 2. 0.398411128930842 3. 1.905446082321839 4. 1.905446082321843 5. 4.4342055632012 Note we are still in a stable, positive definite situation, but the lowest eigenvalues are near enough to zero that we need to take care in increasing the volume. Try an increment of 0.1: body[1].target += 0.1 g 5 hessian hessian ritz(0,5) Eigencounts: 0 1. 0.287925880010193 2. 0.287925880010195 3. 1.775425717998147 4. 1.775425717998151 5. 4.2705109310529 A little linear interpolation suggests try an increment of 0.3: body[1].target += 0.3 g 5 hessian hessian hessian ritz(0,5) Eigencounts: 0 1. 0.001364051154697 2. 0.0013640511547 3. 1.4344757227809 4. 1.4344757227809 5. 3.8350719808531 So we are now very very close to the critical volume. In view of the coarse triangulation here, it is probably not worth the trouble to narrow down the critical volume further, but rather refine and repeat the process. But for now keep this surface running for the next paragraph. Evolving into unstable territory Typically these are surfaces with just a few unstable modes. The idea is to get close to the desired equilibrium and use "hessian" to reach it. Regular 'g' gradient descent iteration should not be used. To change the surface, i.e. to follow an equilibrium curve through a phase diagram, make small changes to the control parameter and use a couple of hessian iterations to reconverge each time. Particular care is needed near bifurcation points, because of the several equilibrium curves that meet. When approaching a bifurcation point, try to jump over it so there is no ambiguity as to which curve you are following. The approach to a bifurcation point can be detected by watching eigenvalues. An eigenvalue crosses 0 when a surface introduces new modes of instability. Example: Catbody.fe, continued. With catbody.fe in the nearly-critical state found above, increase the body volume by steps of .1 and run hessian a couple of times each step: body[1].target += .1 hessian hessian hessian hessian body[1].target += .1 hessian hessian hessian hessian body[1].target += .1 hessian hessian hessian hessian So hessian alone is enough to evolve with, as long as you stay near enough to the equilibrium. Other methods for evolving unstable surfaces: Using symmetry. If the unstable surface is more symmetric than the stable surfaces, then enforcing symmetry can remove the unstable modes. For example, a surface of revolution could be retricted to just a 90 degree wedge between two perpendicular mirror planes (level-set constraints), with 90 degree contact angles on the planes. Using volume constraints. Recall that in general every constraint removes one degree of freedom in the configuration space. Hence a volume constraint has the potential to remove one unstable mode. For example, unstable catenoids can be made stable by adding a volume constraint and adjusting the volume until the pressure is 0. <-------------------- user functions ----------------------------> User-defined functions can be defined in C in userfunc.c. They are meant for situations where expression interpretation is too slow, or functions such as elliptic integrals are wanted. Currently, they are automatically functions of the coordinates. Do not give any arguments in the expression; for example "(usr1 + usr3)/usr10". <-------------------- user-defined mobility ----------------------------> The user may define a mobility tensor in the datafile. There is a scalar form with the keyword MOBILITY and a tensor form with MOBILITY_TENSOR. When in effect, this mobility is multiplied times the velocity to give a new velocity. This happens after any of the previous mobilities of this section have been applied and before projection to constraints. The formulas defining the mobility may include adjustable parameters, permitting the mobility to be adjusted during runtime. The formulas are evaluated at each vertex at each iteration, and so formulas may depend on vertex position and any vertex attributes. <-------------------- usr ----------------------------> usr1, usr2, etc. : user-defined functions; see "user functions". <-------------------- utest ----------------------------> Main prompt commnad. Runs a test to see if triangulation is Delaunay. Meant for higher dimensions and simplex model. <-------------------- v ----------------------------> Single letter main command. Shows target volume, actual volume, and pressure of each body. Also shows named quantities. Pressures are really the Lagrange multipliers. Pressures are computed before an iteration, so the reported values are essentially are one iteration behind. Synonym: show_vol Graphics mode command. Toggles showing of convex and concave edges in different colors. "v" stands for "valleys". <-------------------- V ----------------------------> Single letter main command. Vertex averaging. For each vertex, computes new position as area-weighted average of centroids of adjacent facets. Only adjacent facets with the same constraints and boundaries are used. Preserves volumes, at least to first order. See the rawv command for vertex averaging without volume preservation, and rawestv for ignoring likeness of constraints. Vertices on triple edges are averaged only with adjacent vertices on the triple edge, and then only if there are exactly two neighboring triple edge vertices. Fixed vertices do not move. Vertices on constraints are projected back onto their constraints. The computation of new vertex positions are all done before any vertex is moved. For sequential movement applied to a subset of vertices, see the vertex_average command. <-------------------- __v_constraint_list ----------------------------> Internal vertex read-only attribute. List of constraints a vertex is on. __v_constraint_list[1] is the number of constraints in the list, followed by the numbers of the constraints. Note that for named constraints, the internally assigned numbers are used. <-------------------- valence ----------------------------> An attribute of a vertex, which is the number of incident edges, or an attribute of an edge, which is the number of incident facets, or an attribute of a facet, which is the number of edges on the facet. Example: refine edge where valence == 1 and length > 0.05 <-------------------- valid_boundary ----------------------------> Boolean function. Returns 1 or 0 depending on whether a parametric boundary with the given number exists (note that the name of a named boundary is internally interpreted as a number). Syntax: VALID_BOUNDARY(expression) One use is in looping through all parametric boundaries, in conjunction with the high_boundary internal variable. For example, for ( bnum := 1 ; bnum <= high_boundary ; bnum += 1 ) if valid_boundary(bnum) then printf "Boundary %d has %d vertices on it\n",bnum, sum(vertex,on_boundary bnum); <-------------------- valid_constraint ----------------------------> Boolean function. Returns 1 or 0 depending on whether a level-set constraint with the given number exists (note that the name of a named constraint is internally interpreted as a number). Syntax: VALID_CONSTRAINT(expression) One use is in looping through all level-set constraints, in conjunction with the high_constraint internal variable. For example, for ( cnum := 1 ; cnum <= high_constraint ; cnum += 1 ) if valid_constraint(cnum) then printf "Constraint %d has %d vertices on it\n",cnum, sum(vertex,on_constraint cnum); <-------------------- valid_element ----------------------------> Boolean function. Returns 1 or 0 depending on whether an element of a given index exists. Syntax: VALID_ELEMENT(indexed_element) Examples: if valid_element(edge[12]) then refine edge[12] if valid_element(body[2]) then set body[2].facet color red <-------------------- value ----------------------------> The value of a quantity may be displayed with the A or v commands, or as an expression "quantityname.value". Furthermore, using the quantity name as an element attribute evaluates to the sum of all the applicable component instance values on that element. For example, supposing there is a quantity named vol, one could do print vol.value print facet[2].vol histogram(facet,vol) <-------------------- variable assignment ----------------------------> Values can be assigned to variables. Values can be numeric or string. The variable names must be two or more letters, in order they not be confused with single-letter commands. Syntax: identifier := expr identifier := stringexpr If the variable does not exist, it will be created. These are the same class of variables as the adjustable parameters in the datafile, hence are all of global scope and may also be inspected and changed with the 'A' command. Examples: maxlen := max(edge,length) newname := sprintf "file%03g",counter <-------------------- variables ----------------------------> The Evolver command language has its own version of the user-defined variables common to most programming languages. Variables are typed according to the types of the values assigned to them: numeric or string. Users may define numeric variables either by variable declarations in the datafile, or both types by assigning a value to an identifier in a command. A variable may be subjected to optimization by declaring it an optimizing_parameter in the datafile. <-------------------- vector_integrand ----------------------------> Additional attribute of some named methods, such as edge_vector_integral and facet_vector_integral. The following syntax is added to the quantity or method declaration: VECTOR_INTEGRAND: Q1: expr Q2: expr Q3: expr where the expressions may use space coordinates and element attributes, besides any global values. <-------------------- __velocity ----------------------------> Internal vertex read-only attribute. This is an indexed attribute giving the components of the vector used for vertex motion in the 'g' command. The motion of a vertex is the scale factor times this vector. The velocity vector is calculated from the force vector by applying area normalization, mobilty, etc. Also, if a vertex is on a boundary, the velocity is projected back to parameters. <-------------------- verbose ----------------------------> Evolver toggle command. Toggles printing of progress messages during surface modification commands such as refine, delete, notch, edgeswap, o, O, c. <-------------------- version ----------------------------> If a datafile contains features present only after a certain version of the Evolver, the datafile can contain a line of the form evolver_version "2.10" This will generate a version error message if the current version is earlier, or just a syntax error if run on an Evolver version earlier than 2.10. <-------------------- vertex ----------------------------> One of the basic geometric elements. As a keyword, it is used in element generators. <-------------------- vertex boundary ----------------------------> Vertex, edge, or facet read-only attribute. The status of whether an element is on a boundary can be queried with the Boolean attribute on_boundary. Elements can be unset from boundaries, but not set on them (since parameter values would be unknown). Examples: list vertex where on_boundary 1 unset vertex boundary 2 <-------------------- vertex edges ----------------------------> Vertex read-only attribute. Generates edges attached to a vertex, oriented so vertex is the edge tail. The edges are in no particular order. Examples: list vertex[3].edges foreach vertex vv do { foreach vv.edge do print id } Always use "vertexspec.edges" to generate vertex edges; using "edges" with an implicit element, as in "foreach vertex do list edges" will list all edges in the surface over and over again. <-------------------- vertex facets ----------------------------> Vertex read-only attribute. Generates facets attached to a vertex, with positive facet orientation. The facets are in no particular order. Examples: list vertex[3].facets foreach vertex vv do { foreach vv.facet do print id } Always use "vertexspec.facets" to generate vertex facets; using "facets" with an implicit element, as in "foreach vertex do list facets" will list all facets in the surface over and over again. <-------------------- vertex valence ----------------------------> Vertex read-only attribute. The valence of a vertex is defined to be the number of edges it is a member of. Example: list vertices where valence == 6 histogram(vertex,valence) <-------------------- vertex_average ----------------------------> Main prompt command. Does vertex averaging for one vertex at a time. Syntax: VERTEX_AVERAGE vertex_generator The action is the same as the V command, except that each new vertex position is calculated sequentially, instead of simultaneously, and an arbitrary subset of vertices may be specified. Fixed vertices do not move. Examples: vertex_average vertex[2] vertex_average vertex where id < 10 vertex_average vertex vv where max(vv.facet,color==red) == 1 See also "raw_vertex_average" and "rawest_vertex_average" for less restricted averaging. <-------------------- vertex_count ----------------------------> Internal read-only variable. Number of vertices. <-------------------- vertex_dissolve_count ----------------------------> Internal read-only variable. Number of vertices dissolved by the dissolve command. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- vertex_merge ----------------------------> Main prompt command. Merges two soapfilm-model vertices into one. Meant for joining together surfaces that bump into each other. Should not be used for vertices already joined by an edge. Syntax: vertex_merge(integer,integer) Note the syntax is a function taking integer vertex id arguments, not element generators. Example: vertex_merge(3,12) <-------------------- vertex_pop_count ----------------------------> Internal read-only variable. Number of vertices popped by "pop" or 'o' command. Prints and resets to 0 at the end of a command execution, or when flush_counts is executed. Also reset by reset_counts. <-------------------- vertex_scalar_integral ----------------------------> Named method. Description: Function value at a vertex. This actually produces a sum over vertices, but as a mathematician, I think of a sum over vertices as a point-weighted integral. Element: vertex. Parameters: scalar_integrand. Models: linear, quadratic, Lagrange, simplex. Ambient dimension: any. Hessian: yes. Example datafile declaration: quantity point_value energy method vertex_scalar_integral scalar_integrand: x^2 + y^2 - 2x + 3 <-------------------- vertexnormal ----------------------------> Vertex read-only attribute. This is an indexed attribute consisting of the components of a normal to the surface at a vertex, normalized to unit length. This is the same normal as used in hessian_normal mode. For most vertices in the soapfilm model, the normal is the number average of the unit normals of the surrounding facets. Along triple edges and such where hessian_normal has a multi-dimensional normal plane, the vertexnormal is the first basis vector of the normal plane. Example: To print the normal components of vertex 3: print vertex[3].vertexnormal[1]; print vertex[3].vertexnormal[2]; print vertex[3].vertexnormal[3]; The vertexnormal vector can also be printed as an array: print vertex[3].vertexnormal <-------------------- vertices ----------------------------> A vertex is a point in space. The coordinates of the vertices are the parameters that determine the location of the surface. It is the coordinates that are changed when the surface evolves. A vertex carries no default energy, but may have energy by being on a level set constraint in the string model, or by having a named quantity energy applied to it. The vertices of the original surface are defined in the vertices section of the datafile. Attributes: > id > original > coordinates > parameter values > fixed > constraints > on_constraint > hit_constraint > __v_constraint_list > boundary > on_boundary > bare > edges > facets > valence > quantities > on_quantity > on_method_instance > mid_edgedihedral > mid_facet > square mean curvature > axial_point > triple_point > tetra_point > vertexnormal > __force > __velocity > extra attributes <-------------------- vertices section ----------------------------> he datafile vertex list is started by the keyword VERTICES at the start of a line. It is followed by lines with one vertex specification per line. If the vertex is not on a parametric boundary, the syntax is k x y ... [FIXED] [CONSTRAINT c1 c2 ...] [BARE] [quantityname ...] [methodname ...] The syntax for a vertex on a parametric boundary is k p1 [p2 ...] BOUNDARY b [FIXED] [BARE] [quantityname ...] [methodname ...] Here k is the vertex number, a positive integer. Vertices do not need to be listed in order, and there may be gaps in the numbering. However, if they are not in consecutive order, then the numbering in dump files will be different. x y ... are constant expressions for coordinates. In the parametric boundary format, the boundary parameter values are given instead of the coordinates. If FIXED is given, then the vertex never moves, except possibly for an initial projection to constraints. If CONSTRAINT is given, then one or more constraint numbers must follow. You can list as many constraints as you want, as long as those that apply exactly at any time are consistent and independent. The given coordinates need not lie exactly on the constraints; they will be projected onto them. A vertex on a parametric boundary cannot also be on a constraint. The BARE attribute is just an instruction to the checking routines that this vertex is not supposed to have an adjacent facet in the soapfilm model, so spurious warnings will not be generated. This is useful when you want to show bare wires or outline fundamental domains. An arbitrary number of named quantities or method instances may be listed. These add method values for this element to the named quantity. The named quantity or instance must have been declared in the top section of the datafile. The "list vertices" command prints the datafile format listing of vertices. <-------------------- view generators ----------------------------> Listing all the view transforms is tedious and inflexible. An alternative is to list just a few matrices that can generate transforms. See the transform_expr command for instructions on entering the expression that generates the actual transforms. Special Note: in the torus model, the period translations are automatically added to the end of the list. So in the torus model, these are always available, even if you don't have view_transform_generators in the datafile. Syntax in the top of the datafile: VIEW_TRANSFORM_GENERATORS n SWAP_COLORS constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr ... The number of matrices follows the keyword VIEW_TRANSFORM_GENERATORS. Each matrix is in homogeneous coordinates, with translation in the last column. The size of each matrix is one more than the space dimension. Individual matrices need no special separation; Evolver just goes on an expression reading frenzy until it has all the numbers it wants. If SWAP_COLORS is present, facet frontcolor and backcolor will be swapped when this matrix is applied. The internal variable transform_count records the number of transforms, and the transform matrices are accessible at runtime as a three-dimensional matrix view_transforms[][][]. <-------------------- view matrix ----------------------------> The native screen graphics view is controlled by a view transformation matrix, which may be specified in the datafile, and which is dumped by the d or list topinfo commands. The view matrix may be changed with graphics mode commands. The view matrix elements may be read or set at runtime by view_matrix[i][j], where the indices start at 1. In particular, one can write command scripts to save and reload particular view matrices; see saveview.cmd in the distribution package. The view matrix does not affect geomview. The display consists entirely of facets and edges. Special edges (fixed edges, boundary edges, constraint edges, triple edges, bare edges) are always shown, unless you make their color CLEAR. The individual facet edges can be toggled with the graphics mode command `e'. <-------------------- view transforms ----------------------------> For the display of several transformations of the surface simultaneously, a number of viewing transformation matrices may be given in the top section of the datafile: VIEW_TRANSFORMS n COLOR color SWAP_COLORS constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr ... The transforms apply to all graphics, internal and external, and are prior to the viewing matrix for internal graphics. The identity transform is always done, so it does not need to be specified. The number of matrices follows the keyword VIEW_TRANSFORMS. Each matrix is in homogeneous coordinates, with translation in the last column. The size of each matrix is one more than the space dimension. Individual matrices need no special separation; Evolver just goes on an expression reading frenzy until it has all the numbers it wants. Each matrix may be preceded by an optional color that applies to facets transformed by that matrix. The color applies to one transform only; it does not continue until the next color specification. If SWAP_COLORS is present instead, facet frontcolor and backcolor will be swapped when this matrix is applied. Transforms may be activated or deactivated interactively with the transforms toggle. The internal variable transform_count records the number of transforms, and the transform matrices are accessible at runtime as a three-dimensional matrix view_transforms[][][]. View transform generators are a more sophisticated way to control view transforms. <-------------------- view_4d ----------------------------> Evolver toggle command. Toggles sending 4D information to geomview. <-------------------- view_matrix ----------------------------> The top section of the datafile may contain an initial viewing matrix: VIEW_MATRIX constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr The matrix is in homogeneous coordinates with translations in the last column. The size of the matrix is one more than the space dimension. This matrix will be part of all dump files, so the view can be saved between sessions. This matrix is used and set by native screen graphics ('s' command) and only applies to internal graphics (Postscript, Xwindows, etc.) but not external graphics (geomview). The elements may be read or set at runtime by view_matrix[i][j], where the indices start at 1. In particular, one can write command scripts to save and reload particular view matrices; see saveview.cmd in the distribution package. <-------------------- view_transform_generators ----------------------------> Listing all the view transforms is tedious and inflexible. An alternative is to list just a few matrices that can generate transforms. See the transform_expr command for instructions on entering the expression that generates the actual transforms. Special Note: in the torus model, the period translations are automatically added to the end of the list. So in the torus model, these are always available, even if you don't have view_transform_generators in the datafile. Syntax in the top of the datafile: VIEW_TRANSFORM_GENERATORS n SWAP_COLORS constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr ... The number of matrices follows the keyword VIEW_TRANSFORM_GENERATORS. Each matrix is in homogeneous coordinates, with translation in the last column. The size of each matrix is one more than the space dimension. Individual matrices need no special separation; Evolver just goes on an expression reading frenzy until it has all the numbers it wants. If SWAP_COLORS is present, facet frontcolor and backcolor will be swapped when this matrix is applied. The internal variable transform_count records the number of transforms, and the transform matrices are accessible at runtime as a three-dimensional matrix view_transforms[][][]. <-------------------- view_transforms ----------------------------> For the display of several transformations of the surface simultaneously, a number of viewing transformation matrices may be given in the top section of the datafile: VIEW_TRANSFORMS n COLOR color SWAP_COLORS constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr constexpr ... The transforms apply to all graphics, internal and external, and are prior to the viewing matrix for internal graphics. The identity transform is always done, so it does not need to be specified. The number of matrices follows the keyword VIEW_TRANSFORMS. Each matrix is in homogeneous coordinates, with translation in the last column. The size of each matrix is one more than the space dimension. Individual matrices need no special separation; Evolver just goes on an expression reading frenzy until it has all the numbers it wants. Each matrix may be preceded by an optional color that applies to facets transformed by that matrix. The color applies to one transform only; it does not continue until the next color specification. If SWAP_COLORS is present instead, facet frontcolor and backcolor will be swapped when this matrix is applied. Transforms may be activated or deactivated interactively with the transforms toggle. The internal variable transform_count records the number of transforms, and the transform matrices are accessible at runtime as a three-dimensional matrix view_transforms[][][]. View transform generators are a more sophisticated way to control view transforms. <----------------- view_transform_parity -----------------------> view_transform_parity[] Internal read-only array that contains 0 or 1 for transforms that don't or do swap front and back colors. The order of transforms is the same as for view_transforms[][][]. <----------------- view_transform_swap_colors -----------------------> view_transform_swap_colors[] Internal read-only array that contains 0 or 1 for transforms that don't or do swap front and back colors. The order of transforms is the same as for view_transforms[][][]. For my debugging purposes. <-------------------- visibility_debug ----------------------------> visibility_debug Evolver toggle command, which causes printing of verbose information when visibility_test is on; don't use it. <-------------------- visibility_test ----------------------------> Evolver toggle command. Toggles an occluded-triangle test for graphics output that uses the Painter's Algorithm to produce 2D output (PostScript, Xwindows). This can greatly reduce the size of a PostScript file, but inspect the output since the implementation of the algorithm may have flaws. <-------------------- volconst ----------------------------> Body read-write attribute. A constant added to the calculated volume. Useful for correcting for omitted parts of body boundaries. Also used internally as a correction in the torus model , which will use the target volume to calculate volconst internally. In the torus model, the target volume should be set within 1/12 of a torus volume of the actual volume for each body, so the correct volconst can be computed. Each volconst will be adjusted proportionately when the volume of a fundamental torus domain is change by changing the period formulas. Volconst can be set in the datafile bodies section, or interactively by the set command or by assignment. Examples: print body[1].volconst set body[2] volconst 1.2 body[2].volconst := 1.2 It is best to avoid using volconst except in the torus model. Rather, use edge content integrals so that the proper adjustments will be made if the boundary of the surface is moved, or rebody is done. Named quantities can also have a volconst value. <-------------------- volfixed ----------------------------> Body read-only attribute. Value is 1 if the volume of the body is fixed, 0 if not. <-------------------- volgrads_every ----------------------------> Evolver toggle command. Toggles recalculating volume constraint gradients every projection step during constraint enforcement. Good for stiff problems. <-------------------- volume ----------------------------> Body read-only attribute. Actual volume of a body. This is the sum of three parts, in the soapfilm model: 1. An integral over the facets bounding the body. This is \int z dx dy normally, but \int (x dy dz + y dz dx + z dx dy)/3 if SYMMETRIC_CONTENT is in effect. 2. Any constraint content edge integrals applying to the body. 3. The body's volconst attribute. In the string model, the parts are 1. An integral over the edges bounding the body's facet. This is \int -y dx. 2. Any constraint content vertex integrals applying to the body. 3. The body's volconst attribute. Body volumes can be displayed with the v command, or with standard attribute syntax. Example: print body[1].volume foreach body where volume > 2 do print id <-------------------- volume_method_name ----------------------------> This top-of-datafile item, volume_method_name, specifies the name of the pre-defined method to use as the method to compute body volumes in place of the default edge_area method in the string model or facet_volume method in the soapfilm model. It is optional. Developed so circular arcs can be used in two-dimensional foams. Synonymous with area_method_name in the string model. Usage implies converting to everything_quantities mode. Syntax: volume_method_name quoted_method_name For example, string space_dimension 2 volume_method_name "circular_arc_area" <-------------------- W ----------------------------> Single letter main command. Toggles homothety. If homothety ON, then after every iteration, the surface is scaled up so that the total volume of all bodies is 1. Meant to be used on surfaces without any blowup constraints of any kind, to see the limiting shape as surface collapses to a point. <-------------------- w ----------------------------> Single letter main command. Tries to weed out small triangles. You will be prompted for the cutoff area value if you don't give a value with the command. If you enter h, you will get a histogram of areas to guide you. If you hit RETURN with no value, nothing will be done. Some small triangles may not be eliminable due to constraints or other such obstacles. The action is to eliminate an edge on the triangle, eliminating several facets in the process. Edges will be tried for elimination in shortest to longest order. WARNING: Although checks are made to see if it is reasonable to eliminate an edge, it is predicated on facets being relatively small. If you tell it to eliminate all below area 5, Evolver may eliminate your entire surface without compunction. Graphics mode command. Toggles display of facets entirely on constraints. For a one-sided constraint, applies to facets whose vertices all hit the constraint. "w" stands for "wall". <-------------------- warning_messages ----------------------------> To print warning messages that have been emitted during reading of the datafile but have scrolled off the top of your screen, you may use the command "print warning_messages". <-------------------- where ----------------------------> Clause in element generators to specify a subset of elements. Examples: list facet where color == red foreach edge ee where ee.length < .3 do list ee.vertex print max(edge where on_constraint 1, length) <-------------------- where_count ----------------------------> Internal read-only variable. Number of items satisfying last "where" condition. Prints and resets to 0 at the end of a command execution, or when flush_counts is done. Also reset by reset_counts. <-------------------- whereami ----------------------------> Debug prompt command that prints a stack trace showing the sequence of function or procedure calls made to reach the current spot. See "breakpoint". <-------------------- while ----------------------------> Command syntax for pre-test iteration loop. Syntax: WHILE expr DO command where expr is true if nonzero. Parentheses around expr are not needed, but do not hurt. Example: count := 0 while count < 10 do { g 10; u; print total_energy; count := count + 1 } <-------------------- whitespace ----------------------------> In the datafile, whitespace consists of spaces, tabs, commas, colons, and semicolons. So it's fine if you want to use commas to separate coordinate values, and colons to prettify constraint definitions. In commands, whitespace consists of spaces and tabs. CTRL-Z is also whitespace, for the benefit of files imported from DOS. <-------------------- window_aspect_ratio ----------------------------> Internal read-write variable. The ratio of the the vertical to horizontal dimensions of the display window. If set, this locks the aspect ratio to the given value. The window may be resized with the mouse, but the aspect ratio will stay the same. The unset value of window_aspect_ratio is 0; setting window_aspect_ratio to 0 will unlock the aspect ratio. Applies also to the PostScript bounding box, if full_bounding_box is on. Currently implemented only in Evolver with GLUT graphics. Caveat: the window doesn't always fully follow the mouse; just keep trying. <-------------------- wrap ----------------------------> Edge read-write attribute. When a symmetry group is in effect (such as the torus model) and an edge crosses the boundary of a fundamental domain, the edge is labelled with the group element that moves the edge head vertex to its proper position relative to the tail vertex. The label is internally encoded as an integer, the encoding peculiar to each symmetry group. Edge wrappings are set in the datafile. The torus model has its own peculiar wrap representation in the datafile: * for no wrap, + for positive wrap, and - for negative wrap. Wraps are maintained automatically by Evolver during surface manipulations. The numeric edge wrap values can be queried with attribute syntax. Example: list edge where wrap != 0 Unfortunately, the torus model wraps come out rather opaquely, since one cannot print hex. The torus wrap number is the sum of numbers for the individual directions: +x = 1; -x = 31; +y = 64; -y = 1984; +z = 4096; -z = 127040. Caution: even though this attribute can be written by the user at runtime, only gurus should try it. <-------------------- wrap_compose ----------------------------> wrap_compose(w1,w2) : Function returning the symmetry group code for the composition of symmetry group elements w1, w2. Useful only if a symmetry group has been declared in the datafile. <-------------------- wrap_inverse ----------------------------> wrap_inverse(w) : Function returning the symmetry group numerical code for the inverse of symmetry group element w. Useful only if a symmetry group has been declared in the datafile. <-------------------- wrap_vertex ----------------------------> Main prompt command. Syntax: wrap_vertex(vexpr,wexpr) In a symmetry group model, transforms the coordinates of vertex number vexpr by symmetry group element wexpr and adjusts wraps of adjacent edges accordingly. Useful for re-setting vertices in torus model that get too far outside the unit cell. <-------------------- writhe ----------------------------> Named method. Description: An average crossing number calculation. This one does have a gradient. Suggested by Hermann Gluck. Programmed by John Sullivan. Between pairs of edges, energy is inverse cube power of distance between midpoints of edges, times triple product of edge vectors and distance vector. E = 1/d^3 * (e1,e2,d) Element: edge. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: quantity writhy energy method writhe global <-------------------- wulff ----------------------------> To declare that surface area energy should be calculated with a crystalline integrand, the top section of the datafile should contain a line of the form WULFF "filename" The double-quoted filename (with path) refers to a file giving the Wulff vectors of the integrand. The format of the file is one Wulff vector per line with its components in ASCII decimal format separated by spaces. The first blank line ends the specification. Some special integrands can be used by giving a special name in place of the file name. Currently, these are "hemisphere" for a Wulff shape that is an upper unit hemisphere, and "lens" for two unit spherical caps of thickness 1/2 glued together on a horizontal plane. These two don't need separate files. <-------------------- wulff_energy ----------------------------> Named method. Description: Method version of wulff energy. If Wulff filename is not given in top section of datafile, then the user will be prompted for it. Element: facet. Parameters: none. Models: linear. Ambient dimension: 3. Hessian: no. Example datafile declaration: wulff "crystal.wlf" quantity wolf energy method wulff_energy global <-------------------- X ----------------------------> Single letter main command. List the current extra attributes, including name, dimension, type, size in bytes, and offset within the element structure. Some internal attributes are also listed, whose names begin with a double underscore. <------------------------------ x -----------------------------------> Single letter main command. Same as q. Exit Evolver, or start new surface. Graphics mode command. Exit from graphics mode, and return to main command mode. Vertex attribute. Alone, the first coordinate of the vertex. With index, the indexed coordinate. For treating coordinates as a single vector, use the vertex attribute __x. Edge attribute. Alone, the first component of the edge vector. With index, the indexed component. For treating the edge vector as a single vector, use the edge attribute __edge_vector. Facet attribute. Alone, the first component of the facet normal vector (normalized so length is equal to facet area). With index, the indexed component. For treating the facet normal as a single vector, use the edge attribute __facet_normal. <-------------------- xyz symmetry group ----------------------------> The orientation-preserving subgroup of cubocta. Same representation. <-------------------- y ----------------------------> Single letter main command. Torus duplication. In torus model, prompts for a period number (1,2, or 3) and then doubles the torus unit cell in that direction. Useful for extending simple configurations into more extensive ones. Vertex attribute. The second coordinate of the vertex. For treating coordinates as a single vector, use the vertex attribute __x. Edge attribute. The second component of the edge vector. For treating the edge vector as a single vector, use the edge attribute __edge_vector. Facet attribute. The second component of the facet normal vector (normalized so length is equal to facet area). For treating the facet normal as a single vector, use the edge attribute __facet_normal. <-------------------- ysmp ----------------------------> Evolver toggle command. Toggles between Yale Sparse Matrix Package routines for factoring hessians, and my own minimal degree factoring. Default is YSMP off. <-------------------- z ----------------------------> Single letter main command. Do curvature test on QUADRATIC model. Supposed to be useful if you're seeking a surface with monotone mean curvature. Currently checks angle of creases along edges and samples curvature on facet interiors. Orientation is with respect the way facets were originally defined. Deprecated. Graphics mode command. Zoom. Expands image by factor, default 1.2. Examples: `z' zooms by 1.2, `2z' zooms by 1.44, '2.0z' zooms by 2. Vertex attribute. The third coordinate of the vertex. For treating coordinates as a single vector, use the vertex attribute __x. Edge attribute. The third component of the edge vector. For treating the edge vector as a single vector, use the edge attribute __edge_vector. Facet attribute. The third component of the facet normal vector (normalized so length is equal to facet area). For treating the facet normal as a single vector, use the edge attribute __facet_normal. <-------------------- Z ----------------------------> Single letter main command. Zooms in on a vertex. Asks for vertex number and radius. Number is as given in vertex list in datafile. Beware that vertex numbers change in a dump (but correct current zoom vertex number will be recorded in dump). Eliminates all elements outside radius distance from vertex 1. New edges at the radius are made FIXED. Meant to investigate tangent cones and intricate behavior, for example, where wire goes through surface in the overhand knot surface. Zooming is only implemented for surfaces without bodies. <-------------------- zener_coeff ----------------------------> See "zener_drag". <-------------------- zener_drag ----------------------------> Evolver toggle command. Toggles Zener drag feature, in which the velocity of the surface is reduced by a magnitude given by the variable zener_coeff, and the velocity is set to zero if it is smaller than zener_coeff. <-------------------- zoom ----------------------------> Main prompt command. For isolating a region of a surface. Syntax: ZOOM integer expr Zooms in on vertex whose id is the given integer, with radius the given expr. Same as the 'Z' command, but not interactive. <-------------------- zoom_radius ----------------------------> Datafile keyword setting the current zoom radius. Used in dump files after a zoom command has been given. See "Z". <-------------------- zoom_vertex ----------------------------> Datafile keyword setting the current zoom vertex. Used in dump files after a zoom command has been given. See "Z". evolver-2.30c.dfsg/doc/column.htm0000644000175300017530000001567211410765113017215 0ustar hazelscthazelsct Surface Evolver Documentation - Column Example

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Example: Column of liquid solder

Here we have a tiny drop of liquid solder that bridges between two parallel, horizontal planes at z = 0 and z = ZH. On each plane there is a circular pad that the solder perfectly wets, and the solder is perfectly nonwetting off the pads. This would be just a catenoid problem with fixed volume, except that the pads are offset, and it is desired to find out what aligning force the solder exerts. The surface is defined the same way as in the catenoid example, except the lower boundary ring has a shift variable "SHIFT" in it to provide an offset in the y direction. This makes the shift adjustable at run time. Since the top and bottom facets of the body are not included, the constant volume they account for is provided by content integrals around the upper boundary, and the gravitational energy is provided by an energy integral. One could use the volconst attribute of the body instead for the volume, but then one would have to reset that every time ZH changed.

The interesting part of this example is the calculation of the forces. One could incrementally shift the pad, minimize the energy at each shift, and numerically differentiate the energy to get the force. Or one could set up integrals to calculate the force directly. But the simplest method is to use the Principle of Virtual Work by shifting the pad, recalculating the energy without re-evolving, and correcting for the volume change. Re-evolution is not necessary because when a surface is at an equilibrium, then by definition any perturbation that respects constraints does not change the energy to first order. To adjust for changes in constraints such as volume, the Lagrange multipliers (pressure for the volume constraint) tell how much the energy changes for given change in the constraints:

          DE = L*DC
where DE is the energy change, L is the row vector of Lagrange multipliers and DC is the column vector of constraint value changes. Therefore, the adjusted energy after a change in a parameter is
          E_adj = E_raw - L*DC
where E_raw is the actual energy and DC is the vector of differences of constraint values from target values. The commands do_yforce and do_zforce in the datafile do central difference calculations of the forces on the top pad, and put the surface back to where it was originally. Note that the perturbations are made smoothly, i.e. the shear varies linearly from bottom to top. This is not absolutely necessary, but it gives a smoother perturbation and hence a bit more accuracy.
column The initial column skeleton, with vertices and edges numbered.
// column.fe
// Example of calculating forces exerted by a
// column of liquid solder in shape of skewed catenoid.

// All units cgs
parameter RAD = 0.05     // ring radius
parameter ZH = 0.08      // total height
parameter SHIFT = 0.025    // shift
#define SG 8      // specific gravity of solder
#define TENS 460  // surface tension of solder
#define GR  980   // acceleration of gravity

gravity_constant GR

BOUNDARY 1  PARAMETERS 1
X1: RAD*cos(P1)
X2: RAD*sin(P1) + SHIFT
X3: ZH
CONTENT  // used to compensate for missing top facets
c1: 0
c2: -ZH*x
c3: 0
ENERGY  // used to compensate for gravitational energy under top facets
e1: 0
e2: GR*ZH^2/2*x
e3: 0

BOUNDARY 2  PARAMETERS 1
X1: RAD*cos(P1)
X2: RAD*sin(P1)
X3: 0

vertices   // given in terms of boundary parameter
1    0.00  boundary 1   fixed
2    pi/3  boundary 1   fixed
3  2*pi/3  boundary 1   fixed
4    pi    boundary 1   fixed
5  4*pi/3  boundary 1   fixed
6  5*pi/3  boundary 1   fixed
7    0.00  boundary 2   fixed
8    pi/3  boundary 2   fixed
9  2*pi/3  boundary 2   fixed
10   pi    boundary 2   fixed
11 4*pi/3  boundary 2   fixed
12 5*pi/3  boundary 2   fixed

edges
1    1  2  boundary 1   fixed
2    2  3  boundary 1   fixed
3    3  4  boundary 1   fixed
4    4  5  boundary 1   fixed
5    5  6  boundary 1   fixed
6    6  1  boundary 1   fixed
7    7  8  boundary 2   fixed
8    8  9  boundary 2   fixed
9    9  10 boundary 2   fixed
10   10 11 boundary 2   fixed
11   11 12 boundary 2   fixed
12   12 7  boundary 2   fixed
13   1  7
14   2  8
15   3  9
16   4  10
17   5  11
18   6  12

faces
1   1 14  -7 -13   density TENS
2   2 15  -8 -14   density TENS
3   3 16  -9 -15   density TENS
4   4 17 -10 -16   density TENS
5   5 18 -11 -17   density TENS
6   6 13 -12 -18   density TENS

bodies
1    -1 -2 -3 -4 -5 -6 volume 0.00045 density SG

read

// horizontal force on upper pad by central differences
dy := .0001
do_yforce := { oldshift := shift; shift := shift + dy;
               set vertex y  y+dy*z/zh; // uniform shear
               recalc;
               energy1 := total_energy - 
                     body[1].pressure*(body[1].volume - body[1].target);
               oldshift := shift; shift := shift - 2*dy;
               set vertex y  y-2*dy*z/zh; // uniform shear
               recalc;
               energy2 := total_energy - 
                     body[1].pressure*(body[1].volume - body[1].target);
               yforce := -(energy1-energy2)/2/dy;
               printf "restoring force: %20.15f\n",yforce;
               // restore everything
               oldshift := shift; shift := shift + dy;
               set vertex y  y+dy*z/zh; // uniform shear
               recalc;
             }

// vertical force on upper pad by central differences.
dz := .0001
do_zforce := { oldzh := zh; zh := zh + dz; 
               set vertex z  z+dz*z/oldzh; recalc; // uniform stretch
               energy1 := total_energy - 
                     body[1].pressure*(body[1].volume - body[1].target);
               oldzh := zh; zh := zh - 2*dz; 
               set vertex z  z-2*dz*z/oldzh; recalc; // uniform stretch
               energy2 := total_energy - 
                     body[1].pressure*(body[1].volume - body[1].target);
               zforce := -(energy1-energy2)/2/dz;
               printf "vertical force:  %20.15f\n",zforce;
               // restore everything
               oldzh := zh; zh := zh + dz; 
               set vertex z  z+dz*z/oldzh; recalc; // uniform stretch
             }


Back to top of tutorial. Back to top of Evolver documentation. Index. evolver-2.30c.dfsg/doc/news_18.htm0000644000175300017530000002105111410765113017170 0ustar hazelscthazelsct Surface Evolver Documentation - Newsletter 18

Surface Evolver Newsletter no. 18

Back to top of Surface Evolver documentation.

                         Surface Evolver Newsletter 18
                              October 13, 2004 
                     by Ken Brakke, brakke@susqu.edu


Surface Evolver version 2.24 is available for download at
http://www.susqu.edu/brakke/evolver.
This is the first release since version 2.20 in August, 2003 (although
version 2.23 was posted unannounced in June).

For those who compile Evolver themselves, the same makefile may
be used, except there is an additional source file method5.c.

Trinity College Workshop - While on sabbatical at Trinity College, Dublin,
I gave a six-session Evolver workshop.  The web pages and datafiles are
available at http://www.susqu.edu/brakke/evolver/workshop.

New features in version 2.24:

 Added on_quantity and on_method_instance element attributes,
 e.g. "list vertex where on_quantity bigvol".

 Named quantities and method instances, constraints and boundaries can
 be defined at runtime by using "define" followed by the definition
 as in the top of a datafile.   

 Added readable quantity attributes fixed, energy, info_only, conserved.

 New t1_edgeswap command. String model T1 topology change; flips an edge
 joining two triple points.

 New topology changing commands in soapfilm model:
  pop_quad_to_quad
  pop_tri_to_edge
  pop_edge_to_tri

 Pop control toggles:
  pop_disjoin - films separate with no new film generated between
  pop_to_face - forces six-triple edge vertex pop to triangle
  pop_to_edge - forces six-triple edge vertex pop to edge

 Added vertex_merge(v1,v2), edge_merge(e1,e2), and facet_merge(f1,f2)
 for gluing bits of surface together.  The first of the pair is the 
 one kept.  Tensions do not add; you should set them yourself if needed.

 Added commands matrix_multiply and matrix_inverse to help working with
 arrays.

 New named methods for working with a string network on a sphere:
 spherical_arc_length, spherical_arc_area_n, spherical_arc_area_s
 These work in the linear string model.

 Circular_arc mode does exact postscript arc drawing.

 New named method for integral of squared mean curvature:
 star_perp_sq_mean_curvature
 Designed so that its zeros are the equilibria of gradient descent with
 normal_motion in effect and hessian with hessian_normal. Now the 
 recommended method for squared mean curvature.

 All the "star_..." varieties of squared mean curvature now work with 
 partial stars, i.e. you can bound the surface with mirror symmetry
 planes, and the curvature will be correctly calculated on the sides
 and in the corners.

 hessian_menu now has a subshell command, "=", to make it easier to do
 things in the middle of looking at eigenmodes and stuff. Good for
 making postscript files of perturbations and then returning to the
 original.

 Added ps_labelsize read-write variable to control the relative size
 of labels in PostScript files. Default value 3.

 full_bounding_box toggle writes the bounding box in a postscript file
 as the grapics window boundary rather than the actual bounding box
 of the surface.  Good for creating a series of images with the same
 bounding box.

 Added list quantity, list constraint, list boundary, list method_instance,
 so you can see what one of these is at runtime without doing a whole
 list topinfo.

 For really high-resolution timing, there is now the cpu_counter variable,
 which reports the value of the CPU cycle counter.  Currently implemented
 only on x86 architecture.

 valid_element(etype[index]) function to query whether an element of
 a given number exists.

 The Windows version is now compiled to use a 3GB address space, if 
 the system supports it.  Windows defaults to only 2GB user address 
 space per process, but you can up that to 3GB by adding the /3GB option 
 to the boot line in your boot.ini. (if you don't know what this means, 
 you shouldn't be fiddling with it.) If your physical memory is 1GB or 
 above, I advise you to do this, since virtual memory space tends to
 get fragmented.
 
 The "set" command can now be used to put vertices, edges, and facets
 on parametric boundaries.  Set the vertex boundary parameters p1, p2, etc. 
 as needed first.

 Added display_periods for controlling display of sheared torus
 unit cells.  

 Added window_aspect_ratio variable to lock graphics window aspect ratio; 
 GLUT version only.
 
 Added stokes2d method for doing 2D viscous flow.
 Added stokes2d_laplacian info only method to extract stream function
   laplacian conveniently.

 Added general_linear_elastic method for doing non-isotropic elastic surfaces.

 New sample command files (see the top of each file for more information):
    wetfoam2.cmd - create Plateau borders on a dry foam; update of wetfoam.cmd
    rewrap.cmd - adjust vertices in 3D torus model back to unit cell
    rewrap2.cmd - adjust vertices in 2D torus model back to unit cell
    gaussmap.cmd - convert surface to its Gauss map
    gaussref.cmd - refine according to area on Gauss map
    multiplicate.cmd - print datafile for full surface shown by view transforms
    band.cmd - draw colored bands along designated edges
    unshear.cmd - adjust a 2D sheared torus unit cell back toward squareness
    xray.cmd - make xray image of the Plateau borders of a wet foam

Changed features:

  torus mode connected bodies changed to keep bodies in same places
  when topological changes made. May be some initial differences in
  display; old way technically had a bug in it anyway.

  is_defined() now runtime evaluation.  Be careful in using this to
  detect previously defined variables, since the complete command is
  parsed before is_defined() is executed.  Try  "define variablename type"
  and then testing for the default initial value.

  10 argument limit on printf and sprintf removed.

  pop vertex modified for touching cones to do merging instead of
  splitting if cones wide enough.  Septum depends on body agreement.

  Added ! in transform_expr to indicate single transform necessary.
  ! after single transform means use it, not alternate with identity.
  ! at start of transform string means do not include identity transform.

  Changed semantics of autorecalc.  Now controls doing of recalc at
  end of exec() when recalc_flag has been set.  Default changed to ON.
  Old uses of SURFACE_PARAMETER recalculation causing recalc() when
  autorecalc on now sets recalc_flag.

  Bezier_basis mode now changes control points to maintain curve image
  during refinement and order change.  Note this means old control points
  move, so have to be careful with alignment constraints.  And facets.

  "smooth_graph" toggles smoother segmented drawing of Lagrange model.
  And in bezier_basis. And quadratic string mode. 
  But not in torus connected mode, nor quadratic soapfilm mode.

  Made current state of quiet_flag propagate to read files, but reverts
  when file done.

  The vertexnormals vertex attribute now projects the normal to constraints.

  Limited vertex averaging in Lagrange model added; internal to facets
  and edges only.

  Individual facet areas in string model.

  Got vertex.facet working for string model.  Facets reported as having
  positive orientation.  And only one reported per edge.
  
  Overhaul of count reporting.  About 20 count variables now:
  equi_count;
  edge_delete_count;
  facet_delete_count;
  edge_refine_count;
  facet_refine_count;
  vertex_dissolve_count;
  edge_dissolve_count;
  facet_dissolve_count;
  body_dissolve_count;
  vertex_pop_count;
  edge_pop_count;
  facet_pop_count;
  pop_tri_to_edge_count;
  pop_edge_to_tri_count;
  pop_quad_to_quad_count;
  where_count;
  edgeswap_count;
  t1_edgeswap_count;
  fix_count;
  unfix_count;
  skinny_refine_count;
  notch_count;
  
  "list procedures" lists prototypes but not bodies of procedures.
  
  'o' reports edge and vertex pop counts seperately; was adding edge pops
  to vertex pops.
  
  "pop vertices" has special consideration for vertices on constraints.
  Put in special pop handlers for cube cone and tri-prism cone (formerly
  kraynikpop).
  
  "delete facet" really works in string model now.
  
  augmented_hessian default state on for at least 50 constraints, off for less.

End of Newsletter 18

evolver-2.30c.dfsg/doc/news_08.htm0000644000175300017530000001631111410765113017172 0ustar hazelscthazelsct Surface Evolver Documentation - Newsletter 8

Surface Evolver Newsletter no. 8

Back to top of Surface Evolver documentation.


                      Surface Evolver Newsletter Number 8
                              June 23, 1994

                    Editor: Ken Brakke, brakke@geom.umn.edu

Contents:

X-geomview
Version 1.95 
Bibliography

X-geomview:

The Geometry Center has created an X-windows version of geomview, which
Evolver has long used as its display program on Silicon Graphics systems.
Beware, though, that X-geomview has to emulate all 3D graphics in software,
as X-windows is a 2D system.  Hence X-geomview is slower, and does not use
any built-in 3D graphics hardware a machine may have.  It is also still
under development, and may have a few bugs and porting problems.
X-geomview is available by anonymous ftp from geom.umn.edu in 
/pub/software/geomview.  There are precompiled binaries in 
geomview-next.tar and geomview-sun.x11-beta.tar.Z, and source in
geomview-src.x11-beta.tar.Z for other systems.

Version 1.95 is now available for ftp.  Still haven't been able to get
the Macintosh version updated since last summer.  Any desperate mac
users should let me know if I should spend time trying to do that.

Version 1.95 features:

The Tutorial chapter of the manual has two new examples: a toroidal
liquid ring on a spinning rod, and a column of liquid solder between
two offset pads.  The datafiles are ringblob.fe and column.fe.

Conjugate gradient option: A toggle command "ribiere" has been added
that makes the conjugate gradient method use the Polak-Ribiere version
instead of Fletcher-Reeves. (The toggle doesn't turn on conjugate gradient.)
Polak-Ribiere seems to recover much better from stalling.  If you have
to stop and restart conjugate gradient often, try "ribiere".  It handles
the catenoid example in the manual very well.  Pending further experience,
I will make Polak-Ribiere the default in future versions.  Let me know if
you have any problems with it.

Conjugate gradient blowups: Sometimes conjugate gradient wants to move
so far and fast that it loses contact with volume constraints.  Before,
Evolver did only one or two Newton method projections back to volume
constraints each iteration; now it will do up to 10.  Ordinary iteration
does only one projection still, but you can get the extra projections
with the "post_project" toggle.  If convergence fails after 10 iterations,
you will get a warning message, repeated iterations will stop, and the
variable "iteration_counter" will be negative.

Extra attributes: It is now possible to dynamically define extra attributes 
for elements, which may be single values or vectors.  The definition syntax is
 DEFINE elementtype ATTRIBUTE name REAL|INTEGER [dim]
where  elementtype is  vertex, edge, facet, or body,  name is an identifier 
of your choice, and  dim is an optional expression
for the vector dimension.  There is no practical distinction between real
and integer types at the moment, since everything is stored internally
as reals.  But there may be more datatypes added in the future. Examples:
  define edge attribute charlie real 
  define vertex attribute oldx real[3] 
The same definition syntax works in the top section of the datafile as
well as from the command prompt.  Use in the same way as built-in 
attributes like color.  The named quantity method hooke2_energy uses
an extra attribute hooke_size as the equilibrium length for each
individual edge.

General quantities: Named quantities for basic geometric integrals
are now available that work in both the linear and quadratic model,
and in the hessian command for optimization.  The basic methods are:
vertex_scalar_integral: Function value at a vertex.
edge_tension or edge_length: Length of edge.
edge_scalar_integral: Integral of a scalar function over arclength.
edge_vector_integral: Integral of a vectorfield over an edge.
edge_general_integral: Integral of a scalar function of position and 
    tangent over an edge.
facet_tension or facet_area: Area of facet.
facet_scalar_integral: Integral of a scalar function over facet area.
facet_vector_integral: Integral of a vectorfield over a facet.
facet_general_integral: Integral of a scalar function of position and 
    normal vector over  a facet.
See the manual for a full list, and further details.

Quadratic model: More has been added for the quadratic model, in particular
various named quantity methods (see above).  Also the midv attribute for
an edge gets the midpoint of an edge in the quadratic model.  Beware that
some old-fashioned things don't work in quadratic model, since they will
be replaced eventually with the named quantity methods internally.
Prominent among these are string length (use edge_length quantity) and
volume in the torus model (use facet_volume quantity).   Further, the
edge midpoints are recorded in dump files as the third vertex for an
edge, so quadratic models can be reloaded.

PostScript labels: PostScript output now has an option to print element
numbers, for easy cross-referencing with datafiles.  Edge orientation is
indicated by the labels being slightly displaced toward the edge head,
and face labels are signed according to which side you are seeing.
By responding with 'i' or 'o' at the labels prompt, you can get either
current id numbers or original id numbers.

Square mean curvature: Some extreme shapes caused problems for effective_area
and normal_curvature versions of square mean curvature, in detecting a
coherent local orientation for a surface.  There is now an "assume_oriented"
toggle which lets Evolver assume facets are defined with consistent local
orientation, so it doesn't have to figure it out itself.

The size of PostScript output box has been corrected to 8x8 inches,
as was claimed in the PS file header.  Had been 7.6x7.6 inches.

Minneview removed:  Minneview, the precursor of geomview, has been
removed. The Makefile doesn't need -DNO_MV anymore.

The "quietgo" toggle will suppress normal output from the 'g' command.

New math functions: tanh, asinh, acosh, atanh.

New read-only variable: "iteration_counter" is the index of the 
current iteration loop (printed in the first column of 'g' output).

Named quantity methods for squared curvature: sq_mean_curvature,
  eff_area_sq_mean_curvature, normal_sq_mean_curvature.
  All work with prescribed curvature h_zero.

Under-relaxation: Optimizing scale iteration finds the energy minimum
in the direction of motion.  You can set the variable "scale_scale",
which multiplies the optimum scale factor to get the actual motion,
to explore the effects of under- or over-relaxation.  Under-relaxation
might be good for reducing short wavelength oscillations.


Bibliography.

S.J. Townsend and C.S. Nichols, "The Geometry of Grain Disappearance in Thin
Polycrystalline Films" to appear in volume 343 of the Materials Research
Society Symposium Proceedings Series, 1994.



Back to top of Surface Evolver documentation. evolver-2.30c.dfsg/doc/square.gif0000644000175300017530000006523611410765113017176 0ustar hazelscthazelsctGIF89ad    ,,,,,-,-,,---,,-,---,---/0//000//000;;;;;<;<;;<<<;;<;<<<;<<W^zիW^RӫW^zBM^zիW^HիW^{+ (zիW^ ԫW^zի#E2իW^hիW^zիW/REhիWXNhիW^hիW^P+E;$>իW^@ 0` 0` 0` 0` 0`h"UDEXzꕪH^z"EBM^H 0zիW^zł%V^z+VEHիW^z!W^zիW/C4իW^20ԫW^zիW^W^h@իW/zիW^zcBM^hիW^zիW^zVLdիW^z*W^z իW^z*0` 0` 0` 0` 0`WXyh!իW/zF^zW^ ѠaW^z2!S^zիW^z*B"O^zW^zիW^zjTb1)W^zիW^z+"ԫW^Reʔ)SL2eʔ)404 @0`qb„ $H Q 0$PW^z T^zիW^jh U^z+X 0` 0` 0` 0`G^zJ; I^z+U^z+`REL^zիW^zիW<4bU^"eʔ)RL2Yʔ 0$p 0`0 *T(8 ` 0`(RL4PDLzիW^zիW^Hdʐ "zիW^4bիW^JU!4 իW^ 0` 0` 0` 0` GbիW^(I]zիW^4bիW^zeB 0`h  $H $H8p'` 0`1d)S&K2r% (zիW^zիW^zѪE>իW^zիW^20ԫWLBhիW^zիW^H ^W^իW^`hW&0zR̪^zQ 0` 0` 0` 0` 0`YPӫW^zḰjbիW^zӫW^zՊ իW^zիW^zńF^yHԫW^zիW^zS^2 !W^zիW^z"W^zAM^zիW^b W^zFUzիW^ իWX4z+ !zuW^0իW^$zիW"4u0` 0` 0` 0` 0`vA^z+ 0h!$S^zKE^z W4"UF^zիW^zIE^z 4zիW^zJEL^z+VRRիW^zC#V^zիW;4dիW^z1"W^zիW^ӫW^HHӫW^zIիW^RAѐ&O0իW^zӫW^zR 0` 0` 0` 0` /zիz*Ҫ*0$ԤW)zՋ;zRsWXwH0!իW^zXzիW^ aW^zF^zիW^z*B#O^zTP^zիW^zZXz"W^zիW^z+ (zW^z L^zի!%HdԫW^zD^zիW/R% 0` 0` 0` 0` 0`Kh  0+^z2D^Z+z+^z;zLzիW^z+`R^zիW^zիW<$UI^zիW^zZ)SLN@MLi 0`# &8@#V^zիW/zիW^bpAͫ^zիW"zիW^P0` 0` 0` 0` 0`yzիW/zիW*5*B+ C{իW^EZիW^(B* 0zիW^zʔ)SL21q@;` .`tCC &H8(`HPa„'h 0`qy2e(SL2eիW^zŒRB@իW^իW^z)AꎚE4hիW^HEիW^2` 0` 0` 0` 0`BjzիW^իW^zTA)zիW^HAիW^z28A $ 0`0Hɔ)SL2իW^zꕊ^wh!իW^zիW^zī^2 aW^zիW^z2D"STBzb"O^zEW^zW^(zzWzEW^b5WX 0` 0` 0` 0` 0:իW^zEW^zիע2LqիW^zꅢW^z T 54(W^zիW^z @&zTƝ^zիW^zK Rzj 5zիW^zꕉ^z+VNVիW^zEL^zшիW<4dիWTzbU^zԫW^`ԫם;zeJW^z 0` 0` 0` 0` 0`z(BH^zիWWzիW*]zɔ;zիWӫW^ze 0` 0` 0` 0` 0`Xzի^z+(zшKM.zիVzK PzԫWEHիW Surface Evolver Documentation - Energies

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Energies

The Evolver usually works by minimizing the total energy of the surface, subject to constraints. This energy can have several components:

Surface tension energy

Soap films and interfaces between different fluids have an energy content proportional to their area. Hence they shrink to minimize energy. The energy per unit area can also be regarded as a surface tension, or force per unit length. Each facet has a surface tension, which is 1 unless the datafile specifies otherwise (see TENSION attribute for faces). Different facets may have different surface tensions. Facet tensions may be changed interactively with the set facet tension ... command. The contribution to the total energy is the sum of all the facet areas times their respective surface tensions. The surface tension of a facet may also be specified as depending on the phases of the bodies it separates. In the string model, the tension resides on edges instead of facets.

Example datafile: cube.fe


Gravitational potential energy

If a body has a density, then that body contributes its gravitational energy to the total. The acceleration of gravity G is under user control with the G command. Letting \rho be the body density, the energy is defined as
  E = \int\int\int_{body} G \rho  z  dV 
but is calculated using the Divergence Theorem as
  E =  \int\int_{body surface} G\rho {z^2\over 2} \vec k \cdot \vec{dS}.  
This integral is done over each facet that bounds a body. If a facet bounds two bodies of different density, then the appropriate difference in density is used. Facets lying in the z = 0 plane make no contribution, and may be omitted if they are otherwise unneeded. Facets lying in constraints may be omitted if their contributions to the gravitational energy are contained in constraint energy integrals. In the string model, all this happens in one lower dimension.

Example datafile: mound.fe


Constraint energy integrals

An edge on a level-set constraint may have an energy given by integrating a vectorfield F over the oriented edge:
           E = \int_{edge} F . dl. 
  
The integrand is defined in the constraint declaration in the datafile. The integral uses the innate orientation of the edge, but if the orientation attribute of the edge is negative, the value is negated. This is useful for prescribed contact angles on walls (in place of wall facets with equivalent tension) and for gravitational potential energy that would otherwise require facets in the constraint. The mound example illustrates this.

Named quantity energies

There are a large number of named methods for calculating various quantities, which all follow the same syntax. These may be used as energy by defining an energy-type named quantity in the datafile.

Example datafile: ringblob.fe


Convex constraint gap energy

Consider a soap film spanning a circular cylinder. The Evolver must approximate this surface with a collection of facets. The straight edges of these facets cannot conform to the curved wall, and hence the computed area of the surface leaves out the gaps between the outer edges and the wall. The Evolver will naturally try to minimize area by moving the outer vertices around so the gaps increase, ultimately resulting in a surface collapsed to a line. This is not good. Therefore there is provision for a "gap energy" to discourage this. A level-set constraint may be declared CONVEX in the datafile. For an edge on such a constraint, an energy is calculated as
  E = k\left\Vert \vec S \times \vec Q \right\Vert / 6
    
where \vec S is the edge vector and \vec Q is the projection of the edge on the tangent plane of the constraint at the tail vertex of the edge. The constant k is a global constant called the "gap constant". A gap constant of 1 gives the best approximation to the actual area of the gap. A larger value minimizes gaps and gets vertices nicely spread out along a constraint. You can set the value of k in the datafile or with the k command.

The gap energy falls off quadratically as the surface is refined. That is, refining once reduces the gap energy by a factor of four. You can see if this energy has a significant effect on the surface by changing the gap constant.

Note: gap energy is effective only in the linear model.

Example datafile: tankex.fe


Prescribed pressure energy

Each body with a prescribed pressure P contributes energy E = PV. where V is the actual volume of the body. This can be used to generate surfaces of prescribed mean curvature, since mean curvature is proportional to pressure. Pressure can be prescribed in the bodies section of the datafile, and can be changed with the b command, or by assigning a value to the pressure attribute of a body.

Compressibility energy

If the ideal gas mode is in effect (set by the PRESSURE keyword in the datafile), then each body contributes an energy
         E = P*V_0*ln(V/V_0)  
where P is the ambient pressure, V_0 is the target volume of the body, and V is the actual volume. To account for work done against the ambeint pressure, each body also makes a negative contribution of
 
	 E = -P*V. 
The ambient pressure can be set in the datafile or with the p command. This energy is calculated only for bodies given a target volume.

Crystalline energy

The Evolver can model energies of crystalline surfaces. These energies are proportional to the area of a facet, but they also depend on the direction of the normal. The energy is given by the largest dot product of the surface normal with a set of vectors known as the Wulff vectors. Surface area can be regarded as a crystalline integrand whose Wulff vectors are the unit sphere. See the datafile section on Wulff vectors for more. A surface has either crystalline energy or surface tension, not both. Use is not recommended since nonsmoothness makes Evolver work poorly.

Example datafile: crystal.fe


Back to top of Surface Evolver documentation. Index. evolver-2.30c.dfsg/doc/default.htm0000644000175300017530000000566311410765113017343 0ustar hazelscthazelsct Surface Evolver Documentation

The Surface Evolver

Kenneth A. Brakke
Mathematics Department
Susquehanna University
Selinsgrove, PA, 17870
brakke@susqu.edu

with past support from
The Geometry Center
University of Minnesota
Minneapolis, MN 55454

Version 2.26, August 20, 2005


Contents

This HTML version of the Evolver documentation contains most of what the printed manual has, with the main exception of sections with a lot of mathematical formulas, such as the more advanced examples and the Techical Reference chapter. Not all possible links are in yet, but you can consult the Index to find something.

Web home page

My Web home page is http://www.susqu.edu/brakke/. Evolver-related material and links will be posted there. In particular, there are many examples of surfaces.
Back to top of Surface Evolver documentation.

Author's home page. evolver-2.30c.dfsg/doc/model.htm0000644000175300017530000012317611410765113017017 0ustar hazelscthazelsct Surface Evolver Documentation - Mathematical model

Surface Evolver Documentation

Back to top of Surface Evolver documentation. Index.

Surface models.

The Surface Evolver can handle several different models of surfaces. In fact, there are several different categories of models regarding different features, so one variety of each category is active. However, some combinations are incompatible.

Model categories:


Incompatible models

The following combinations of surface models are incompatible:

Surface representation and combinatorics

All surfaces are simplicial complexes made of the basic elements: vertices, edges, and facets. The Evolver has three different ways of representing the combinatorics of the surface, depending on the dimension of the surface. Any of these may be used in any ambient space dimension at least as great at the surface dimension.
  • String model for dimension 1 surface.
  • Soapfilm model (default) for dimension 2 surface.
  • Simplex model for dimension 3 or higher surface.

  • String model

    The term "string model" means that the surface is one-dimensional. The datafile must have the keyword "STRING" or "SURFACE_DIMENSION 1" in its top section. Edges are defined in terms of their vertices, and facets by a list of boundary edges. Facets are not divided into triangles, and may have any number of edges. The edges of a facet need not form a closed loop, for example if the facet is partly bounded by a constraint. A body is defined by associating one facet to it, and the volume of the body is the area of the facet. The default energy is edge length.

    Soapfilm model

    The term "soapfilm model" means that the dimension of the surface is 2. This is the default model. The surface is subdivided into triangular facets, and the default energy is surface area. Edges are defined by their vertices. Facets are defined by an oriented list of three edges, which must form a closed loop. However, faces in the datafile may have more than three edges, since they are automatically refined into facets when loaded. In official Evolver-speak, a "face" is what appears in the datafile, and a "facet" is the triangle in the internal Evolver representation of the surface. Bodies are defined by a set of oriented facets, which need not form the complete boundary of the body, for example if part of the boundary is on a constraint.

    Internally, the surface is held together by a set of structures called "facet-edges". There is one such structure for each incidence of an edge on a facet. There is a doubly linked list of facet-edges around each facet, so edges can be traversed in order, and there is a doubly-linked list around each edge, so the facets around an edge can be traversed in geometric order. Evolver figures out the geometric order from the geometric data in the datafile. If geometric order does not make sense, as when the space dimension is 4 or more, then the order is random.


    Simplex model

    The simplex model enables the representation of arbitrary dimension surfaces, but many Evolver features are not available with it. Here each facet is represented as an oriented list of k+1 vertices, where k is the dimension of the surface. Edges may be specified as k-1 dimensional simplices, but they are used only to compute constraint and named quantity integrals; a complete list of edges is not needed. Bodies are specified as lists of oriented facets.

    The datafile must have the keyword SIMPLEX_REPRESENTATION in the top section, and the phrase 'SURFACE_DIMENSION k' if k isn't 2. k = 1 and k = 2 are allowed, but not very useful in comparison to the string or soapfilm models. If the domain is not 3-dimensional, then 'SPACE_DIMENSION n' must also be included. The datafile edges section is optional. Each facet should list k+1 vertex numbers. Non-simplicial facets are not allowed. See the sample datafile simplex3.fe.

    Most features are not implemented. The quadratic model is not allowed, but the Lagrange model is. Vertices may be FIXED. Constraints are allowed, with energy integrands. Several basic named quantity methods work. No torus model or symmetry groups. No changing of surface topology or combinatorics is allowed except global refining with the r command. Refining subdivides each simplex 1-edge, with the edge midpoint inheriting the common attributes of the edge endpoints. Refining will increase the number of facets by a factor of 2^k.



    Representation of surface elements

    A surface is represented by a finite element method, where each element is a simplex. The simplest types of elements are just straight line segments and flat triangles. These combine to represent a piecewise linear surface, for which calculated quantities generally have an error of order h^2 compared to the ideal smooth surface, where h is the linear size of an element. Various ways of representing more accurate curved elements exist, and Evolver implements a couple of versions of what are called "Lagrange elements".
  • Linear model (default)
  • Quadratic model
  • Lagrange model

  • Linear model

    In the linear model, all edges and triangular facets are flat line segments and triangles, respectively. For all calculations, an edge is defined by its two endpoints, and a facet (in the soapfilm model) is defined by its three vertices. This is the default. Quadratic or Lagrange models may be changed to linear with the M 1 or linear commands. An exception is if the spherical_arc_length method is used for length_method_name in the string model, in which case edges are computed and drawn on a sphere centered at the origin.

    Quadratic model

    By default, edges and facets are linear. But it is possible to represent edges as quadratic curves and facets as quadratic patches by using the quadratic model. Each edge is endowed with a midpoint vertex. Most features are implemented for the quadratic representation, but some named quantity methods are not available, such as those involving curvature.

    A special case is circular arc mode, which is effective in quadratic mode in the string model with the circular_arc_length method used for length_method_name. Then edges are calculated and drawn as exact circular arcs through their three vertices.

    The smoothness of graphing of curved quadratic edges can be controlled with the internal variable string_curve_tolerance, which is the desired angle in degrees between successive graphed segments making up the edge.

    The quadratic model can be invoked by putting the QUADRATIC keyword in the top section of the datafile or by using the quadratic or M 2 commands.


    Lagrange model

    The Evolver has a very limited implementation of higher-order elements. In the Lagrange model of order n, each edge is defined by interpolation on n+1 vertices evenly spaced in the parameter domain, and each facet is defined by interpolation on (n+1)(n+2)/2 vertices evenly spaced in a triangular pattern in the parameter domain. That is, the elements are Lagrange elements in the terminology of finite element analysis.

    The Lagrange model is defined only for named quantities and methods, so Evolver will automatically do convert_to_quantities when you invoke the Lagrange model. The methods that currently accept the Lagrange model are

    A surface may be converted to an order n Lagrange model with the command "lagrange n". This will convert linear or quadratic models to Lagrange, and will convert between different order Lagrange models. The commands linear and quadratic will convert Lagrange model back to the linear or quadratic models.

    No triangulation manipulations are available in the Lagrange model. No refining, equiangulation, or anything. There is some vertex averaging, but just internal to edges and facets. Use the linear or quadratic model to establish your final triangulation, and just use the Lagrange model to get extra precision.

    The current order can be accessed through the read-only internal variable lagrange_order. The Lagrange model can be dumped and reloaded.

    As the Lagrange order is raised, calculations slow down rapidly. This is not only due to the large number of points involved, but is also due to the fact that the order of Gaussian integration is also raised.

    Lagrange elements are normally plotted subdivided on their vertices, but if the smooth_graph flag is on, they are plotted with 8-fold subdivision.

    The toggle command bezier_basis toggle replaces the Lagrange interpolation polynomials (which pass through the control points) with Bezier basis polynomials (which do not pass through interior control points, but have positive values, which guarantees the edge or facet is within the convex hull of the control points). This is experimental at the moment, and not all features such as graphing or refinement have been suitably adjusted.



    Space dimension

    By default, surfaces live in 3 dimensional space. However, the phrase "SPACE_DIMENSION n" in the datafile header sets the dimension to n. This means that all coordinates and vectors have n components. The only restriction is that Evolver has to be compiled with the MAXCOORD macro defined to be at least n in Makefile or in model.h. The default MAXCOORD is 4. Change MAXCOORD and recompile if you want more than four dimensions. The actual space dimension can be accessed in commands through the read-only variable space_dimension. Graphics will display only the first three dimensions of spaces with more than three dimensions, except for geomview, which has a four-dimensional viewer built in (although its use is awkward now).

    Metric

    For length and area to make sense, the ambient space must be endowed with a metric. The Evolver offers several choices, but keep in mind that they are only used to calculate default length and area. Other quantities that depend on the metric, such as volume, are up to the user to put in by hand with named quantities. All displaying is done as if the metric is Euclidean.
  • Euclidean metric (default)
  • Riemannian metric
  • Conformal metric
  • Klein hyperbolic metric

  • Euclidean metric

    The default metric on the ambient space is the ordinary Euclidean metric. There are no built-in units of measurement like meters or grams, so the user should express all physical quantities in some consistent system of units, such as MKS or cgs.

    Riemannian metric

    The ambient space can be endowed with a general Riemannian metric by putting the keyword METRIC in the datafile followed by the elements of the metric tensor. Only one coordinate patch is allowed, but the quotient space feature makes this quite flexible. Edges and facets are linear in coordinates, they are not geodesic. The metric is used solely to calculate lengths and areas. It is not used for volume. To get a volume constraint on a body, you will have to define your own named quantity constraint. See quadm.fe for an example of a metric.

    Conformal metric

    The ambient space can be endowed with a conformal Riemannian metric by putting the keyword CONFORMAL_METRIC in the datafile followed by a formula for the conformal factor, i.e. the multiple of the identity matrix that gives the metric. Only one coordinate patch is allowed, but the quotient space feature makes this quite flexible. Edges and facets are linear in coordinates, they are not geodesic. The metric is used solely to calculate lengths and areas. It is not used for volume. To get a volume constraint on a body, you will have to define your own named quantity constraint. See quadm.fe for an example of a metric.

    Klein hyperbolic metric

    One special metric is available built-in. It is the Klein model of hyperbolic space in n dimensions. The domain is the unit disk or sphere in Euclidean coordinates. Including the keyword KLEIN_METRIC in the top section of the datafile will invoke this metric. Lengths and areas are calculated exactly, but as with other metrics you are on your own for volumes and quantities.

    Symmetries

    There are many interesting problems dealing with symmetric surfaces. A natural way to deal with a symmetric surface is to compute with only one fundamental domain of the symmetry, and use special boundary conditions. Some symmetries, such as mirror symmetries, can be handled with normal Evolver features. For example, a mirror can be implemented as a planar level set constraint. But symmetries such as translational or rotational symmetry require some built-in features. In any case, multiple copies of the fundamental domain may be displayed with the view_transforms command.
  • No symmetry (default)
  • Torus model (translational symmetry)
  • Symmetry groups (general symmetry)

  • No symmetry

    By default, the domain of a surface is Euclidean space. A symmetric surface can be done this way if its fundamental domain is bounded by mirror planes. Each mirror plane should be implemented as a linear level set constraint.

    Torus model

    The Evolver can take as its domain a flat torus with an arbitrary parallelpiped as its unit cell, i.e. the domain is a parallelpiped with its opposite faces identified. This is indicated by the TORUS keyword in the datafile. The defining basis vectors of the parallelpiped are given in the TORUS_PERIODS entry of the datafile. See twointor.fe for an example.

    Vertex coordinates are given as Euclidean coordinates within the unit cell, not as linear combinations of the period vectors. The coordinates need not lie within the parallelpiped, as the exact shape of the unit cell is somewhat arbitrary.

    The way the surface wraps around in the torus is given by saying how the edges cross the faces of the unit cell. In the datafile edges section, each edge has one symbol per dimension indicating how the edge vector crosses each identified pair of faces, and how the vector between the endpoints needs to be adjusted to get the true edge vector:
    * does not cross face
    + crosses in same direction as basis vector, so basis vector added to edge vector
    - crosses in opposite direction, so basis vector subtracted from edge vector.
    Wraps are automatically maintained by the various triangulation manipulation operations.

    There are several commands for ways of displaying a torus surface:
    raw_cells - Graph the facets as they are, without clipping. The first vertex of a facet is used as the basepoint for any unwrapping of other vertices needed.
    connected - Each body's facets are unwrapped in the torus, so the body appears in one connected piece. Nicest option, but won't show facets not on bodies.
    clipped - Shows the unit cell specified in the datafile. Facets are clipped on the parallelpiped faces.

    A few features are not available with the torus model, such as gravity and the simplex model. (Actually, you could put them in, but they will not take into account the torus model.)

    Volumes and volume constraints are available. However, if the torus is completely partitioned into bodies of prescribed volume, then the volumes must add up to the volume of the unit cell and the TORUS_FILLED keyword must be given in the datafile. Or just don't prescribe the volume of one body.

    Volumes are somewhat ambiguous. The volume calculation method is accurate only to one torus volume, so it is possible that a body whose volume is positive gets its volume calculated as negative. Evolver adjusts volumes after changes to be as continuous as possible with the previous volumes as possible, or with target volumes when available. You can also set a body's volconst attribute if you don't like the Evolver's actions.

    Level set constraints can be used in the torus model, but be cautious when using them as mirror symmetry planes with volumes. The torus volume algorithm does not cope well with such partial surfaces. If you must, then use y=const symmetry planes rather than x=const, and use the -q option or do convert_to_quantities. Double-check that your volumes are turning out correctly; use volconst if necessary.

    Display_periods: The displayed parallelogram unit cell can be different from the actual unit cell if you put an array called display_periods in the top of the datafile, in addition to the regular periods. For a string model example,

       parameter shear = 1
       torus_filled
       periods
       4  0
       shear  4
       display_periods
       4 0
       0 4 
    This will always display a square, no matter how much the actual unit cell is sheared. This feature works well for shears; it may not work nicely for other kinds of deformation. Display_periods works better for the string model than the soapfilm model. For the soapfilm model, it seems to do horizontal shears best, but it can't cope with large shears, so if your shear gets too large, I advise resetting your fundamental region to less shear, say with the unshear command in unshear.cmd.

    Display_origin: For a torus mode surface, if clipped mode is in effect, the center of the clip box is set with the display_origin[] array whose dimension is the dimension of the ambient space. This array does not exist by default, it has to be created by the user in the top of the datafile with the syntax

      display_origin x y z
    
    where x y z are the coordinates for the desired center of the clip box. At runtime, the array elements may be changed as normal:
      display_origin[2] := 0.5
    
    Changing display_origin will automatically cause the graphics to re-display.

    Symmetry groups and quotient spaces

    As a generalization of the torus model, you may declare the domain to be the quotient space of R^n with respect to some symmetry group. Several built-in groups are available, and ambitious users can compile C code into Evolver to define group operations. Group elements are represented by integers attached to edges (like the wrap specifications in the torus model at runtime). You define the integer representation of the group elements. See the file quotient.c for an example. See khyp.c and khyp.fe for a more intricate example modelling an octagon in Klein hyperbolic space identified into a genus 2 surface.

    The datafile requires the keyword SYMMETRY_GROUP followed by the name for the group in quotes. Edges that wrap have their group element specified in the datafile by the phrase "wrap n", where n is the number of the group element. The wrap values are accessible at run time with the wrap attribute of edges. The group operations are accessible by the functions wrap_inverse(w) and wrap_compose( w1,w2).

    Using any Hessian commands with any symmetry group (other than the built-in torus model) will cause automatic converting to named quantities (with the " convert_to_quantities" command, since only named quantity Hessian evaluation routines have the proper symmetry transformation of the Hessian programmed in.

    Volumes of bodies might not be calculated correctly with a symmetry group. The volume calculation only knows about the built-in torus model. For other symmetry groups, if you declare a body, it will use the Euclidean volume calculation. It is up to you to design an alternate volume calculation using named quantities and methods.

    The currently implemented symmetry groups are:

    • torus - The underlying group for the torus model.
    • rotate - Cyclic group of rotations in the x-y plane.
    • flip_rotate - Cyclic group of rotations in the x-y plane with z mapping to -z with every odd rotation.
    • cubocta - Full point group of a cube.
    • xyz - The orientation-preserving subgroup of cubocta.
    • genus2 - For a 2 dimensional genus 2 hyperbolic quotient space.
    • dodecahedron - For a 3D hyperbolic quotient space with dodecahedral fundamental region.
    • central_symmetry - Inversion through the origin, X mapping to -X.
    • screw_symmetry - Screw motion along z axis.
    • quarter_turn

    TORUS symmetry group

    This is the underlying symmetry for the torus model. Although the torus model has a number of special features built in to the Evolver, it can also be accessed through the general symmetry group interface. The torus group is the group on n-dimensional Euclidean space generated by n independent vectors, called the period vectors. The torus group uses the torus periods listed in the datafile.

    Datafile declaration: symmetry_group "torus"

    Group element encoding: The 32-bit code word is divided into 6-bit fields, one field for the wrap in each dimension, with low bits for the first dimension. Hence the maximum space dimension is 5. Within each bitfield, 1 codes for positive wrap and 011111 codes for negative wrap. The coding is actually a 2's complement 5-bit integer, so higher wraps could be represented.


    ROTATE symmetry group

    This is the cyclic symmetry group of rotations in the x-y plane, where the order of the group is given by the internal variable rotation_order (default value 4). There is also an internal variable generator_power (default 1) such that the angle of rotation is 2*pi*generator_power/rotation_order. Note: Since this group has fixed points, some special precautions are necessary. Vertices on the rotation axis must be labelled with the attribute axial_point in the datafile. Edges out of an axial point must have the axial point at their tail, and must have zero wrap. Facets including an axial point must have the axial point at the tail of the first edge in the facet.

    Datafile declaration:

      symmetry_group "rotate"
      parameter rotation_order = 6 

    Group element encoding: An element is encoded as the power of the group generator.


    FLIP_ROTATE symmetry group

    This is the cyclic symmetry group of rotations in the x-y plane with a flip z mapping to -z on every odd rotation, where the order of the group is given by the internal variable rotation_order, which had better be even. Note: Since this group has points that are fixed under an even number of rotations, some special precautions are necessary. Vertices on the rotation axis must be labelled with the attribute "double_axial_point" in the datafile. Edges out of an axial point must have the axial point at their tail, and must have zero wrap. Facets including an axial point must have the axial point at the tail of the first edge in the facet.

    Datafile declaration:

      symmetry_group "flip_rotate"
      parameter rotation_order = 6 

    Group element encoding: An element is encoded as the power of the group generator.


    CUBOCTA symmetry group

    This is the full symmetry group of the cube. It can be viewed as all permutations and sign changes of (x,y,z).

    Datafile declaration: symmetry_group "cubocta"

    Group element encoding: wrap & {1,2,4} gives the sign changes for x,y,z respectively; (wrap&24)/8 is the power of the (xyz) permutation cycle; (wrap&32)/32 tells whether to then swap x,y. (By John Sullivan; source in quotient.c under name pgcube)


    XYZ symmetry group

    The orientation-preserving subgroup of cubocta. Same representation.

    GENUS2 symmetry group

    This is a symmetry group on the Klein model of hyperbolic space whose quotient group is a genus 2 hyperbolic manifold. The fundamental region is an octagon.

    Datafile declaration:

       Klein_metric
       symmetry_group "genus2" 
    Group element encoding: There are 8 translation elements that translate the fundamental region to one of its neighbors. Translating around a vertex gives a circular string of the 8 elements. The group elements encoded are substrings of the 8, with null string being the identity. Encoding is 4 bits for each element. See khyp.fe for an example.

    DODECAHEDRON symmetry group

    This is the symmetry group of translations of hyperbolic 3 space tiled with right-angled dodecahedra. The elements of the group are represented as integers. There are 32 generators of the group so each generator is represented by five bits. Under this scheme any element that is the composition of up to five generators can be represented. If you want to use this group, you'll have to check out the source code in dodecgroup.c, since somebody else wrote this group and I don't feel like figuring it all out right now.

    Datafile declaration:

       Klein_metric
       symmetry_group "dodecahedron" 

    CENTRAL_SYMMETRY symmetry group

    This is the order 2 symmetry group of inversion through the origin, X maps to -X.

    Datafile declaration:

       symmetry_group "central_symmetry" 
    Group element encoding: 0 for identity, 1 for inversion.

    SCREW_SYMMETRY symmetry group

    This is the symmetry group of screw motions along the z axis. The global parameter screw_height is the translation distance (default 1), and the global parameter screw_angle is the rotation angle in degrees (default 0).

    Datafile declaration:

      parameter screw_height = 4.0
      parameter screw_angle  = 180.0
      symmetry_group "screw_symmetry"
    
    Group element encoding: the integer value is the power of the group generator.

    quarter_turn

    Symmetry group 3D torus with quarter turn in identification of top and bottom. x and y periods taken to be 1. z period is the user-defined variable quarter_turn_period. Generators x,y,z. x and y as in regular torus mode. z is vertical translation with quarter turn: (x,y,z)->(-y,x,z).
    Relations: x z = z y^-1, y z = z x
    Numerical representation: as in the torus symmetry group, for powers of x,y,z with generators applied in that order.

    Mobility

    There is a choice to be made in the conversion of the forces on vertices into velocities of vertices. Technically, force is the gradient of energy, hence a covector on the manifold of all possible configurations. In the Evolver representations of surfaces, that global covector can be represented as a covector at each vertex. The velocity is a global vector, which is represented as a vector at each vertex. Conversion from the global covector to the global vector requires multiplication by a metric tensor, i.e. singling out a particular inner product on global vectors and covectors. The tensor converting from force to velocity is the mobility tensor, represented as the mobility matrix M in some coordinate system. Its inverse, converting from velocity to force, is the resistance tensor S = M^{-1}. The same inner product has to be used in projecting the velocity tangent to the constraints, whether they be level set constraints on vertices or constraints on body volumes or quantity integrals. There are several choices implemented in the Evolver, corresponding to several different physical pictures of how the medium resists the motion of the surface through it:
  • unit mobility
  • area normalization
  • area normalization with effective area
  • approximate polyhedral curvature
  • user-defined mobility

  • Unit mobility

    This is the default mobility, in which the velocity is equal to the force. Hence M and S are the identity matrices in standard coordinates. The physical interpretation of this is that there is a resistance to motion of each vertex through the medium proportional to its velocity, but not for the edges. This does not approximate motion by mean curvature, but it is very easy to calculate.

    Area normalization

    A type of mobility. In motion by mean curvature, the resistance to motion is really due to the surfaces, not the vertices. One way to approximate this is to say the resistance to motion of a vertex is proportional to the area associated with the vertex. So this scheme counts the area of a vertex equal to 1/3 of the area of the star of facets around it (or 1/2 the area of the star of edges in the string model). This is easy to calculate, since it is a local calculation for each vertex. S and M are diagonal matrices (see mobility). The result is that motion = force/area, which is a much better approximation to motion by mean curvature than the default of motion = force. Area normalization can be toggled with the a command or the area_normalizaton toggle.

    Area normalization with effective area

    A type of mobility. Simple area normalization as described in the previous paragraph isn't what's really wanted in certain circumstances. It has equal resistance for motion in all directions, both parallel and normal to the surface. If a vertex is a triple junction and migrating along the direction of one of the edges, it shouldn't matter how long that edge is. Therefore, if the effective area mode is in effect, the area associated with a vertex is the area of its star projected normal to the force at the vertex. This is a little more complicated calculation, but it is still local. S and M are block diagonal matrices, with one block for each vertex (see mobility). At a free edge not on any constraint, the force is tangent to the surface, the resistance is zero, and the mobility is infinite. But this accurately describes a popping soapfilm. Effective area can be toggled with the effective_area toggle. Note that area normalization itself must still be toggled with a or area_normalizaton.

    Approximate polyhedral curvature

    A type of mobility. Following a suggestion of Gerhard Dzuik and Alfred Schmidt, the inner product of global vectors is taken to be the integral of the scalar product of their linear interpolations over the facets (or edges in the string model). This has the advantage that the rate of area decrease of the surface is equal to the rate volume is swept out by the surface, which is a characteristic of motion by mean curvature. A big disadvantage is that the matrices M and S are no longer local (see mobility). S is a sparse matrix with entries corresponding to each pair of vertices joined by an edge, and M is its dense inverse. Approximate polyhedral curvature can be toggled with the approx_curv toggle command.

    Approximate polyhedral curvature with effective area.

    Polyhedral curvature does not make any distinction between motion parallel and perpendicular to the surface. A better approximation is to count only motion perpendicular to the surface. This can be done by projecting the interpolated vectorfields normal to the facets before integrating their scalar product. Now the rate of area decrease is equal to the rate geometric volume is swept out, as opposed to the slightly flaky way one had to calculate volume sweeping in the previous paragraph. Again S is a sparse matrix with entries corresponding to each pair of vertices joined by an edge, and M is its dense inverse. The effective area option may be toggled with effective_area.

    User-defined mobility

    The user may define a mobility tensor in the datafile. There is a scalar form with the keyword MOBILITY and a tensor form with MOBILITY_TENSOR. When in effect, this mobility is multiplied times the velocity to give a new velocity. This happens after any of the previous mobilities of this section have been applied and before projection to constraints. The formulas defining the mobility may include adjustable parameters, permitting the mobility to be adjusted during runtime. The formulas are evaluated at each vertex at each iteration, and so formulas may depend on vertex position and any vertex attributes.
    Back to top of Surface Evolver documentation. Index. evolver-2.30c.dfsg/doc/eigentut.htm0000644000175300017530000012672711410765113017550 0ustar hazelscthazelsct Surface Evolver Documentation

    Surface Evolver Documentation

    Back to top of Surface Evolver documentation. Index.

    Hessian, Eigenvalues, Eigenvectors, and Stability in the Surface Evolver

    Our motto: The purpose of calculus is to turn every problem into a linear algebra problem.

    Contents:

    1. How to think of surfaces discretized in the Evolver.
    2. What is the Hessian?
    3. Eigenvalues and eigenvectors.
    4. Physical interpretation of eigenvalues and eigenvectors.
    5. Eigenprobe command.
    6. Ritz command.
    7. The Square example.
    8. Hessian with metric.
    9. Normal motion mode.
    10. Visualizing eigenvectors.
    11. Hessian iteration.
    12. Hessian_seek.
    13. Saddle.
    14. Unstable surfaces.
    15. Accuracy of eigenvalues.
    16. Caveats and warnings.
    For background, see any decent linear algebra text, such as Strang [SG], especially chapter 6. For more on stability and bifurcations, see Arnol'd [AV] or Hale and Kocak [HK].

    1. How to think of surfaces discretized in the Evolver.

    The Surface Evolver parameterizes a surface in terms of vertex coordinates. Restrict attention to one fixed triangulation of a surface. Let X be the vector of all vertex coordinates, containing N entries, where N could be thousands. Call X the "configuration vector", and call the N-dimensional vector space it inhabits "configuration space". The total energy E(X) is a scalar function on configuration space. Visualize the graph of the energy function as a rolling landscape with mountains, valleys, and mountain passes. The gradient of the energy is the steepest uphill direction. The ordinary 'g' iteration step of Evolver minimizes energy by moving in the downhill direction, the negative of the gradient direction, seeking a local minimum.

    A constraint is a scalar function on configuration space such that X is restricted to lie on a particular level set. A global constraint, such as a volume constraint, determines a hypersurface, that is, it removes one degree of freedom from the configuration space. Level-set constraints on vertices, such as x^2 + y^2 = 4, are actually a separate constraint for each vertex they apply to. Each removes one degree of freedom for each vertex it applies to. The intersection of all the constraint hypersurfaces is called the "feasible set", and is the set of all possible configurations satisfying all the constraints. If there are any nonlinear constraints, such as curved walls or volume constraints, then the feasible set will be curved. When a surface is moved, by 'g' for example, the vector for the direction of motion is always first projected to be tangent to the feasible set. However, if the feasible set is curved, straight-line motion will deviate slightly from the feasible set. Therefore, after every motion X is projected back to the feasible set using Newton's Method.


    2. What is the Hessian?

    Consider a particular surface in a particular configuration X, and consider a small perturbation Y added to X. Then the energy may be expanded in a Taylor series:
    E(X+Y) = E0 + GT Y + 1/2 YT H Y + ...
    Here the constant E0 is the original energy, E0 = E(X). G is the vector of first derivatives, the gradient, which is a vector the same dimension as Y or X (GT is a 1-form, if you want to get technical). Here T denotes the transpose. H is the square matrix of second derivatives, the Hessian. The Hessian is automatically a symmetric matrix. The gradient G determines the best linear approximation to the energy, and the Hessian H determines the best quadratic approximation.

    Positive definiteness. If the quadratic term 1/2 YT H Y is always positive for any nonzero Y, then H is said to be positive definite. At an equilibrium point, this means the point is a strict local minimum; this is the multivariable version of the second derivative test for a minimum. If the quadratic term is zero or positive, then H is positive semi-definite. No conclusion can be drawn about being a local minimum, since third-order terms may prevent that. If the quadratic term has positive and negative values, then H is indefinite.

    Constraints. If there are constraints on the surface, then the the perturbation vector Y is required to be tangent to the feasible set in configuration space. The curvature of the feasible set can have an effect on H, but this is taken care of automatically by the Evolver. This effect arises from the slight change in energy due to the projection back to the feasible set after motion. This does not affect the gradient, but it does affect the Hessian. In particular, if Q is the Hessian of a global constraint and q is the Lagrange multiplier for the constraint, then the adjusted energy Hessian is H - qQ. Therefore it is necessary to do a 'g' step to calculate Lagrange multipliers before doing any Hessians when there are global constraints.


    3. Eigenvalues and eigenvectors.

    The Hessian H is a real symmetric matrix. Therefore it can be diagonalized by an orthogonal change of basis of configuration space. The new basis vectors are called eigenvectors, and the entries on the diagonal version of H are called eigenvalues. In this eigenvector basis, the shape of the graph of the quadratic term becomes obvious. Directions along eigenvectors with negative eigenvalues have curvature downward. Directions with positive eigenvalues have upward curvature. Remember that eigenvectors are in configuration space, so each eigenvector represents a particular perturbation of the surface.

    Another characterization of an eigenvector Q with an eigenvalue q is

    		       H Q = q Q.
    
    which is obvious in an eigenvector basis, but serves to define eigenvectors in an arbitrary basis.

    Index and inertia. The number of negative eigenvalues of H is called the index of H. The triple of numbers of negative, zero, and positive eigenvalues is called the inertia of H. One can also speak of the inertia relative to a value c as the inertia of H-cI, i.e. the number of eigenvalues less than c, equal to c, and greater than c. Sylvester's Law of Inertia says that if P is a nonsingular matrix of the same dimension as H, then the matrix

    K = P H PT
    has the same inertia as H, although not the same eigenvalues. The Evolver can factor a Hessian into a form
    H = L D LT
    where L is a lower triangular matrix and D is diagonal. Hence the inertia of H can be found by counting signs on the diagonal of D. Inertia relative to c can be found by factoring H-cI.

    The eigenvectors associated to a particular eigenvalue form a subspace, whose dimension is called the "multiplicity" of the eigenvalue.


    4. Physical interpretation of eigenvalues and eigenvectors.

    One can think of an eigenvector as a mode of pertubation of the surface, and the corresponding eigenvalue an indicator of its stability. Consider a surface at equilibrium. Recall that the gradient after a small perturbation from equilibrium is
         grad E = H Y.
    
    So if we move along an eigenvector direction,
        grad E = H Q = q Q.
    
    Note the force is aligned exactly with the perturbation. Since force is negative gradient, if q is positive the force will restore the surface to equilibrium (stable perturbation), and if q is negative the force will cause the pertubation to grow (unstable perturbation). Each eigenvector thus represents a mode of perturbation. Furthermore, different modes grow or decay independently, since in an eigenvector basis the Hessian is diagonal and there are no terms linking the different modes.

    5. Eigenprobe command

    The inertia of the Hessian with respect to some value may be found with the eigenprobe command. The syntax to find the inertia relative to a probe value c is "eigenprobe c". For example, "eigenprobe 1" would report the number of eigenvalues less than 1, equal to 1, and greater than 1. For example, suppose we use cube.fe, do "r; r; g 5; eigenprobe 1".
    Vertices: 50  Edges: 144  Facets: 96  Bodies: 1  Facetedges: 288
    Element memory: 39880
    Total data memory: 294374 bytes.
    Enter command: r
    Vertices: 194  Edges: 576  Facets: 384  Bodies: 1  Facetedges: 1152
    Element memory: 157384
    Total data memory: 422022 bytes.
    Enter command: g 5
      5. area:  5.35288108682446 energy:  5.35288108682446  scale: 0.233721
      4. area:  5.14937806689761 energy:  5.14937806689761  scale: 0.212928
      3. area:  5.04077073041990 energy:  5.04077073041990  scale: 0.197501
      2. area:  4.97600054126748 energy:  4.97600054126748  scale: 0.213378
      1. area:  4.93581733363156 energy:  4.93581733363156  scale: 0.198322
    Enter command: eigenprobe 1
    Eigencounts:    18 <,  0 ==,  175 >
    
    This means the Hessian has 18 eigenvalues less than 1, none equal to 1, and 175 greater than 1. Note the total number of eigenvalues is equal to the total number of degrees of freedom of the surface, in this case
       (# vertices)-(# constraints) = 194 - 1 = 193 = 18+175
    
    where the constraint is the volume constraint. Why there is one degree of freedom per vertex instead of three is explained below in the normal_motion section.


    6. Ritz command

    For calculation of eigenvalues near a particular probe value, there is the ritz command (named after the Rayleigh-Ritz algorithm). The syntax is "ritz(c,n)", where c is the probe value and n is the desired number of eigenvalues.

    For example, evolve cube.fe with "g 5; r; g 5; r; g 5", and do "ritz(0,5)". You should get output like this:

    Enter command: ritz(0,5)
    Eigencounts:    0 <,  0 ==,  193 >
      1.    0.001687468263013
      2.    0.001687468263013
      3.    0.001687468263013
      4.    0.2517282974725
      5.    0.2517282974731
    Iterations: 710. Total eigenvalue changes in last iteration: 4.0278891e-013
    
    The first line of output is the inertia, exactly as in eigenprobe. Then come the eigenvalues as the algorithm finds them, usually in order away from the probe value.

    How ritz works: For ritz(c,n), Evolver creates a random n-dimensional subspace and applies shifted inverse iteration to it, i.e. repeatedly applies (H - cI)-1 to the subspace and orthonormalizes it. Eigenvalues of H near p correspond to large eigenvalues of (H - cI)-1, so the corresponding eigenvector components grow by large factors each inverse iteration. Evolver reports eigenvalues as they converge to machine precision. When all desired eigenvalues converge (as by the total change in eigenvalues in an iteration being less than 1e-10), the iteration ends and the values are printed. Lesser precision is shown on these to indicate they are not converged to machine precision. You can interrupt ritz by hitting the interrupt key (usually CTRL-C), and it will report the current eigenvalue approximations.

    NOTE: It is best to use probe values below the least eigenvalue, so H-cI is positive definite. Makes the factoring more numerically stable.


    7. The Square example

    square We now explore the eigenvalues of a very simple surface, a flat square with fixed sides. For a continuous surface, classical calculus of variations reveals that the Hessian for area is the Laplace operator. For a square of side length pi, this has eigenvalues m2 + n2 for m,n = 1,2,3,..., or in ascending sequence 2, 5, 5, 8, 10, 10, 13, 13, 17, 17, .. (you may remember this from PDE analysis of the heat equation). Run the datafile square.fe and refine it twice (no need to evolve, since it is already flat). Do "ritz(0,10)".
    Enter command: ritz(0,10)
    Eigencounts:    0 <,  0 ==,  25 >
      1.    0.585786437626905
      2.    1.386874070247247
      3.    1.386874070247247
      4.    2.000000000000000
      5.    2.585786437626905
      6.    2.585786437626906
      7.    2.917607799707606
      8.    2.9176077997076
      9.    3.4142135623733
     10.    4.0000000000000
    
    Doesn't look exactly like we expected. It does have multiplicity two eigenvalues in the right places, though. Maybe we haven't refined enough. Refine again and do "ritz(0,10)". You get
    Enter command: ritz(0,10)
    Eigencounts:    0 <,  0 ==,  113 >
      1.    0.152240934977427
      2.    0.375490214588449
      3.    0.375490214588449
      4.    0.585786437626905
      5.    0.738027372604331
      6.    0.738027372604331
      7.    0.927288973154334
      8.    0.927288973154335
      9.    1.225920309355705
     10.    1.2259203093583
    
    Things are getting worse! The eigenvalues all shrunk drastically! They are not converging! What's going on? The next section explains.

    8. Hessian with metric.

    One would expect that refining the surface would lead the eigenvalues to converge to the eigenvalues for the smooth surface. But as the previous section showed, refining caused the eigenvectors to all shrink by about a factor of 4. This is no way to converge. The explanation is that so far we have been looking at eigenvectors in slightly the wrong way. An eigenvector is supposed to represent a mode of perturbation that is proportional to the force. However, the response of a surface to a force need not be numerically equal to the force. After all, forces and displacements are different kinds of things. They have different units: displacement has units of distance, and force has units of energy per distance. In other words, displacement is a vector and force is a covector. Note that matrix multiplication by the Hessian H turns a vector into a covector. In general, to turn a vector into an equivalent covector, one needs an inner product, or metric. So far we have been using the Euclidean inner product on configuration space, but that is not the proper one to use if you want to approximate smooth surfaces. The proper inner product of perturbations f and g of a smooth surface is the integral over the surface of the pointwise inner product:
               /
       <f,g> = | <f(x),g(x)> dA.
               /
    
    In discrete terms, the inner product takes the form of a square matrix M such that <Y,Z> = YT M Z for vectors Y,Z. We want this inner product to approximate integration with respect to area. Having such an M, the eigenvalue equation becomes
                   H Q = q M Q.
    
    Officially, Q is now called a "generalized eigenvector" and q is a "generalized eigenvalue". But we will drop the "generalized". An intuitive interpretation of this equation is as Newton's Law of Motion,
                  Force = Mass * Acceleration 
    where HQ is the force, M is the mass, and qQ is the acceleration. In other words, in an eigenmode, the acceleration is proportional to the perturbation.

    The Evolver command "linear_metric" includes M in the eigenvector calculations. Two metrics are available. In the simplest, the "star metric", M is a diagonal matrix, and the "mass" associated with each vertex is 1/3 of the area of the adjacent facets (1/3 since each facet gets shared among 3 vertices). The other, the "linear metric", extends functions from vertices to facets by linear interpolation and integrates with respect to area. By default, "linear_metric" uses a 50/50 mix of the two, which seems to work best. See the more detailed discussion below in the eigenvalue accuracy section. The fraction of linear metric can be set by assigning the fraction to the internal variable linear_metric_mix. By default, linear_metric_mix is 0.5. In quadratic mode, however, only the quadratic interpolation metric is used, since the star metric restricts convergence to order h2 while the quadratic interpolation metric permits h4 convergence.

    Example: Run square.fe, and refine twice. Do "linear_metric" and "ritz(0,10)".

    Enter command: linear_metric
    Using linear interpolation metric with Hessian.
    Enter command: ritz(0,10)
    Eigencounts:    0 <,  0 ==,  25 >
      1.    2.036549240354335
      2.    4.959720306550875
      3.    4.959720306550875
      4.    7.764634143334637
      5.   10.098798069316683
      6.   10.499717069102575
      7.   12.274525789880887
      8.   12.274525789880890
      9.   15.7679634642721
     10.   16.7214142904405
    Iterations: 127. Total eigenvalue changes in last iteration: 1.9602375e-014
    
    After refining again:
    Enter command: ritz(0,10)
    Eigencounts:    0 <,  0 ==,  113 >
      1.    2.009974216370147
      2.    4.999499042685446
      3.    4.999499042685451
      4.    7.985384943789077
      5.   10.090156419079085
      6.   10.186524915471155
      7.   13.008227978344527
      8.   13.008227978344527
      9.   17.242998229925817
     10.   17.2429982299600
    Iterations: 163. Total eigenvalue changes in last iteration: 1.9186042e-014
    
    This looks much more convergent.

    Using linear_metric does NOT change the inertia of the Hessian, by Sylvester's Law of Inertia. So the moral of the story is that if you care only about stability, you don't need to use linear_metric. If you do care about the actual values of eigenvectors, and want them relatively independent of your triangulation, then use linear_metric.


    9. Normal motion mode.

    The alert reader will have notice that in the examples so far there has been only one degree of freedom per vertex, instead of the three one might expect, since a vertex has three degrees of freedom to move in space. The answer is that in Hessian activities, it is usually best to only allow motion perpendicular to the surface, and suppress the two degrees of freedom of motion of a vertex tangential to the surface. The reason is that tangential motion changes the energy of the surface very little if at all, leading to many small eigenvalues and a severely singular Hessian. For example, run square.fe, refine twice, and do "hessian_normal off" to enable all degrees of freedom. Now "eigenprobe 0" reveals 50 zero eigenvalues:
    Enter command: hessian_normal off
    hessian_normal OFF. (was on)
    Enter command: eigenprobe 0
    Eigencounts:    0 <,  50 ==,  25 >
    
    For a curved surface, the extra eigenvalues generally won't be zero, but they will all be close to zero, both positive and negative, which can really foul things up. The default value of hessian_normal is therefore the ON state. The moral of the story is to always leave hessian normal on, unless you really really know what you are doing.

    On some surfaces, for example soap films with triple junctions and tetrahedral points, there are vertices with no natural normal vector. Evolver hessian_normal mode assigns those points normal subspaces instead, so that vertices on a triple line can move in a two-dimensional normal space perpendicular to the triple line, and tetrahedral points can move in all three dimensions.

    The reason for possible negative extra eigenvalues when hessian_normal is off is that one rarely has the best possible vertex locations for a given triangulation of a surface, even when its overall shape is very close to optimal. Vertices always seem to want to slither sideways to save very very small amounts of energy. The amount of energy saved this way is usually much less than the error due to discrete approximation, so it is usually advisable not to try to get the absolute best vertex positions.

    There is one effect of hessian_normal that may be a little puzzling at first. Many times a surface is known to have modes with zero eigenvalue; translational or rotational modes, for example. Yet no zero eigenvalues are reported. For example, with the cube eigenvalues found above,

     Eigencounts:    0 <,  0 ==,  193 >
      1.    0.001687468263013
      2.    0.001687468263013
      3.    0.001687468263013
      4.    0.2517282974725
      5.    0.2517282974731
    
    one might expect 6 zero eigenvalues from three translational modes and three rotational modes. But the rotational modes are eliminated by the hessian_normal restriction. The three translational modes have eigenvalue near 0, but not exactly 0, since normal motion can approximate the translation of a cube, but not exactly. The effective translation results from vertices moving in on one hemisphere and out on the other. This distorts the triangulation, raising the energy, hence the positive eigenvalue. This effect decreases with refinement.

    There are times when the normal direction is not the direction one wants. If one knows the perturbation direction, one can use the hessian_special_normal feature to use that instead of the default normal. Beware that hessian_special_normal also applies to the normal calculated by the vertexnormal attribute and the normal used by regular vertex averaging.


    10. Visualizing eigenvectors.

    Naturally, you want to see what various modes look like. At present. Evolver can do this through a subsidiary menu invoked by the command "hessian_menu". This is a menu I use to test out various Hessian-related features.
    OptionAction
    1 Initialize Hessian.
    Z Do ritz calculation of multiple eigenpairs. This prompts for a shift value. Pick a value near the eigenvalues you want, preferably below them so the shifted Hessian is positive definite. This also prompts for a number of eigenpairs to do. Eigenvalue approximations are printed as they converge. You can stop the iterations with a keyboard interrupt.
    X Pick which eigenvector you want to use for moving. You can enter the eigenvector by its number in the list from the Z option. As a special bonus useful when there are multiple eigenvectors for an eigenvalue, you can enter the vector as a linear combination of eigenvectors, e.g. "0.4 v1 + 1.3 v2 - 2.13 v3".
    4 Move. This prompts you for a scale factor. 1 is a reasonable first guess. If it is too big or too small, option 7 restores the original surface so you can try option 4 again.
    7 Restore original surface. Do this unless you want your surface to stay moved when you exit hessian_menu. Can repeat cycle by doing option X again.
    q Quit back to main prompt. The surface is not automatically restored to its original state. Be sure to do option 7 before quitting if you don't want to keep the changes!

    Square example. Let us see how to display the lower eigenmodes for the square, pictured here:

    square eigenmode 1 square eigenmode 2 square eigenmode 3 square eigenmode 4 square eigenmode 5 square eigenmode 6 square eigenmode 7 square eigenmode 8
    Run square.fe, display, and refine three times. Run "linear_metric". Run "hessian_menu". At the hessian_menu prompt, do option 1 (which calculates the Hessian matrix), then option z (ritz command) and respond to the prompt for number of eigenvectors with 10, then option x and pick eigenvector 1. Then pick option 4 and Step size 1. You should see the mode displayed in the graphics window. Your image may look the opposite of the first one pictured above, since the eigenvector comes out of a random initial choice and thus may point in the opposite direction of what I got.

    After admiring the mode a while, do option 7 to restore the surface. Then do option 4 again with Step size -1 to see the opposite mode. Do option 7 to get back to the orignal. By doing the cycle of options x, 4, and 7 you should be able to look at all the eigenmodes found by the ritz command.

    Hessian_menu can do sundry other things, mostly for my use in debugging. Would-be gurus can consult the hessian_menu command documentation for a smorgasbord of more things to do with the Hessian.


    11. Hessian iteration.

    Or how to converge really really fast.

    Suppose we assume the quadratic approximation to the surface energy is a good one. Then we can find an equilibrium point by solving for motion Y that makes the energy gradient zero. Recalling that G and H depend only on X, the energy gradient as a function of Y is

    grad E = GT + YT H.
    So we want GT + YT H = 0, or transposing,
    G + H Y = 0.
    Solving for Y gives
    Y = - H-1 G.
    This is actually the Newton-Raphson Method applied to the gradient. The Evolver's "hessian" command does this calculation and motion. It works best when the surface is near an equilibrium point. It doesn't matter if the equilibrium is a minimum, a saddle, or a maximum. However, nearness does matter. Remember we are dealing with thousands of variables, and you don't have to be very far away from an equilibrium for the equilibrium to not be within the scope of the quadratic approximation. When it does work, "hessian" will converge very quickly, 4 or 5 iterations at most.

    Example: Start with the cube datafile cube.fe. Evolve with "r; g 5; r; g 5;". This gets very close to an equilibrium. Doing "hessian" a couple of times gets to the equilibrium:

    Enter command: hessian
      1. area:  4.85807791572284 energy:  4.85807791572284
    Enter command: hessian
      1. area:  4.85807791158432 energy:  4.85807791158432
    Enter command: hessian
      1. area:  4.85807791158431 energy:  4.85807791158431
    
    So Hessian iteration converged in two steps. Furthermore, this is a local minimum rather than a saddle point, since Evolver did not complain about a non-positive definite Hessian.

    NOTE: The hessian command will work with indefinite Hessians, as long as there are no eigenvalues too close to zero. The warning about non-positive definite is for your information; it is does not mean hessian has failed.


    12. Hessian_seek.

    Even when the Hessian is positive definite, a hessian iteration may blow up since the surface is not near enough to the minimum for the Hessian approximation to be valid. For this circumstance, the hessian_seek command does the same calculation as the hessian command, except it does a line search along the direction of motion for the minimum energy instead of jumping directly to the supposed equilibrium. Basically, it does the same thing as the 'g' command in optimizing mode, except using the hessian solution instead of the gradient.

    Cube example. Run cube.fe, do g, r, and hessian_seek.

    Enter command: g
      0. area:  5.13907252918614 energy:  5.13907252918614  scale: 0.185675
    Enter command: r
    Vertices: 50  Edges: 144  Facets: 96  Bodies: 1  Facetedges: 288
    Element memory: 40312
    Total data memory: 299082 bytes.
    Enter command: hessian_seek
      1. area:  4.91067149153091 energy:  4.91067149153091  scale: 0.757800
    
    This is kind of a trivial example, since hessian doesn't blow up here. But in general, when in doubt that hessian will work, use hessian_seek. If hessian_seek reports a scale near 1, then it is probably safe to use hessian to get the last few decimal places of convergence. Hessian_seek cannot be as accurate as hessian at final convergence, since hessian_seek uses differences of energies, which are very small near the minimum.

    Hessian_seek is safe to use even when the Hessian is not positive definite. I have sometimes found it surprisingly effective even when the surface is nowhere near a minimum.


    13. Saddle.

    Sometimes your surface may reach a saddle point of energy where gradient descent becomes very very slow or even useless. Or you may be wanting to use hessian, but there are pesky negative eigenvalues. The obvious thing to do is pick a negative eigenvalue and move in the eigenvector direction. You could do it by hand with hessian_menu, but the "saddle" command packages it all nicely. It finds the most negative eigenvector and does a line search in that direction for the lowest energy. To see an example, run catbody.fe with the following evolution:

    catenoid catenoid

     u
     body[1].target := 4
     g 5
     r
     g 5
     V
     V
     r
     g 10
     eigenprobe 0
     saddle
    
    Further evolution after "saddle" is necessary to find the lowest energy surface; "saddle" just gives an initial push. It is not necessary to run "eigenprobe 0" first; if saddle finds no negative eigenvalues, it says so and exits without moving the surface.

    Saddle is safe to use even when nowhere near an equilibrium point.


    14. Detecting the onset of instability and evolving unstable surfaces.

    The Hessian features can be used to detect the onset of instability as some parameter changes, and even evolve unstable equilibrium surfaces.

    Instability detection is done by watching eigenvalues with the ritz command. As an example, consider a ring of liquid outside a cylinder, with the volume increasing until the symmetric ring becomes unstable. This is set up in the datafile catbody.fe, which is just cat.fe with a body defined from the facets. Run catbody.fe with this initial evolution:

      u
      g 5
      r
      g 5
      body[1].target := 2
      g 5
      r
      body[1].target := 3
      g 5
      hessian
      hessian
      linear_metric
      ritz(0,5)
    
    This gives eigenvalues
    Eigencounts:    0 <,  0 ==,  167 >
      1.    0.398411128930840
      2.    0.398411128930842
      3.    1.905446082321839
      4.    1.905446082321843
      5.    4.4342055632012
    
    Note we are still in a stable, positive definite situation, but the lowest eigenvalues are near enough to zero that we need to take care in increasing the volume. Try an increment of 0.1:
      body[1].target += 0.1
      g 5
      hessian
      hessian
      ritz(0,5)
    Eigencounts:    0 <,  0 ==,  167 >
      1.    0.287925880010193
      2.    0.287925880010195
      3.    1.775425717998147
      4.    1.775425717998151
      5.    4.2705109310529
    
    A little linear interpolation suggests try an increment of 0.3:
      body[1].target += 0.3
      g 5
      hessian
      hessian
      hessian
      ritz(0,5)
    Eigencounts:    0 <,  0 ==,  167 >
      1.    0.001364051154697
      2.    0.0013640511547
      3.    1.4344757227809
      4.    1.4344757227809
      5.    3.8350719808531
    
    So we are now very very close to the critical volume. In view of the coarse triangulation here, it is probably not worth the trouble to narrow down the critical volume further, but rather refine and repeat the process. But for now keep this surface running for the next paragraph.

    Evolving into unstable territory Typically these are surfaces with just a few unstable modes. The idea is to get close to the desired equilibrium and use "hessian" to reach it. Regular 'g' gradient descent iteration should not be used. To change the surface, i.e. to follow an equilibrium curve through a phase diagram, make small changes to the control parameter and use a couple of hessian iterations to reconverge each time. Particular care is needed near bifurcation points, because of the several equilibrium curves that meet. When approaching a bifurcation point, try to jump over it so there is no ambiguity as to which curve you are following. The approach to a bifurcation point can be detected by watching eigenvalues. An eigenvalue crosses 0 when a surface introduces new modes of instability.

    Example: Catbody.fe, continued. With catbody.fe in the nearly-critical state found above, increase the body volume by steps of .1 and run hessian a couple of times each step:

     body[1].target += .1
     hessian
     hessian
     hessian
     hessian
     body[1].target += .1
     hessian
     hessian
     hessian
     hessian
     body[1].target += .1
     hessian
     hessian
     hessian
     hessian
    
    So hessian alone is enough to evolve with, as long as you stay near enough to the equilibrium.

    Other methods for evolving unstable surfaces:

    • Using symmetry. If the unstable surface is more symmetric than the stable surfaces, then enforcing symmetry can remove the unstable modes. For example, a surface of revolution could be retricted to just a 90 degree wedge between two perpendicular mirror planes (level-set constraints), with 90 degree contact angles on the planes.
    • Using volume constraints. Recall that in general every constraint removes one degree of freedom in the configuration space. Hence a volume constraint has the potential to remove one unstable mode. For example, unstable catenoids can be made stable by adding a volume constraint and adjusting the volume until the pressure is 0.

    15. Eigenvalue Accuracy.

    For the hard-core Evolver guru.

    The question here is how well the eigenvalues of the discrete surface approximate the eigenvalues of the smooth surface. This is significant when deciding the stability of a surface. I present some comparisons in cases where an analytic result is possible. All are done in hessian_normal mode with linear_metric.

    Any general theorem on accuracy is difficult, since any of these situations may arise:

    • The discrete surface exists, but the smooth surface does not.
    • The smooth surface exists, but the discrete surface does not.
    • The smooth and discrete surfaces exist, but their eigenvalues are not close due to proximity to a bifurcation point.
    • The discrete surface is a good approximation to the smooth surface, and their eigenvalues are close. In general, linear model eigenvalues will have error on the order h2, where h is a typical edge length, and quadratic model eigenvalues will have error h4.

    Example: Flat square membrane with fixed sides of length pi.
    Smooth eigenvalues: n2 + m2, or 2,5,5,8,10,10,13,13,17,17,18,...

    64 facets:
          linear_metric_mix:=0  linear_metric_mix:=.5   linear_metric_mix:=1
     1.    1.977340485013809      2.036549240354332      2.098934776481970
     2.    4.496631115530167      4.959720306550873      5.522143605551107
     3.    4.496631115530168      4.959720306550873      5.522143605551109
     4.    6.484555753109618      7.764634143334637      9.618809107463317
     5.    8.383838160213188     10.098798069316681     12.451997407229225
     6.    8.958685299442784     10.499717069102577     12.677342602918362
     7.    9.459695221455723     12.274525789880890     17.295660791788656
     8.    9.459695221455725     12.274525789880887     17.295660791788663
     9.   11.5556960351898       15.7679635512509       23.585015056138165
    10.   12.9691115062193       16.7214142904454       23.5850152363232
    
    256 facets:
         linear_metric_mix:=0   linear_metric_mix:=.5  linear_metric_mix:=1
     1.    1.994813709598124      2.009974216370146      2.025331529636075
     2.    4.869774462491779      4.999499042685448      5.135641457408189
     3.    4.869774462491777      4.999499042685451      5.135641457408191
     4.    7.597129628414264      7.985384943789075      8.411811839672165
     5.    9.571559289947587     10.090156419079083     10.639318311067063
     6.    9.759745949169531     10.186524915471141     10.665025703718413
     7.   12.026114091326091     13.008227978344539     14.149533399632906
     8.   12.026114091326104     13.008227978344539     14.149533399632912
     9.   15.899097189772919     17.242998229925817     18.8011199268796
    10.   15.8990975402264       17.2429983900303       18.8011200311777
    
    1024 facets:
         linear_metric_mix:=0   linear_metric_mix:=.5  linear_metric_mix:=1
     1.    1.998755557957786      2.002568891165917      2.006394465437547
     2.    4.967163232244397      5.0005039961225        5.0342462883517
     3.    4.967163232244401      5.0005039961225        5.0342462883517
     4.    7.897718646133286      7.9990889953386        8.1028624365882
     5.    9.891301374223204     10.0268080202750       10.1637119963890
     6.    9.941758861492074     10.0519390576227       10.1658353297015
     7.   12.750608847206168     13.0141591049184       13.2878054981609
     8.   12.750608847206173     13.0141591049184       13.2878054981609
     9.   16.718575011911319     17.0839068189583       17.4625185925160 
    10.   16.7185751321348       17.0839069326949       17.4625186893608
    
    A curious fact here is that eigenvalues 5 and 6 are identical in the smooth limit, but are split in the discrete approximation. This is due to the fact that the 2-dimension eigenspace of the eigenvalue 10 can have a basis of two perturbations that each have the symmetry of the square, but are differently affected by the discretization.

    Example: Sphere of radius 1 with fixed volume.
    Analytic eigenvalues: (n+2)(n-1), n=1,2,3,...
    with multiplicities: 0,0,0,4,4,4,4,4,10,10,10,10,10,10,10

    Linear mode, linear_metric_mix:=.5.
              24 facets          96 facets     384 facets       1536 facets
     1.    0.3064952760319  0.1013854786956  0.0288140848394  0.0078006105505
     2.    0.3064952760319  0.1013854786956  0.0288140848394  0.0078006105505
     3.    0.3064952760319  0.1013854786956  0.0288140848394  0.0078006105505
     4.    2.7132437122162  3.9199431944791  4.0054074145696  4.0036000699357
     5.    2.7132437122162  3.9199431944791  4.0054074145696  4.0036000699357
     6.    2.7132437122162  3.9199431944791  4.0054074145696  4.0036000699357
     7.    3.6863290283837  4.3377418342267  4.1193008989205  4.0347069118257
     8.    4.7902142880145  4.3377418342267  4.1193008989205  4.0347069118257
     9.    4.7902142880145  9.0031399793203  9.9026247196089  9.9870390309740
    10.    6.7380098215325  9.7223042306475 10.0981057119891 10.0387404054572
    11.    6.7380098215325  9.7223042306545 10.0981057120367 10.0387404054607
    12.    6.7380098215325  9.7223042307334 10.0981057121432 10.0387404055516
    13.    8.6898276285107  9.9763109502799 10.1298354477703 10.0466252566958
     
    In quadratic mode (net 4 times as many vertices per facet)
              24 facets          96 facets     384 facets       1536 facets
    
     1.    0.0311952242811   0.0025690857791  0.0002802874477  0.0000358409262
     2.    0.0311952242812   0.0025690857791  0.0002802874477  0.0000358409262
     3.    0.0311952242812   0.0025690857791  0.0002802874477  0.0000358409262
     4.    4.2142037235384   4.0160946848738  4.0009978658738  4.0000515986034
     5.    4.2142037235384   4.0160946848738  4.0009978658738  4.0000515986034
     6.    4.2564592100813   4.0222815310390  4.0014892600742  4.0000883321792
     7.    4.2564592100813   4.0222815310390  4.0014892600742  4.0000883321792
     8.    4.2564592100813   4.0222815310390  4.0014892600742  4.0000883321792
     9.    9.9900660130172  10.1007993043211 10.0076507048408 10.0004402861468
    10.    9.9900660130172  10.1007993043271 10.0076507049338 10.0004402861545
    11.    9.9900660130173  10.1007993047758 10.0076507050506 10.0004402861829
    12.   12.1019587923328  10.1262981041797 10.0089922470136 10.0005516796159
    13.   12.1019587923350  10.1262981042009 10.0089922470510 10.0005516796196
    14.   12.1019587927495  10.1262981044110 10.0089922470904 10.0005516797052
    15.   12.6934178610912  10.1478397915001 10.0106695599620 10.0007050467653
    

    16. Caveats and warnings.

    When not near enough to an equilibrium, incautious use of "hessian" can wreck a surface. If you don't know what's going to happen, save the surface first. Or use hessian_seek. Or use hessian_menu, where you can restore the surface with option 7. Or set " check_increase", which will abort a hessian iteration that would increase energy. But remember sometimes hessian should increase energy, for example to satisfy a volume constraint or reach an unstable equilibrium.

    Not all energies have built-in Hessians. If Evolver complains about lacking a Hessian for a particular energy, you will either have to forego hessian or find a way to phrase your problem in terms of an energy with a Hessian.

    There are three methods of sparse matrix factoring built into Evolver: YSMP (Yale Sparse Matrix Package), my own minimal degree algorithm, and METIS recursive bisection. The ysmp and metis_factor toggles can be used to control which is active. METIS is the best overall, particularly on large surfaces, but requires a separate download and compilation of the METIS library (but it's easy; see the installation instructions).


    back to Hessian top
    Back to top of Surface Evolver documentation. Index. evolver-2.30c.dfsg/doc/square-e8.gif0000644000175300017530000005400411410765113017477 0ustar hazelscthazelsctGIF89ad     ###''('(((''((',,,//0/0//000//0/000/444778787877878887<<;??@?@??@@@??@?@@@?CCCGGHGHGGHHHGGHGHHHGKKKOOPOPOOPPPOOPOPPPOSSSWWXWXWWXXXWWXWXXXW\\\__`_`__```__`_```_cccgghghgghhhgghghhhgjjjopoopppoopopppotttwwxwxwwxxxwwxwxxxw{{{!!B Image generated by AFPL Ghostscript BETA RELEASE (device=ppmraw) ,d[jժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժU+ZjժUVZjժUVZjժUVZjժUVZZ$ W~XԪUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժU+=58)S*]PUVZjժUVZjժVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZj 7:JSN:VZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZj `VZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVB8usduL:5 ZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjUVZjժUVZjժUVZjժթ=@H UN:q'ShvPt7TdDjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZDHPBY'N8uQ&#XԪUVZتUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVN!a)TPVҩS'N:qiQCF2U'Ǒ=0,y"CRVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZlժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjUVZjժUVZjժUVZjժUVZjժUVn(*TB$ĉSN:qĩӤL2%$*09ʉ4ZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVjժUVZjժUVZjժU$T *Ԗ#pĩS'N>qĉS'N7Иeʔ(SA*PiԪUVZjժUVL-z$FEb._|"fPVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZBɟGBʔ 8q&E>uԉS'NL2ej0 \@ $H A Td!q@VZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjꔤE"5zI#@^e-_lbc *PPB *XhҊBQ?+2*Rrpd'Nnز"GI$APN@hqN'V:!&ԩSN:ԩS9ENVZjժUVZjժUVZjժU+Zjժ՛-^l/[HAD )RHb .\p tD2HD!C %:dȐ!CW&$ I82!N4j(QJ2e(S~H !I$I9ƅO>q"fśPH\CXb% >N:u)1@6:GBVZjժUVZjժUVZjժUVZj @ @A 4$ \r劘=u쩳Ξ=uC@ПB?:L$):bŊ)'N8qIPG^xx@ 'N8ZQg(SL*Uʔ(QGC$I$I2DbQ>uroNT?b䊡+@pJuJ >N:uVZjժUVZjժUVZjժUVZjI?-2hE-2"49\gϞ:{SgO:{q䍌?3ϟB Ϟ&I4IҤIG"N$y@C N&U‰'N8q")QJ25 4 $II: :*}&LurP)SL2ej*G`$ISRRuJˑVZjժUVZjժUVZjժUVZj*%Zdh"D%ZdhѢ~SgO=u쩳gO=u!4)Ϡ? 4?$M$II& 'NnA'E%}4C&KFSL:eʔSް`pST: VZjժUVZjժUVZjUVZjժ N%Jh"E -2-~@cǎ=u쩳gO=ugO П? gI$I$iIĬX!'N@X@LJ,ĉ'N8qĉS#FBPB ZMC"IE͢EthƥK.]2 1L2eʔE,Ҙ3i pNbu VZjժUVZjժUVZjժUVZjժP p)J"E -2G?̰cgO=uSgO=urg#gП? 8HşI&M$IҤE;q‰'1S'+pĉ'N8]℉?+RPB Л $I "I9 K,qɐ $*2uӑ#LZ$Ɣ)S{x8tjVZjժUVZjժUVZjժUVZjժU=$Z"C!Z?~xcǎ:{ٓ'O<{Hg{gPAc$I&I$ ?VpIFBt '8qĉ'N8q2Tlj*TPB ZaЛ" EER48Yd'IN1"SL2Ô)SLU:dIZjժUVZjժUVZjժUVZjժՖH,ZdhE Bϟ?T9bg=u쩓gO=NÇ72 TϟA ZGI$MtȦMb`̞8q N8m'+gQh1TPB 2Ő!8 I4CE-#O?8Y'Ld8bʔ)SLɔ)SL2VZjժUVZjժUVjժUVZjժU(Yh!E -ϟ?~!QgϞ:{sgO;$᳧=; O?̑QI$I$QQ&I*8q ':qg:bqDOL2e꓌?hYQPB *4B!Co:$ IEZTȅ8Yd&IM!Ɣ)SLSǔ)SL2QZjժUVZjժUVZjժUVZjժU+YH"Cp珟?SgϞ:{Թs'|Ϟ>3hП@#iR%IȐd #~8SDFN:@s7~L2eʔ)SL2HƟDࠂPB *t!Ct0$IE$-$i2,qR~,)YfE%SLÔ)SLZ#ԏ-ZjժUVZjժUVZjժUVZjժ՟:h!D{V?!gO={칳7~>[~)O?sH$I"b,qdNbVN'NTXT 6L2uʔ)SL-ǐ!CpPB #! `H-$-"1 pdHL,թIR4L2uĔ)SL-:r ZjժUVZjժUVZjժUVZjժU+ TТE+ϟ?Ξ:uԹs=|g=~L3ϟ@6M4 C,Yi$1+|g=|ܬ3П:G&MZH&K8YT7 Q #9>2eԩS~&!G&K,YᏇxpI L2uʔ)SL2H49 2dPB О~ 2dhB J!Eh$ ;rdɒ%K0'NTN:u'ZjժUVZjժUVZjժUVZjժU+ GI `?ϟ?~qreϞ:{A=~g=|xSH̑INHd)'N8Q@F%F+dTzC'SL2eԧ?vH~ *TPB ]Xhџp * C JdO 1@I 4b'K.ĉ'N8YAB̑SN:e)[ZjժUVZjժUVZjժUVZjժU+I$1bT??CbO=ui>|g?~lh0Y)%K$1@R%@ pĔ)OPA7B uJ$*TPB QٲHѢ?*T!E 2O -ТEb\b%KH⤉'N8q`?N:uԢZjժUVZتUVZjժUVZjժUVHZ$ɃN珟?͞:{!g?~g=|d I6Yd&LR*+8*o :uԩSNIQ"'bHIdPB ZhDB1dhE 2d B@hѢEhHZ'%Nĉ'N816KVA句 uZjժUVZjժUVZjժUVZjժU+4$1bc/rϟ?~SΞ:{Pg>|>{#hԩSǐ%KTi$йǛPN:u)T("I/ 8R(QB FѢE8c E2d? VZhEhHA&LP K$'N8qd(ZjժUVZjժUVZjժUVZjժU+THb?xȁǏ?Ξ=uH=~30`hV@&PTXĊT$:u)$N-rH$*WaPB HѢE.Z1; 2d$,Zh"FHb䇄K0ĉ'Nd'N6qĉFjժUVZjժUVZjժUVZjժUVGHrfH?A?~C初=hr>~C 'u@Ƒ:Lq:rʩS]+VXŠ+V 45Z$iѢE=MB GQ"E-ZA E :D  IZ$iѢE9I%Ndpt'*p~ȸ?oԨQBZa INS CX*Ç+VXt!AFIZ$iQEh,zThC bC Zt?!E3T,ZiѢE0$Ir$ KVpĉS By#'N4qҔH'BZjժUVZjժUVZjժUVZjժUV, ?:3D|Ç Nlj GYÐQLS#bNa)1$ʘb+V H-jH"IbD 2ϢE-Z#cȐ3hlZhQEG$IH ?LIO< 'N8qbҧOjժUVZjժUVZjժUVZjժUVx?ϟ?pVS*|!I?|8YÏ)D@8ШQL%cʔ;+B-r*+V8V I䄄b$IZhQEl,i~ zd@E -Zdh+2dС3IY(ҢE$I$IR!iPBy'N8q3'NbVjժUVZjժUVZjժUVZjժUVXDB8ϟ?~1 10`8Ǐ*&1eT8 SDq)S`9"+VXrI$IIGr,jH"IHZQ:$2DB"E -RHL* 2dH rY($IZHR up'PuLN}ϛ <lժUVZjժUVZjժUVZjUVZA$*]ϟ?~سʊDbޘ2uʔĉ'N8ĉ'NT#@,2dŢE$-cI$IBC$$I$I#EIZd( C-Rdh7M2dE YhQ"$=$I S8*U Jr쁴hENtC2djժUVZjժUVZjժUVZjժUVȠ"fQ?b!7L2eT@rĉ'N8ĉ'N8IpJ= -2‰E-ZtDҕ*$IC$IWL$I7$,R"I$-bC E)ZЊE+%2dE )Z'"#I$OJD'Os,ZhѢEX,g iժUVZjժUVZjժUVZjժUVHXJpϟ?d SNH pĉ'Nĉ'N8Ɛ!C"'ǢE-"h I$I:g$I- I$ID$" #H p" =2h ET(2TC%c"$=$INBQS(K$-ZhѢE yi 8ժUVZjժUVZjժUVZjժUVZxs+`8A 2F)SR `dĉ'Nĉ'N&ɐF"C)HE-:H'$IF$IoH$I$Iȡf"1$,ZTh C)2"E+2d"E -gQI$EP(S[rTӍ>AZѢEd 6pӪUVZjժUVZjժUVZjժUVZٹpԩSN~LCB bqiBG,q‰'N%ĉ'N`""E-gϊE-R"$Iz#F$It$I$I$!+$-HG?RH"E@2TD -cQE$&'hCI"!I$I$$ID0@"I$-Db*Z"Ep(RtE Z ɓ'OBqhѢE=ZhѢE-BpN~dpUVZjժUVZjժUVZjժUVZ!ԩSotx'K28‰'N8R#,YdɒNDQAs"C bH*-:I"I0H:"I$I$2i$IDG=ZcѢ )Z!EV2"E)2'NV<ɓ=N< @РAhѢE-ZD'NTxxӪUVZjժUVZjժUVZjժUVZ bpԛ#TVI1.ĉ'NVThR%*u,]dɒ%>ļF"E 9c"E h IұhE~`H"!I$I$I"I$I͕E$=zt"*[)R!EP1E)ZF<ڒ2haH"EgΠ@H $N<0ЪUVZjժUVZjժUVZjժUVZu4qĉ K,Yĉ'NNXT4,]t%IAdȐ"Cl1DEYH1X$iEG$I$I鐠H$I$CBIZ͢E0-R!C0d"E )RG 9`LCiҤ=.сF"C)2dȐD)RFB,`UVZjժUVZjժUVZjժUVZCN8iIN8‰E@,UTR!$,YdɒK@ BH"C1dh!C ]PТH"It #I$Ib`G$I$I!͑EIBfѢ#h-BĐ!C1B"E͢I&M4 1GǐD)*dȐD*Rʼn7)UVZjժUVZjժUVZjժUVZ@%N8Y$IF&K8q' ,Ud%,Ydɒ%4AP1dȐE1dEX" hyX$iEy#IN&I:DB$I$I:bHҋB$-zdB9Đ"E0D!CHcRJ&UxAÐ!C )2dȐB CGx UVZjժUVZjժUVZjժUVZA&N,qb('N.ĩΑJ+YTR% ,qdɒ!HH"E1d(OE1hѢ?:H"I %p"C$I$I$IF IZt$ҢE9,RdDĐ!C$gI&UsP!!C *dȐB8 IUVZjժUVZjժUVZjժUVZZ'N8'K8éI*YT K,YdɃ!I.PQH"EF2dHCH,:h|H"G F$I$IDB$$Iz#F$~$-cѢE{r$ZD!C d?1iҤI(@h7~'D 2dP!C7I:"UVZjժUVZjժUVZjժUVZj% 'K8ɐd%N.]!$K,UdK2Y$I:0dh"CV2dLEf,RdТ=1Y$iE`#I$I$I$I$9"I$-zhE9,ah"E<4iҤIw:Zh?~sG"C 2dBBO$1z`PVZjժUVZjժUVZjժUVZjf'M0d%M.qRJ*Ydi:,YsD#IDhQ"E*dD$ZhE8ĠY$i$1$`C$I$I$ "I$I "I|I:hQEu,H"CmY]vL4iҤ= -ZT>|GC 2d(Q"&T ?$=PUVZjժUVZjժUVZjժUVZ:'M24Yd&Mud$@"K,UT4,I"!$I@,""Cr2dД-1H"Cp:a '$-PDI$I$ I$I"!I$C9"E"(ҢEZ,ZH"C-ZCF GL4i -Z?Ç? 2d(џ#I" TVZjժUVZjժUVZjժUVZj '1b4YbqB*UdKR$鑤5ob(E# 2d ET,ZHE+Hr¢ERŕI$IPґ7$I "$I@D:hQEPYH!C !B:&M4i A-Ï?~ϛ2dȐ 8~ϟ$IzIVZjժUVZjժUVZjժUVZj)'4ʢOVLӣ"6Xd KK,H7b-"G"E@ *d=hR"Eh<#FEZi$I$ I$$I$C$)(ҢH:YС;9 8&MRТE?~O- X?*WI:@VZjժUVZjժUVZjժUVZjJ1TVYĩOYp)P̤b:E I$i:B-zd"E@ 2TОh,Jh!C@YȑEEzdѢ:$H$Iҕ+$$I$IGhIhѢG\#:]eI&-h"F ϟ?~ǏJtϟ?ϟ$9y#NVZjժUVZjժUVZjժUVZj&?:uĩӢ8tS(OB} -$I$I@-ZD⏡E@*!hZhhHph"I$EZDEt$I$$# $I^A#iѢE@C%S(hQ IhѢE?~珟7+ϟ?u"BeEVZjժUVZjժUVZjժUVZju*N:qԩ I8ՁɓP<)I$-h$-zĐ-h 2T#@ZtdCVPQH"I$-dQE!H7G$q#IEphѢE Ɣ)S:XTIƤI^ZhѢE>Ǐ>Ǐ?Dܑϟ?ϟ;1$2(VZjժUVZjժUVZjժUVZj*N:u N8Ir)'OǏ?Cϟ??( IZ!)VZjժUVZjժUVZjժUVZjRN:uTi'N8qӧP:$I$I$IZ(8@ )RTH?,ZhEc#"EhE$-:G$IR#IZd0+VXAb!,mٲFE-Zh~?|c7-HPYϟ??+$ĞVZjժUVZjժUVZjժUVZjժN8‰'N4Q))'OVLI%*W$I$iE-Z" 2dh,ZHE0dЖ-GE-jhQ!NH$ɐpXMrNX0AE*yXCТE-Zh 8~?oxq:$8ƃ=~ϟ?N#鍇VZjժUVZjժUVZjժUVZjjՁE:ɐĉ'L8)ꓧO9pI'K@,$IҐB-Z( cI?. R"EcȐ7+HZ舟E$-Ѣ+$yq+VX]Ċ {.X䤎J[ hѢE-rTH?&g  bP:ϟ?VA#iQLZjժUVZjժUVZjժUVZjժU ĉ&N8qS2b8q M`H4IZ !Ɛ"C+H"CRPBVYE-$iEuÊ+VX+S8sR%K${Ç={/tϟ?:Y$ɉVZjժUVZjժUVZjժUVZjRI+8qp'N8q)T! 8qĉJK$4-ZH p1dP!Cp"E%QPBi4$-ZѢ:+8eNX2% ( PR%9,ZiѢE-ZD `Ç>|gˑ:{ϟ?~бϟ?{`VZjժUVZjժUVZjժUVZjժU 8qĉ'N8Ti$N8mT%K8q$鍓E$-Zρo2dP!C+ G"EbP!C HP$IZiѕ#N:uT$@bD҅@ެd"2<r bޠB2~g=|g=u?~C?~d$):ZjժUVZjժUVZjժUVZjժ6É'N8Q'N8q4 'N6y#БE-ZH&)2d!CT#E)PP!C C7-*ΩSN:uԩS @TX'K4mP:ud>|Ç=|SD={ϟ?NpHR ZjժUVZjժUVZjժUVZjժU<C%G&T긱'N8qI%K8YRGE -C T)2T!CV2TǖE-ŐB 2Y!ԩSN:Uh N`@ 1@,U%N,Ydɒ%I4HQQg>|Ǐ>|!fO=vx?'Z$iEZjժUVZjժUVZjժUVZjժ:uꔩSPHM0q 'K,YA FQhF(*dHQ!C)T *)ZB 2dБT5*0O%S%K6mt ȑIoc ?{g>~P9gO=u)??{V8FҢZjժUVZjժUVZjժUVZjժU+oN:eԩ7hLYgN8Yt'K,]DEB#-$i" ~L1dȐBV)T)T-##QB *d 1o`Pd)SL2e"EtzK% 1d%N6)fҤ D>~{ȳΞ:{Ɂϟ??2$G#oZjժUVZjժUVZjժUVZjժU:N:uꔘ7LUb*8qT 'K0]T'E5ZHҢB:$iȐ!C)TPBSV,RNB *ThSLU:)SL:ej"@:UJ cb(ҦMP4I')$&?~g>oVܹΞ=uI>O ѹ' ZjժUVZjժUVZjժUVZjժV2uGSLaʔ:+ q%K6IDR-hQE@HBŐ!C (TPBXgEB 2Ï)SL:e #L289IPdž1fHɒ%I+H$i҅? g>{g=;s'O={aaϟ?ǏC-rZjժUVZjժUVZjժUVZjժU-@NґƔ)SLdʔN:pT'NTdP$-Z(ҢB$$IĐCL)TPBPB ʱȔ)SN2uʔJT`Cb NaNbXdɒ$I4iBg ?|Ç=$ܹs'Ϟ:u䏟?|'E-Zc+GZjժUVZjժUVZjժUVZjժUR|*GsgO={رϟ?g"C-J(EENjժUVZjժUVZjժUVZjժUV8.,2eʔSNaʔ)SLMZI,qd%*WdiI"IHH$ C QAcȐB zsB?2eʔ)?hJCVS:iS?0!GI$M$П@珇:|Ç:{쩳gO=u9"揟?~)gE-RgE'aZjժUVZjժUVZjժUVZتUVcʔ)SLaʔ)SLsCN8Y'N@91hQE$IYaPB 2u9d1u8qĉ'N@xԉf I K4i$I&I'Р@3(~͊=ySΞ=uC⏟?p"E )2h= ZjժUVZjժUVZjժUVZjժUV!)SLcʔ)SX1eD18q&I+*ah$IX@$IҕSPB 4T Nĉ'N8qĉ6DpI$CL$IҤI$mhPA SOn':{쩳gϞ:{ٳ?~\Xdh!E)RHQ= jժUVZjժUVZjժUVZjժUV^JU ELU:bT'*G<2eʔ+Xd%NVt DϢE$I$++QPB r'8qĉ'N,]ϊ:Q 8qD E$I4IIV Ϡ@'{Ç=uٓgO=uكF?~t1ТEZdh"NpjժUVZjժUVZjժUVZjժUV:uꔩ*0֘aΏEL:eꔩJ@dp"ӥKTiFI$IWVPB јʔ)?+pĉ%N8q4ÊII8q c$I&I$i$I[hП?( 'TrϞ:uٳΞ<{C8+ -BdhQE)ZBjժUVZjժUVZjժUV[jժUV\9u)T@)SN2eʔSxs$N0Q'$U"IG@CP!C~HeT(Sĉ'N8i$#EQH$N8QgI$M$i$I+*TO? )4Ϡ@+!N={SgOc C.铘y$I$I`*$e*PDY%N8q+ m9C'Ib<йCƖ?(I4IҤ#b OA gП@hrYQgϞ:{ٳΞ~&eJ)SJ2g!Nxc>8YA!C\ʕ+Wd@ā .\p… .RXD *P q"._l"f0_,bHҢENjժUVZjժUVZj `VZjժUVP$ƇSN:ԩSNqJ)Vp@ -[H 'N#GV` "E (P F1a€ #F1j$iQEAԪUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUV4@ $H  aĉ*R8 &p .\ pŊ+Vx @ @$ժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVNAbȑ$IIUVZjժUVZjժUVZj 2`@@zH <ȋ; @q ZjժUVZjժUVZjժUVZjժUVZتUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժU+K\ TVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժU+ZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUV@t$XjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժVZr BZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZjժUVZZF/b$1jժUVZjժUVZjժUVZjժUVZjժUVZjժUVZj `VZjժUVZjժUVZjժUVZjժUVZjժUVZjժUV;evolver-2.30c.dfsg/doc/general.htm0000644000175300017530000002672211410765113017333 0ustar hazelscthazelsct Surface Evolver Documentation

    Surface Evolver Documentation

    Back to top of Surface Evolver documentation. Index.

    Some miscellaneous topics


    Command line options

    The syntax for starting the Evolver from the system command prompt is:

    evolver [-ffilename] [-a-] [-d] [-e] [-i] [-m] [-pn] [-q] [-Q] [-w] [-x] [-y] [datafile]
    

    The current directory and EVOLVERPATH will be searched for datafile. If the datafile is not found, then a new search with extension .fe is done. Wildcard matching is in effect on some systems (Windows, linux, maybe others), but be very careful when using wildcards since there can be unexpected matches. If the datafile still not found, or the datafile is not given on the command line, then the user will be prompted. Options:

    -a-
    Do not enable automatic conversion to named methods and quantities mode when a situation requiring it arises.
    -d
    Prints YACC debugging trace as datafile is parsed. May be helpful if you can't figure out why your datafile doesn't get read in properly AND you know YACC.
    -ffilename
    Specifies the name of a file to be used as command input after the datafile is read. At the end of this file, input reverts to standard input. The effect is the same as redirecting input from the file, except that -f will echo commands to the screen and revert to standard input at the end. Also note that errors will cause input to revert to standard input.
    -e
    Echo input. Meant for echoing commands of piped input to screen so the user can follow what is going on in case Evolver is being controlled by another process.
    -i
    Keeps elements numbers as listed in the datafile, instead of renumbering them consecutively. The same effect can be achieved by putting the keyword keep_originals in the top of the datafile.
    -m
    Turn memory debugging on at start of program. Same effect as Evolver memdebug command.
    -pn
    Forces use of n processes for an Evolver compiled in multi-processor mode. n may be larger or smaller than the physically available number of processors. The default is 1. This option should be regarded as experimental; there is still too much overhead for it to be useful usually.
    -q
    Convert everything to named quantities internally. There are a few things for which no quantities exist yet; they will produce error messages.
    -Q
    Suppresses echoing of read section of datafile, and of files input with the read command; same as quietload toggle.
    -w
    Causes Evolver to exit whenever a warning occurs. Meant to be used when Evolver is run in a shell script.
    -x
    Causes Evolver to exit whenever an error occurs. Meant to be used when Evolver is run in a shell script, so that Evolver doesn't get hung up waiting for input in response to an error.
    -y
    Causes Evolver to cease execution of commands and return to command prompt after any warning message. Same effect as break_after_warning runtime toggle.


    Bugs

    Bug reports should be submitted by email to brakke@susqu.edu. Please include the Evolver version number, a description of the problem, the initial data file, and the sequence of commands necessary to reproduce the problem.

    Evolver Newsletters

    The group of Surface Evolver users has grown large enough that I have started a newsletter. Contents include announcement of new versions and latest features, bibliography, and anything else anybody would like to contribute. If you would like to be on the mailing list, send your email address to brakke@susqu.edu.

    Back issues:


    Error handling

    When the Surface Evolver detects an error, it prints an error message and tries to take appropriate action. If the -x command line option was given when Evolver was started, then Evolver exits immediately with a nonzero error code. This is useful when running Evolver from shell scripts. There are several categories of errors:

    • WARNING - Something has happened that you should know about, but Evolver proceeds normally after printing the message.
    • SYNTAX ERROR - The parser has detected an error. Recent input up to the detection of the error is printed, but the actual problem may be earlier.
    • DATAFILE ERROR - There is an error in the datafile being read in. Evolver attempts to recover by skipping to the next recognizable part of the datafile, but will abandon the datafile after 5 such errors. The surface data that was read in is available for your inspection, but it probably forms an inconsistent surface and you should not try to evolve it.
    • ERROR - This is an error encountered during the execution of a command. The command is abandoned and Evolver returns to the main prompt. The actions of the command are not undone.
    • FATAL ERROR - An error from which recovery is impossible. Evolver exits immediately.


    Surface initialization

    Whenever the Surface Evolver loads a new surface, either on startup or in response to the q or load commands, the following actions occur:

    • Any previous surface has all memory deallocated. All user-defined variables and commands are deleted, including any currently executing commands.
    • Internal variables are initialized to default values.
    • The datafile is read in.
    • In the soapfilm model, any nontriangular face is divided into triangles by creating a vertex in the center and making edges from the center to the vertices.
    • In the soapfilm model, the order of facets around each edge is determined geometrically.
    • Vertices are projected to any level set constraints.
    • Checks as described for the C command are done.
    • Initial areas, energies, and volumes are calculated.
    • The viewing matrix is reset to center the object in the graphics window.
    • If any graphics are active, the new surface is drawn.
    • The main command prompt is started.


    Interrupting execution

    Evolver operation may be interrupted with the standard keyboard interrupt, CTRL-C usually (SIGINT for you unix gurus). During repeated operations, this will set a flag which is checked at the end of each loop. Repetition will cease after the current step and control will return to the main prompt. If you give a second interrupt before the loop has ended, Evolver will abort the command and return to the main prompt. Beware that this may leave the surface in an inconsistent state if surface topology changing operations were going on. An immediate abort will also happen if an interrupt is received outside a loop. If Evolver receives SIGTERM (say from the unix kill command), it will dump to the default dump file and exit. This is useful for stopping a background Evolver running a script. The same thing will happen with SIGHUP, so losing a modem connection will save the current surface.

    Note: In Microsoft Windows, the second interrupt doesn't do anything much since Windows creates a separate thread to handle the interrupt, and I can't find any way to force the offending thread to stop and longjmp back to where it should. So if the Evolver is really, really stuck, you may just have to kill the whole program.


    Parallel computation

    Parallel versions of the Surface Evolver are available for several types of systems, but these are experimental and not very useful.

    Silicon Graphics parallel version

    Silicon Graphics systems with multiple processors may run a version that will use some or all processor by including -DSGI_MULTI in CFLAGS in Makefile. This version will run fine with one processor, also. Currently, the only calculations done in parallel are the "named quantities", so it is best to start Evolver with the -q option. The number n of processes actually done in parallel can be controlled with the -pn command line option. It will slow things down to use more processes than are physically available, but it is possible to run multiple processes on a single-processor system. SGI's use shared memory, so no message passing is necessary, and this version runs pretty well.



    Back to top of Surface Evolver documentation. Index.
    Author's home page. evolver-2.30c.dfsg/doc/news_20.htm0000644000175300017530000001770511410765113017174 0ustar hazelscthazelsct Surface Evolver Documentation - Newsletter 18

    Surface Evolver Newsletter no. 20

    Back to top of Surface Evolver documentation.
    
                             Surface Evolver Newsletter 20 
                                  January 1, 2008
                         by Ken Brakke, brakke@susqu.edu
    
    
    Surface Evolver version 2.30 available for download at
    http://www.susqu.edu/brakke/evolver.
    This is the first release since version 2.26 in August, 2005.
    
    For those who compile Evolver themselves, the same makefile may be used.
    
    New features in version 2.30:
    
    Clip view:
      The graphics display now has user-controlled clipping planes, so you
      can see inside your surface.  In the OpenGL graphics window, if you 
      hit the 'l' key (lower case 'L'), a clipping plane will appear. Dragging 
      the mouse will translate the clipping plane, and hitting 'k' will let you 
      rotate the clipping plane.  See "clip view" in the documentation for more.
    
    Slice view:
      There is also a slice view mode, that works like clip view, except it
      produces a two-dimensional cross-section of the surface.  To initiate it,
      use the "slice_view" toggle command; thereafter the 'l' and 'k' keys
      control the mouse mode.
    
    Movie viewer:
      For scrolling through stages of an evolution in full 3D fashion, I have
      written a program "evmovie" that reads simple geometry files and makes
      an Evolver-like display, so you can manipulate the surface with the mouse
      (with clipping plane!) and scroll through the files.  It can handle
      hundreds or even thousands of files pretty smoothly.  There is a new
      Evolver command "binary_off_file" for producing files for evmovie.
      To download and see documentation, go to
      http://www.susqu.edu/brakke/evmovie.
    
    Loading multiple surfaces:
      The "addload" command will load a new datefile without deleting the
      current surface.  Good for building up a surface from several component
      parts, particularly if one componont is repeated in multiple places.
      See the addload_example.fe datafile in the distribution for an example.
    
    Graphics text:
      You can display simple text in graphics (OpenGL and PostScript graphics,
      anyway). Syntax:
    
         text_id := display_text(x,y,string)
    
      The x,y coordinates of the start of the string are in window units, i.e.
      the window coordinates run from (0,0) in the lower left to (1,1) in the
      upper right. The return value should be saved in a variable in case you want
      to delete the text later with delete_text(text_id); even if you don't want to
      delete it, you must have something on the left of the assignment for syntax
      purposes. No font size control or font type or color implemented. Meant for
      captioning images, for example a timer in frames of a movie.
    
    Showing string facets:
      In the string model, facets will now be displayed according to the
      "show facet where expr" command.  Facets can be colored and picked.
      If the facet edge loop is not closed, then a closing edge will be
      assumed.  May not work too well for non-convex facets.
    
    Array operations:
      Several basic operations that work on whole arrays have been added
      to the Evolver command language, so scripts can avoid tedious "for"
      loops:
    
        array := array
        array := scalar
        array := scalar * array
        array := array + array
        array := array - array
        vector dot_product vector
    
      These work on element attributes that are vectors or arrays, as well
      as stand-alone arrays.  I decided to use "dot_product" as the name of
      the inner product operator instead of just "dot" since I had several 
      scripts using "dot" as a variable, and I assume other people do also,
      and I didn't want to break those. There are also matrix_inverse() and 
      matrix_determinant() functions.  
    
      NOTE: the matrix_inverse() function is now a function returning 0 for
      singular and 1 for nonsingular matrix, rather than a procedure not 
      returning a value. This means that scripts using matrix_inverse() will 
      have to assign the return value to a variable.
    
    Element array attributes:
      For use with the whole-array commands, these element attributes
      are defined as 1-dimensional arrays:
      vertices: __x (coordinates), __p (boundary parameters), __vertex_normal
      edges: __edge_vector
      facets: __facet_normal
     
    Local names:
      The syntax for declaring identifiers to have local scope has been extended
      to permit multiple names in the same declaration, such as
         local ax,bx,cx,ay,by,by,cx,cy,cz;
      
    Deeper recursive calls:
      The stack mechanism of the expression interpreter has been modified
      to permit much deeper recursion, for example in geometric search
      algorithms. 
    
    Debugging scripts:
      Several features have been added to help in debugging scripts:
    
      "subcommand" command: Displays a command prompt and accepts user
       commands.  At "exit", it resumes the script.
    
      "abort" ends all script execution and returns to the main
       command prompt.
    
      "breakpoint" can be used to cause a command prompt to appear when
       execution reaches a certain line in a script.  The syntax is
           breakpoint scriptname linenumber
       where scriptname is hte name of the procedure or function and 
       linenumber is the line number in the file the procedure is defined in.
       There is also an "unset breakpoint" commmand.
    
      "whereami" at a subcommand or breakpoint prompt will print a stack
       trace so you can tell where you are in your scripts.
    
    Expression profiling: 
      On systems where Evolver has cpu_counter available and Evolver has been
      compiled with the manifest constant PROF_EVALS defined, the expression
      evaluator inside Evolver keeps track of the clock cycles elapsed during
      each expression evaluation. These expressions include procedures,
      functions, constraint and boundary formulas, content integrands, energy
      integrands, quantity integrands, etc; everything that prints out as code in
      a dump file. The "print profiling" command will print the accumulated CPU
      cycles so far for each type of expression. The times are inclusive of any
      child functions or procedures.  The command reset_profiling will set all 
      the cycle values back to 0.
    
    
    New named methods:
    
      dirichlet_elastic: to do conformal mapping by minimizing Dirichlet energy.
    
      laplacian_mean_curvature: to do motion by the laplacian of the
        mean curvature.
    
    New toggle:
       function_quantity_sparse: uses sparse matrices with function-type quantities.
    
    Warning suppression:
      If your datafile or script generates harmless warnings, you can suppress
      particular warnings by number.  Syntax:
         suppress_warning number
         unsuppress_warning number
    
    Validity checking:
      To help in scripts that want to iterate over constraints or boundaries,
      there are read-only variables high_constraint and high_boundary for the
      maximum constraint and boundary numbers (remember that for named 
      constraints and boundaries, the names are synonyms for numbers),
      and there are boolean functions valid_constraint() and valid_boundary()
      that take numbers or names as arguments. 
    
    
    Autopop and autochop improvements:
    
      Autopop in the soapfilm model now deletes small facets.
     
      The immediate_autopop toggle causes deletion of short edges or facets 
      to be done immediately on detection before proceeding with detetecting
      further small edges or facets.
    
      The read-write internal variahle autochop_length can be used to read or
      set the autochop critical length without changing the autochop toggle
      state.
    
    Edge deletion options:
    
      Toggle force_deletion will force edge or facet deletion to happen
      even if it creates two edges with the same endpoints.
    
      Togglw star_fcnagling will refine edges that would otherwise wind
      up with same endpoints due to an edge or facet deleteon.
    
    End of Newsletter 20
    
    
    evolver-2.30c.dfsg/doc/news_05.htm0000644000175300017530000001626111410765113017173 0ustar hazelscthazelsct Surface Evolver Documentation - Newsletter 5

    Surface Evolver Newsletter no. 5

    Back to top of Surface Evolver documentation.
    
    
    		      Surface Evolver Newsletter Number 5
    			      July 28, 1993
    
                        Editor: Ken Brakke, brakke@geom.umn.edu
    
    Contents:
      Positions available
      New features of version 1.92
      Parallelizing Evolver
      New printed manual
    
    Positions available.
    
    Two PhD research assistant positions available. Positions are NSF-funded and
    will eventually contribute to a consortium between several national labs/
    universities/microelectronics corporations. Looking for (1) Theoretically-
    oriented student to do combined analytical/computational research on menisci
    stability. Experience with Surface Evolver desirable (2) Experimentalist
    to conduct research in reactive wetting involving liquid metals. Candidates
    with applied mathematics, physics, engineering science backrounds encouraged to
    submit resume/vita to Professor Timothy Singler, Dept. of Mechanical
    Engineering, State University of New York at Binghamton, Binghamton, NY 13902-
    6000, phone (607)-777-4330, E-mail Singler@bingvmb.cc.binghamton.edu.
    
    New features
    
      New named quantity syntax.  See below.
    
      New named quantity methods:
        Edge, facet scalar integrands. Method names are "edge_scalar_integrand"
        and "facet_scalar_integrand". Both need scalar functions specified.
        Example for using a string model to simulate a surface of revolution:
    
        quantity rev_area energy global_method edge_scalar_integrand
        scalar_integrand: 2*pi*x
    
      "INFO_ONLY" quantities are not calculated until their values are
      needed.  This should speed up some calculations.
    
      "Fix" and "unfix" can be used as verbs, like list, delete, etc.
      I kept writing commands like 
         fix vertices where on_constraint 1
      instead of 
         set vertices fixed where on_constraint 1
      so I made it legal.
    
      The verbs set, unset, list, delete, refine, fix, unfix, dissolve
      can be used on single named elements inside an iteration construct.
      Example:
        foreach edge ee where ee.length < .01 do {
           printf "Refining edge %g.\",ee.id; refine ee; }
    
      All toggle command words (not single letters) can be used as Boolean 
      variables in expressions.  Example: 
        if conj_grad then {g10; u; g10} else {g30; u; g30}
    
      There are more internal variables available as read-only
      values: space_dimension, surface_dimension, torus (Boolean),
      torus_filled (Boolean), symmetry_group (Boolean), 
      simplex_representation (Boolean), integration_order.
    
      Mac version repeated commands interruptable with control-'.'.
    
      Macintosh and Dos versions pipe to a file instead of a command:
        Enter command: list vertices | "filename"
    
      For a torus domain, the torus periods may be specified using
      expressions with parameters, so the fundamental cell may
      be changed interactively.  Do a "recalc" after changing
      such a parameter to update the torus periods.
    
      gv_binary toggle for binary/ascii data to geomview. Default ON
      for binary, which is faster.  Ascii mode useful for debugging.
    
    Named quantities.
    
      As the Evolver has accumulated more and more types of energies,
    it has become very obvious that adding each one in as a special
    calculation is not a good way to go.  The system I've been
    experimenting with I call "named quantities", and the energies
    I've added lately have been in this system.  Gradually, all
    energies, volumes, etc. will be converted internally, but
    existing datafile and command syntax will remain valid (where
    possible).  The system has changed somewhat since its inception,
    necessitating a slight change of datafile syntax.  The system
    has three components:
    1) Methods: A method is a way of calculating a scalar value.
    Each method is implemented as functions calculating its value
    and gradient.  Methods are described in the manual, and have names.
    2) Method instances: A method instance is a particular application
    of a method, with any particular data such as scalar integrands
    or parameters.  Method instances may be explicitly created, or 
    may be implicitly created inside quantity definitions.
    Explicitly created instances are given names by the user.
    3) Quantities.  A quantity is the sum of method instances.
    Quantities are given names by the user, and referred to that way.
    A quantity may be part of the total energy, it may have a
    fixed value, or it may be computed for information only.
    Usually quantities have only one method instance, but there
    are occasions where several might be used.  For example, in calculating
    the volume of a body with free boundary on a wall, one might
    combine a surface integral and an edge integral.
    
    See the 1.92 manual for full details on methods, instances, and
    quantities.  The syntax change mentioned above applies only to
    quantities being applied to individual elements, not globally.
    Now the method must be included in the quantity definition, and it
    is sufficient to just list the quantity name on the lines of the
    desired elements in the datafile.  For example, to sum the lengths 
    of triple junction lines in a soap film,
    
       quantity triple_length energy method edge_length
    
    and then each triple junction edge would look like
    
       edges
       1   1 2  triple_length
    
    Adding the layer of method instances between methods and quantities
    necessitated the change.
    
    
    Parallelizing Evolver.
    
      Everybody knows that parallel computers are the wave of the future!
    The past couple of months, I have started to dabble in parallelizing
    the Evolver.  The only feature ready to announce is for multi-processor
    Silicon Graphics systems.  If you compile with -DSGI_MULTI in CFLAGS
    in the Makefile, then named quantity calculations will be farmed out
    among the processors.  Note this only for named quantities, such
    as edge_length and the knot energies, and not for ordinary length,
    area, constraints, etc.  For knot energies, I have gotten a factor
    of 3 speed-up on a 4-processor machine.  
      SGI parallelization is fairly simple, since all the processors
    share the same memory.  On a more distributed machine like the
    Connection Machine CM-5, each processor has its own memory, and
    message passing has to be used to keep all the nodes updated.
    So it looks like a port to the CM-5 will have to parcel out the
    surface in patches to nodes, and each node will have to keep its
    neighbors updated.  I am a neophyte at parallelization, and I would
    like to hear from anybody who has a good algorithm for splitting
    an Evolver-type mesh among nodes, maybe with load-balancing to take
    into account that some nodes (such as those on constraints) take
    more processing than others.
    
    
    New printed manual.
    
      The Version 1.92 Manual is being printed up in hardcopy 
    as Research Report GCG 55 of The Geometry Center. 
    The last hardcopy version was 1.87.  Of course, the version
    distributed with the ftp archive is always current.
    
    End of Evolver Newsletter 5
    
    

    Back to top of Surface Evolver documentation. evolver-2.30c.dfsg/doc/news_11.htm0000644000175300017530000001403411410765113017164 0ustar hazelscthazelsct Surface Evolver Documentation - Newsletter 11

    Surface Evolver Newsletter no. 11

    Back to top of Surface Evolver documentation.
    
    
    
                        Surface Evolver Newsletter Number 11
                                  March 15, 1995
    
                       Editor: Ken Brakke, brakke@geom.umn.edu
    
    Contents
      Postdoc position wanted.
      Version 1.98 ready.
      New features.
    
    Postdoc/teaching position wanted
    
      Anmin Zhu expects his PhD in August 1995 from Arizona State 
      University. His thesis under Hans Mittelmann is concerned
      with the computation of capillary and minimal surfaces.
      His interests are in the numerical solution of ODE's, PDE's,
      and applications of Differential Geometry. He has extensive
      EVOLVER experience, a broad background in Math and Computer
      Science, and is a successful teacher of Algebra and Calculus
      including Reform Calculus.
    
      contact: Hans Mittelmann (mittelmann@math.la.asu.edu, (602)965-6595)
      	   Anmin Zhu       (azhu@asu.edu, (602)965-0406)
    
    
    Version 1.98 ready.
      The new version is available by anonymous ftp from geom.umn.edu
      in the directory /pub/software/evolver.  The Mac and DOS versions
      have also been updated.  The Mac version is now in Evolver.sea.Bin,
      which is a self-extracting archive treated with BinHex 5.0.  No
      PowerMac version yet, but soon I hope.  There is a PostScript version 
      of the new manual in /pub/documents/preprints/GCG71/manual.198.ps.
    
    New features:
    
      A little advanced calculus tutorial has been added to the manual
      at end of the Tutorial chapter.
    
      Geomview picking: If you right-click the mouse in the geomview
      window, Evolver will print the id numbers of the vertex, edge,
      or facet you click on.  These id numbers are saved in the
      variables pickvnum, pickenum, pickfnum.
    
      Hessian stuff:
     
        A section on Eigenvalues and Eigenvectors has been added to
        the Model chapter of the manaul as a brief guide to using
        some of the relevant commands for analyzing the Hessian.
    
        The command Hessian_normal permits vertices to move only
        perpendicular to the surface.  Much easier to converge if you
        don't let the vertices slither sideways.
    
        The command "eigenprobe x" will report the number of eigenvalues
        less than, equal to, or greater than the value x.  The numbers
        are recorded in the variables eigenneg, eigenzero, and eigenpos.
    
        "Lanczos x" prints 15 approximate eigenvalues nearest x.
        "Lanczos(x,n)" will print n approximate eigenvalues nearest x.
        Multiplicities are unreliable, though.
    
        "Ritz(x,n)" does a shifted inverse iteration on an n-dimensional
        random subspace.  Slower than Lanczos, but much more accurate in
        values and multiplicities.  Hit CTRL-C when you get impatient
        for it to converge, and it will print current approximations.
    
        To get eigenvalues and eigenvectors that can be compared to those
        for smooth surfaces defined using the L_2 norm, a linear_metric 
        command has been added.  The metric used is a convex combination
        of weighting vertices with the adjacent area, and the L_2 norm
        applied to linear interpolation over the facets.  The variable
        linear_metric_mix gives the proportion of the latter.  Default
        value of 0.5 seems to give the most accurate eigenvalues for
        a couple of test cases.  Use only with hessian_normal!!!
    
        You can actually see the eigenvectors if use use hessian_menu.
        Use menu choices 1, V, and 4.  See manual for more details.
    
        If your files involve energies or constraints that don't have
        Hessians implemented, run Evolver with the -q option.  That
        converts everything it can to named quantities internally, and 
        they have a lot more Hessians implemented.  Actually, -q is going
        to become the default before long, but all the current datafile
        syntax will still be recognized.
    
      End of Hessian stuff.
    
      BREAK and CONTINUE have been added to the command language for
      breaking out of loops. Also BREAK n and CONTINUE n for n levels.
    
      'P' command takes menu option number, i.e. 'P 8' for quick geomview.
      Also have 'geomview' command for same.
    
        -q option
      
      Added 'random_seed' internal read-write variable, for seeding
      random number generator.  Reseeded to 1 at start of datafile.
    
      Added 12-pt degree 6 and 28-pt degree 11 integration rules for facets.
      Degree 6 is now default for quadratic area, since previous degree 5
      always got itself fouled up (for me, anyway).
    
      Degree of numerical integration for edges and facets is now separately
      controllable by the variables integral_order_1d and integral_order_2d,
      which are the degrees of polynomials exactly integrated.  But old
      integral_order syntax remains for compatibility, though obsolete.
    
      DOS and Mac versions that can't pipe to processes, but rather
      write directly to files, now recognize "+" at the start of the
      file as meaning append.
    
      The gravity_method named quantity now takes into account the
      densities of the adjacent bodies, whereas in earlier versions
      it just calculated for density 1, and the user had to account
      for density in the modulus.
    
      Added "target" attribute for body volumes, set and get. I.e.
      set body[1].target 3
    
      Added maximum(x,y) and minimum(x,y) to arithmetic.  These have two
      values  for arguments, in contrast to the min() and max() functions
      which take iterators.
    
      Added "set element orientation -1 where ... " command to negate oriented 
      integrals on edges and facets.  Useful when hacking up the surface
      and sticking edges on constraints when they point the wrong way.
    
      There are density_ versions of length and area named quantities that
      multiply by density.
    
      Streamlined asking for torus display mode.
    
    End Newsletter 11.
    

    Back to top of Surface Evolver documentation. evolver-2.30c.dfsg/doc/evolver.htm0000644000175300017530000000572611410765113017401 0ustar hazelscthazelsct Surface Evolver Documentation

    The Surface Evolver

    Kenneth A. Brakke
    Mathematics Department
    Susquehanna University
    Selinsgrove, PA, 17870
    brakke@susqu.edu

    with past support from
    The Geometry Center
    University of Minnesota
    Minneapolis, MN 55454

    Version 2.30, January 1, 2008


    Contents

    This HTML version of the Evolver documentation contains most of what the printed manual has, with the main exception of sections with a lot of mathematical formulas, such as the more advanced examples and the Techical Reference chapter. Not all possible links are in yet, but you can consult the Index to find something.

    Web home page

    My Web home page is http://www.susqu.edu/brakke/. Evolver-related material and links will be posted there. In particular, there are many examples of surfaces.
    Back to top of Surface Evolver documentation.

    Author's home page. evolver-2.30c.dfsg/doc/news_06.htm0000644000175300017530000000773411410765113017201 0ustar hazelscthazelsct Surface Evolver Documentation - Newsletter 6

    Surface Evolver Newsletter no. 6

    Back to top of Surface Evolver documentation.
    
    
                          Surface Evolver Newsletter Number 6
                                  December 16, 1993
    
                        Editor: Ken Brakke, brakke@geom.umn.edu
    
    Contents:
      Kelvin's partition beaten
      Ftp directory changes
      New features of version 1.93
      Bibliography
    
    
    
    Kelvin's partition beaten:
    In 1887, Lord Kelvin posed the problem of finding the partition
    of space into equal volume cells minimizing the interface area.
    He suggested the cell shown in twointor.fe, which is basically
    the voronoi cell for a bcc lattice.  Now Robert Phelan and Denis
    Weaire of Trinity College, Dublin, have found a structure using
    two types of cells that has 0.3% less area than Kelvin's. Their
    Evolver datafile, phelan.fe, is now included in the Evolver package.
    
    Ftp directory changes: 
    
    Version 1.93 is now available for ftp.  Due to a
    rearrangement of The Geometry Center's ftp directory, the path
    is now  pub/software/evolver/evolver.tar.Z.  There is not a Mac
    version of 1.93, as I do not have access to a Mac at the moment.
    
    When you download, please check the date on the file and be sure
    it is Dec. 16 or later.  I am sending this newsletter out before
    the files actually get into the ftp directory, due to my travel plans.
    
    Also the archive now unpacks into three subdirectories: src (source code),
    doc (manual), and fe (sample datafiles).
    
    New features:
    
     "history" command added to print command history.  Single-letter
      commands now included in history for convenience.  But history
      does not record responses to prompts commands may issue.
    
      Named quantities for scalar and vector integrands over edges and 
      facets added.  Method names are edge_scalar_integral,
      facet_scalar_integral, edge_vector_integral, and facte_vector_integral.
      Example use in datafile:
    
         quantity zweight info_only method facet_scalar_integral global
         scalar_integrand z^2
    
         quantity fvec energy method facet_vector_integral global
         vector_integrand
         q1: y^2
         q2: x*z 
         q3: y^4
    
      Quantity names may now be used as element attributes. Value
      of total quantity must be referred to as "total quantityname".
    
      Command repeat counts can now be expressions.
    
      More internal variables for counters on command events:
      equi_count, delete_count, notch_count, dissolve_count, pop_count, 
      where_count.  These can be used in queries to make actions
      depend on the outcome of previous equiangulation, delete, notch,
      dissolve, or pop commands, or to use the number of items satisfying
      the last where clause of a command.  These variables keep their
      values until the next command of the appropriate type.
    
      The manual index in 1.92 was slightly off in its page numbers.
      Fixed in current manual.
    
      DOS version has improved graphics.  Recognizes higher resolution
      and more colors.  See evolver.doc in evolver.zip.
    
      Can apply named quantities to elements with SET command. For
      example, if you have defined a named quantity, say pogo, for
      facets, you could do
         set facet quantity pogo where color == red
      Unset also works.  Of course, these commands do not apply to
      quantities declared global.
    
    
    Bibliography
    
      D. Weaire and R. Phelan, "A counter-example to Kelvin's conjecture
      on minimal surfaces", preprint.
    
      X. Michalet, D. Bensimon,a nd B. Fourcade, "Fluctuating vesicles
      of high topology", to appear in Physical Review Letters.
      Uses Evolver with squared mean curvature energy to model
      elastic cell membranes.
    
    
    Have a Merry Solstice and a Happy Perihelion!
    
    End of Evolver Newsletter 6
    

    Back to top of Surface Evolver documentation. evolver-2.30c.dfsg/doc/catenoid.htm0000644000175300017530000001424311410765113017477 0ustar hazelscthazelsct Surface Evolver catenoid example

    Surface Evolver Documentation

    Back to top of Surface Evolver documentation. Index.

    Example: Catenoid.

    The catenoid is the minimal surface formed between two rings not too far apart. In cylindrical coordinates, its equation is r = (1/a)cosh(az). In cat.fe, both the upper and lower rings are given as one-parameter boundary wires. The separation and radius are parameters, so you can change them during a run with the A command. The initial radius given is the minimum for which a catenoid can exist for the given separation of the rings. To get a stable catenoid, you will have to increase this value. However, if you do run with the original value, you can watch the neck pinch out. The initial surface consists of six rectangles forming a cylinder between the two circles. The vertices on the boundaries are fixed, elsewise they would slide along the boundary to short-cut the curvature; two diameters is shorter than one circumference. The boundary edges are fixed so that vertices arising from subdividing the edges are likewise fixed.

    catenoid skeleton The initial catenoid skeleton, with vertices and edges numbered.
    Here is the catenoid datafile:

    // cat.fe
    // Evolver data for catenoid.
    
    PARAMETER  RMAX = 1.5088795   // minimum radius for height
    PARAMETER  ZMAX = 1.0
    
    boundary 1 parameters 1     //  upper ring
    x1:  RMAX * cos(p1)
    x2:  RMAX * sin(p1)
    x3:  ZMAX
    
    boundary 2 parameters 1    //   lower ring
    x1:  RMAX * cos(p1)
    x2:  RMAX * sin(p1)
    x3:  -ZMAX
    
    vertices   // given in terms of boundary parameter
    1    0.00  boundary 1   fixed
    2    pi/3  boundary 1   fixed
    3  2*pi/3  boundary 1   fixed
    4    pi    boundary 1   fixed
    5  4*pi/3  boundary 1   fixed
    6  5*pi/3  boundary 1   fixed
    7    0.00  boundary 2   fixed
    8    pi/3  boundary 2   fixed
    9  2*pi/3  boundary 2   fixed
    10   pi    boundary 2   fixed
    11 4*pi/3  boundary 2   fixed
    12 5*pi/3  boundary 2   fixed
    
    edges
    1    1  2  boundary 1   fixed
    2    2  3  boundary 1   fixed
    3    3  4  boundary 1   fixed
    4    4  5  boundary 1   fixed
    5    5  6  boundary 1   fixed
    6    6  1  boundary 1   fixed
    7    7  8  boundary 2   fixed
    8    8  9  boundary 2   fixed
    9    9  10 boundary 2   fixed
    10   10 11 boundary 2   fixed
    11   11 12 boundary 2   fixed
    12   12 7  boundary 2   fixed
    13   1  7
    14   2  8
    15   3  9
    16   4  10
    17   5  11
    18   6  12
    
    faces
    1   1 14 -7 -13
    2   2 15 -8 -14
    3   3 16 -9 -15
    4   4 17 -10 -16
    5   5 18 -11 -17
    6   6 13 -12 -18
    
    
    The parameter in a boundary definition is always P1 (and P2 in a two-parameter boundary). The Evolver can handle periodic parameterizations, as is done in this example. Try this sequence of commands (displaying at your convenience):
        r       (refine to get a crude, but workable, triangulation) 
        u       (equiangulation makes much better triangulation) 
        g 120   (takes this many iterations for neck to collapse)
        t 0.05  (collapse neck to single vertex by eliminating all
                          edges shorter than 0.05)
        o       (split neck vertex to separate top and bottom surfaces)
        g       (spikes collapse) 
    
    The catenoid shows some of the subtleties of evolution. Suppose the initial radius is set to RMAX = 1.0 and the initial height to ZMAX = 0.55 (these are pre-set in catman.fe). Fifty iterations with optimizing scale factor result in an area of 6.458483. At this point, each iteration is reducing the area by only .0000001, the triangles are all nearly equilateral, everything looks nice, and the innocent user might conclude the surface is very near its minimum. But this is really a saddle point of energy. Further iteration shows that the area change per iteration bottoms out about iteration 70, and by iteration 300 the area is down to 6.4336. The triangulation really wants to twist around so that there are edges following the lines of curvature, which are vertical meridians and horizontal circles. Hence the optimum triangulation appears to be rectangles with diagonals.

    The evolution can be speeded up by turning on the conjugate gradient method with the U command. With catman.fe, try the script "r; u; U; g 70". For conjugate gradient cognoscenti, the saddle point demonstrates the difference between the Fletcher-Reeves and Polak-Ribiere versions of conjugate gradient. The saddle point seems to confuse the Fletcher-Reeves version (which used to be the default). However, the Polak-Ribiere version (the current default) has little problem. The U toggles conjugate gradient on and off, and ribiere toggles the Polak-Ribiere version. With Fletcher-Reeves conjugate gradient in effect, the saddle point is reached at iteration 17 and area starts decreasing again until iteration 30, when it reaches 6.4486. But then iteration stalls out, and the conjugate gradient mode has to be turned off and on to erase the history vector. Once restarted, another 20 iterations will get the area down to 6.4334. In Polak-Ribiere mode, no restart is necessary.

    Exercise for the reader: Get the Surface Evolver to display an unstable catenoid by declaring the catenoid facets to be the boundary of a body, and adjusting the body volume with the b command to get zero pressure. See the sample datafile catbody.fe.


    Kelvin foam example. Back to top of tutorial.
    Back to top of Evolver documentation. Index. evolver-2.30c.dfsg/doc/catbody-saddle.gif0000644000175300017530000005162611410765113020553 0ustar hazelscthazelsctGIF89ad      ###''('(''(((''('(((',,,//0/0//000//0/000/333778787788877887<<:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:u"LqbC "JCdSRu:J1S  PH"[F)" .] /` ;D 0`'ND!^X!E)RH"E)!PHiI 6D(.RF:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSNa"hT"\8bT)RJTRJ*UQ/, 0` 0`q\p 0\| 0`pccǙ3\>dÅ 0` 0`!b ^"E)XH"E K0"eIH!f)N:uԩSN:uԩSN:uԩSN:uSN:uԩSN:uT*RHRɔQP"UT)RJ1¨TRJ*U"` 0` &Μ9c0\0`| .`l8S. 0` 0`dɐE&6N)RH"eH"RDYBʈ(q#%N:uԩSN:uԩSN:uԩSN:uԩSN:uԩSNi`CJ FA"^,TTR@ UT:$X 0` 0`b3gΜ 0` .` gΜ9s` 0` , b 0`0!BDDH$ &LD,ʋp!e EN:uԩSN:uԩSN:uԩSN:uԩSN:u$ x!:DAE 'Fp!I@ula6mBd 0` 0Y>p9s̙3`x 0\ 0`p̙3`Μ9#E0` .`̙3hh 2`l9!b)Yd0K]@*USN:uԩSN:uԩSN:uԩSN:uԩSN􄑨RJCQJxJE 'SJ25Q .hs̙,! 0`)g΀9s̙3gt 0`p Μs̙3gΜ 0\ 0`DH9sf͙3R>0`"5D4`Ê,RH"H"ԩSN:uԩSN:uԩSN:uԩSN:uԩJ B*UT"J*eT)SJ"RLX hΜi ` 0`̙3g΀9s̙3=lp 0`pb#ę3gΜ9s0g 0\ )gΜ9s)' 0`΀ 0*P(!)YHQ@$'HI)ԩSN:uԩSN:uԩSN:uԩSN:ASJ!ˆTRJI)U)SL"S)Sp͙3hΠ9 0` =Μ)s̙3gΜ9sL`p 0=@9s0gΜ9s̙3#| 0_d̙3gΜAs ` 0`Ɔ6/pa!,R$ RiT) psԩSN:uԩSN:uԩSN:uԩS:RTuH*UTJxJ*eT)SL"S 9s4gМ 0`Dę3gΜ9̙3gΜ9s 0`… 1gĜ9s̙3g!s̙K =֜9s4gΠ90e 0aphÆ.gdŒ `):uԩSN:uԩSN:uԩS:uT)"KAQT)SJ*U (SLň#F\@9p`3hМ9̙3ht 0`8s 3dΜ9s̘3g̜90\,1q̙3gΜ9 3gΜ9s挈%\cƙ3gά9s̙3gМ 0`LlڰaEę3gΜB‰N@< D0:uԩSN:uԩSN:uԩSNBA鄍:1bTNj/BP4@/\8rȑ)S9̙6hΜA"/f.`ΐ9s̙3gΌ9s̙3ɜ` %$9s̙3`Μ9s̙3gȜ9B0`8s4gΠ9s̙3gΈ 3` 6lذ!B㌘3bΜYr)@50"I N:uԩSN:uԩSN:uԩSTeȲe=%DT-g "HKL2eʔ)hМA4hΜAs &`ِ̙3gΜ!s1gΜ9c̙3g>,Y2̙3gĜ9s̙3gȜ!s̙3gpِeɉ3gΜYs̙3kΜAs4' /ذa 6lN@9sgET,gH"!C)N:uԩSN:uԩSN:uH H"E(RH=T'(L2e)\2e 4gΜA̙5gМAsFC0"9s̙3gΜ9s̙3gΜAd  xBŋ.^@Œ%K,Yd %Gh(hp̙3gΜYs4gΜ9s RC6lؤa& 1"9sK?!5JLHaFɩSN:uԩSN:uԩS!B )RF"Ejԙ,F"u)SL2e!)Rs @4hΜA 3hΠ3gΜAd,^dxAċ:lɲ`ѣG=`dْ%K,Ydْe)xᢆ/,r̙3gΜA -`|H 6nظa g΀ Nz09IL`0RԩSN:uԩSN:u=^`2(RHa(RHubʔQp!:"E(R6@A !FL2eʔ)"Π9̖%/D@=ѣG?zѣG=ѣG%Ys=zѣG=zѣG=zd HD=zѣG=zѣG͖-6ld 7lذI&M6lش!bQF2jԨQ8iFS0nN:uԩSN:u)L<S')F)RHA:GLB .x2e/D8㋛@ ĥG aѣG =zѣG=zsF@PG=zѣG=zѣG҃B=zѣG=zѣG14H1 >aM4L(XiԨQ̸5jԨQxERN:uԩSN:EN/0q$C40$ǒ9H)L2eQ .D@D=@zD9=~H>ѣ'=uPP@ڼУG=zѣG=za"P@eG=zѣG=zcŠA"G"/^0bB7z,iԨQK5jDR˩SN:uԩSL@ÉK N8IeK&L<5QgENL2eʔ)SFm1@H) #=zѣG=zѣG 6 (P6/ѣG=zѣG:("(P@5ѣG=zѣG=b214 >zG7i@zaiԨFՃ&"4ԩSN:u)89NDrhԨQH5ђQF)#SL2e!Lq@ "С@AQG = GO =zY"%С@ t(PzѣG=zѣ@ (uѣG=zѣ= 2DdK=|bO> O|,Qzt'>T:uԩSNą:u$CF"5jQHDj(RFŔ)SFacʔ)S@~H P@ؠУG=zѣG=`h (P@ H=zѣG=z(P@ DM=zѣG=zA!C BQ 4H©SN>u$eTQ,ѳdTN8="‰% zN:uԩS$%P'Nx" RFEԨ,FEjԨQH"2T L2eʔ%GAC th 6/G@zC:(P@!E=zѣG 4(PC""E=ѣG:( (С@z|4pL%LRHRiԧO:)4SO!N`RN:uԩI LnptQFu(˨QHEԨQ0mр .\lp†Ja)u("zѣG =z`(С@ PAG=z#"P@:(P B`G=z#BO@!a 6:uIJ'NSO=8'F ^<ĩ#).hԩSN:ՀSN^&4q#ШQ: 5ԨQH:tfɒ%"cʔ)SJ*URLUH4H=zң)D P@ (СCPѣG=zD $(P A (P@zPѣG=u2CF2ԩSN:uѩSNԼpS%,:dF%L^ԉȥ3||tԩSKJ0a'N8YiTLF:tf*z,uǧPz|UR*I)U)SL 2p=za"С@@ $(P ="ѣG 4HP@ d(P .6aFD0J0uԩSNtԩSNਢI̠QdG`py$H%.g:u)0"zЃi&Lnp℉'L %RXSQ>a3jTPHTT%L"ŨΖ'ByDN Fgh K2 (P@ PC Hω:z,i(P@ ( =`0# DHSN:uzщQF:uԩSN8uԉSN)Q =0i" &L`pXQɑN:uJ0a„ J4eI&M[ӨQF4ӨO=FuiTP0 M"LzD)RHzT(RFz(!C=z` 0`#)L(2dKXqCN:uԩSNt N:u4SN:uԩSN ` &LnX„ &4.YԩSH` &N0a" 'N4qb =/,}jN:d QF *% Nd)RH"E)RH-!E)RHB䣎=::tP,Y0@'N8! =T`A:u)TN^`N8uԩS')8uN:IĩN:q 74h0„ &0a*tԩCNT„ &L`I&Mgl<ӣ(`>} (P:-(PFUJBR =,a)RH"E-H"ET uFdQH5jԨ3>,1zi'OFuiT'NK8ԩS'P@uIN:a(E N:uIJ'N8uԣNNxuԩ'L\^ydNG4`„&LxD$ : :u*ˀC64a¤ J0qT K F}iT'NKF %=&uIJN:YdHNH"E QH Rg(RF5ԨQF:ШQ84jT'O<4j NzhSNuSNz9ĉ'Ndɖ@F̨QF}jԨQFi #O8ԩSN:ubS'M=:uԩN:uĩ'LD^pA3*UT 84Xd%KTTRJ(QGD9baShĈ,L^8dɒ%K.Yt&L0a„ &LD(N:uԩSN:-S':uL8qBs'N8qdI" MC>5ӨQF%:uԩSN:1Z҉ʣN:uԉ@H!BD TTRJ* R84X„ 5*Ű,8Π葑CNA F61jĈ0YtҥK0]j &L0aIF=,9H=:uԩSN>Bu)T%Dqĉ'Npĉ'N8qd㌙3* &=Hye @-Y"SN!'N8qĉ8qĉ'Nr ¤M6ii&N6qtH,"Xrdɑ%K,ad ԱdR%K.]tҥKdDzHơJ*UTiIHUzyZ"K#GqP%J  D"ˆQ#F/h ǣK,Yd ;0a„I 8=zJ*UTR:J~au8qĉ'N8qԉ%N8qB4at&N6m 'N4iҤȒJ6YtҥK,YT' #K,Ydɒ%K*Yt="=bOJ*Uz)ңH(= /0jĨ#G`(R萛 1GċDt4bĈ=>%L.YZ &LHy(ңH"ѨTRJ*UTRJ| 'N8qԉ'N8թÉ'N &L09I'N8m'McR#,YdҥK,шT,UdR%K.]#QG1ѧRJ<(RH )yņ:5jыC:tF@y"0ձq萍C:sKl`IFEzQG1*UTRJ*UT ;=a!N8qԉ'NpĉS"0i„i&MΜф M4i”i F*YFK.Yt.UK,]dɒG(EzQGzTH*Ez䦇 =9dF5hCѡClqF!4x#F':CZB%(RG=(>*UTRJ*Aј-=$Ň:8qĉ'"8q&M4aڄ &Lfpi'L*SRJ*UĒ%K.YR$H"QGK.]tƠH"=QG1T2#FR^daĈ#Z#Fz:tFC/-23RΰaD̡CP:tСCXbƙH"=zQH"=$RJ*UT"5zQHL\qI#ND`„ S&L0m¤i&g8qĉ,UtҥJ*Qs钥JԁңJ*EjcɥK,9QG=(##[z<F1bGɡCI7h th˒@j' )l8d!1:(ѡD~ QH"=QGTRJ*5QGQG=zɐ="&L0a” M0m´N*RRJ*UtҥJ?]tQJ*E)RH !Fz(ңGEz4 'bĈ!F-sСCܜ8d7bQ'P:D"ơz:tC:(HCOG=(ңG1*UTR%>(=jѣGD(ңH"=Q3! )RɓGM6ad,YTK*UTR%,]qRGUJ YB7KAH @yCDC1bĈ:tС@/8tСC/#MN( С@9s#Fʠ猔@2GGEzIR:*U$ǣF=jQ"EzQGEzI9<9rȑ7nܸB"(|,,u4dɒJ*Ud KTTQG*1sF "/( XC1:#FzP Æ F1J4GɡC:FY2t @gP,i ԩ#h:4РClbtС@) С@!BʠG= RF4h0G " N:ű plP@nZ O@Bq(PB:4Ȑgl(P@zD=(!1J(D1J(5rѡpRt!EyшQF5bT'C tz1bĈ#u t(СCH.ŋ)u90" =zȢg:؜y̩SN=CJ8n^¥@'@=xP@b=z^ (P@ʃ R@Q"F%bQ"F%JHF9J$͡C1:(QDdaĈ#F8t(РC"DH .bą!B :t(СCdɋ=(П@u@SGPBgɉ,zd!B(2uԩSA8R,$(@(th!7(H (P@ M| =%b#E)rÑC0:ТCbt%YdĈQ:!B 4 FthC :fK"d(@|(P7zS'DDȢ:ԩSH.RN:uԩ l^)B(@F@:$=zǙ@ (P@BBO3¥>PFI!rE F-:h!F1:Ћ:ِС@ :PCJ >ڀE9pS$" P@RG:zԉb=zCJ8hHGSN:ujI.5ЩCȋYP`'P@С@УG@|詃"P@ (P@LNE@| (@pC /((!d 3gΜ9!F:Fl 0`Cć=zd "\G=Sǎ<HO &>ԩ /=zꈨS)l8uԩSNE:n8#6LD'P@Pt=zѣG=zT (P@ Ć;zAQ'P = (P@(=e˖-\# =,@ =nC .\|>(yQ>z H\ԩSL tE=h|xOKܸAFclFzN:uԩS^BN7(dAᆍ7nx%PuآG={ѣG=" (P@(SG=u(P@z O@1qa&zh@z3"ؼء'@'="K:YN@z!buԩSN\򢄏3uƅ7nXF nHA#éSN:uԩglC4glDqƍ7p@񢊏 fѣG=zG" ( "v詣G=`^(P = 'PDԩ@zH=t愛:uG@z Ǐ?uNlSN76H>KzԩSN:vԩå DSN:n6ƍ7"@ h^9uԩSN:) Ryq nqƍ7n -z>zѣ@B=v詣GO,('@z#:vԱCd=٣g=zSN:n:|G= L6uԩSN&'̋:uԩSL/\D:vԩS' 0tܸ# Yܸq#c N:uԩSN $8RAsf 7nܸqƍ66dB䅊DѣG=z(P`SGO:z詣Ȓ@ (P3"ةcN^ =yN7uԩSNgP,6^AM:(aÄ=u!Bč04Dr7pΩSN:uԩSNB -hΠ97nܸq3QDiÆ"/^=zpL=y詓GO=ucI@| DE:zc:zHO =-QN:uԩSl GuԩSN:uԩS 6\L:uԩ 6n؜3(b 4B:ԩSN:uԩSxd4gЀƍ7pܰy̙("аaÆ 6i!).ѣG:yѣǎ;uq&P@bSGO:uԱSN:DPDԩSN:uԩS %|!BN:uԩSG4D :uԩC:u\\aÆ 6lPDڠ9愔0gƼRN:uԩSN:uNA4DqǍ,/Μ9#& 6mذa&M6lذB1NذA$K=y䩣G=y^.uS:vرN"(H:uSN:uԩSȋ:/ԡ"/D "eK7hD#bI:uЩS ,nظqÆM3JD 3g,yqf̙3=,PԩSN:uԩSN:uS `ΜAs gܸqF0`̜9G6lذQ&6iҼ04h,Y 5\a#K0XpSN:uS:v詣G @/ԩSN:uPBE4hH0 DqSǍ7uܸF3hP:uD7nN9 2e9F gܸQr2gȜ9eIX|@6 0`a /\p`)"D!†,Yjd"e ) 09uԩSN:uԩSN:uԩSN:uԩSN@$,RDHY)Yd" 4̘3`€sL0f ds̙3`΀# -I1gs 0g#,8^pYP B 0\8e ` 06l| .\,y )YdɒE )RH ŧSN:uԩSN:uԩSN:uԩSN:uԩSl R!%K)RHPJ8q" "^BH0g:Ha 0`9 0b =ڈf 3`ɲ(X`(z 0`pL=dɒň,\ .\tᒥ((%,YP"E"9uԩSN:uԩSN:uԩSN:uԩSN:uԩSD4x!e(RzPX"%K)Ydb DH"E%=z0q P P@ /P@BAD@Aĉ'lCJ,YD.\p 0\H,[ld0`@ %/6!E!R6dɒ% K0yNN:uԩSN:uԩSN:uԩSN:uԩSN:u)RLD&K6ؐ"%,R`dRHɒEJ)RHE >dɒ%K)Yd"%,YH@,YdɒK,Ydɒ%"RH.\ "lٲ%K,YHٰ Y|l)V!E Y 0:uԩSN:uԩSN:uԩSNH"EDDX@AcRN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:u/H"% DPQ" &LD!b)XX"EJQHBd,Rdɒ%K,Yf")RHH,Ydɒ%K) `%,RPy.Y6ؒeK-R8q" 6@ "EJ)RHҀ)RH@!>sԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uT AdɆX2,y$ʓ(QH!B,RdBA(LHb)YdEJ,"^H"E)R8%K*Yd)YH")Y'6 BD KHa""<,")H"XDN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uT'7"PȰAA Q0"%J&L@D)RNܐ"%)Q0r",Td")RH"E)R|P %K,R2H"%)YH  h`F)LD„"P0adɒ"D0"% (6d%4ltԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uT'8/D@@D(Ph!ER0'RHYD,YHX"E)RH"E)L6(V`ʼn 6c KP (LHyrb&L,a„"P(ȰA "B!Ҡ=N:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩNgD0"2d ⅍%/B0D(R<&R)RH"E)RH"Ez !" "^Xdɒ%Kd@d %(6Ha &=&`d&K86!"B"PsԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSpBD>yb !B">AD"D8b"6p!C ,`dɒ%L,Yd 4,QdI KDYA%K†"(l 2d@ʣSN:uԩSN:uԩSN:uԩSN:uԩSN:u SN:uԩSN:uԩSN:uԩSN:uԩS2Mya (!B(lA"D!BXp"D!%Dl6 dɒ%K,!ǒ%K,YG%408/|!2 "D4 n0:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSHE"Cc 4|HA&(d0"D!"D^PH"D!Bȉ/!B"62d8dɒ%=Ydɒ%>haA .l!B'( B cԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:)2lE D^X "D^l B"DhX@"DPPxA"D!B"42<8 "(la Dx1a"D` |FҩSN:uԩSNxԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩS"=BF >P0D!B"DzP6bx"Ć24ذaX8`"@|@ď!C p%УSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSNj@>|G 6ذA`̣ON:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:u}SN: 1c̸PBE(A (P 1`։өSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:uԩSN:;evolver-2.30c.dfsg/doc/mound.htm0000644000175300017530000001523011410765113017030 0ustar hazelscthazelsct Surface Evolver mound example

    Surface Evolver Documentation

    Back to top of Surface Evolver documentation. Index.

    Example: Mound with gravity.

    This example is a mound of liquid sitting on a tabletop with gravity acting on it. The contact angle between the drop surface and the tabletop is adjustable, to simulate the different degrees to which the liquid wets the table. This example illustrates macros, variables, constraints with energy, and omitting faces from body surfaces.

    The drop starts as a cube with one face (face 6 of the cube example) on the tabletop (the z = 0 plane). The most straightforward way to specify a contact angle is to declare face 6 to be constrained to stay on the tabletop and give it a surface tension different than the default of 1. But this leads to problems described below. The way the contact angle is handled instead is to omit face 6 and give the edges around face 6 an energy integrand that results in the same energy we would get if we did include face 6. If we let the interface energy density for face 6 be T, then we want a vectorfield w such that

      
            /             /
            |  T k . dS = | w . dl
            / face 6      / bdry of face 6
    
    
    So by Green's Theorem, all we need is curl w = Tk, and I will use w = -Tyi. Here i j k are the standard unit basis vectors. In practice, I don't think about Green's Theorem as such; I just write down a line integral that sums up strips of surface.

    I have chosen to parameterize the contact angle as the angle in degrees between the table and the surface on the interior of the drop. This angle can be adjusted by assigning a new value to the variable "angle" at runtime. I could have made WALLT the parameter directly, but then I wouldn't have had an excuse to show a macro.

    mound skeleton The initial mound skeleton, with vertices and edges numbered.
    Here is the datafile mound.fe:

    // mound.fe
    // Evolver data for drop of prescribed volume sitting on plane with gravity.
    // Contact angle with plane can be varied.
    
    PARAMETER angle = 90    // interior angle between plane and surface, degrees
    
    gravity_constant 0  // start with gravity off
    
    #define WALLT  (-cos(angle*pi/180))  // virtual tension of facet on plane
     
    constraint 1   /* the table top */
    formula: x3 = 0
    energy:  // for contact angle
    e1: -(WALLT*y)
    e2: 0
    e3: 0 
    
    vertices
    1   0.0  0.0 0.0  constraint 1  /* 4 vertices on plane */
    2   1.0  0.0 0.0  constraint 1
    3   1.0  1.0 0.0  constraint 1
    4   0.0  1.0 0.0  constraint 1
    5   0.0  0.0 1.0
    6   1.0  0.0 1.0
    7   1.0  1.0 1.0
    8   0.0  1.0 1.0
    9   2.0  2.0 0.0  fixed   /* for table top */
    10  2.0 -1.0 0.0  fixed
    11 -1.0 -1.0 0.0  fixed
    12 -1.0  2.0 0.0  fixed
    
    edges  /* given by endpoints and attribute */
    1   1 2    constraint 1 /* 4 edges on plane */
    2   2 3    constraint 1
    3   3 4    constraint 1
    4   4 1    constraint 1
    5   5 6
    6   6 7  
    7   7 8 
    8   8 5
    9   1 5   
    10  2 6  
    11  3 7 
    12  4 8
    13  9 10   fixed  /* for table top */
    14 10 11   fixed
    15 11 12   fixed
    16 12  9   fixed
    
    faces  /* given by oriented edge loop */
    1   1 10 -5  -9
    2   2 11 -6 -10
    3   3 12 -7 -11
    4   4  9 -8 -12
    5   5  6  7   8
    7  13 14 15  16  density 0 fixed /* table top for display */
    
    bodies  /* one body, defined by its oriented faces */
    1   1 2 3 4 5   volume 1  density 1
    
    read
    re := refine edges where on_constraint 1
    
    The mound itself was basically copied from cube.fe, but with face 6 deleted. The reason for this is that face 6 is not needed, and would actually get in the way. It is not needed for the volume calculation since it would always be at z = 0 and thus not contribute to the surface integral for volume. The bottom edges of the side faces are constrained to lie in the plane z = 0, so face 6 is not needed to keep them from catastrophically shrivelling up. We could have handled the contact angle by including face 6 with a surface tension equal to the interface energy density between the liquid and surface, but that can cause problems if the edges around face 6 try to migrate inward. After refinement a couple of times, interior vertices of the original face 6 have no forces acting on them, so they don't move. Hence it would be tough for face 6 to shrink when its outer vertices ran up against its inner vertices. The tabletop face, face 7, is entirely extraneous to the calculations. Its only purpose is to make a nice display. You could remove it and all its vertices and edges without affecting the shape of the mound. It's constraint 1 that is the tabletop as far as the mound is concerned. To see what happens with the bottom face present, load moundB.fe and do "run".

    Now run Evolver on mound.fe. The command "re" defined at the end of the datafile is good to use first in order to refine some edges that need it. Refine and iterate a while. You should get a nice mound. It's not a hemisphere, since gravity is on by default with G = 1. If you use the G command to set "G 0" and iterate a while, you get a hemisphere. Try changing the contact angle, to 45 degrees (with the command "angle := 45"} or 135 degrees for example. You can also play with gravity. Set "G 10" to get a flattened drop, or "G -5" to get a drop hanging from the ceiling. "G -10" will cause the drop to try to break loose, but it can't, since its vertices are still constrained.


    Catenoid example. Back to top of tutorial.
    Back to top of Evolver documentation. Index.